summaryrefslogtreecommitdiff
path: root/src/engine
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine')
-rw-r--r--src/engine/3dgui.cpp2534
-rw-r--r--src/engine/animmodel.h3165
-rw-r--r--src/engine/bih.cpp572
-rw-r--r--src/engine/bih.h146
-rw-r--r--src/engine/blend.cpp1168
-rw-r--r--src/engine/client.cpp354
-rw-r--r--src/engine/command.cpp5030
-rw-r--r--src/engine/console.cpp1110
-rw-r--r--src/engine/decal.cpp1168
-rw-r--r--src/engine/depthfx.h252
-rw-r--r--src/engine/dynlight.cpp338
-rw-r--r--src/engine/engine.h79
-rw-r--r--src/engine/explosion.h474
-rw-r--r--src/engine/glare.cpp50
-rw-r--r--src/engine/iqm.h708
-rw-r--r--src/engine/lightmap.cpp4485
-rw-r--r--src/engine/lightmap.h146
-rw-r--r--src/engine/lightning.h178
-rw-r--r--src/engine/main.cpp2094
-rw-r--r--src/engine/master.cpp1040
-rw-r--r--src/engine/material.cpp1491
-rw-r--r--src/engine/md3.h302
-rw-r--r--src/engine/md5.h792
-rw-r--r--src/engine/menus.cpp992
-rw-r--r--src/engine/model.h152
-rw-r--r--src/engine/mpr.h1138
-rw-r--r--src/engine/normal.cpp588
-rw-r--r--src/engine/octa.cpp2878
-rw-r--r--src/engine/octa.h364
-rw-r--r--src/engine/octaedit.cpp4221
-rw-r--r--src/engine/octarender.cpp2833
-rw-r--r--src/engine/physics.cpp3244
-rw-r--r--src/engine/ragdoll.h916
-rw-r--r--src/engine/rendergl.cpp2997
-rw-r--r--src/engine/rendermodel.cpp1434
-rw-r--r--src/engine/renderparticles.cpp2402
-rw-r--r--src/engine/rendersky.cpp1230
-rw-r--r--src/engine/rendertarget.h766
-rw-r--r--src/engine/rendertext.cpp550
-rw-r--r--src/engine/renderva.cpp2856
-rw-r--r--src/engine/server.cpp1128
-rw-r--r--src/engine/serverbrowser.cpp1160
-rw-r--r--src/engine/shader.cpp2373
-rw-r--r--src/engine/shadowmap.cpp400
-rw-r--r--src/engine/skelmodel.h3664
-rw-r--r--src/engine/sound.cpp1278
-rw-r--r--src/engine/textedit.h1421
-rw-r--r--src/engine/texture.cpp4963
-rw-r--r--src/engine/texture.h1227
-rw-r--r--src/engine/vertmodel.h956
-rw-r--r--src/engine/water.cpp1670
-rw-r--r--src/engine/world.cpp1895
-rw-r--r--src/engine/world.h72
-rw-r--r--src/engine/worldio.cpp2307
54 files changed, 40465 insertions, 41286 deletions
diff --git a/src/engine/3dgui.cpp b/src/engine/3dgui.cpp
index cc9d61b..41a5d47 100644
--- a/src/engine/3dgui.cpp
+++ b/src/engine/3dgui.cpp
@@ -38,1118 +38,1118 @@ static int lastpreview = 0;
static inline bool throttlepreview(bool loaded)
{
- if(loaded) return true;
- if(totalmillis - lastpreview < guipreviewtime) return false;
- lastpreview = totalmillis;
- return true;
+ if(loaded) return true;
+ if(totalmillis - lastpreview < guipreviewtime) return false;
+ lastpreview = totalmillis;
+ return true;
}
struct gui : g3d_gui
{
- struct list
- {
- int parent, w, h, springs, curspring, column;
- };
-
- int firstlist, nextlist;
- int columns[MAXCOLUMNS];
-
- static vector<list> lists;
- static float hitx, hity;
- static int curdepth, curlist, xsize, ysize, curx, cury;
- static bool shouldmergehits, shouldautotab;
-
- static void reset()
- {
- lists.setsize(0);
- }
-
- static int ty, tx, tpos, *tcurrent, tcolor; //tracking tab size and position since uses different layout method...
-
- bool allowautotab(bool on)
- {
- bool oldval = shouldautotab;
- shouldautotab = on;
- return oldval;
- }
-
- void autotab()
- {
- if(tcurrent)
- {
- if(layoutpass && !tpos) tcurrent = NULL; //disable tabs because you didn't start with one
- if(shouldautotab && !curdepth && (layoutpass ? 0 : cury) + ysize > guiautotab*FONTH) tab(NULL, tcolor);
- }
- }
-
- bool shouldtab()
- {
- if(tcurrent && shouldautotab)
- {
- if(layoutpass)
- {
- int space = guiautotab*FONTH - ysize;
- if(space < 0) return true;
- int l = lists[curlist].parent;
- while(l >= 0)
- {
- space -= lists[l].h;
- if(space < 0) return true;
- l = lists[l].parent;
- }
- }
- else
- {
- int space = guiautotab*FONTH - cury;
- if(ysize > space) return true;
- int l = lists[curlist].parent;
- while(l >= 0)
- {
- if(lists[l].h > space) return true;
- l = lists[l].parent;
- }
- }
- }
- return false;
- }
-
- bool visible() { return (!tcurrent || tpos==*tcurrent) && !layoutpass; }
-
- //tab is always at top of page
- void tab(const char *name, int color)
- {
- if(curdepth != 0) return;
- if(color) tcolor = color;
- tpos++;
- if(!name) name = intstr(tpos);
- int w = max(text_width(name) - 2*INSERT, 0);
- if(layoutpass)
- {
- ty = max(ty, ysize);
- ysize = 0;
- }
- else
- {
- cury = -ysize;
- int h = FONTH-2*INSERT,
- x1 = curx + tx,
- x2 = x1 + w + ((skinx[3]-skinx[2]) + (skinx[5]-skinx[4]))*SKIN_SCALE,
- y1 = cury - ((skiny[6]-skiny[1])-(skiny[3]-skiny[2]))*SKIN_SCALE-h,
- y2 = cury;
- bool hit = tcurrent && windowhit==this && hitx>=x1 && hity>=y1 && hitx<x2 && hity<y2;
- if(hit && (!guiclicktab || mousebuttons&G3D_DOWN))
- *tcurrent = tpos; //roll-over to switch tab
-
- drawskin(x1-skinx[visible()?2:6]*SKIN_SCALE, y1-skiny[1]*SKIN_SCALE, w, h, visible()?10:19, 9, gui2d ? 1 : 2, light, alpha);
- text_(name, x1 + (skinx[3]-skinx[2])*SKIN_SCALE - (w ? INSERT : INSERT/2), y1 + (skiny[2]-skiny[1])*SKIN_SCALE - INSERT, tcolor, visible());
- }
- tx += w + ((skinx[5]-skinx[4]) + (skinx[3]-skinx[2]))*SKIN_SCALE;
- }
-
- bool ishorizontal() const { return curdepth&1; }
- bool isvertical() const { return !ishorizontal(); }
-
- void pushlist()
- {
- if(layoutpass)
- {
- if(curlist>=0)
- {
- lists[curlist].w = xsize;
- lists[curlist].h = ysize;
- }
- list &l = lists.add();
- l.parent = curlist;
- l.springs = 0;
- l.column = -1;
- curlist = lists.length()-1;
- xsize = ysize = 0;
- }
- else
- {
- curlist = nextlist++;
- if(curlist >= lists.length()) // should never get here unless script code doesn't use same amount of lists in layout and render passes
- {
- list &l = lists.add();
- l.parent = curlist;
- l.springs = 0;
- l.column = -1;
- l.w = l.h = 0;
- }
- list &l = lists[curlist];
- l.curspring = 0;
- if(l.springs > 0)
- {
- if(ishorizontal()) xsize = l.w; else ysize = l.h;
- }
- else
- {
- xsize = l.w;
- ysize = l.h;
- }
- }
- curdepth++;
- }
-
- void poplist()
- {
- if(!lists.inrange(curlist)) return;
- list &l = lists[curlist];
- if(layoutpass)
- {
- l.w = xsize;
- l.h = ysize;
- if(l.column >= 0) columns[l.column] = max(columns[l.column], ishorizontal() ? ysize : xsize);
- }
- curlist = l.parent;
- curdepth--;
- if(lists.inrange(curlist))
- {
- int w = xsize, h = ysize;
- if(ishorizontal()) cury -= h; else curx -= w;
- list &p = lists[curlist];
- xsize = p.w;
- ysize = p.h;
- if(!layoutpass && p.springs > 0)
- {
- list &s = lists[p.parent];
- if(ishorizontal()) xsize = s.w; else ysize = s.h;
- }
- layout(w, h);
- }
- }
-
- int text (const char *text, int color, const char *icon) { autotab(); return button_(text, color, icon, false, false); }
- int button(const char *text, int color, const char *icon) { autotab(); return button_(text, color, icon, true, false); }
- int title (const char *text, int color, const char *icon) { autotab(); return button_(text, color, icon, false, true); }
-
- void separator() { autotab(); line_(FONTH/3); }
- void progress(float percent) { autotab(); line_((FONTH*4)/5, percent); }
-
- //use to set min size (useful when you have progress bars)
- void strut(float size) { layout(isvertical() ? int(size*FONTW) : 0, isvertical() ? 0 : int(size*FONTH)); }
- //add space between list items
- void space(float size) { layout(isvertical() ? 0 : int(size*FONTW), isvertical() ? int(size*FONTH) : 0); }
-
- void spring(int weight)
- {
- if(curlist < 0) return;
- list &l = lists[curlist];
- if(layoutpass) { if(l.parent >= 0) l.springs += weight; return; }
- int nextspring = min(l.curspring + weight, l.springs);
- if(nextspring <= l.curspring) return;
- if(ishorizontal())
- {
- int w = xsize - l.w;
- layout((w*nextspring)/l.springs - (w*l.curspring)/l.springs, 0);
- }
- else
- {
- int h = ysize - l.h;
- layout(0, (h*nextspring)/l.springs - (h*l.curspring)/l.springs);
- }
- l.curspring = nextspring;
- }
-
- void column(int col)
- {
- if(curlist < 0 || !layoutpass || col < 0 || col >= MAXCOLUMNS) return;
- list &l = lists[curlist];
- l.column = col;
- }
-
- int layout(int w, int h)
- {
- if(layoutpass)
- {
- if(ishorizontal())
- {
- xsize += w;
- ysize = max(ysize, h);
- }
- else
- {
- xsize = max(xsize, w);
- ysize += h;
- }
- return 0;
- }
- else
- {
- bool hit = ishit(w, h);
- if(ishorizontal()) curx += w;
- else cury += h;
- return (hit && visible()) ? mousebuttons|G3D_ROLLOVER : 0;
- }
- }
-
- bool mergehits(bool on)
- {
- bool oldval = shouldmergehits;
- shouldmergehits = on;
- return oldval;
- }
-
- bool ishit(int w, int h, int x = curx, int y = cury)
- {
- if(shouldmergehits) return windowhit==this && (ishorizontal() ? hitx>=x && hitx<x+w : hity>=y && hity<y+h);
- if(ishorizontal()) h = ysize;
- else w = xsize;
- return windowhit==this && hitx>=x && hity>=y && hitx<x+w && hity<y+h;
- }
-
- int image(Texture *t, float scale, const char *overlaid)
- {
- autotab();
- if(scale==0) scale = 1;
- int size = (int)(scale*2*FONTH)-SHADOW;
- if(visible()) icon_(t, overlaid!=NULL, curx, cury, size, ishit(size+SHADOW, size+SHADOW), overlaid);
- return layout(size+SHADOW, size+SHADOW);
- }
-
- int texture(VSlot &vslot, float scale, bool overlaid)
- {
- autotab();
- if(scale==0) scale = 1;
- int size = (int)(scale*2*FONTH)-SHADOW;
- if(visible()) previewslot(vslot, overlaid, curx, cury, size, ishit(size+SHADOW, size+SHADOW));
- return layout(size+SHADOW, size+SHADOW);
- }
-
- int playerpreview(int model, int team, int weap, float sizescale, const char *overlaid)
- {
- autotab();
- if(sizescale==0) sizescale = 1;
- int size = (int)(sizescale*2*FONTH)-SHADOW;
- if(model>=0 && visible())
- {
- bool hit = ishit(size+SHADOW, size+SHADOW);
- float xs = size, ys = size, xi = curx, yi = cury;
- if(overlaid && hit && actionon)
- {
- hudnotextureshader->set();
- gle::colorf(0, 0, 0, 0.75f);
- rect_(xi+SHADOW, yi+SHADOW, xs, ys);
- hudshader->set();
- }
- int x1 = int(floor(screenw*(xi*scale.x+origin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*scale.y+origin.y)))),
- x2 = int(ceil(screenw*((xi+xs)*scale.x+origin.x))), y2 = int(ceil(screenh*(1 - (yi*scale.y+origin.y))));
- glDisable(GL_BLEND);
- modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid!=NULL);
- game::renderplayerpreview(model, team, weap);
- modelpreview::end();
- hudshader->set();
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_BLEND);
- if(overlaid)
- {
- if(hit)
- {
- hudnotextureshader->set();
- glBlendFunc(GL_ZERO, GL_SRC_COLOR);
- gle::colorf(1, 0.5f, 0.5f);
- rect_(xi, yi, xs, ys);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- hudshader->set();
- }
- if(overlaid[0]) text_(overlaid, xi + xs/12, yi + ys - ys/12 - FONTH, hit ? 0xFF0000 : 0xFFFFFF, hit, hit);
- if(!overlaytex) overlaytex = textureload("data/guioverlay.png", 3);
- gle::color(light);
- glBindTexture(GL_TEXTURE_2D, overlaytex->id);
- rect_(xi, yi, xs, ys, 0);
- }
- }
- return layout(size+SHADOW, size+SHADOW);
- }
-
- int modelpreview(const char *name, int anim, float sizescale, const char *overlaid, bool throttle)
- {
- autotab();
- if(sizescale==0) sizescale = 1;
- int size = (int)(sizescale*2*FONTH)-SHADOW;
- if(name[0] && visible() && (!throttle || throttlepreview(modelloaded(name))))
- {
- bool hit = ishit(size+SHADOW, size+SHADOW);
- float xs = size, ys = size, xi = curx, yi = cury;
- if(overlaid && hit && actionon)
- {
- hudnotextureshader->set();
- gle::colorf(0, 0, 0, 0.75f);
- rect_(xi+SHADOW, yi+SHADOW, xs, ys);
- hudshader->set();
- }
- int x1 = int(floor(screenw*(xi*scale.x+origin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*scale.y+origin.y)))),
- x2 = int(ceil(screenw*((xi+xs)*scale.x+origin.x))), y2 = int(ceil(screenh*(1 - (yi*scale.y+origin.y))));
- glDisable(GL_BLEND);
- modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid!=NULL);
- model *m = loadmodel(name);
- if(m)
- {
- entitylight light;
- light.color = vec(1, 1, 1);
- light.dir = vec(0, -1, 2).normalize();
- vec center, radius;
- m->boundbox(center, radius);
- float yaw;
- vec o = calcmodelpreviewpos(radius, yaw).sub(center);
- rendermodel(&light, name, anim, o, yaw, 0, 0, NULL, NULL, 0);
- }
- modelpreview::end();
- hudshader->set();
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_BLEND);
- if(overlaid)
- {
- if(hit)
- {
- hudnotextureshader->set();
- glBlendFunc(GL_ZERO, GL_SRC_COLOR);
- gle::colorf(1, 0.5f, 0.5f);
- rect_(xi, yi, xs, ys);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- hudshader->set();
- }
- if(overlaid[0]) text_(overlaid, xi + xs/12, yi + ys - ys/12 - FONTH, hit ? 0xFF0000 : 0xFFFFFF, hit, hit);
- if(!overlaytex) overlaytex = textureload("data/guioverlay.png", 3);
- gle::color(light);
- glBindTexture(GL_TEXTURE_2D, overlaytex->id);
- rect_(xi, yi, xs, ys, 0);
- }
- }
- return layout(size+SHADOW, size+SHADOW);
- }
-
- int prefabpreview(const char *prefab, const vec &color, float sizescale, const char *overlaid, bool throttle)
- {
- autotab();
- if(sizescale==0) sizescale = 1;
- int size = (int)(sizescale*2*FONTH)-SHADOW;
- if(prefab[0] && visible() && (!throttle || throttlepreview(prefabloaded(prefab))))
- {
- bool hit = ishit(size+SHADOW, size+SHADOW);
- float xs = size, ys = size, xi = curx, yi = cury;
- if(overlaid && hit && actionon)
- {
- hudnotextureshader->set();
- gle::colorf(0, 0, 0, 0.75f);
- rect_(xi+SHADOW, yi+SHADOW, xs, ys);
- hudshader->set();
- }
- int x1 = int(floor(screenw*(xi*scale.x+origin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*scale.y+origin.y)))),
- x2 = int(ceil(screenw*((xi+xs)*scale.x+origin.x))), y2 = int(ceil(screenh*(1 - (yi*scale.y+origin.y))));
- glDisable(GL_BLEND);
- modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid!=NULL);
- previewprefab(prefab, color);
- modelpreview::end();
- hudshader->set();
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_BLEND);
- if(overlaid)
- {
- if(hit)
- {
- hudnotextureshader->set();
- glBlendFunc(GL_ZERO, GL_SRC_COLOR);
- gle::colorf(1, 0.5f, 0.5f);
- rect_(xi, yi, xs, ys);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- hudshader->set();
- }
- if(overlaid[0]) text_(overlaid, xi + FONTH/2, yi + FONTH/2, hit ? 0xFF0000 : 0xFFFFFF, hit, hit);
- if(!overlaytex) overlaytex = textureload("data/guioverlay.png", 3);
- gle::color(light);
- glBindTexture(GL_TEXTURE_2D, overlaytex->id);
- rect_(xi, yi, xs, ys, 0);
- }
- }
- return layout(size+SHADOW, size+SHADOW);
- }
-
- void slider(int &val, int vmin, int vmax, int color, const char *label)
- {
- autotab();
- int x = curx;
- int y = cury;
- line_((FONTH*2)/3);
- if(visible())
- {
- if(!label) label = intstr(val);
- int w = text_width(label);
-
- bool hit;
- int px, py, offset = vmin < vmax ? clamp(val, vmin, vmax) : clamp(val, vmax, vmin);
- if(ishorizontal())
- {
- hit = ishit(FONTH, ysize, x, y);
- px = x + (FONTH-w)/2;
- py = y + (ysize-FONTH) - ((ysize-FONTH)*(offset-vmin))/((vmax==vmin) ? 1 : (vmax-vmin)); //vmin at bottom
- }
- else
- {
- hit = ishit(xsize, FONTH, x, y);
- px = x + FONTH/2 - w/2 + ((xsize-w)*(offset-vmin))/((vmax==vmin) ? 1 : (vmax-vmin)); //vmin at left
- py = y;
- }
-
- if(hit) color = 0xFF0000;
- text_(label, px, py, color, hit && actionon, hit);
- if(hit && actionon)
- {
- int vnew = (vmin < vmax ? 1 : -1)+vmax-vmin;
- if(ishorizontal()) vnew = int((vnew*(y+ysize-FONTH/2-hity))/(ysize-FONTH));
- else vnew = int((vnew*(hitx-x-FONTH/2))/(xsize-w));
- vnew += vmin;
- vnew = vmin < vmax ? clamp(vnew, vmin, vmax) : clamp(vnew, vmax, vmin);
- if(vnew != val) val = vnew;
- }
- }
- }
-
- char *field(const char *name, int color, int length, int height, const char *initval, int initmode)
- {
- return field_(name, color, length, height, initval, initmode, FIELDEDIT);
- }
-
- char *keyfield(const char *name, int color, int length, int height, const char *initval, int initmode)
- {
- return field_(name, color, length, height, initval, initmode, FIELDKEY);
- }
-
- char *field_(const char *name, int color, int length, int height, const char *initval, int initmode, int fieldtype = FIELDEDIT)
- {
- editor *e = useeditor(name, initmode, false, initval); // generate a new editor if necessary
- if(layoutpass)
- {
- if(initval && e->mode==EDITORFOCUSED && (e!=currentfocus() || fieldmode == FIELDSHOW))
- {
- if(strcmp(e->lines[0].text, initval)) e->clear(initval);
- }
- e->linewrap = (length<0);
- e->maxx = (e->linewrap) ? -1 : length;
- e->maxy = (height<=0)?1:-1;
- e->pixelwidth = abs(length)*FONTW;
- if(e->linewrap && e->maxy==1)
- {
- int temp;
- text_bounds(e->lines[0].text, temp, e->pixelheight, e->pixelwidth); //only single line editors can have variable height
- }
- else
- e->pixelheight = FONTH*max(height, 1);
- }
- int h = e->pixelheight;
- int w = e->pixelwidth + FONTW;
-
- bool wasvertical = isvertical();
- if(wasvertical && e->maxy != 1) pushlist();
-
- char *result = NULL;
- if(visible() && !layoutpass)
- {
- e->rendered = true;
-
- bool hit = ishit(w, h);
- if(hit)
- {
- if(mousebuttons&G3D_DOWN) //mouse request focus
- {
- if(fieldtype==FIELDKEY) e->clear();
- useeditor(name, initmode, true);
- e->mark(false);
- fieldmode = fieldtype;
- }
- }
- bool editing = (fieldmode != FIELDSHOW) && (e==currentfocus());
- if(hit && editing && (mousebuttons&G3D_PRESSED)!=0 && fieldtype==FIELDEDIT) e->hit(int(floor(hitx-(curx+FONTW/2))), int(floor(hity-cury)), (mousebuttons&G3D_DRAGGED)!=0); //mouse request position
- if(editing && ((fieldmode==FIELDCOMMIT) || (fieldmode==FIELDABORT) || !hit)) // commit field if user pressed enter or wandered out of focus
- {
- if(fieldmode==FIELDCOMMIT || (fieldmode!=FIELDABORT && !hit)) result = e->currentline().text;
- e->active = (e->mode!=EDITORFOCUSED);
- fieldmode = FIELDSHOW;
- }
- else fieldsactive = true;
-
- e->draw(curx+FONTW/2, cury, color, hit && editing);
-
- hudnotextureshader->set();
- glDisable(GL_BLEND);
- if(editing) gle::colorf(1, 0, 0);
- else gle::colorub(color>>16, (color>>8)&0xFF, color&0xFF);
- rect_(curx, cury, w, h, true);
- glEnable(GL_BLEND);
- hudshader->set();
- }
- layout(w, h);
-
- if(e->maxy != 1)
- {
- int slines = e->limitscrolly();
- if(slines > 0)
- {
- int pos = e->scrolly;
- slider(e->scrolly, slines, 0, color, NULL);
- if(pos != e->scrolly) e->cy = e->scrolly;
- }
- if(wasvertical) poplist();
- }
-
- return result;
- }
-
- void rect_(float x, float y, float w, float h, bool lines = false)
- {
- gle::defvertex(2);
- gle::begin(lines ? GL_LINE_LOOP : GL_TRIANGLE_STRIP);
- gle::attribf(x, y);
- gle::attribf(x + w, y);
- if(lines) gle::attribf(x + w, y + h);
- gle::attribf(x, y + h);
- if(!lines) gle::attribf(x + w, y + h);
- xtraverts += gle::end();
- }
-
- void rect_(float x, float y, float w, float h, int usetc)
- {
- gle::defvertex(2);
- gle::deftexcoord0();
- gle::begin(GL_TRIANGLE_STRIP);
- static const vec2 tc[5] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1), vec2(0, 0) };
- gle::attribf(x, y); gle::attrib(tc[usetc]);
- gle::attribf(x + w, y); gle::attrib(tc[usetc+1]);
- gle::attribf(x, y + h); gle::attrib(tc[usetc+3]);
- gle::attribf(x + w, y + h); gle::attrib(tc[usetc+2]);
- xtraverts += gle::end();
- }
-
- void text_(const char *text, int x, int y, int color, bool shadow, bool force = false)
- {
- if(shadow) draw_text(text, x+SHADOW, y+SHADOW, 0x00, 0x00, 0x00, -0xC0);
- draw_text(text, x, y, color>>16, (color>>8)&0xFF, color&0xFF, force ? -0xFF : 0xFF);
- }
-
- void background(int color, int inheritw, int inherith)
- {
- if(layoutpass) return;
- hudnotextureshader->set();
- gle::colorub(color>>16, (color>>8)&0xFF, color&0xFF, 0x80);
- int w = xsize, h = ysize;
- if(inheritw>0)
- {
- int parentw = curlist, parentdepth = 0;
- for(;parentdepth < inheritw && lists[parentw].parent>=0; parentdepth++)
- parentw = lists[parentw].parent;
- list &p = lists[parentw];
- w = p.springs > 0 && (curdepth-parentdepth)&1 ? lists[p.parent].w : p.w;
- }
- if(inherith>0)
- {
- int parenth = curlist, parentdepth = 0;
- for(;parentdepth < inherith && lists[parenth].parent>=0; parentdepth++)
- parenth = lists[parenth].parent;
- list &p = lists[parenth];
- h = p.springs > 0 && !((curdepth-parentdepth)&1) ? lists[p.parent].h : p.h;
- }
- rect_(curx, cury, w, h);
- hudshader->set();
- }
-
- void icon_(Texture *t, bool overlaid, int x, int y, int size, bool hit, const char *title = NULL)
- {
- float scale = float(size)/max(t->xs, t->ys); //scale and preserve aspect ratio
- float xs = t->xs*scale, ys = t->ys*scale;
- x += int((size-xs)/2);
- y += int((size-ys)/2);
- const vec &color = hit ? vec(1, 0.5f, 0.5f) : (overlaid ? vec(1, 1, 1) : light);
- glBindTexture(GL_TEXTURE_2D, t->id);
- if(hit && actionon)
- {
- gle::colorf(0, 0, 0, 0.75f);
- rect_(x+SHADOW, y+SHADOW, xs, ys, 0);
- }
- gle::color(color);
- rect_(x, y, xs, ys, 0);
-
- if(overlaid)
- {
- if(!overlaytex) overlaytex = textureload("data/guioverlay.png", 3);
- glBindTexture(GL_TEXTURE_2D, overlaytex->id);
- gle::color(light);
- rect_(x, y, xs, ys, 0);
- if(title) text_(title, x + xs/12, y + ys - ys/12 - FONTH, hit ? 0xFF0000 : 0xFFFFFF, hit && actionon, hit);
- }
- }
-
- void previewslot(VSlot &vslot, bool overlaid, int x, int y, int size, bool hit)
- {
- Slot &slot = *vslot.slot;
- if(slot.sts.empty()) return;
- VSlot *layer = NULL;
- Texture *t = NULL, *glowtex = NULL, *layertex = NULL;
- if(slot.loaded)
- {
- t = slot.sts[0].t;
- if(t == notexture) return;
- Slot &slot = *vslot.slot;
- if(slot.texmask&(1<<TEX_GLOW)) { loopvj(slot.sts) if(slot.sts[j].type==TEX_GLOW) { glowtex = slot.sts[j].t; break; } }
- if(vslot.layer)
- {
- layer = &lookupvslot(vslot.layer);
- if(!layer->slot->sts.empty()) layertex = layer->slot->sts[0].t;
- }
- }
- else if(slot.thumbnail && slot.thumbnail != notexture) t = slot.thumbnail;
- else return;
- float xt = min(1.0f, t->xs/(float)t->ys), yt = min(1.0f, t->ys/(float)t->xs), xs = size, ys = size;
- if(hit && actionon)
- {
- hudnotextureshader->set();
- gle::colorf(0, 0, 0, 0.75f);
- rect_(x+SHADOW, y+SHADOW, xs, ys);
- hudshader->set();
- }
- SETSHADER(hudrgb);
- gle::defvertex(2);
- gle::deftexcoord0();
- const vec &color = hit ? vec(1, 0.5f, 0.5f) : (overlaid ? vec(1, 1, 1) : light);
- vec2 tc[4] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
- float xoff = vslot.offset.x, yoff = vslot.offset.y;
- if(vslot.rotation)
- {
- const texrotation &r = texrotations[vslot.rotation];
- if(r.swapxy) { swap(xoff, yoff); loopk(4) swap(tc[k].x, tc[k].y); }
- if(r.flipx) { xoff *= -1; loopk(4) tc[k].x *= -1; }
- if(r.flipy) { yoff *= -1; loopk(4) tc[k].y *= -1; }
- }
- loopk(4) { tc[k].x = tc[k].x/xt - xoff/t->xs; tc[k].y = tc[k].y/yt - yoff/t->ys; }
- if(slot.loaded) gle::color(vec(color).mul(vslot.colorscale));
- else gle::color(color);
- glBindTexture(GL_TEXTURE_2D, t->id);
- gle::begin(GL_TRIANGLE_STRIP);
- gle::attribf(x, y); gle::attrib(tc[0]);
- gle::attribf(x+xs, y); gle::attrib(tc[1]);
- gle::attribf(x, y+ys); gle::attrib(tc[3]);
- gle::attribf(x+xs, y+ys); gle::attrib(tc[2]);
- gle::end();
- if(glowtex)
- {
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- glBindTexture(GL_TEXTURE_2D, glowtex->id);
- if(hit || overlaid) gle::color(vec(vslot.glowcolor).mul(color));
- else gle::color(vslot.glowcolor);
- gle::begin(GL_TRIANGLE_STRIP);
- gle::attribf(x, y); gle::attrib(tc[0]);
- gle::attribf(x+xs, y); gle::attrib(tc[1]);
- gle::attribf(x, y+ys); gle::attrib(tc[3]);
- gle::attribf(x+xs, y+ys); gle::attrib(tc[2]);
- gle::end();
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
- if(layertex)
- {
- glBindTexture(GL_TEXTURE_2D, layertex->id);
- gle::color(vec(color).mul(layer->colorscale));
- gle::begin(GL_TRIANGLE_STRIP);
- gle::attribf(x+xs/2, y+ys/2); gle::attrib(tc[0]);
- gle::attribf(x+xs, y+ys/2); gle::attrib(tc[1]);
- gle::attribf(x+xs/2, y+ys); gle::attrib(tc[3]);
- gle::attribf(x+xs, y+ys); gle::attrib(tc[2]);
- gle::end();
- }
-
- hudshader->set();
- if(overlaid)
- {
- if(!overlaytex) overlaytex = textureload("data/guioverlay.png", 3);
- glBindTexture(GL_TEXTURE_2D, overlaytex->id);
- gle::color(light);
- rect_(x, y, xs, ys, 0);
- }
- }
-
- void line_(int size, float percent = 1.0f)
- {
- if(visible())
- {
- if(!slidertex) slidertex = textureload("data/guislider.png", 3);
- glBindTexture(GL_TEXTURE_2D, slidertex->id);
- if(percent < 0.99f)
- {
- gle::colorf(light.x, light.y, light.z, 0.375f);
- if(ishorizontal())
- rect_(curx + FONTH/2 - size/2, cury, size, ysize, 0);
- else
- rect_(curx, cury + FONTH/2 - size/2, xsize, size, 1);
- }
- gle::color(light);
- if(ishorizontal())
- rect_(curx + FONTH/2 - size/2, cury + ysize*(1-percent), size, ysize*percent, 0);
- else
- rect_(curx, cury + FONTH/2 - size/2, xsize*percent, size, 1);
- }
- layout(ishorizontal() ? FONTH : 0, ishorizontal() ? 0 : FONTH);
- }
-
- void textbox(const char *text, int width, int height, int color)
- {
- width *= FONTW;
- height *= FONTH;
- int w, h;
- text_bounds(text, w, h, width);
- if(h > height) height = h;
- if(visible()) draw_text(text, curx, cury, color>>16, (color>>8)&0xFF, color&0xFF, 0xFF, -1, width);
- layout(width, height);
- }
-
- int button_(const char *text, int color, const char *icon, bool clickable, bool center)
- {
- const int padding = 10;
- int w = 0;
- if(icon) w += ICON_SIZE;
- if(icon && text) w += padding;
- if(text) w += text_width(text);
-
- if(visible())
- {
- bool hit = ishit(w, FONTH);
- if(hit && clickable) color = 0xFF0000;
- int x = curx;
- if(isvertical() && center) x += (xsize-w)/2;
-
- if(icon)
- {
- if(icon[0] != ' ')
- {
- const char *ext = strrchr(icon, '.');
- defformatstring(tname, "packages/icons/%s%s", icon, ext ? "" : ".png");
- icon_(textureload(tname, 3), false, x, cury, ICON_SIZE, clickable && hit);
- }
- x += ICON_SIZE;
- }
- if(icon && text) x += padding;
- if(text) text_(text, x, cury, color, center || (hit && clickable && actionon), hit && clickable);
- }
- return layout(w, FONTH);
- }
-
- static Texture *skintex, *overlaytex, *slidertex;
- static const int skinx[], skiny[];
- static const struct patch { ushort left, right, top, bottom; uchar flags; } patches[];
-
- static void drawskin(int x, int y, int gapw, int gaph, int start, int n, int passes = 1, const vec &light = vec(1, 1, 1), float alpha = 0.80f)//int vleft, int vright, int vtop, int vbottom, int start, int n)
- {
- if(!skintex) skintex = textureload("data/guiskin.png", 3);
- glBindTexture(GL_TEXTURE_2D, skintex->id);
- int gapx1 = INT_MAX, gapy1 = INT_MAX, gapx2 = INT_MAX, gapy2 = INT_MAX;
- float wscale = 1.0f/(SKIN_W*SKIN_SCALE), hscale = 1.0f/(SKIN_H*SKIN_SCALE);
-
- loopj(passes)
- {
- bool quads = false;
- if(passes>1) glDepthFunc(j ? GL_LEQUAL : GL_GREATER);
- gle::color(j ? light : vec(1, 1, 1), passes<=1 || j ? alpha : alpha/2); //ghost when its behind something in depth
- loopi(n)
- {
- const patch &p = patches[start+i];
- int left = skinx[p.left]*SKIN_SCALE, right = skinx[p.right]*SKIN_SCALE,
- top = skiny[p.top]*SKIN_SCALE, bottom = skiny[p.bottom]*SKIN_SCALE;
- float tleft = left*wscale, tright = right*wscale,
- ttop = top*hscale, tbottom = bottom*hscale;
- if(p.flags&0x1)
- {
- gapx1 = left;
- gapx2 = right;
- }
- else if(left >= gapx2)
- {
- left += gapw - (gapx2-gapx1);
- right += gapw - (gapx2-gapx1);
- }
- if(p.flags&0x10)
- {
- gapy1 = top;
- gapy2 = bottom;
- }
- else if(top >= gapy2)
- {
- top += gaph - (gapy2-gapy1);
- bottom += gaph - (gapy2-gapy1);
- }
-
- //multiple tiled quads if necessary rather than a single stretched one
- int ystep = bottom-top;
- int yo = y+top;
- while(ystep > 0)
- {
- if(p.flags&0x10 && yo+ystep-(y+top) > gaph)
- {
- ystep = gaph+y+top-yo;
- tbottom = ttop+ystep*hscale;
- }
- int xstep = right-left;
- int xo = x+left;
- float tright2 = tright;
- while(xstep > 0)
- {
- if(p.flags&0x01 && xo+xstep-(x+left) > gapw)
- {
- xstep = gapw+x+left-xo;
- tright = tleft+xstep*wscale;
- }
- if(!quads)
- {
- quads = true;
- gle::defvertex(2);
- gle::deftexcoord0();
- gle::begin(GL_QUADS);
- }
- gle::attribf(xo, yo); gle::attribf(tleft, ttop);
- gle::attribf(xo+xstep, yo); gle::attribf(tright, ttop);
- gle::attribf(xo+xstep, yo+ystep); gle::attribf(tright, tbottom);
- gle::attribf(xo, yo+ystep); gle::attribf(tleft, tbottom);
- if(!(p.flags&0x01)) break;
- xo += xstep;
- }
- tright = tright2;
- if(!(p.flags&0x10)) break;
- yo += ystep;
- }
- }
- if(quads) xtraverts += gle::end();
- else break; //if it didn't happen on the first pass, it won't happen on the second..
- }
- if(passes>1) glDepthFunc(GL_ALWAYS);
- }
-
- vec origin, scale, *savedorigin;
- float dist;
- g3d_callback *cb;
- bool gui2d;
-
- static float basescale, maxscale;
- static bool passthrough;
- static float alpha;
- static vec light;
-
- void adjustscale()
- {
- int w = xsize + (skinx[2]-skinx[1])*SKIN_SCALE + (skinx[10]-skinx[9])*SKIN_SCALE, h = ysize + (skiny[9]-skiny[7])*SKIN_SCALE;
- if(tcurrent) h += ((skiny[5]-skiny[1])-(skiny[3]-skiny[2]))*SKIN_SCALE + FONTH-2*INSERT;
- else h += (skiny[6]-skiny[3])*SKIN_SCALE;
-
- float aspect = forceaspect ? 1.0f/forceaspect : float(screenh)/float(screenw), fit = 1.0f;
- if(w*aspect*basescale>1.0f) fit = 1.0f/(w*aspect*basescale);
- if(h*basescale*fit>maxscale) fit *= maxscale/(h*basescale*fit);
- origin = vec(0.5f-((w-xsize)/2 - (skinx[2]-skinx[1])*SKIN_SCALE)*aspect*scale.x*fit, 0.5f + (0.5f*h-(skiny[9]-skiny[7])*SKIN_SCALE)*scale.y*fit, 0);
- scale = vec(aspect*scale.x*fit, scale.y*fit, 1);
- }
-
- void start(int starttime, float initscale, int *tab, bool allowinput)
- {
- if(gui2d)
- {
- initscale *= 0.025f;
- if(allowinput) hascursor = true;
- }
- basescale = initscale;
- if(layoutpass) scale.x = scale.y = scale.z = guifadein ? basescale*min((totalmillis-starttime)/300.0f, 1.0f) : basescale;
- alpha = allowinput ? 0.80f : 0.60f;
- passthrough = scale.x<basescale || !allowinput;
- curdepth = -1;
- curlist = -1;
- tpos = 0;
- tx = 0;
- ty = 0;
- tcurrent = tab;
- tcolor = 0xFFFFFF;
- pushlist();
- if(layoutpass)
- {
- firstlist = nextlist = curlist;
- memset(columns, 0, sizeof(columns));
- }
- else
- {
- if(tcurrent && !*tcurrent) tcurrent = NULL;
- cury = -ysize;
- curx = -xsize/2;
-
- if(gui2d)
- {
- hudmatrix.ortho(0, 1, 1, 0, -1, 1);
- hudmatrix.translate(origin);
- hudmatrix.scale(scale);
-
- light = vec(1, 1, 1);
- }
- else
- {
- float yaw = atan2f(origin.y-camera1->o.y, origin.x-camera1->o.x);
- hudmatrix = camprojmatrix;
- hudmatrix.translate(origin);
- hudmatrix.rotate_around_z(yaw - 90*RAD);
- hudmatrix.rotate_around_x(-90*RAD);
- hudmatrix.scale(-scale.x, scale.y, scale.z);
-
- vec dir;
- lightreaching(origin, light, dir, false, 0, 0.5f);
- float intensity = vec(yaw, 0.0f).dot(dir);
- light.mul(1.0f + max(intensity, 0.0f));
- }
-
- resethudmatrix();
- hudshader->set();
-
- drawskin(curx-skinx[2]*SKIN_SCALE, cury-skiny[6]*SKIN_SCALE, xsize, ysize, 0, 9, gui2d ? 1 : 2, light, alpha);
- if(!tcurrent) drawskin(curx-skinx[5]*SKIN_SCALE, cury-skiny[6]*SKIN_SCALE, xsize, 0, 9, 1, gui2d ? 1 : 2, light, alpha);
- }
- }
-
- void adjusthorizontalcolumn(int col, int i)
- {
- int h = columns[col], dh = 0;
- for(int d = 1; i >= 0; d ^= 1)
- {
- list &p = lists[i];
- if(d&1) { dh = h - p.h; if(dh <= 0) break; p.h = h; }
- else { p.h += dh; h = p.h; }
- i = p.parent;
- }
- ysize += max(dh, 0);
- }
-
- void adjustverticalcolumn(int col, int i)
- {
- int w = columns[col], dw = 0;
- for(int d = 0; i >= 0; d ^= 1)
- {
- list &p = lists[i];
- if(d&1) { p.w += dw; w = p.w; }
- else { dw = w - p.w; if(dw <= 0) break; p.w = w; }
- i = p.parent;
- }
- xsize = max(xsize, w);
- }
-
- void adjustcolumns()
- {
- if(lists.inrange(curlist))
- {
- list &l = lists[curlist];
- if(l.column >= 0) columns[l.column] = max(columns[l.column], ishorizontal() ? ysize : xsize);
- }
- int parent = -1, depth = 0;
- for(int i = firstlist; i < lists.length(); i++)
- {
- list &l = lists[i];
- if(l.parent > parent) { parent = l.parent; depth++; }
- else if(l.parent < parent)
- {
- while(parent > l.parent && depth > 0)
- {
- parent = lists[parent].parent;
- depth--;
- }
- }
- if(l.column >= 0)
- {
- if(depth&1) adjusthorizontalcolumn(l.column, i);
- else adjustverticalcolumn(l.column, i);
- }
- }
- }
-
- void end()
- {
- if(layoutpass)
- {
- adjustcolumns();
- xsize = max(tx, xsize);
- ysize = max(ty, ysize);
- ysize = max(ysize, (skiny[7]-skiny[6])*SKIN_SCALE);
- if(tcurrent) *tcurrent = max(1, min(*tcurrent, tpos));
- if(gui2d) adjustscale();
- if(!windowhit && !passthrough)
- {
- float dist = 0;
- if(gui2d)
- {
- hitx = (cursorx - origin.x)/scale.x;
- hity = (cursory - origin.y)/scale.y;
- }
- else
- {
- plane p;
- p.toplane(vec(origin).sub(camera1->o).set(2, 0).normalize(), origin);
- if(p.rayintersect(camera1->o, camdir, dist) && dist>=0)
- {
- vec hitpos(camdir);
- hitpos.mul(dist).add(camera1->o).sub(origin);
- hitx = vec(-p.y, p.x, 0).dot(hitpos)/scale.x;
- hity = -hitpos.z/scale.y;
- }
- }
- if((mousebuttons & G3D_PRESSED) && (fabs(hitx-firstx) > 2 || fabs(hity - firsty) > 2)) mousebuttons |= G3D_DRAGGED;
- if(dist>=0 && hitx>=-xsize/2 && hitx<=xsize/2 && hity<=0)
- {
- if(hity>=-ysize || (tcurrent && hity>=-ysize-(FONTH-2*INSERT)-((skiny[6]-skiny[1])-(skiny[3]-skiny[2]))*SKIN_SCALE && hitx<=tx-xsize/2))
- windowhit = this;
- }
- }
- }
- else
- {
- if(tcurrent && tx<xsize) drawskin(curx+tx-skinx[5]*SKIN_SCALE, -ysize-skiny[6]*SKIN_SCALE, xsize-tx, FONTH, 9, 1, gui2d ? 1 : 2, light, alpha);
- }
- poplist();
- }
-
- void draw()
- {
- cb->gui(*this, layoutpass);
- }
+ struct list
+ {
+ int parent, w, h, springs, curspring, column;
+ };
+
+ int firstlist, nextlist;
+ int columns[MAXCOLUMNS];
+
+ static vector<list> lists;
+ static float hitx, hity;
+ static int curdepth, curlist, xsize, ysize, curx, cury;
+ static bool shouldmergehits, shouldautotab;
+
+ static void reset()
+ {
+ lists.setsize(0);
+ }
+
+ static int ty, tx, tpos, *tcurrent, tcolor; //tracking tab size and position since uses different layout method...
+
+ bool allowautotab(bool on)
+ {
+ bool oldval = shouldautotab;
+ shouldautotab = on;
+ return oldval;
+ }
+
+ void autotab()
+ {
+ if(tcurrent)
+ {
+ if(layoutpass && !tpos) tcurrent = NULL; //disable tabs because you didn't start with one
+ if(shouldautotab && !curdepth && (layoutpass ? 0 : cury) + ysize > guiautotab*FONTH) tab(NULL, tcolor);
+ }
+ }
+
+ bool shouldtab()
+ {
+ if(tcurrent && shouldautotab)
+ {
+ if(layoutpass)
+ {
+ int space = guiautotab*FONTH - ysize;
+ if(space < 0) return true;
+ int l = lists[curlist].parent;
+ while(l >= 0)
+ {
+ space -= lists[l].h;
+ if(space < 0) return true;
+ l = lists[l].parent;
+ }
+ }
+ else
+ {
+ int space = guiautotab*FONTH - cury;
+ if(ysize > space) return true;
+ int l = lists[curlist].parent;
+ while(l >= 0)
+ {
+ if(lists[l].h > space) return true;
+ l = lists[l].parent;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool visible() { return (!tcurrent || tpos==*tcurrent) && !layoutpass; }
+
+ //tab is always at top of page
+ void tab(const char *name, int color)
+ {
+ if(curdepth != 0) return;
+ if(color) tcolor = color;
+ tpos++;
+ if(!name) name = intstr(tpos);
+ int w = max(text_width(name) - 2*INSERT, 0);
+ if(layoutpass)
+ {
+ ty = max(ty, ysize);
+ ysize = 0;
+ }
+ else
+ {
+ cury = -ysize;
+ int h = FONTH-2*INSERT,
+ x1 = curx + tx,
+ x2 = x1 + w + ((skinx[3]-skinx[2]) + (skinx[5]-skinx[4]))*SKIN_SCALE,
+ y1 = cury - ((skiny[6]-skiny[1])-(skiny[3]-skiny[2]))*SKIN_SCALE-h,
+ y2 = cury;
+ bool hit = tcurrent && windowhit==this && hitx>=x1 && hity>=y1 && hitx<x2 && hity<y2;
+ if(hit && (!guiclicktab || mousebuttons&G3D_DOWN))
+ *tcurrent = tpos; //roll-over to switch tab
+
+ drawskin(x1-skinx[visible()?2:6]*SKIN_SCALE, y1-skiny[1]*SKIN_SCALE, w, h, visible()?10:19, 9, gui2d ? 1 : 2, light, alpha);
+ text_(name, x1 + (skinx[3]-skinx[2])*SKIN_SCALE - (w ? INSERT : INSERT/2), y1 + (skiny[2]-skiny[1])*SKIN_SCALE - INSERT, tcolor, visible());
+ }
+ tx += w + ((skinx[5]-skinx[4]) + (skinx[3]-skinx[2]))*SKIN_SCALE;
+ }
+
+ bool ishorizontal() const { return curdepth&1; }
+ bool isvertical() const { return !ishorizontal(); }
+
+ void pushlist()
+ {
+ if(layoutpass)
+ {
+ if(curlist>=0)
+ {
+ lists[curlist].w = xsize;
+ lists[curlist].h = ysize;
+ }
+ list &l = lists.add();
+ l.parent = curlist;
+ l.springs = 0;
+ l.column = -1;
+ curlist = lists.length()-1;
+ xsize = ysize = 0;
+ }
+ else
+ {
+ curlist = nextlist++;
+ if(curlist >= lists.length()) // should never get here unless script code doesn't use same amount of lists in layout and render passes
+ {
+ list &l = lists.add();
+ l.parent = curlist;
+ l.springs = 0;
+ l.column = -1;
+ l.w = l.h = 0;
+ }
+ list &l = lists[curlist];
+ l.curspring = 0;
+ if(l.springs > 0)
+ {
+ if(ishorizontal()) xsize = l.w; else ysize = l.h;
+ }
+ else
+ {
+ xsize = l.w;
+ ysize = l.h;
+ }
+ }
+ curdepth++;
+ }
+
+ void poplist()
+ {
+ if(!lists.inrange(curlist)) return;
+ list &l = lists[curlist];
+ if(layoutpass)
+ {
+ l.w = xsize;
+ l.h = ysize;
+ if(l.column >= 0) columns[l.column] = max(columns[l.column], ishorizontal() ? ysize : xsize);
+ }
+ curlist = l.parent;
+ curdepth--;
+ if(lists.inrange(curlist))
+ {
+ int w = xsize, h = ysize;
+ if(ishorizontal()) cury -= h; else curx -= w;
+ list &p = lists[curlist];
+ xsize = p.w;
+ ysize = p.h;
+ if(!layoutpass && p.springs > 0)
+ {
+ list &s = lists[p.parent];
+ if(ishorizontal()) xsize = s.w; else ysize = s.h;
+ }
+ layout(w, h);
+ }
+ }
+
+ int text (const char *text, int color, const char *icon) { autotab(); return button_(text, color, icon, false, false); }
+ int button(const char *text, int color, const char *icon) { autotab(); return button_(text, color, icon, true, false); }
+ int title (const char *text, int color, const char *icon) { autotab(); return button_(text, color, icon, false, true); }
+
+ void separator() { autotab(); line_(FONTH/3); }
+ void progress(float percent) { autotab(); line_((FONTH*4)/5, percent); }
+
+ //use to set min size (useful when you have progress bars)
+ void strut(float size) { layout(isvertical() ? int(size*FONTW) : 0, isvertical() ? 0 : int(size*FONTH)); }
+ //add space between list items
+ void space(float size) { layout(isvertical() ? 0 : int(size*FONTW), isvertical() ? int(size*FONTH) : 0); }
+
+ void spring(int weight)
+ {
+ if(curlist < 0) return;
+ list &l = lists[curlist];
+ if(layoutpass) { if(l.parent >= 0) l.springs += weight; return; }
+ int nextspring = min(l.curspring + weight, l.springs);
+ if(nextspring <= l.curspring) return;
+ if(ishorizontal())
+ {
+ int w = xsize - l.w;
+ layout((w*nextspring)/l.springs - (w*l.curspring)/l.springs, 0);
+ }
+ else
+ {
+ int h = ysize - l.h;
+ layout(0, (h*nextspring)/l.springs - (h*l.curspring)/l.springs);
+ }
+ l.curspring = nextspring;
+ }
+
+ void column(int col)
+ {
+ if(curlist < 0 || !layoutpass || col < 0 || col >= MAXCOLUMNS) return;
+ list &l = lists[curlist];
+ l.column = col;
+ }
+
+ int layout(int w, int h)
+ {
+ if(layoutpass)
+ {
+ if(ishorizontal())
+ {
+ xsize += w;
+ ysize = max(ysize, h);
+ }
+ else
+ {
+ xsize = max(xsize, w);
+ ysize += h;
+ }
+ return 0;
+ }
+ else
+ {
+ bool hit = ishit(w, h);
+ if(ishorizontal()) curx += w;
+ else cury += h;
+ return (hit && visible()) ? mousebuttons|G3D_ROLLOVER : 0;
+ }
+ }
+
+ bool mergehits(bool on)
+ {
+ bool oldval = shouldmergehits;
+ shouldmergehits = on;
+ return oldval;
+ }
+
+ bool ishit(int w, int h, int x = curx, int y = cury)
+ {
+ if(shouldmergehits) return windowhit==this && (ishorizontal() ? hitx>=x && hitx<x+w : hity>=y && hity<y+h);
+ if(ishorizontal()) h = ysize;
+ else w = xsize;
+ return windowhit==this && hitx>=x && hity>=y && hitx<x+w && hity<y+h;
+ }
+
+ int image(Texture *t, float scale, const char *overlaid)
+ {
+ autotab();
+ if(scale==0) scale = 1;
+ int size = (int)(scale*2*FONTH)-SHADOW;
+ if(visible()) icon_(t, overlaid!=NULL, curx, cury, size, ishit(size+SHADOW, size+SHADOW), overlaid);
+ return layout(size+SHADOW, size+SHADOW);
+ }
+
+ int texture(VSlot &vslot, float scale, bool overlaid)
+ {
+ autotab();
+ if(scale==0) scale = 1;
+ int size = (int)(scale*2*FONTH)-SHADOW;
+ if(visible()) previewslot(vslot, overlaid, curx, cury, size, ishit(size+SHADOW, size+SHADOW));
+ return layout(size+SHADOW, size+SHADOW);
+ }
+
+ int playerpreview(int model, int team, int weap, float sizescale, const char *overlaid)
+ {
+ autotab();
+ if(sizescale==0) sizescale = 1;
+ int size = (int)(sizescale*2*FONTH)-SHADOW;
+ if(model>=0 && visible())
+ {
+ bool hit = ishit(size+SHADOW, size+SHADOW);
+ float xs = size, ys = size, xi = curx, yi = cury;
+ if(overlaid && hit && actionon)
+ {
+ hudnotextureshader->set();
+ gle::colorf(0, 0, 0, 0.75f);
+ rect_(xi+SHADOW, yi+SHADOW, xs, ys);
+ hudshader->set();
+ }
+ int x1 = int(floor(screenw*(xi*scale.x+origin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*scale.y+origin.y)))),
+ x2 = int(ceil(screenw*((xi+xs)*scale.x+origin.x))), y2 = int(ceil(screenh*(1 - (yi*scale.y+origin.y))));
+ glDisable(GL_BLEND);
+ modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid!=NULL);
+ game::renderplayerpreview(model, team, weap);
+ modelpreview::end();
+ hudshader->set();
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ if(overlaid)
+ {
+ if(hit)
+ {
+ hudnotextureshader->set();
+ glBlendFunc(GL_ZERO, GL_SRC_COLOR);
+ gle::colorf(1, 0.5f, 0.5f);
+ rect_(xi, yi, xs, ys);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ hudshader->set();
+ }
+ if(overlaid[0]) text_(overlaid, xi + xs/12, yi + ys - ys/12 - FONTH, hit ? 0xFF0000 : 0xFFFFFF, hit, hit);
+ if(!overlaytex) overlaytex = textureload("data/guioverlay.png", 3);
+ gle::color(light);
+ glBindTexture(GL_TEXTURE_2D, overlaytex->id);
+ rect_(xi, yi, xs, ys, 0);
+ }
+ }
+ return layout(size+SHADOW, size+SHADOW);
+ }
+
+ int modelpreview(const char *name, int anim, float sizescale, const char *overlaid, bool throttle)
+ {
+ autotab();
+ if(sizescale==0) sizescale = 1;
+ int size = (int)(sizescale*2*FONTH)-SHADOW;
+ if(name[0] && visible() && (!throttle || throttlepreview(modelloaded(name))))
+ {
+ bool hit = ishit(size+SHADOW, size+SHADOW);
+ float xs = size, ys = size, xi = curx, yi = cury;
+ if(overlaid && hit && actionon)
+ {
+ hudnotextureshader->set();
+ gle::colorf(0, 0, 0, 0.75f);
+ rect_(xi+SHADOW, yi+SHADOW, xs, ys);
+ hudshader->set();
+ }
+ int x1 = int(floor(screenw*(xi*scale.x+origin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*scale.y+origin.y)))),
+ x2 = int(ceil(screenw*((xi+xs)*scale.x+origin.x))), y2 = int(ceil(screenh*(1 - (yi*scale.y+origin.y))));
+ glDisable(GL_BLEND);
+ modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid!=NULL);
+ model *m = loadmodel(name);
+ if(m)
+ {
+ entitylight light;
+ light.color = vec(1, 1, 1);
+ light.dir = vec(0, -1, 2).normalize();
+ vec center, radius;
+ m->boundbox(center, radius);
+ float yaw;
+ vec o = calcmodelpreviewpos(radius, yaw).sub(center);
+ rendermodel(&light, name, anim, o, yaw, 0, 0, NULL, NULL, 0);
+ }
+ modelpreview::end();
+ hudshader->set();
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ if(overlaid)
+ {
+ if(hit)
+ {
+ hudnotextureshader->set();
+ glBlendFunc(GL_ZERO, GL_SRC_COLOR);
+ gle::colorf(1, 0.5f, 0.5f);
+ rect_(xi, yi, xs, ys);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ hudshader->set();
+ }
+ if(overlaid[0]) text_(overlaid, xi + xs/12, yi + ys - ys/12 - FONTH, hit ? 0xFF0000 : 0xFFFFFF, hit, hit);
+ if(!overlaytex) overlaytex = textureload("data/guioverlay.png", 3);
+ gle::color(light);
+ glBindTexture(GL_TEXTURE_2D, overlaytex->id);
+ rect_(xi, yi, xs, ys, 0);
+ }
+ }
+ return layout(size+SHADOW, size+SHADOW);
+ }
+
+ int prefabpreview(const char *prefab, const vec &color, float sizescale, const char *overlaid, bool throttle)
+ {
+ autotab();
+ if(sizescale==0) sizescale = 1;
+ int size = (int)(sizescale*2*FONTH)-SHADOW;
+ if(prefab[0] && visible() && (!throttle || throttlepreview(prefabloaded(prefab))))
+ {
+ bool hit = ishit(size+SHADOW, size+SHADOW);
+ float xs = size, ys = size, xi = curx, yi = cury;
+ if(overlaid && hit && actionon)
+ {
+ hudnotextureshader->set();
+ gle::colorf(0, 0, 0, 0.75f);
+ rect_(xi+SHADOW, yi+SHADOW, xs, ys);
+ hudshader->set();
+ }
+ int x1 = int(floor(screenw*(xi*scale.x+origin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*scale.y+origin.y)))),
+ x2 = int(ceil(screenw*((xi+xs)*scale.x+origin.x))), y2 = int(ceil(screenh*(1 - (yi*scale.y+origin.y))));
+ glDisable(GL_BLEND);
+ modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid!=NULL);
+ previewprefab(prefab, color);
+ modelpreview::end();
+ hudshader->set();
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ if(overlaid)
+ {
+ if(hit)
+ {
+ hudnotextureshader->set();
+ glBlendFunc(GL_ZERO, GL_SRC_COLOR);
+ gle::colorf(1, 0.5f, 0.5f);
+ rect_(xi, yi, xs, ys);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ hudshader->set();
+ }
+ if(overlaid[0]) text_(overlaid, xi + FONTH/2, yi + FONTH/2, hit ? 0xFF0000 : 0xFFFFFF, hit, hit);
+ if(!overlaytex) overlaytex = textureload("data/guioverlay.png", 3);
+ gle::color(light);
+ glBindTexture(GL_TEXTURE_2D, overlaytex->id);
+ rect_(xi, yi, xs, ys, 0);
+ }
+ }
+ return layout(size+SHADOW, size+SHADOW);
+ }
+
+ void slider(int &val, int vmin, int vmax, int color, const char *label)
+ {
+ autotab();
+ int x = curx;
+ int y = cury;
+ line_((FONTH*2)/3);
+ if(visible())
+ {
+ if(!label) label = intstr(val);
+ int w = text_width(label);
+
+ bool hit;
+ int px, py, offset = vmin < vmax ? clamp(val, vmin, vmax) : clamp(val, vmax, vmin);
+ if(ishorizontal())
+ {
+ hit = ishit(FONTH, ysize, x, y);
+ px = x + (FONTH-w)/2;
+ py = y + (ysize-FONTH) - ((ysize-FONTH)*(offset-vmin))/((vmax==vmin) ? 1 : (vmax-vmin)); //vmin at bottom
+ }
+ else
+ {
+ hit = ishit(xsize, FONTH, x, y);
+ px = x + FONTH/2 - w/2 + ((xsize-w)*(offset-vmin))/((vmax==vmin) ? 1 : (vmax-vmin)); //vmin at left
+ py = y;
+ }
+
+ if(hit) color = 0xFF0000;
+ text_(label, px, py, color, hit && actionon, hit);
+ if(hit && actionon)
+ {
+ int vnew = (vmin < vmax ? 1 : -1)+vmax-vmin;
+ if(ishorizontal()) vnew = int((vnew*(y+ysize-FONTH/2-hity))/(ysize-FONTH));
+ else vnew = int((vnew*(hitx-x-FONTH/2))/(xsize-w));
+ vnew += vmin;
+ vnew = vmin < vmax ? clamp(vnew, vmin, vmax) : clamp(vnew, vmax, vmin);
+ if(vnew != val) val = vnew;
+ }
+ }
+ }
+
+ char *field(const char *name, int color, int length, int height, const char *initval, int initmode)
+ {
+ return field_(name, color, length, height, initval, initmode, FIELDEDIT);
+ }
+
+ char *keyfield(const char *name, int color, int length, int height, const char *initval, int initmode)
+ {
+ return field_(name, color, length, height, initval, initmode, FIELDKEY);
+ }
+
+ char *field_(const char *name, int color, int length, int height, const char *initval, int initmode, int fieldtype = FIELDEDIT)
+ {
+ editor *e = useeditor(name, initmode, false, initval); // generate a new editor if necessary
+ if(layoutpass)
+ {
+ if(initval && e->mode==EDITORFOCUSED && (e!=currentfocus() || fieldmode == FIELDSHOW))
+ {
+ if(strcmp(e->lines[0].text, initval)) e->clear(initval);
+ }
+ e->linewrap = (length<0);
+ e->maxx = (e->linewrap) ? -1 : length;
+ e->maxy = (height<=0)?1:-1;
+ e->pixelwidth = abs(length)*FONTW;
+ if(e->linewrap && e->maxy==1)
+ {
+ int temp;
+ text_bounds(e->lines[0].text, temp, e->pixelheight, e->pixelwidth); //only single line editors can have variable height
+ }
+ else
+ e->pixelheight = FONTH*max(height, 1);
+ }
+ int h = e->pixelheight;
+ int w = e->pixelwidth + FONTW;
+
+ bool wasvertical = isvertical();
+ if(wasvertical && e->maxy != 1) pushlist();
+
+ char *result = NULL;
+ if(visible() && !layoutpass)
+ {
+ e->rendered = true;
+
+ bool hit = ishit(w, h);
+ if(hit)
+ {
+ if(mousebuttons&G3D_DOWN) //mouse request focus
+ {
+ if(fieldtype==FIELDKEY) e->clear();
+ useeditor(name, initmode, true);
+ e->mark(false);
+ fieldmode = fieldtype;
+ }
+ }
+ bool editing = (fieldmode != FIELDSHOW) && (e==currentfocus());
+ if(hit && editing && (mousebuttons&G3D_PRESSED)!=0 && fieldtype==FIELDEDIT) e->hit(int(floor(hitx-(curx+FONTW/2))), int(floor(hity-cury)), (mousebuttons&G3D_DRAGGED)!=0); //mouse request position
+ if(editing && ((fieldmode==FIELDCOMMIT) || (fieldmode==FIELDABORT) || !hit)) // commit field if user pressed enter or wandered out of focus
+ {
+ if(fieldmode==FIELDCOMMIT || (fieldmode!=FIELDABORT && !hit)) result = e->currentline().text;
+ e->active = (e->mode!=EDITORFOCUSED);
+ fieldmode = FIELDSHOW;
+ }
+ else fieldsactive = true;
+
+ e->draw(curx+FONTW/2, cury, color, hit && editing);
+
+ hudnotextureshader->set();
+ glDisable(GL_BLEND);
+ if(editing) gle::colorf(1, 0, 0);
+ else gle::colorub(color>>16, (color>>8)&0xFF, color&0xFF);
+ rect_(curx, cury, w, h, true);
+ glEnable(GL_BLEND);
+ hudshader->set();
+ }
+ layout(w, h);
+
+ if(e->maxy != 1)
+ {
+ int slines = e->limitscrolly();
+ if(slines > 0)
+ {
+ int pos = e->scrolly;
+ slider(e->scrolly, slines, 0, color, NULL);
+ if(pos != e->scrolly) e->cy = e->scrolly;
+ }
+ if(wasvertical) poplist();
+ }
+
+ return result;
+ }
+
+ void rect_(float x, float y, float w, float h, bool lines = false)
+ {
+ gle::defvertex(2);
+ gle::begin(lines ? GL_LINE_LOOP : GL_TRIANGLE_STRIP);
+ gle::attribf(x, y);
+ gle::attribf(x + w, y);
+ if(lines) gle::attribf(x + w, y + h);
+ gle::attribf(x, y + h);
+ if(!lines) gle::attribf(x + w, y + h);
+ xtraverts += gle::end();
+ }
+
+ void rect_(float x, float y, float w, float h, int usetc)
+ {
+ gle::defvertex(2);
+ gle::deftexcoord0();
+ gle::begin(GL_TRIANGLE_STRIP);
+ static const vec2 tc[5] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1), vec2(0, 0) };
+ gle::attribf(x, y); gle::attrib(tc[usetc]);
+ gle::attribf(x + w, y); gle::attrib(tc[usetc+1]);
+ gle::attribf(x, y + h); gle::attrib(tc[usetc+3]);
+ gle::attribf(x + w, y + h); gle::attrib(tc[usetc+2]);
+ xtraverts += gle::end();
+ }
+
+ void text_(const char *text, int x, int y, int color, bool shadow, bool force = false)
+ {
+ if(shadow) draw_text(text, x+SHADOW, y+SHADOW, 0x00, 0x00, 0x00, -0xC0);
+ draw_text(text, x, y, color>>16, (color>>8)&0xFF, color&0xFF, force ? -0xFF : 0xFF);
+ }
+
+ void background(int color, int inheritw, int inherith)
+ {
+ if(layoutpass) return;
+ hudnotextureshader->set();
+ gle::colorub(color>>16, (color>>8)&0xFF, color&0xFF, 0x80);
+ int w = xsize, h = ysize;
+ if(inheritw>0)
+ {
+ int parentw = curlist, parentdepth = 0;
+ for(;parentdepth < inheritw && lists[parentw].parent>=0; parentdepth++)
+ parentw = lists[parentw].parent;
+ list &p = lists[parentw];
+ w = p.springs > 0 && (curdepth-parentdepth)&1 ? lists[p.parent].w : p.w;
+ }
+ if(inherith>0)
+ {
+ int parenth = curlist, parentdepth = 0;
+ for(;parentdepth < inherith && lists[parenth].parent>=0; parentdepth++)
+ parenth = lists[parenth].parent;
+ list &p = lists[parenth];
+ h = p.springs > 0 && !((curdepth-parentdepth)&1) ? lists[p.parent].h : p.h;
+ }
+ rect_(curx, cury, w, h);
+ hudshader->set();
+ }
+
+ void icon_(Texture *t, bool overlaid, int x, int y, int size, bool hit, const char *title = NULL)
+ {
+ float scale = float(size)/max(t->xs, t->ys); //scale and preserve aspect ratio
+ float xs = t->xs*scale, ys = t->ys*scale;
+ x += int((size-xs)/2);
+ y += int((size-ys)/2);
+ const vec &color = hit ? vec(1, 0.5f, 0.5f) : (overlaid ? vec(1, 1, 1) : light);
+ glBindTexture(GL_TEXTURE_2D, t->id);
+ if(hit && actionon)
+ {
+ gle::colorf(0, 0, 0, 0.75f);
+ rect_(x+SHADOW, y+SHADOW, xs, ys, 0);
+ }
+ gle::color(color);
+ rect_(x, y, xs, ys, 0);
+
+ if(overlaid)
+ {
+ if(!overlaytex) overlaytex = textureload("data/guioverlay.png", 3);
+ glBindTexture(GL_TEXTURE_2D, overlaytex->id);
+ gle::color(light);
+ rect_(x, y, xs, ys, 0);
+ if(title) text_(title, x + xs/12, y + ys - ys/12 - FONTH, hit ? 0xFF0000 : 0xFFFFFF, hit && actionon, hit);
+ }
+ }
+
+ void previewslot(VSlot &vslot, bool overlaid, int x, int y, int size, bool hit)
+ {
+ Slot &slot = *vslot.slot;
+ if(slot.sts.empty()) return;
+ VSlot *layer = NULL;
+ Texture *t = NULL, *glowtex = NULL, *layertex = NULL;
+ if(slot.loaded)
+ {
+ t = slot.sts[0].t;
+ if(t == notexture) return;
+ Slot &slot = *vslot.slot;
+ if(slot.texmask&(1<<TEX_GLOW)) { loopvj(slot.sts) if(slot.sts[j].type==TEX_GLOW) { glowtex = slot.sts[j].t; break; } }
+ if(vslot.layer)
+ {
+ layer = &lookupvslot(vslot.layer);
+ if(!layer->slot->sts.empty()) layertex = layer->slot->sts[0].t;
+ }
+ }
+ else if(slot.thumbnail && slot.thumbnail != notexture) t = slot.thumbnail;
+ else return;
+ float xt = min(1.0f, t->xs/(float)t->ys), yt = min(1.0f, t->ys/(float)t->xs), xs = size, ys = size;
+ if(hit && actionon)
+ {
+ hudnotextureshader->set();
+ gle::colorf(0, 0, 0, 0.75f);
+ rect_(x+SHADOW, y+SHADOW, xs, ys);
+ hudshader->set();
+ }
+ SETSHADER(hudrgb);
+ gle::defvertex(2);
+ gle::deftexcoord0();
+ const vec &color = hit ? vec(1, 0.5f, 0.5f) : (overlaid ? vec(1, 1, 1) : light);
+ vec2 tc[4] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
+ float xoff = vslot.offset.x, yoff = vslot.offset.y;
+ if(vslot.rotation)
+ {
+ const texrotation &r = texrotations[vslot.rotation];
+ if(r.swapxy) { swap(xoff, yoff); loopk(4) swap(tc[k].x, tc[k].y); }
+ if(r.flipx) { xoff *= -1; loopk(4) tc[k].x *= -1; }
+ if(r.flipy) { yoff *= -1; loopk(4) tc[k].y *= -1; }
+ }
+ loopk(4) { tc[k].x = tc[k].x/xt - xoff/t->xs; tc[k].y = tc[k].y/yt - yoff/t->ys; }
+ if(slot.loaded) gle::color(vec(color).mul(vslot.colorscale));
+ else gle::color(color);
+ glBindTexture(GL_TEXTURE_2D, t->id);
+ gle::begin(GL_TRIANGLE_STRIP);
+ gle::attribf(x, y); gle::attrib(tc[0]);
+ gle::attribf(x+xs, y); gle::attrib(tc[1]);
+ gle::attribf(x, y+ys); gle::attrib(tc[3]);
+ gle::attribf(x+xs, y+ys); gle::attrib(tc[2]);
+ gle::end();
+ if(glowtex)
+ {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ glBindTexture(GL_TEXTURE_2D, glowtex->id);
+ if(hit || overlaid) gle::color(vec(vslot.glowcolor).mul(color));
+ else gle::color(vslot.glowcolor);
+ gle::begin(GL_TRIANGLE_STRIP);
+ gle::attribf(x, y); gle::attrib(tc[0]);
+ gle::attribf(x+xs, y); gle::attrib(tc[1]);
+ gle::attribf(x, y+ys); gle::attrib(tc[3]);
+ gle::attribf(x+xs, y+ys); gle::attrib(tc[2]);
+ gle::end();
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ if(layertex)
+ {
+ glBindTexture(GL_TEXTURE_2D, layertex->id);
+ gle::color(vec(color).mul(layer->colorscale));
+ gle::begin(GL_TRIANGLE_STRIP);
+ gle::attribf(x+xs/2, y+ys/2); gle::attrib(tc[0]);
+ gle::attribf(x+xs, y+ys/2); gle::attrib(tc[1]);
+ gle::attribf(x+xs/2, y+ys); gle::attrib(tc[3]);
+ gle::attribf(x+xs, y+ys); gle::attrib(tc[2]);
+ gle::end();
+ }
+
+ hudshader->set();
+ if(overlaid)
+ {
+ if(!overlaytex) overlaytex = textureload("data/guioverlay.png", 3);
+ glBindTexture(GL_TEXTURE_2D, overlaytex->id);
+ gle::color(light);
+ rect_(x, y, xs, ys, 0);
+ }
+ }
+
+ void line_(int size, float percent = 1.0f)
+ {
+ if(visible())
+ {
+ if(!slidertex) slidertex = textureload("data/guislider.png", 3);
+ glBindTexture(GL_TEXTURE_2D, slidertex->id);
+ if(percent < 0.99f)
+ {
+ gle::colorf(light.x, light.y, light.z, 0.375f);
+ if(ishorizontal())
+ rect_(curx + FONTH/2 - size/2, cury, size, ysize, 0);
+ else
+ rect_(curx, cury + FONTH/2 - size/2, xsize, size, 1);
+ }
+ gle::color(light);
+ if(ishorizontal())
+ rect_(curx + FONTH/2 - size/2, cury + ysize*(1-percent), size, ysize*percent, 0);
+ else
+ rect_(curx, cury + FONTH/2 - size/2, xsize*percent, size, 1);
+ }
+ layout(ishorizontal() ? FONTH : 0, ishorizontal() ? 0 : FONTH);
+ }
+
+ void textbox(const char *text, int width, int height, int color)
+ {
+ width *= FONTW;
+ height *= FONTH;
+ int w, h;
+ text_bounds(text, w, h, width);
+ if(h > height) height = h;
+ if(visible()) draw_text(text, curx, cury, color>>16, (color>>8)&0xFF, color&0xFF, 0xFF, -1, width);
+ layout(width, height);
+ }
+
+ int button_(const char *text, int color, const char *icon, bool clickable, bool center)
+ {
+ const int padding = 10;
+ int w = 0;
+ if(icon) w += ICON_SIZE;
+ if(icon && text) w += padding;
+ if(text) w += text_width(text);
+
+ if(visible())
+ {
+ bool hit = ishit(w, FONTH);
+ if(hit && clickable) color = 0xFF0000;
+ int x = curx;
+ if(isvertical() && center) x += (xsize-w)/2;
+
+ if(icon)
+ {
+ if(icon[0] != ' ')
+ {
+ const char *ext = strrchr(icon, '.');
+ defformatstring(tname, "packages/icons/%s%s", icon, ext ? "" : ".png");
+ icon_(textureload(tname, 3), false, x, cury, ICON_SIZE, clickable && hit);
+ }
+ x += ICON_SIZE;
+ }
+ if(icon && text) x += padding;
+ if(text) text_(text, x, cury, color, center || (hit && clickable && actionon), hit && clickable);
+ }
+ return layout(w, FONTH);
+ }
+
+ static Texture *skintex, *overlaytex, *slidertex;
+ static const int skinx[], skiny[];
+ static const struct patch { ushort left, right, top, bottom; uchar flags; } patches[];
+
+ static void drawskin(int x, int y, int gapw, int gaph, int start, int n, int passes = 1, const vec &light = vec(1, 1, 1), float alpha = 0.80f)//int vleft, int vright, int vtop, int vbottom, int start, int n)
+ {
+ if(!skintex) skintex = textureload("data/guiskin.png", 3);
+ glBindTexture(GL_TEXTURE_2D, skintex->id);
+ int gapx1 = INT_MAX, gapy1 = INT_MAX, gapx2 = INT_MAX, gapy2 = INT_MAX;
+ float wscale = 1.0f/(SKIN_W*SKIN_SCALE), hscale = 1.0f/(SKIN_H*SKIN_SCALE);
+
+ loopj(passes)
+ {
+ bool quads = false;
+ if(passes>1) glDepthFunc(j ? GL_LEQUAL : GL_GREATER);
+ gle::color(j ? light : vec(1, 1, 1), passes<=1 || j ? alpha : alpha/2); //ghost when its behind something in depth
+ loopi(n)
+ {
+ const patch &p = patches[start+i];
+ int left = skinx[p.left]*SKIN_SCALE, right = skinx[p.right]*SKIN_SCALE,
+ top = skiny[p.top]*SKIN_SCALE, bottom = skiny[p.bottom]*SKIN_SCALE;
+ float tleft = left*wscale, tright = right*wscale,
+ ttop = top*hscale, tbottom = bottom*hscale;
+ if(p.flags&0x1)
+ {
+ gapx1 = left;
+ gapx2 = right;
+ }
+ else if(left >= gapx2)
+ {
+ left += gapw - (gapx2-gapx1);
+ right += gapw - (gapx2-gapx1);
+ }
+ if(p.flags&0x10)
+ {
+ gapy1 = top;
+ gapy2 = bottom;
+ }
+ else if(top >= gapy2)
+ {
+ top += gaph - (gapy2-gapy1);
+ bottom += gaph - (gapy2-gapy1);
+ }
+
+ //multiple tiled quads if necessary rather than a single stretched one
+ int ystep = bottom-top;
+ int yo = y+top;
+ while(ystep > 0)
+ {
+ if(p.flags&0x10 && yo+ystep-(y+top) > gaph)
+ {
+ ystep = gaph+y+top-yo;
+ tbottom = ttop+ystep*hscale;
+ }
+ int xstep = right-left;
+ int xo = x+left;
+ float tright2 = tright;
+ while(xstep > 0)
+ {
+ if(p.flags&0x01 && xo+xstep-(x+left) > gapw)
+ {
+ xstep = gapw+x+left-xo;
+ tright = tleft+xstep*wscale;
+ }
+ if(!quads)
+ {
+ quads = true;
+ gle::defvertex(2);
+ gle::deftexcoord0();
+ gle::begin(GL_QUADS);
+ }
+ gle::attribf(xo, yo); gle::attribf(tleft, ttop);
+ gle::attribf(xo+xstep, yo); gle::attribf(tright, ttop);
+ gle::attribf(xo+xstep, yo+ystep); gle::attribf(tright, tbottom);
+ gle::attribf(xo, yo+ystep); gle::attribf(tleft, tbottom);
+ if(!(p.flags&0x01)) break;
+ xo += xstep;
+ }
+ tright = tright2;
+ if(!(p.flags&0x10)) break;
+ yo += ystep;
+ }
+ }
+ if(quads) xtraverts += gle::end();
+ else break; //if it didn't happen on the first pass, it won't happen on the second..
+ }
+ if(passes>1) glDepthFunc(GL_ALWAYS);
+ }
+
+ vec origin, scale, *savedorigin;
+ float dist;
+ g3d_callback *cb;
+ bool gui2d;
+
+ static float basescale, maxscale;
+ static bool passthrough;
+ static float alpha;
+ static vec light;
+
+ void adjustscale()
+ {
+ int w = xsize + (skinx[2]-skinx[1])*SKIN_SCALE + (skinx[10]-skinx[9])*SKIN_SCALE, h = ysize + (skiny[9]-skiny[7])*SKIN_SCALE;
+ if(tcurrent) h += ((skiny[5]-skiny[1])-(skiny[3]-skiny[2]))*SKIN_SCALE + FONTH-2*INSERT;
+ else h += (skiny[6]-skiny[3])*SKIN_SCALE;
+
+ float aspect = forceaspect ? 1.0f/forceaspect : float(screenh)/float(screenw), fit = 1.0f;
+ if(w*aspect*basescale>1.0f) fit = 1.0f/(w*aspect*basescale);
+ if(h*basescale*fit>maxscale) fit *= maxscale/(h*basescale*fit);
+ origin = vec(0.5f-((w-xsize)/2 - (skinx[2]-skinx[1])*SKIN_SCALE)*aspect*scale.x*fit, 0.5f + (0.5f*h-(skiny[9]-skiny[7])*SKIN_SCALE)*scale.y*fit, 0);
+ scale = vec(aspect*scale.x*fit, scale.y*fit, 1);
+ }
+
+ void start(int starttime, float initscale, int *tab, bool allowinput)
+ {
+ if(gui2d)
+ {
+ initscale *= 0.025f;
+ if(allowinput) hascursor = true;
+ }
+ basescale = initscale;
+ if(layoutpass) scale.x = scale.y = scale.z = guifadein ? basescale*min((totalmillis-starttime)/300.0f, 1.0f) : basescale;
+ alpha = allowinput ? 0.80f : 0.60f;
+ passthrough = scale.x<basescale || !allowinput;
+ curdepth = -1;
+ curlist = -1;
+ tpos = 0;
+ tx = 0;
+ ty = 0;
+ tcurrent = tab;
+ tcolor = 0xFFFFFF;
+ pushlist();
+ if(layoutpass)
+ {
+ firstlist = nextlist = curlist;
+ memset(columns, 0, sizeof(columns));
+ }
+ else
+ {
+ if(tcurrent && !*tcurrent) tcurrent = NULL;
+ cury = -ysize;
+ curx = -xsize/2;
+
+ if(gui2d)
+ {
+ hudmatrix.ortho(0, 1, 1, 0, -1, 1);
+ hudmatrix.translate(origin);
+ hudmatrix.scale(scale);
+
+ light = vec(1, 1, 1);
+ }
+ else
+ {
+ float yaw = atan2f(origin.y-camera1->o.y, origin.x-camera1->o.x);
+ hudmatrix = camprojmatrix;
+ hudmatrix.translate(origin);
+ hudmatrix.rotate_around_z(yaw - 90*RAD);
+ hudmatrix.rotate_around_x(-90*RAD);
+ hudmatrix.scale(-scale.x, scale.y, scale.z);
+
+ vec dir;
+ lightreaching(origin, light, dir, false, 0, 0.5f);
+ float intensity = vec(yaw, 0.0f).dot(dir);
+ light.mul(1.0f + max(intensity, 0.0f));
+ }
+
+ resethudmatrix();
+ hudshader->set();
+
+ drawskin(curx-skinx[2]*SKIN_SCALE, cury-skiny[6]*SKIN_SCALE, xsize, ysize, 0, 9, gui2d ? 1 : 2, light, alpha);
+ if(!tcurrent) drawskin(curx-skinx[5]*SKIN_SCALE, cury-skiny[6]*SKIN_SCALE, xsize, 0, 9, 1, gui2d ? 1 : 2, light, alpha);
+ }
+ }
+
+ void adjusthorizontalcolumn(int col, int i)
+ {
+ int h = columns[col], dh = 0;
+ for(int d = 1; i >= 0; d ^= 1)
+ {
+ list &p = lists[i];
+ if(d&1) { dh = h - p.h; if(dh <= 0) break; p.h = h; }
+ else { p.h += dh; h = p.h; }
+ i = p.parent;
+ }
+ ysize += max(dh, 0);
+ }
+
+ void adjustverticalcolumn(int col, int i)
+ {
+ int w = columns[col], dw = 0;
+ for(int d = 0; i >= 0; d ^= 1)
+ {
+ list &p = lists[i];
+ if(d&1) { p.w += dw; w = p.w; }
+ else { dw = w - p.w; if(dw <= 0) break; p.w = w; }
+ i = p.parent;
+ }
+ xsize = max(xsize, w);
+ }
+
+ void adjustcolumns()
+ {
+ if(lists.inrange(curlist))
+ {
+ list &l = lists[curlist];
+ if(l.column >= 0) columns[l.column] = max(columns[l.column], ishorizontal() ? ysize : xsize);
+ }
+ int parent = -1, depth = 0;
+ for(int i = firstlist; i < lists.length(); i++)
+ {
+ list &l = lists[i];
+ if(l.parent > parent) { parent = l.parent; depth++; }
+ else if(l.parent < parent)
+ {
+ while(parent > l.parent && depth > 0)
+ {
+ parent = lists[parent].parent;
+ depth--;
+ }
+ }
+ if(l.column >= 0)
+ {
+ if(depth&1) adjusthorizontalcolumn(l.column, i);
+ else adjustverticalcolumn(l.column, i);
+ }
+ }
+ }
+
+ void end()
+ {
+ if(layoutpass)
+ {
+ adjustcolumns();
+ xsize = max(tx, xsize);
+ ysize = max(ty, ysize);
+ ysize = max(ysize, (skiny[7]-skiny[6])*SKIN_SCALE);
+ if(tcurrent) *tcurrent = max(1, min(*tcurrent, tpos));
+ if(gui2d) adjustscale();
+ if(!windowhit && !passthrough)
+ {
+ float dist = 0;
+ if(gui2d)
+ {
+ hitx = (cursorx - origin.x)/scale.x;
+ hity = (cursory - origin.y)/scale.y;
+ }
+ else
+ {
+ plane p;
+ p.toplane(vec(origin).sub(camera1->o).set(2, 0).normalize(), origin);
+ if(p.rayintersect(camera1->o, camdir, dist) && dist>=0)
+ {
+ vec hitpos(camdir);
+ hitpos.mul(dist).add(camera1->o).sub(origin);
+ hitx = vec(-p.y, p.x, 0).dot(hitpos)/scale.x;
+ hity = -hitpos.z/scale.y;
+ }
+ }
+ if((mousebuttons & G3D_PRESSED) && (fabs(hitx-firstx) > 2 || fabs(hity - firsty) > 2)) mousebuttons |= G3D_DRAGGED;
+ if(dist>=0 && hitx>=-xsize/2 && hitx<=xsize/2 && hity<=0)
+ {
+ if(hity>=-ysize || (tcurrent && hity>=-ysize-(FONTH-2*INSERT)-((skiny[6]-skiny[1])-(skiny[3]-skiny[2]))*SKIN_SCALE && hitx<=tx-xsize/2))
+ windowhit = this;
+ }
+ }
+ }
+ else
+ {
+ if(tcurrent && tx<xsize) drawskin(curx+tx-skinx[5]*SKIN_SCALE, -ysize-skiny[6]*SKIN_SCALE, xsize-tx, FONTH, 9, 1, gui2d ? 1 : 2, light, alpha);
+ }
+ poplist();
+ }
+
+ void draw()
+ {
+ cb->gui(*this, layoutpass);
+ }
};
Texture *gui::skintex = NULL, *gui::overlaytex = NULL, *gui::slidertex = NULL;
//chop skin into a grid
const int gui::skiny[] = {0, 7, 21, 34, 43, 48, 56, 104, 111, 117, 128},
- gui::skinx[] = {0, 11, 23, 37, 105, 119, 137, 151, 215, 229, 246, 256};
+ gui::skinx[] = {0, 11, 23, 37, 105, 119, 137, 151, 215, 229, 246, 256};
//Note: skinx[3]-skinx[2] = skinx[7]-skinx[6]
-// skinx[5]-skinx[4] = skinx[9]-skinx[8]
+// skinx[5]-skinx[4] = skinx[9]-skinx[8]
const gui::patch gui::patches[] =
{ //arguably this data can be compressed - it depends on what else needs to be skinned in the future
- {1,2,3,6, 0}, // body
- {2,9,5,6, 0x01},
- {9,10,3,6, 0},
-
- {1,2,6,7, 0x10},
- {2,9,6,7, 0x11},
- {9,10,6,7, 0x10},
-
- {1,2,7,9, 0},
- {2,9,7,9, 0x01},
- {9,10,7,9, 0},
-
- {5,6,3,5, 0x01}, // top
-
- {2,3,1,2, 0}, // selected tab
- {3,4,1,2, 0x01},
- {4,5,1,2, 0},
- {2,3,2,3, 0x10},
- {3,4,2,3, 0x11},
- {4,5,2,3, 0x10},
- {2,3,3,5, 0},
- {3,4,3,5, 0x01},
- {4,5,3,5, 0},
-
- {6,7,1,2, 0}, // deselected tab
- {7,8,1,2, 0x01},
- {8,9,1,2, 0},
- {6,7,2,3, 0x10},
- {7,8,2,3, 0x11},
- {8,9,2,3, 0x10},
- {6,7,3,5, 0},
- {7,8,3,5, 0x01},
- {8,9,3,5, 0},
+ {1,2,3,6, 0}, // body
+ {2,9,5,6, 0x01},
+ {9,10,3,6, 0},
+
+ {1,2,6,7, 0x10},
+ {2,9,6,7, 0x11},
+ {9,10,6,7, 0x10},
+
+ {1,2,7,9, 0},
+ {2,9,7,9, 0x01},
+ {9,10,7,9, 0},
+
+ {5,6,3,5, 0x01}, // top
+
+ {2,3,1,2, 0}, // selected tab
+ {3,4,1,2, 0x01},
+ {4,5,1,2, 0},
+ {2,3,2,3, 0x10},
+ {3,4,2,3, 0x11},
+ {4,5,2,3, 0x10},
+ {2,3,3,5, 0},
+ {3,4,3,5, 0x01},
+ {4,5,3,5, 0},
+
+ {6,7,1,2, 0}, // deselected tab
+ {7,8,1,2, 0x01},
+ {8,9,1,2, 0},
+ {6,7,2,3, 0x10},
+ {7,8,2,3, 0x11},
+ {8,9,2,3, 0x10},
+ {6,7,3,5, 0},
+ {7,8,3,5, 0x01},
+ {8,9,3,5, 0},
};
vector<gui::list> gui::lists;
@@ -1164,117 +1164,117 @@ VARP(guipushdist, 1, 4, 64);
bool g3d_input(const char *str, int len)
{
- editor *e = currentfocus();
- if(fieldmode == FIELDKEY || fieldmode == FIELDSHOW || !e) return false;
+ editor *e = currentfocus();
+ if(fieldmode == FIELDKEY || fieldmode == FIELDSHOW || !e) return false;
- e->input(str, len);
- return true;
+ e->input(str, len);
+ return true;
}
bool g3d_key(int code, bool isdown)
{
- editor *e = currentfocus();
- if(fieldmode == FIELDKEY)
- {
- switch(code)
- {
- case SDLK_ESCAPE:
- if(isdown) fieldmode = FIELDCOMMIT;
- return true;
- }
- const char *keyname = getkeyname(code);
- if(keyname && isdown)
- {
- if(e->lines.length()!=1 || !e->lines[0].empty()) e->insert(" ");
- e->insert(keyname);
- }
- return true;
- }
-
- if(code==-1 && g3d_windowhit(isdown, true)) return true;
- else if(code==-3 && g3d_windowhit(isdown, false)) return true;
-
- if(fieldmode == FIELDSHOW || !e)
- {
- if(windowhit) switch(code)
- {
- case -4: // window "management"
- if(isdown)
- {
- if(windowhit->gui2d)
- {
- vec origin = *guis2d.last().savedorigin;
- int i = windowhit - &guis2d[0];
- for(int j = guis2d.length()-1; j > i; j--) *guis2d[j].savedorigin = *guis2d[j-1].savedorigin;
- *windowhit->savedorigin = origin;
- if(guis2d.length() > 1)
- {
- if(camera1->o.dist(*windowhit->savedorigin) <= camera1->o.dist(*guis2d.last().savedorigin))
- windowhit->savedorigin->add(camdir);
- }
- }
- else windowhit->savedorigin->add(vec(camdir).mul(guipushdist));
- }
- return true;
- case -5:
- if(isdown)
- {
- if(windowhit->gui2d)
- {
- vec origin = *guis2d[0].savedorigin;
- loopj(guis2d.length()-1) *guis2d[j].savedorigin = *guis2d[j + 1].savedorigin;
- *guis2d.last().savedorigin = origin;
- if(guis2d.length() > 1)
- {
- if(camera1->o.dist(*guis2d.last().savedorigin) >= camera1->o.dist(*guis2d[0].savedorigin))
- guis2d.last().savedorigin->sub(camdir);
- }
- }
- else windowhit->savedorigin->sub(vec(camdir).mul(guipushdist));
- }
- return true;
- }
-
- return false;
- }
- switch(code)
- {
- case SDLK_ESCAPE: //cancel editing without commit
- if(isdown) fieldmode = FIELDABORT;
- return true;
- case SDLK_RETURN:
- [[fallthrough]];
- case SDLK_TAB:
- if(e->maxy != 1) break;
- [[fallthrough]];
- case SDLK_KP_ENTER:
- if(isdown) fieldmode = FIELDCOMMIT; //signal field commit (handled when drawing field)
- return true;
- }
- if(isdown) e->key(code);
- return true;
+ editor *e = currentfocus();
+ if(fieldmode == FIELDKEY)
+ {
+ switch(code)
+ {
+ case SDLK_ESCAPE:
+ if(isdown) fieldmode = FIELDCOMMIT;
+ return true;
+ }
+ const char *keyname = getkeyname(code);
+ if(keyname && isdown)
+ {
+ if(e->lines.length()!=1 || !e->lines[0].empty()) e->insert(" ");
+ e->insert(keyname);
+ }
+ return true;
+ }
+
+ if(code==-1 && g3d_windowhit(isdown, true)) return true;
+ else if(code==-3 && g3d_windowhit(isdown, false)) return true;
+
+ if(fieldmode == FIELDSHOW || !e)
+ {
+ if(windowhit) switch(code)
+ {
+ case -4: // window "management"
+ if(isdown)
+ {
+ if(windowhit->gui2d)
+ {
+ vec origin = *guis2d.last().savedorigin;
+ int i = windowhit - &guis2d[0];
+ for(int j = guis2d.length()-1; j > i; j--) *guis2d[j].savedorigin = *guis2d[j-1].savedorigin;
+ *windowhit->savedorigin = origin;
+ if(guis2d.length() > 1)
+ {
+ if(camera1->o.dist(*windowhit->savedorigin) <= camera1->o.dist(*guis2d.last().savedorigin))
+ windowhit->savedorigin->add(camdir);
+ }
+ }
+ else windowhit->savedorigin->add(vec(camdir).mul(guipushdist));
+ }
+ return true;
+ case -5:
+ if(isdown)
+ {
+ if(windowhit->gui2d)
+ {
+ vec origin = *guis2d[0].savedorigin;
+ loopj(guis2d.length()-1) *guis2d[j].savedorigin = *guis2d[j + 1].savedorigin;
+ *guis2d.last().savedorigin = origin;
+ if(guis2d.length() > 1)
+ {
+ if(camera1->o.dist(*guis2d.last().savedorigin) >= camera1->o.dist(*guis2d[0].savedorigin))
+ guis2d.last().savedorigin->sub(camdir);
+ }
+ }
+ else windowhit->savedorigin->sub(vec(camdir).mul(guipushdist));
+ }
+ return true;
+ }
+
+ return false;
+ }
+ switch(code)
+ {
+ case SDLK_ESCAPE: //cancel editing without commit
+ if(isdown) fieldmode = FIELDABORT;
+ return true;
+ case SDLK_RETURN:
+ [[fallthrough]];
+ case SDLK_TAB:
+ if(e->maxy != 1) break;
+ [[fallthrough]];
+ case SDLK_KP_ENTER:
+ if(isdown) fieldmode = FIELDCOMMIT; //signal field commit (handled when drawing field)
+ return true;
+ }
+ if(isdown) e->key(code);
+ return true;
}
void g3d_cursorpos(float &x, float &y)
{
- if(guis2d.length()) { x = cursorx; y = cursory; }
- else x = y = 0.5f;
+ if(guis2d.length()) { x = cursorx; y = cursory; }
+ else x = y = 0.5f;
}
void g3d_resetcursor()
{
- cursorx = cursory = 0.5f;
+ cursorx = cursory = 0.5f;
}
FVARP(guisens, 1e-3f, 1, 1e3f);
bool g3d_movecursor(int dx, int dy)
{
- if(!guis2d.length() || !hascursor) return false;
- const float CURSORSCALE = 500.0f;
- cursorx = max(0.0f, min(1.0f, cursorx+guisens*dx*(screenh/(screenw*CURSORSCALE))));
- cursory = max(0.0f, min(1.0f, cursory+guisens*dy/CURSORSCALE));
- return true;
+ if(!guis2d.length() || !hascursor) return false;
+ const float CURSORSCALE = 500.0f;
+ cursorx = max(0.0f, min(1.0f, cursorx+guisens*dx*(screenh/(screenw*CURSORSCALE))));
+ cursory = max(0.0f, min(1.0f, cursory+guisens*dy/CURSORSCALE));
+ return true;
}
VARNP(guifollow, useguifollow, 0, 1, 1);
@@ -1282,119 +1282,119 @@ VARNP(gui2d, usegui2d, 0, 1, 1);
void g3d_addgui(g3d_callback *cb, vec &origin, int flags)
{
- bool gui2d = flags&GUI_FORCE_2D || (flags&GUI_2D && usegui2d) || mainmenu;
- if(!gui2d && flags&GUI_FOLLOW && useguifollow) origin.z = player->o.z-(player->eyeheight-1);
- gui &g = (gui2d ? guis2d : guis3d).add();
- g.cb = cb;
- g.origin = origin;
- g.savedorigin = &origin;
- g.dist = flags&GUI_BOTTOM && gui2d ? 1e16f : camera1->o.dist(g.origin);
- g.gui2d = gui2d;
+ bool gui2d = flags&GUI_FORCE_2D || (flags&GUI_2D && usegui2d) || mainmenu;
+ if(!gui2d && flags&GUI_FOLLOW && useguifollow) origin.z = player->o.z-(player->eyeheight-1);
+ gui &g = (gui2d ? guis2d : guis3d).add();
+ g.cb = cb;
+ g.origin = origin;
+ g.savedorigin = &origin;
+ g.dist = flags&GUI_BOTTOM && gui2d ? 1e16f : camera1->o.dist(g.origin);
+ g.gui2d = gui2d;
}
void g3d_limitscale(float scale)
{
- gui::maxscale = scale;
+ gui::maxscale = scale;
}
static inline bool g3d_sort(const gui &a, const gui &b) { return a.dist < b.dist; }
bool g3d_windowhit(bool on, bool act)
{
- extern int cleargui(int n);
- if(act)
- {
- if(actionon || windowhit)
- {
- if(on) { firstx = gui::hitx; firsty = gui::hity; }
- mousebuttons |= (actionon=on) ? G3D_DOWN : G3D_UP;
- }
- } else if(!on && windowhit) cleargui(1);
- return (guis2d.length() && hascursor) || (windowhit && !windowhit->gui2d);
+ extern int cleargui(int n);
+ if(act)
+ {
+ if(actionon || windowhit)
+ {
+ if(on) { firstx = gui::hitx; firsty = gui::hity; }
+ mousebuttons |= (actionon=on) ? G3D_DOWN : G3D_UP;
+ }
+ } else if(!on && windowhit) cleargui(1);
+ return (guis2d.length() && hascursor) || (windowhit && !windowhit->gui2d);
}
void g3d_render()
{
- windowhit = NULL;
- if(actionon) mousebuttons |= G3D_PRESSED;
+ windowhit = NULL;
+ if(actionon) mousebuttons |= G3D_PRESSED;
- gui::reset();
- guis2d.shrink(0);
- guis3d.shrink(0);
+ gui::reset();
+ guis2d.shrink(0);
+ guis3d.shrink(0);
- // call all places in the engine that may want to render a gui from here, they call g3d_addgui()
- extern void g3d_texturemenu();
+ // call all places in the engine that may want to render a gui from here, they call g3d_addgui()
+ extern void g3d_texturemenu();
- if(!mainmenu) g3d_texturemenu();
- g3d_mainmenu();
- if(!mainmenu) game::g3d_gamemenus();
+ if(!mainmenu) g3d_texturemenu();
+ g3d_mainmenu();
+ if(!mainmenu) game::g3d_gamemenus();
- guis2d.sort(g3d_sort);
- guis3d.sort(g3d_sort);
+ guis2d.sort(g3d_sort);
+ guis3d.sort(g3d_sort);
- readyeditors();
- fieldsactive = false;
+ readyeditors();
+ fieldsactive = false;
- hascursor = false;
+ hascursor = false;
- layoutpass = true;
- loopv(guis2d) guis2d[i].draw();
- loopv(guis3d) guis3d[i].draw();
- layoutpass = false;
+ layoutpass = true;
+ loopv(guis2d) guis2d[i].draw();
+ loopv(guis3d) guis3d[i].draw();
+ layoutpass = false;
- if(guis3d.length())
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ if(guis3d.length())
+ {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_ALWAYS);
- glDepthMask(GL_FALSE);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_ALWAYS);
+ glDepthMask(GL_FALSE);
- loopvrev(guis3d) guis3d[i].draw();
+ loopvrev(guis3d) guis3d[i].draw();
- glDepthFunc(GL_LESS);
- glDepthMask(GL_TRUE);
- glDisable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glDepthMask(GL_TRUE);
+ glDisable(GL_DEPTH_TEST);
- glDisable(GL_BLEND);
- }
+ glDisable(GL_BLEND);
+ }
}
void g3d_render2d()
{
- if(guis2d.length())
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ if(guis2d.length())
+ {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- loopvrev(guis2d) guis2d[i].draw();
+ loopvrev(guis2d) guis2d[i].draw();
- glDisable(GL_BLEND);
- }
+ glDisable(GL_BLEND);
+ }
- flusheditors();
- if(!fieldsactive) fieldmode = FIELDSHOW; //didn't draw any fields, so lose focus - mainly for menu closed
- textinput(fieldmode!=FIELDSHOW, TI_GUI);
- keyrepeat(fieldmode!=FIELDSHOW, KR_GUI);
+ flusheditors();
+ if(!fieldsactive) fieldmode = FIELDSHOW; //didn't draw any fields, so lose focus - mainly for menu closed
+ textinput(fieldmode!=FIELDSHOW, TI_GUI);
+ keyrepeat(fieldmode!=FIELDSHOW, KR_GUI);
- mousebuttons = 0;
+ mousebuttons = 0;
}
void consolebox(int x1, int y1, int x2, int y2)
{
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- float bw = x2 - x1, bh = y2 - y1, aspect = bw/bh, sh = bh, sw = sh*aspect;
- bw *= float(4*FONTH)/(SKIN_H*SKIN_SCALE);
- bh *= float(4*FONTH)/(SKIN_H*SKIN_SCALE);
- sw /= bw + (gui::skinx[2]-gui::skinx[1] + gui::skinx[10]-gui::skinx[9])*SKIN_SCALE;
- sh /= bh + (gui::skiny[9]-gui::skiny[7] + gui::skiny[6]-gui::skiny[4])*SKIN_SCALE;
- pushhudmatrix();
- hudmatrix.translate(x1, y1, 0);
- hudmatrix.scale(sw, sh, 1);
- flushhudmatrix();
- gui::drawskin(-gui::skinx[1]*SKIN_SCALE, -gui::skiny[4]*SKIN_SCALE, int(bw), int(bh), 0, 9, 1, vec(1, 1, 1), 0.60f);
- gui::drawskin((-gui::skinx[1] + gui::skinx[2] - gui::skinx[5])*SKIN_SCALE, -gui::skiny[4]*SKIN_SCALE, int(bw), 0, 9, 1, 1, vec(1, 1, 1), 0.60f);
- pophudmatrix();
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ float bw = x2 - x1, bh = y2 - y1, aspect = bw/bh, sh = bh, sw = sh*aspect;
+ bw *= float(4*FONTH)/(SKIN_H*SKIN_SCALE);
+ bh *= float(4*FONTH)/(SKIN_H*SKIN_SCALE);
+ sw /= bw + (gui::skinx[2]-gui::skinx[1] + gui::skinx[10]-gui::skinx[9])*SKIN_SCALE;
+ sh /= bh + (gui::skiny[9]-gui::skiny[7] + gui::skiny[6]-gui::skiny[4])*SKIN_SCALE;
+ pushhudmatrix();
+ hudmatrix.translate(x1, y1, 0);
+ hudmatrix.scale(sw, sh, 1);
+ flushhudmatrix();
+ gui::drawskin(-gui::skinx[1]*SKIN_SCALE, -gui::skiny[4]*SKIN_SCALE, int(bw), int(bh), 0, 9, 1, vec(1, 1, 1), 0.60f);
+ gui::drawskin((-gui::skinx[1] + gui::skinx[2] - gui::skinx[5])*SKIN_SCALE, -gui::skiny[4]*SKIN_SCALE, int(bw), 0, 9, 1, 1, vec(1, 1, 1), 0.60f);
+ pophudmatrix();
}
diff --git a/src/engine/animmodel.h b/src/engine/animmodel.h
index df30231..420d7f2 100644
--- a/src/engine/animmodel.h
+++ b/src/engine/animmodel.h
@@ -4,1426 +4,1427 @@ VARP(fullbrightmodels, 0, 0, 200);
struct animmodel : model
{
- struct animspec
- {
- int frame, range;
- float speed;
- int priority;
- };
-
- struct animpos
- {
- int anim, fr1, fr2;
- float t;
-
- void setframes(const animinfo &info)
- {
- anim = info.anim;
- if(info.range<=1)
- {
- fr1 = 0;
- t = 0;
- }
- else
- {
- int time = info.anim&ANIM_SETTIME ? info.basetime : lastmillis-info.basetime;
- fr1 = (int)(time/info.speed); // round to full frames
- t = (time-fr1*info.speed)/info.speed; // progress of the frame, value from 0.0f to 1.0f
- }
- if(info.anim&ANIM_LOOP)
- {
- fr1 = fr1%info.range+info.frame;
- fr2 = fr1+1;
- if(fr2>=info.frame+info.range) fr2 = info.frame;
- }
- else
- {
- fr1 = min(fr1, info.range-1)+info.frame;
- fr2 = min(fr1+1, info.frame+info.range-1);
- }
- if(info.anim&ANIM_REVERSE)
- {
- fr1 = (info.frame+info.range-1)-(fr1-info.frame);
- fr2 = (info.frame+info.range-1)-(fr2-info.frame);
- }
- }
-
- bool operator==(const animpos &a) const { return fr1==a.fr1 && fr2==a.fr2 && (fr1==fr2 || t==a.t); }
- bool operator!=(const animpos &a) const { return fr1!=a.fr1 || fr2!=a.fr2 || (fr1!=fr2 && t!=a.t); }
- };
-
- struct part;
-
- struct animstate
- {
- part *owner;
- animpos cur, prev;
- float interp;
-
- bool operator==(const animstate &a) const { return cur==a.cur && (interp<1 ? interp==a.interp && prev==a.prev : a.interp>=1); }
- bool operator!=(const animstate &a) const { return cur!=a.cur || (interp<1 ? interp!=a.interp || prev!=a.prev : a.interp<1); }
- };
-
- struct linkedpart;
- struct mesh;
-
- struct shaderparams
- {
- float spec, ambient, glow, glowdelta, glowpulse, specglare, glowglare, fullbright, envmapmin, envmapmax, scrollu, scrollv, alphatest;
-
- shaderparams() : spec(1.0f), ambient(0.3f), glow(3.0f), glowdelta(0), glowpulse(0), specglare(1), glowglare(1), fullbright(0), envmapmin(0), envmapmax(0), scrollu(0), scrollv(0), alphatest(0.9f) {}
- };
-
- struct shaderparamskey
- {
- static hashtable<shaderparams, shaderparamskey> keys;
- static int firstversion, lastversion;
-
- int version;
-
- shaderparamskey() : version(-1) {}
-
- bool checkversion()
- {
- if(version >= firstversion) return true;
- version = lastversion;
- if(++lastversion <= 0)
- {
- enumerate(keys, shaderparamskey, key, key.version = -1);
- firstversion = 0;
- lastversion = 1;
- version = 0;
- }
- return false;
- }
-
- static inline void invalidate()
- {
- firstversion = lastversion;
- }
- };
-
- struct skin : shaderparams
- {
- part *owner;
- Texture *tex, *masks, *envmap, *normalmap;
- Shader *shader;
- bool alphablend, cullface;
- shaderparamskey *key;
-
- skin() : owner(0), tex(notexture), masks(notexture), envmap(NULL), normalmap(NULL), shader(NULL), alphablend(true), cullface(true), key(NULL) {}
-
- bool masked() const { return masks != notexture; }
- bool envmapped() { return envmapmax>0 && envmapmodels; }
- bool bumpmapped() { return normalmap && bumpmodels; }
- bool tangents() { return bumpmapped(); }
- bool alphatested() const { return alphatest > 0 && tex->type&Texture::ALPHA; }
-
- void setkey()
- {
- key = &shaderparamskey::keys[*this];
- }
-
- void setshaderparams(mesh *m, const animstate *as)
- {
- if(!Shader::lastshader) return;
-
- float mincolor = as->cur.anim&ANIM_FULLBRIGHT ? fullbrightmodels/100.0f : 0.0f;
- if(fullbright)
- {
- gle::colorf(fullbright/2, fullbright/2, fullbright/2, transparent);
- }
- else
- {
- gle::color(vec(lightcolor).max(mincolor), transparent);
- }
-
- if(key->checkversion() && Shader::lastshader->owner == key) return;
- Shader::lastshader->owner = key;
-
- if(alphatested()) LOCALPARAMF(alphatest, alphatest);
-
- if(fullbright)
- {
- LOCALPARAMF(lightscale, 0, 0, 2);
- }
- else
- {
- float bias = max(mincolor-1.0f, 0.2f), scale = 0.5f*max(0.8f-bias, 0.0f),
- minshade = scale*max(ambient, mincolor);
- LOCALPARAMF(lightscale, scale - minshade, scale, minshade + bias);
- }
- float curglow = glow;
- if(glowpulse > 0)
- {
- float curpulse = lastmillis*glowpulse;
- curpulse -= floor(curpulse);
- curglow += glowdelta*2*fabs(curpulse - 0.5f);
- }
- LOCALPARAMF(maskscale, 0.5f*spec, 0.5f*curglow, 16*specglare, 4*glowglare);
- LOCALPARAMF(texscroll, scrollu*lastmillis/1000.0f, scrollv*lastmillis/1000.0f);
- if(envmapped()) LOCALPARAMF(envmapscale, envmapmin-envmapmax, envmapmax);
- }
-
- Shader *loadshader()
- {
- #define DOMODELSHADER(name, body) \
- do { \
- static Shader *name##shader = NULL; \
- if(!name##shader) name##shader = useshaderbyname(#name); \
- body; \
- } while(0)
- #define LOADMODELSHADER(name) DOMODELSHADER(name, return name##shader)
- #define SETMODELSHADER(m, name) DOMODELSHADER(name, (m)->setshader(name##shader))
- if(shader) return shader;
-
- string opts;
- int optslen = 0;
- if(alphatested()) opts[optslen++] = 'a';
- if(owner->tangents()) opts[optslen++] = 'q';
- if(bumpmapped()) opts[optslen++] = 'n';
- if(envmapped()) opts[optslen++] = 'e';
- if(masked()) opts[optslen++] = 'm';
- if(!fullbright && (masked() || spec>=0.01f)) opts[optslen++] = 's';
- opts[optslen++] = '\0';
-
- defformatstring(name, "model%s", opts);
- shader = generateshader(name, "modelshader \"%s\"", opts);
- return shader;
- }
-
- void cleanup()
- {
- if(shader && shader->standard) shader = NULL;
- }
-
- void preloadBIH()
- {
- if(tex->type&Texture::ALPHA && !tex->alphamask) loadalphamask(tex);
- }
-
- void preloadshader(bool force)
- {
- if(force) cleanup();
- loadshader();
- }
-
- void setshader(mesh *m, const animstate *as)
- {
- m->setshader(loadshader());
- }
-
- void bind(mesh *b, const animstate *as)
- {
- if(!cullface && enablecullface) { glDisable(GL_CULL_FACE); enablecullface = false; }
- else if(cullface && !enablecullface) { glEnable(GL_CULL_FACE); enablecullface = true; }
-
- if(as->cur.anim&ANIM_NOSKIN)
- {
- if(enablealphablend) { glDisable(GL_BLEND); enablealphablend = false; }
- if(shadowmapping) SETMODELSHADER(b, shadowmapcaster);
- else /*if(as->cur.anim&ANIM_SHADOW)*/ SETMODELSHADER(b, notexturemodel);
- return;
- }
- setshader(b, as);
- setshaderparams(b, as);
- int activetmu = 0;
- if(tex!=lasttex)
- {
- glBindTexture(GL_TEXTURE_2D, tex->id);
- lasttex = tex;
- }
- if(bumpmapped() && normalmap !=lastnormalmap)
- {
- glActiveTexture_(GL_TEXTURE3);
- activetmu = 3;
- glBindTexture(GL_TEXTURE_2D, normalmap->id);
- lastnormalmap = normalmap;
- }
- if(tex->type&Texture::ALPHA)
- {
- if(alphablend)
- {
- if(!enablealphablend && !reflecting && !refracting)
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- enablealphablend = true;
- }
- }
- else if(enablealphablend) { glDisable(GL_BLEND); enablealphablend = false; }
- }
- else if(enablealphablend && transparent>=1) { glDisable(GL_BLEND); enablealphablend = false; }
- if(masked() && masks!=lastmasks)
- {
- glActiveTexture_(GL_TEXTURE1);
- activetmu = 1;
- glBindTexture(GL_TEXTURE_2D, masks->id);
- lastmasks = masks;
- }
- if(envmapped())
- {
- GLuint emtex = envmap ? envmap->id : closestenvmaptex;
- if(lastenvmaptex!=emtex)
- {
- glActiveTexture_(GL_TEXTURE2);
- activetmu = 2;
- glBindTexture(GL_TEXTURE_CUBE_MAP, emtex);
- lastenvmaptex = emtex;
- }
- }
- if(activetmu != 0) glActiveTexture_(GL_TEXTURE0);
- }
- };
-
- struct meshgroup;
-
- struct mesh
- {
- meshgroup *group;
- char *name;
- bool noclip;
-
- mesh() : group(NULL), name(NULL), noclip(false)
- {
- }
-
- virtual ~mesh()
- {
- DELETEA(name);
- }
-
- virtual void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m) {}
-
- virtual void genBIH(BIH::mesh &m) {}
- void genBIH(skin &s, vector<BIH::mesh> &bih, const matrix4x3 &t)
- {
- BIH::mesh &m = bih.add();
- m.xform = t;
- m.tex = s.tex;
- if(s.tex->type&Texture::ALPHA) m.flags |= BIH::MESH_ALPHA;
- if(noclip) m.flags |= BIH::MESH_NOCLIP;
- if(s.cullface) m.flags |= BIH::MESH_CULLFACE;
- genBIH(m);
- while(bih.last().numtris > BIH::mesh::MAXTRIS)
- {
- BIH::mesh &overflow = bih.dup();
- overflow.tris += BIH::mesh::MAXTRIS;
- overflow.numtris -= BIH::mesh::MAXTRIS;
- bih[bih.length()-2].numtris = BIH::mesh::MAXTRIS;
- }
- }
-
- virtual void setshader(Shader *s)
- {
- if(glaring) s->setvariant(0, 1);
- else s->set();
- }
-
- template<class V, class T> void smoothnorms(V *verts, int numverts, T *tris, int numtris, float limit, bool areaweight)
- {
- hashtable<vec, int> share;
- int *next = new int[numverts];
- for(int i=0;i<numverts;++i)next[i]=-1;
- //~memset(next, -1, numverts*sizeof(int));
- loopi(numverts)
- {
- V &v = verts[i];
- v.norm = vec(0, 0, 0);
- int idx = share.access(v.pos, i);
- if(idx != i) { next[i] = next[idx]; next[idx] = i; }
- }
- loopi(numtris)
- {
- T &t = tris[i];
- V &v1 = verts[t.vert[0]], &v2 = verts[t.vert[1]], &v3 = verts[t.vert[2]];
- vec norm;
- norm.cross(vec(v2.pos).sub(v1.pos), vec(v3.pos).sub(v1.pos));
- if(!areaweight) norm.normalize();
- v1.norm.add(norm);
- v2.norm.add(norm);
- v3.norm.add(norm);
- }
- vec *norms = new vec[numverts];
- for(int i=0;i<numverts;++i)norms[i]=vec(0,0,0);
- //~memclear(norms, numverts);
- loopi(numverts)
- {
- V &v = verts[i];
- norms[i].add(v.norm);
- if(next[i] >= 0)
- {
- float vlimit = limit*v.norm.magnitude();
- for(int j = next[i]; j >= 0; j = next[j])
- {
- V &o = verts[j];
- if(v.norm.dot(o.norm) >= vlimit*o.norm.magnitude())
- {
- norms[i].add(o.norm);
- norms[j].add(v.norm);
- }
- }
- }
- }
- loopi(numverts) verts[i].norm = norms[i].normalize();
- delete[] next;
- delete[] norms;
- }
-
- template<class V, class T> void buildnorms(V *verts, int numverts, T *tris, int numtris, bool areaweight)
- {
- loopi(numverts) verts[i].norm = vec(0, 0, 0);
- loopi(numtris)
- {
- T &t = tris[i];
- V &v1 = verts[t.vert[0]], &v2 = verts[t.vert[1]], &v3 = verts[t.vert[2]];
- vec norm;
- norm.cross(vec(v2.pos).sub(v1.pos), vec(v3.pos).sub(v1.pos));
- if(!areaweight) norm.normalize();
- v1.norm.add(norm);
- v2.norm.add(norm);
- v3.norm.add(norm);
- }
- loopi(numverts) verts[i].norm.normalize();
- }
-
- template<class V, class T> void buildnorms(V *verts, int numverts, T *tris, int numtris, bool areaweight, int numframes)
- {
- if(!numverts) return;
- loopi(numframes) buildnorms(&verts[i*numverts], numverts, tris, numtris, areaweight);
- }
-
- static inline void fixqtangent(quat &q, float bt)
- {
- static const float bias = -1.5f/65535, biasscale = sqrtf(1 - bias*bias);
- if(bt < 0)
- {
- if(q.w >= 0) q.neg();
- if(q.w > bias) { q.mul3(biasscale); q.w = bias; }
- }
- else if(q.w < 0) q.neg();
- }
-
- template<class V> static inline void calctangent(V &v, const vec &n, const vec &t, float bt)
- {
- matrix3 m;
- m.c = n;
- m.a = t;
- m.b.cross(m.c, m.a);
- quat q(m);
- fixqtangent(q, bt);
- v.tangent = q;
- }
-
- template<class B, class V, class TC, class T> void calctangents(B *bumpverts, V *verts, TC *tcverts, int numverts, T *tris, int numtris, bool areaweight)
- {
- vec *tangent = new vec[2*numverts], *bitangent = tangent+numverts;
- for(int i=0;i<2*numverts;++i)tangent[i]=vec(0,0,0);
- //~memclear(tangent, 2*numverts);
- loopi(numtris)
- {
- const T &t = tris[i];
- const vec &e0 = verts[t.vert[0]].pos;
- vec e1 = vec(verts[t.vert[1]].pos).sub(e0), e2 = vec(verts[t.vert[2]].pos).sub(e0);
-
- const vec2 &tc0 = tcverts[t.vert[0]].tc,
- &tc1 = tcverts[t.vert[1]].tc,
- &tc2 = tcverts[t.vert[2]].tc;
- float u1 = tc1.x - tc0.x, v1 = tc1.y - tc0.y,
- u2 = tc2.x - tc0.x, v2 = tc2.y - tc0.y;
- vec u(e2), v(e2);
- u.mul(v1).sub(vec(e1).mul(v2));
- v.mul(u1).sub(vec(e1).mul(u2));
-
- if(vec().cross(e2, e1).dot(vec().cross(v, u)) >= 0)
- {
- u.neg();
- v.neg();
- }
-
- if(!areaweight)
- {
- u.normalize();
- v.normalize();
- }
-
- loopj(3)
- {
- tangent[t.vert[j]].sub(u);
- bitangent[t.vert[j]].add(v);
- }
- }
- loopi(numverts)
- {
- const vec &n = verts[i].norm,
- &t = tangent[i],
- &bt = bitangent[i];
- B &bv = bumpverts[i];
- matrix3 m;
- m.c = n;
- (m.a = t).project(m.c).normalize();
- m.b.cross(m.c, m.a);
- quat q(m);
- fixqtangent(q, m.b.dot(bt));
- bv.tangent = q;
- }
- delete[] tangent;
- }
-
- template<class B, class V, class TC, class T> void calctangents(B *bumpverts, V *verts, TC *tcverts, int numverts, T *tris, int numtris, bool areaweight, int numframes)
- {
- loopi(numframes) calctangents(&bumpverts[i*numverts], &verts[i*numverts], tcverts, numverts, tris, numtris, areaweight);
- }
- };
-
- struct meshgroup
- {
- meshgroup *next;
- int shared;
- char *name;
- vector<mesh *> meshes;
-
- meshgroup() : next(NULL), shared(0), name(NULL)
- {
- }
-
- virtual ~meshgroup()
- {
- DELETEA(name);
- meshes.deletecontents();
- DELETEP(next);
- }
-
- virtual int findtag(const char *name) { return -1; }
- virtual void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n) {}
-
- void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m)
- {
- loopv(meshes) meshes[i]->calcbb(bbmin, bbmax, m);
- }
-
- void genBIH(vector<skin> &skins, vector<BIH::mesh> &bih, const matrix4x3 &t)
- {
- loopv(meshes) meshes[i]->genBIH(skins[i], bih, t);
- }
-
- virtual void *animkey() { return this; }
- virtual int totalframes() const { return 1; }
- bool hasframe(int i) const { return i>=0 && i<totalframes(); }
- bool hasframes(int i, int n) const { return i>=0 && i+n<=totalframes(); }
- int clipframes(int i, int n) const { return min(n, totalframes() - i); }
-
- virtual void cleanup() {}
- virtual void preload(part *p) {}
- virtual void render(const animstate *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p) {}
-
- void bindpos(GLuint ebuf, GLuint vbuf, void *v, int stride)
- {
- if(lastebuf!=ebuf)
- {
- gle::bindebo(ebuf);
- lastebuf = ebuf;
- }
- if(lastvbuf!=vbuf)
- {
- gle::bindvbo(vbuf);
- if(!lastvbuf) gle::enablevertex();
- gle::vertexpointer(stride, v);
- lastvbuf = vbuf;
- }
- }
-
- void bindtc(void *v, int stride)
- {
- if(!enabletc)
- {
- gle::enabletexcoord0();
- enabletc = true;
- }
- if(lasttcbuf!=lastvbuf)
- {
- gle::texcoord0pointer(stride, v);
- lasttcbuf = lastvbuf;
- }
- }
-
- void bindnormals(void *v, int stride)
- {
- if(!enablenormals)
- {
- gle::enablenormal();
- enablenormals = true;
- }
- if(lastnbuf!=lastvbuf)
- {
- gle::normalpointer(stride, v);
- lastnbuf = lastvbuf;
- }
- }
-
- void bindtangents(void *v, int stride)
- {
- if(!enabletangents)
- {
- gle::enabletangent();
- enabletangents = true;
- }
- if(lastxbuf!=lastvbuf)
- {
- gle::tangentpointer(stride, v, GL_SHORT);
- lastxbuf = lastvbuf;
- }
- }
-
- void bindbones(void *wv, void *bv, int stride)
- {
- if(!enablebones)
- {
- gle::enableboneweight();
- gle::enableboneindex();
- enablebones = true;
- }
- if(lastbbuf!=lastvbuf)
- {
- gle::boneweightpointer(stride, wv);
- gle::boneindexpointer(stride, bv);
- lastbbuf = lastvbuf;
- }
- }
- };
-
- virtual meshgroup *loadmeshes(const char *name, va_list args) { return NULL; }
-
- meshgroup *sharemeshes(const char *name, ...)
- {
- static hashnameset<meshgroup *> meshgroups;
- if(!meshgroups.access(name))
- {
- va_list args;
- va_start(args, name);
- meshgroup *group = loadmeshes(name, args);
- va_end(args);
- if(!group) return NULL;
- meshgroups.add(group);
- }
- return meshgroups[name];
- }
-
- struct linkedpart
- {
- part *p;
- int tag, anim, basetime;
- vec translate;
- vec *pos;
- matrix4 matrix;
-
- linkedpart() : p(NULL), tag(-1), anim(-1), basetime(0), translate(0, 0, 0), pos(NULL) {}
- };
-
- struct part
- {
- animmodel *model;
- int index;
- meshgroup *meshes;
- vector<linkedpart> links;
- vector<skin> skins;
- vector<animspec> *anims[MAXANIMPARTS];
- int numanimparts;
- float pitchscale, pitchoffset, pitchmin, pitchmax;
- vec translate;
-
- part(animmodel *model, int index = 0) : model(model), index(index), meshes(NULL), numanimparts(1), pitchscale(1), pitchoffset(0), pitchmin(0), pitchmax(0), translate(0, 0, 0)
- {
- loopk(MAXANIMPARTS) anims[k] = NULL;
- }
- virtual ~part()
- {
- loopk(MAXANIMPARTS) DELETEA(anims[k]);
- }
-
- virtual void cleanup()
- {
- if(meshes) meshes->cleanup();
- loopv(skins) skins[i].cleanup();
- }
-
- void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m)
- {
- matrix4x3 t = m;
- t.scale(model->scale);
- t.translate(translate);
- meshes->calcbb(bbmin, bbmax, t);
- loopv(links)
- {
- matrix4x3 n;
- meshes->concattagtransform(this, links[i].tag, m, n);
- n.translate(links[i].translate, model->scale);
- links[i].p->calcbb(bbmin, bbmax, n);
- }
- }
-
- void genBIH(vector<BIH::mesh> &bih, const matrix4x3 &m)
- {
- matrix4x3 t = m;
- t.scale(model->scale);
- t.translate(translate);
- meshes->genBIH(skins, bih, t);
- loopv(links)
- {
- matrix4x3 n;
- meshes->concattagtransform(this, links[i].tag, m, n);
- n.translate(links[i].translate, model->scale);
- links[i].p->genBIH(bih, n);
- }
- }
-
- bool link(part *p, const char *tag, const vec &translate = vec(0, 0, 0), int anim = -1, int basetime = 0, vec *pos = NULL)
- {
- int i = meshes ? meshes->findtag(tag) : -1;
- if(i<0)
- {
- loopv(links) if(links[i].p && links[i].p->link(p, tag, translate, anim, basetime, pos)) return true;
- return false;
- }
- linkedpart &l = links.add();
- l.p = p;
- l.tag = i;
- l.anim = anim;
- l.basetime = basetime;
- l.translate = translate;
- l.pos = pos;
- return true;
- }
-
- bool unlink(part *p)
- {
- loopvrev(links) if(links[i].p==p) { links.remove(i, 1); return true; }
- loopv(links) if(links[i].p && links[i].p->unlink(p)) return true;
- return false;
- }
-
- void initskins(Texture *tex = notexture, Texture *masks = notexture, int limit = 0)
- {
- if(!limit)
- {
- if(!meshes) return;
- limit = meshes->meshes.length();
- }
- while(skins.length() < limit)
- {
- skin &s = skins.add();
- s.owner = this;
- s.tex = tex;
- s.masks = masks;
- }
- }
-
- bool envmapped()
- {
- loopv(skins) if(skins[i].envmapped()) return true;
- return false;
- }
-
- bool tangents()
- {
- loopv(skins) if(skins[i].tangents()) return true;
- return false;
- }
-
- void preloadBIH()
- {
- loopv(skins) skins[i].preloadBIH();
- }
-
- void preloadshaders(bool force)
- {
- loopv(skins) skins[i].preloadshader(force);
- }
-
- void preloadmeshes()
- {
- if(meshes) meshes->preload(this);
- }
-
- virtual void getdefaultanim(animinfo &info, int anim, uint varseed, dynent *d)
- {
- info.frame = 0;
- info.range = 1;
- }
-
- bool calcanim(int animpart, int anim, int basetime, int basetime2, dynent *d, int interp, animinfo &info, int &aitime)
- {
- uint varseed = uint((size_t)d);
- info.anim = anim;
- info.basetime = basetime;
- info.varseed = varseed;
- info.speed = anim&ANIM_SETSPEED ? basetime2 : 100.0f;
- if((anim&ANIM_INDEX)==ANIM_ALL)
- {
- info.frame = 0;
- info.range = meshes->totalframes();
- }
- else
- {
- animspec *spec = NULL;
- if(anims[animpart])
- {
- int primaryidx = anim&ANIM_INDEX;
- if(primaryidx < NUMANIMS)
- {
- vector<animspec> &primary = anims[animpart][primaryidx];
- if(primary.length()) spec = &primary[uint(varseed + basetime)%primary.length()];
- }
- if((anim>>ANIM_SECONDARY)&(ANIM_INDEX|ANIM_DIR))
- {
- int secondaryidx = (anim>>ANIM_SECONDARY)&ANIM_INDEX;
- if(secondaryidx < NUMANIMS)
- {
- vector<animspec> &secondary = anims[animpart][secondaryidx];
- if(secondary.length())
- {
- animspec &spec2 = secondary[uint(varseed + basetime2)%secondary.length()];
- if(!spec || spec2.priority > spec->priority)
- {
- spec = &spec2;
- info.anim >>= ANIM_SECONDARY;
- info.basetime = basetime2;
- }
- }
- }
- }
- }
- if(spec)
- {
- info.frame = spec->frame;
- info.range = spec->range;
- if(spec->speed>0) info.speed = 1000.0f/spec->speed;
- }
- else getdefaultanim(info, anim, uint(varseed + info.basetime), d);
- }
-
- info.anim &= (1<<ANIM_SECONDARY)-1;
- info.anim |= anim&ANIM_FLAGS;
- if((info.anim&ANIM_CLAMP) != ANIM_CLAMP)
- {
- if(info.anim&(ANIM_LOOP|ANIM_START|ANIM_END))
- {
- info.anim &= ~ANIM_SETTIME;
- if(!info.basetime) info.basetime = -((int)(size_t)d&0xFFF);
- }
- if(info.anim&(ANIM_START|ANIM_END))
- {
- if(info.anim&ANIM_END) info.frame += info.range-1;
- info.range = 1;
- }
- }
-
- if(!meshes->hasframes(info.frame, info.range))
- {
- if(!meshes->hasframe(info.frame)) return false;
- info.range = meshes->clipframes(info.frame, info.range);
- }
-
- if(d && interp>=0)
- {
- animinterpinfo &ai = d->animinterp[interp];
- if((info.anim&ANIM_CLAMP)==ANIM_CLAMP) aitime = min(aitime, int(info.range*info.speed*0.5e-3f));
- void *ak = meshes->animkey();
- if(d->ragdoll && !(anim&ANIM_RAGDOLL))
- {
- ai.prev.range = ai.cur.range = 0;
- ai.lastswitch = -1;
- }
- else if(ai.lastmodel!=ak || ai.lastswitch<0 || lastmillis-d->lastrendered>aitime)
- {
- ai.prev = ai.cur = info;
- ai.lastswitch = lastmillis-aitime*2;
- }
- else if(ai.cur!=info)
- {
- if(lastmillis-ai.lastswitch>aitime/2) ai.prev = ai.cur;
- ai.cur = info;
- ai.lastswitch = lastmillis;
- }
- else if(info.anim&ANIM_SETTIME) ai.cur.basetime = info.basetime;
- ai.lastmodel = ak;
- }
- return true;
- }
-
- void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d)
- {
- animstate as[MAXANIMPARTS];
- render(anim, basetime, basetime2, pitch, axis, forward, d, as);
- }
-
- void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, animstate *as)
- {
- if(!(anim&ANIM_REUSE)) loopi(numanimparts)
- {
- animinfo info;
- int interp = d && index+numanimparts<=MAXANIMPARTS ? index+i : -1, aitime = animationinterpolationtime;
- if(!calcanim(i, anim, basetime, basetime2, d, interp, info, aitime)) return;
- animstate &p = as[i];
- p.owner = this;
- p.cur.setframes(info);
- p.interp = 1;
- if(interp>=0 && d->animinterp[interp].prev.range>0)
- {
- int diff = lastmillis-d->animinterp[interp].lastswitch;
- if(diff<aitime)
- {
- p.prev.setframes(d->animinterp[interp].prev);
- p.interp = diff/float(aitime);
- }
- }
- }
-
- vec oaxis, oforward;
- matrixstack[matrixpos].transposedtransformnormal(axis, oaxis);
- float pitchamount = pitchscale*pitch + pitchoffset;
- if((pitchmin || pitchmax) && pitchmin <= pitchmax) pitchamount = clamp(pitchamount, pitchmin, pitchmax);
- if(as->cur.anim&ANIM_NOPITCH || (as->interp < 1 && as->prev.anim&ANIM_NOPITCH))
- pitchamount *= (as->cur.anim&ANIM_NOPITCH ? 0 : as->interp) + (as->interp < 1 && as->prev.anim&ANIM_NOPITCH ? 0 : 1-as->interp);
- if(pitchamount)
- {
- ++matrixpos;
- matrixstack[matrixpos] = matrixstack[matrixpos-1];
- matrixstack[matrixpos].rotate(pitchamount*RAD, oaxis);
- }
- matrixstack[matrixpos].transposedtransformnormal(forward, oforward);
-
- if(!(anim&ANIM_NORENDER))
- {
- matrix4 modelmatrix;
- modelmatrix.mul(shadowmapping ? shadowmatrix : camprojmatrix, matrixstack[matrixpos]);
- if(model->scale!=1) modelmatrix.scale(model->scale);
- if(!translate.iszero()) modelmatrix.translate(translate);
- GLOBALPARAM(modelmatrix, modelmatrix);
-
- if(!(anim&ANIM_NOSKIN))
- {
- if(envmapped()) GLOBALPARAM(modelworld, matrix3(matrixstack[matrixpos]));
-
- vec odir, ocampos;
- matrixstack[matrixpos].transposedtransformnormal(lightdir, odir);
- GLOBALPARAM(lightdir, odir);
- matrixstack[matrixpos].transposedtransform(camera1->o, ocampos);
- ocampos.div(model->scale).sub(translate);
- GLOBALPARAM(modelcamera, ocampos);
- }
- }
-
- meshes->render(as, pitch, oaxis, oforward, d, this);
-
- if(!(anim&ANIM_REUSE))
- {
- loopv(links)
- {
- linkedpart &link = links[i];
- link.matrix.translate(links[i].translate, model->scale);
-
- matrixpos++;
- matrixstack[matrixpos].mul(matrixstack[matrixpos-1], link.matrix);
-
- if(link.pos) *link.pos = matrixstack[matrixpos].gettranslation();
-
- if(!link.p)
- {
- matrixpos--;
- continue;
- }
-
- int nanim = anim, nbasetime = basetime, nbasetime2 = basetime2;
- if(link.anim>=0)
- {
- nanim = link.anim | (anim&ANIM_FLAGS);
- nbasetime = link.basetime;
- nbasetime2 = 0;
- }
- link.p->render(nanim, nbasetime, nbasetime2, pitch, axis, forward, d);
-
- matrixpos--;
- }
- }
-
- if(pitchamount) matrixpos--;
- }
-
- void setanim(int animpart, int num, int frame, int range, float speed, int priority = 0)
- {
- if(animpart<0 || animpart>=MAXANIMPARTS) return;
- if(frame<0 || range<=0 || !meshes || !meshes->hasframes(frame, range))
- {
- conoutf(CON_ERROR, "invalid frame %d, range %d in model %s", frame, range, model->name);
- return;
- }
- if(!anims[animpart]) anims[animpart] = new vector<animspec>[NUMANIMS];
- animspec &spec = anims[animpart][num].add();
- spec.frame = frame;
- spec.range = range;
- spec.speed = speed;
- spec.priority = priority;
- }
-
- virtual void loaded()
- {
- meshes->shared++;
- loopv(skins) skins[i].setkey();
- }
- };
-
- enum
- {
- LINK_TAG = 0,
- LINK_COOP,
- LINK_REUSE
- };
-
- virtual int linktype(animmodel *m) const { return LINK_TAG; }
-
- void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, modelattach *a)
- {
- int numtags = 0;
- if(a)
- {
- int index = parts.last()->index + parts.last()->numanimparts;
- for(int i = 0; a[i].tag; i++)
- {
- numtags++;
-
- animmodel *m = (animmodel *)a[i].m;
- if(!m)
- {
- if(a[i].pos) link(NULL, a[i].tag, vec(0, 0, 0), 0, 0, a[i].pos);
- continue;
- }
- part *p = m->parts[0];
- switch(linktype(m))
- {
- case LINK_TAG:
- p->index = link(p, a[i].tag, vec(0, 0, 0), a[i].anim, a[i].basetime, a[i].pos) ? index : -1;
- break;
-
- case LINK_COOP:
- p->index = index;
- break;
-
- default:
- continue;
- }
- index += p->numanimparts;
- }
- }
-
- animstate as[MAXANIMPARTS];
- parts[0]->render(anim, basetime, basetime2, pitch, axis, forward, d, as);
-
- if(a) for(int i = numtags-1; i >= 0; i--)
- {
- animmodel *m = (animmodel *)a[i].m;
- if(!m)
- {
- if(a[i].pos) unlink(NULL);
- continue;
- }
- part *p = m->parts[0];
- switch(linktype(m))
- {
- case LINK_TAG:
- if(p->index >= 0) unlink(p);
- p->index = 0;
- break;
-
- case LINK_COOP:
- p->render(anim, basetime, basetime2, pitch, axis, forward, d);
- p->index = 0;
- break;
-
- case LINK_REUSE:
- p->render(anim | ANIM_REUSE, basetime, basetime2, pitch, axis, forward, d, as);
- break;
- }
- }
- }
-
- void render(int anim, int basetime, int basetime2, const vec &o, float yaw, float pitch, dynent *d, modelattach *a, const vec &color, const vec &dir, float trans)
- {
- yaw += spinyaw*lastmillis/1000.0f;
- pitch += offsetpitch + spinpitch*lastmillis/1000.0f;
-
- vec axis(0, -1, 0), forward(1, 0, 0);
-
- matrixpos = 0;
- matrixstack[0].identity();
- if(!d || !d->ragdoll || anim&ANIM_RAGDOLL)
- {
- matrixstack[0].settranslation(o);
- matrixstack[0].rotate_around_z(yaw*RAD);
- matrixstack[0].transformnormal(vec(axis), axis);
- matrixstack[0].transformnormal(vec(forward), forward);
- if(offsetyaw) matrixstack[0].rotate_around_z(offsetyaw*RAD);
- }
- else pitch = 0;
-
- if(anim&ANIM_NORENDER)
- {
- render(anim, basetime, basetime2, pitch, axis, forward, d, a);
- if(d) d->lastrendered = lastmillis;
- return;
- }
-
- if(!(anim&ANIM_NOSKIN))
- {
- transparent = trans;
- lightdir = dir;
- lightcolor = color;
-
- if(envmapped())
- {
- setupenvmap:
- closestenvmaptex = lookupenvmap(closestenvmap(o));
- GLOBALPARAM(lightdirworld, dir);
- }
- else if(a) for(int i = 0; a[i].tag; i++) if(a[i].m && a[i].m->envmapped()) goto setupenvmap;
- }
-
- if(depthoffset && !enabledepthoffset)
- {
- enablepolygonoffset(GL_POLYGON_OFFSET_FILL);
- enabledepthoffset = true;
- }
-
- if(transparent<1)
- {
- if(anim&ANIM_GHOST)
- {
- glDepthFunc(GL_GREATER);
- glDepthMask(GL_FALSE);
- }
- else if(alphadepth)
- {
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
- render(anim|ANIM_NOSKIN, basetime, basetime2, pitch, axis, forward, d, a);
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, fading ? GL_FALSE : GL_TRUE);
-
- glDepthFunc(GL_LEQUAL);
- }
-
- if(!enablealphablend)
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- enablealphablend = true;
- }
- }
-
- render(anim, basetime, basetime2, pitch, axis, forward, d, a);
-
- if(transparent<1 && (alphadepth || anim&ANIM_GHOST))
- {
- glDepthFunc(GL_LESS);
- if(anim&ANIM_GHOST) glDepthMask(GL_TRUE);
- }
-
- if(d) d->lastrendered = lastmillis;
- }
-
- vector<part *> parts;
-
- animmodel(const char *name) : model(name)
- {
- }
-
- ~animmodel()
- {
- parts.deletecontents();
- }
-
- void cleanup()
- {
- loopv(parts) parts[i]->cleanup();
- }
-
- virtual void flushpart() {}
-
- part &addpart()
- {
- flushpart();
- part *p = new part(this, parts.length());
- parts.add(p);
- return *p;
- }
-
- void initmatrix(matrix4x3 &m)
- {
- m.identity();
- if(offsetyaw) m.rotate_around_z(offsetyaw*RAD);
- if(offsetpitch) m.rotate_around_y(-offsetpitch*RAD);
- }
-
- void genBIH(vector<BIH::mesh> &bih)
- {
- if(parts.empty()) return;
- matrix4x3 m;
- initmatrix(m);
- parts[0]->genBIH(bih, m);
- }
-
- void preloadBIH()
- {
- model::preloadBIH();
- if(bih) loopv(parts) parts[i]->preloadBIH();
- }
-
- BIH *setBIH()
- {
- if(bih) return bih;
- vector<BIH::mesh> meshes;
- genBIH(meshes);
- bih = new BIH(meshes);
- return bih;
- }
-
- bool link(part *p, const char *tag, const vec &translate = vec(0, 0, 0), int anim = -1, int basetime = 0, vec *pos = NULL)
- {
- if(parts.empty()) return false;
- return parts[0]->link(p, tag, translate, anim, basetime, pos);
- }
-
- bool unlink(part *p)
- {
- if(parts.empty()) return false;
- return parts[0]->unlink(p);
- }
-
- bool envmapped()
- {
- loopv(parts) if(parts[i]->envmapped()) return true;
- return false;
- }
-
- virtual bool flipy() const { return false; }
- virtual bool loadconfig() { return false; }
- virtual bool loaddefaultparts() { return false; }
- virtual void startload() {}
- virtual void endload() {}
-
- bool load()
- {
- startload();
- bool success = loadconfig() && parts.length(); // configured model, will call the model commands below
- if(!success)
- success = loaddefaultparts(); // model without configuration, try default tris and skin
- flushpart();
- endload();
- if(flipy()) translate.y = -translate.y;
-
- if(!success) return false;
- loopv(parts) if(!parts[i]->meshes) return false;
-
- loaded();
- return true;
- }
-
- void preloadshaders(bool force)
- {
- loopv(parts) parts[i]->preloadshaders(force);
- }
-
- void preloadmeshes()
- {
- loopv(parts) parts[i]->preloadmeshes();
- }
-
- void setshader(Shader *shader)
- {
- if(parts.empty()) loaddefaultparts();
- loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].shader = shader;
- }
-
- void setenvmap(float envmapmin, float envmapmax, Texture *envmap)
- {
- if(parts.empty()) loaddefaultparts();
- loopv(parts) loopvj(parts[i]->skins)
- {
- skin &s = parts[i]->skins[j];
- if(envmapmax)
- {
- s.envmapmin = envmapmin;
- s.envmapmax = envmapmax;
- }
- if(envmap) s.envmap = envmap;
- }
- }
-
- void setspec(float spec)
- {
- if(parts.empty()) loaddefaultparts();
- loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].spec = spec;
- }
-
- void setambient(float ambient)
- {
- if(parts.empty()) loaddefaultparts();
- loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].ambient = ambient;
- }
-
- void setglow(float glow, float delta, float pulse)
- {
- if(parts.empty()) loaddefaultparts();
- loopv(parts) loopvj(parts[i]->skins)
- {
- skin &s = parts[i]->skins[j];
- s.glow = glow;
- s.glowdelta = delta;
- s.glowpulse = pulse;
- }
- }
-
- void setglare(float specglare, float glowglare)
- {
- if(parts.empty()) loaddefaultparts();
- loopv(parts) loopvj(parts[i]->skins)
- {
- skin &s = parts[i]->skins[j];
- s.specglare = specglare;
- s.glowglare = glowglare;
- }
- }
-
- void setalphatest(float alphatest)
- {
- if(parts.empty()) loaddefaultparts();
- loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].alphatest = alphatest;
- }
-
- void setalphablend(bool alphablend)
- {
- if(parts.empty()) loaddefaultparts();
- loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].alphablend = alphablend;
- }
-
- void setfullbright(float fullbright)
- {
- if(parts.empty()) loaddefaultparts();
- loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].fullbright = fullbright;
- }
-
- void setcullface(bool cullface)
- {
- if(parts.empty()) loaddefaultparts();
- loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].cullface = cullface;
- }
-
- void calcbb(vec &center, vec &radius)
- {
- if(parts.empty()) return;
- vec bbmin(1e16f, 1e16f, 1e16f), bbmax(-1e16f, -1e16f, -1e16f);
- matrix4x3 m;
- initmatrix(m);
- parts[0]->calcbb(bbmin, bbmax, m);
- radius = bbmax;
- radius.sub(bbmin);
- radius.mul(0.5f);
- center = bbmin;
- center.add(radius);
- }
-
- virtual void loaded()
- {
- scale /= 4;
- if(parts.length()) parts[0]->translate = translate;
- loopv(parts) parts[i]->loaded();
- }
-
- static bool enabletc, enablealphablend, enablecullface, enablenormals, enabletangents, enablebones, enabledepthoffset;
- static vec lightdir, lightcolor;
- static float transparent, lastalphatest;
- static GLuint lastvbuf, lasttcbuf, lastnbuf, lastxbuf, lastbbuf, lastebuf, lastenvmaptex, closestenvmaptex;
- static Texture *lasttex, *lastmasks, *lastnormalmap;
- static int matrixpos;
- static matrix4 matrixstack[64];
-
- void startrender()
- {
- enabletc = enablealphablend = enablenormals = enabletangents = enablebones = enabledepthoffset = false;
- enablecullface = true;
- lastalphatest = -1;
- lastvbuf = lasttcbuf = lastxbuf = lastnbuf = lastbbuf = lastebuf = lastenvmaptex = closestenvmaptex = 0;
- lasttex = lastmasks = lastnormalmap = NULL;
- transparent = 1;
- shaderparamskey::invalidate();
- }
-
- static void disablebones()
- {
- gle::disableboneweight();
- gle::disableboneindex();
- enablebones = false;
- }
-
- static void disabletangents()
- {
- gle::disabletangent();
- enabletangents = false;
- }
-
- static void disabletc()
- {
- gle::disabletexcoord0();
- enabletc = false;
- }
-
- static void disablenormals()
- {
- gle::disablenormal();
- enablenormals = false;
- }
-
- static void disablevbo()
- {
- if(lastebuf) gle::clearebo();
- if(lastvbuf)
- {
- gle::clearvbo();
- gle::disablevertex();
- }
- if(enabletc) disabletc();
- if(enablenormals) disablenormals();
- if(enabletangents) disabletangents();
- if(enablebones) disablebones();
- lastvbuf = lasttcbuf = lastxbuf = lastnbuf = lastbbuf = lastebuf = 0;
- }
-
- void endrender()
- {
- if(lastvbuf || lastebuf) disablevbo();
- if(enablealphablend) glDisable(GL_BLEND);
- if(!enablecullface) glEnable(GL_CULL_FACE);
- if(enabledepthoffset) disablepolygonoffset(GL_POLYGON_OFFSET_FILL);
- }
+ struct animspec
+ {
+ int frame, range;
+ float speed;
+ int priority;
+ };
+
+ struct animpos
+ {
+ int anim, fr1, fr2;
+ float t;
+
+ void setframes(const animinfo &info)
+ {
+ anim = info.anim;
+ if(info.range<=1)
+ {
+ fr1 = 0;
+ t = 0;
+ }
+ else
+ {
+ int time = info.anim&ANIM_SETTIME ? info.basetime : lastmillis-info.basetime;
+ fr1 = (int)(time/info.speed); // round to full frames
+ t = (time-fr1*info.speed)/info.speed; // progress of the frame, value from 0.0f to 1.0f
+ }
+ if(info.anim&ANIM_LOOP)
+ {
+ fr1 = fr1%info.range+info.frame;
+ fr2 = fr1+1;
+ if(fr2>=info.frame+info.range) fr2 = info.frame;
+ }
+ else
+ {
+ fr1 = min(fr1, info.range-1)+info.frame;
+ fr2 = min(fr1+1, info.frame+info.range-1);
+ }
+ if(info.anim&ANIM_REVERSE)
+ {
+ fr1 = (info.frame+info.range-1)-(fr1-info.frame);
+ fr2 = (info.frame+info.range-1)-(fr2-info.frame);
+ }
+ }
+
+ bool operator==(const animpos &a) const { return fr1==a.fr1 && fr2==a.fr2 && (fr1==fr2 || t==a.t); }
+ bool operator!=(const animpos &a) const { return fr1!=a.fr1 || fr2!=a.fr2 || (fr1!=fr2 && t!=a.t); }
+ };
+
+ struct part;
+
+ struct animstate
+ {
+ part *owner;
+ animpos cur, prev;
+ float interp;
+
+ bool operator==(const animstate &a) const { return cur==a.cur && (interp<1 ? interp==a.interp && prev==a.prev : a.interp>=1); }
+ bool operator!=(const animstate &a) const { return cur!=a.cur || (interp<1 ? interp!=a.interp || prev!=a.prev : a.interp<1); }
+ };
+
+ struct linkedpart;
+ struct mesh;
+
+ struct shaderparams
+ {
+ float spec, ambient, glow, glowdelta, glowpulse, specglare, glowglare, fullbright, envmapmin, envmapmax, scrollu, scrollv, alphatest;
+
+ shaderparams() : spec(1.0f), ambient(0.3f), glow(3.0f), glowdelta(0), glowpulse(0), specglare(1), glowglare(1), fullbright(0), envmapmin(0), envmapmax(0), scrollu(0), scrollv(0), alphatest(0.9f) {}
+ };
+
+ struct shaderparamskey
+ {
+ static hashtable<shaderparams, shaderparamskey> keys;
+ static int firstversion, lastversion;
+
+ int version;
+
+ shaderparamskey() : version(-1) {}
+
+ bool checkversion()
+ {
+ if(version >= firstversion) return true;
+ version = lastversion;
+ if(++lastversion <= 0)
+ {
+ enumerate(keys, shaderparamskey, key, key.version = -1);
+ firstversion = 0;
+ lastversion = 1;
+ version = 0;
+ }
+ return false;
+ }
+
+ static inline void invalidate()
+ {
+ firstversion = lastversion;
+ }
+ };
+
+ struct skin : shaderparams
+ {
+ part *owner;
+ Texture *tex, *masks, *envmap, *normalmap;
+ Shader *shader;
+ bool alphablend, cullface;
+ shaderparamskey *key;
+
+ skin() : owner(0), tex(notexture), masks(notexture), envmap(NULL), normalmap(NULL), shader(NULL), alphablend(true), cullface(true), key(NULL) {}
+
+ bool masked() const { return masks != notexture; }
+ bool envmapped() { return envmapmax>0 && envmapmodels; }
+ bool bumpmapped() { return normalmap && bumpmodels; }
+ bool tangents() { return bumpmapped(); }
+ bool alphatested() const { return alphatest > 0 && tex->type&Texture::ALPHA; }
+
+ void setkey()
+ {
+ key = &shaderparamskey::keys[*this];
+ }
+
+ void setshaderparams(mesh *m, const animstate *as)
+ {
+ if(!Shader::lastshader) return;
+
+ float mincolor = as->cur.anim&ANIM_FULLBRIGHT ? fullbrightmodels/100.0f : 0.0f;
+ if(fullbright)
+ {
+ gle::colorf(fullbright/2, fullbright/2, fullbright/2, transparent);
+ }
+ else
+ {
+ gle::color(vec(lightcolor).max(mincolor), transparent);
+ }
+
+ if(key->checkversion() && Shader::lastshader->owner == key) return;
+ Shader::lastshader->owner = key;
+
+ if(alphatested()) LOCALPARAMF(alphatest, alphatest);
+
+ if(fullbright)
+ {
+ LOCALPARAMF(lightscale, 0, 0, 2);
+ }
+ else
+ {
+ float bias = max(mincolor-1.0f, 0.2f), scale = 0.5f*max(0.8f-bias, 0.0f),
+ minshade = scale*max(ambient, mincolor);
+ LOCALPARAMF(lightscale, scale - minshade, scale, minshade + bias);
+ }
+ float curglow = glow;
+ if(glowpulse > 0)
+ {
+ float curpulse = lastmillis*glowpulse;
+ curpulse -= floor(curpulse);
+ curglow += glowdelta*2*fabs(curpulse - 0.5f);
+ }
+ LOCALPARAMF(maskscale, 0.5f*spec, 0.5f*curglow, 16*specglare, 4*glowglare);
+ LOCALPARAMF(texscroll, scrollu*lastmillis/1000.0f, scrollv*lastmillis/1000.0f);
+ if(envmapped()) LOCALPARAMF(envmapscale, envmapmin-envmapmax, envmapmax);
+ }
+
+ Shader *loadshader()
+ {
+ #define DOMODELSHADER(name, body) \
+ do { \
+ static Shader *name##shader = NULL; \
+ if(!name##shader) name##shader = useshaderbyname(#name); \
+ body; \
+ } while(0)
+ #define LOADMODELSHADER(name) DOMODELSHADER(name, return name##shader)
+ #define SETMODELSHADER(m, name) DOMODELSHADER(name, (m)->setshader(name##shader))
+ if(shader) return shader;
+
+ string opts;
+ int optslen = 0;
+ if(alphatested()) opts[optslen++] = 'a';
+ if(owner->tangents()) opts[optslen++] = 'q';
+ if(bumpmapped()) opts[optslen++] = 'n';
+ if(envmapped()) opts[optslen++] = 'e';
+ if(masked()) opts[optslen++] = 'm';
+ if(!fullbright && (masked() || spec>=0.01f)) opts[optslen++] = 's';
+ opts[optslen++] = '\0';
+
+ defformatstring(name, "model%s", opts);
+ shader = generateshader(name, "modelshader \"%s\"", opts);
+ return shader;
+ }
+
+ void cleanup()
+ {
+ if(shader && shader->standard) shader = NULL;
+ }
+
+ void preloadBIH()
+ {
+ if(tex->type&Texture::ALPHA && !tex->alphamask) loadalphamask(tex);
+ }
+
+ void preloadshader(bool force)
+ {
+ if(force) cleanup();
+ loadshader();
+ }
+
+ void setshader(mesh *m, const animstate *as)
+ {
+ m->setshader(loadshader());
+ }
+
+ void bind(mesh *b, const animstate *as)
+ {
+ if(!cullface && enablecullface) { glDisable(GL_CULL_FACE); enablecullface = false; }
+ else if(cullface && !enablecullface) { glEnable(GL_CULL_FACE); enablecullface = true; }
+
+ if(as->cur.anim&ANIM_NOSKIN)
+ {
+ if(enablealphablend) { glDisable(GL_BLEND); enablealphablend = false; }
+ if(shadowmapping) SETMODELSHADER(b, shadowmapcaster);
+ else /*if(as->cur.anim&ANIM_SHADOW)*/ SETMODELSHADER(b, notexturemodel);
+ return;
+ }
+ setshader(b, as);
+ setshaderparams(b, as);
+ int activetmu = 0;
+ if(tex!=lasttex)
+ {
+ glBindTexture(GL_TEXTURE_2D, tex->id);
+ lasttex = tex;
+ }
+ if(bumpmapped() && normalmap !=lastnormalmap)
+ {
+ glActiveTexture_(GL_TEXTURE3);
+ activetmu = 3;
+ glBindTexture(GL_TEXTURE_2D, normalmap->id);
+ lastnormalmap = normalmap;
+ }
+ if(tex->type&Texture::ALPHA)
+ {
+ if(alphablend)
+ {
+ if(!enablealphablend && !reflecting && !refracting)
+ {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ enablealphablend = true;
+ }
+ }
+ else if(enablealphablend) { glDisable(GL_BLEND); enablealphablend = false; }
+ }
+ else if(enablealphablend && transparent>=1) { glDisable(GL_BLEND); enablealphablend = false; }
+ if(masked() && masks!=lastmasks)
+ {
+ glActiveTexture_(GL_TEXTURE1);
+ activetmu = 1;
+ glBindTexture(GL_TEXTURE_2D, masks->id);
+ lastmasks = masks;
+ }
+ if(envmapped())
+ {
+ GLuint emtex = envmap ? envmap->id : closestenvmaptex;
+ if(lastenvmaptex!=emtex)
+ {
+ glActiveTexture_(GL_TEXTURE2);
+ activetmu = 2;
+ glBindTexture(GL_TEXTURE_CUBE_MAP, emtex);
+ lastenvmaptex = emtex;
+ }
+ }
+ if(activetmu != 0) glActiveTexture_(GL_TEXTURE0);
+ }
+ };
+
+ struct meshgroup;
+
+ struct mesh
+ {
+ meshgroup *group;
+ char *name;
+ bool noclip;
+
+ mesh() : group(NULL), name(NULL), noclip(false)
+ {
+ }
+
+ virtual ~mesh()
+ {
+ DELETEA(name);
+ }
+
+ virtual void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m) {}
+
+ virtual void genBIH(BIH::mesh &m) {}
+ void genBIH(skin &s, vector<BIH::mesh> &bih, const matrix4x3 &t)
+ {
+ BIH::mesh &m = bih.add();
+ m.xform = t;
+ m.tex = s.tex;
+ if(s.tex->type&Texture::ALPHA) m.flags |= BIH::MESH_ALPHA;
+ if(noclip) m.flags |= BIH::MESH_NOCLIP;
+ if(s.cullface) m.flags |= BIH::MESH_CULLFACE;
+ genBIH(m);
+ while(bih.last().numtris > BIH::mesh::MAXTRIS)
+ {
+ BIH::mesh &overflow = bih.dup();
+ overflow.tris += BIH::mesh::MAXTRIS;
+ overflow.numtris -= BIH::mesh::MAXTRIS;
+ bih[bih.length()-2].numtris = BIH::mesh::MAXTRIS;
+ }
+ }
+
+ virtual void setshader(Shader *s)
+ {
+ if(glaring) s->setvariant(0, 1);
+ else s->set();
+ }
+
+ template<class V, class T> void smoothnorms(V *verts, int numverts, T *tris, int numtris, float limit, bool areaweight)
+ {
+ hashtable<vec, int> share;
+ int *next = new int[numverts];
+ for(int i=0;i<numverts;++i)next[i]=-1;
+ //~memset(next, -1, numverts*sizeof(int));
+ loopi(numverts)
+ {
+ V &v = verts[i];
+ v.norm = vec(0, 0, 0);
+ int idx = share.access(v.pos, i);
+ if(idx != i) { next[i] = next[idx]; next[idx] = i; }
+ }
+ loopi(numtris)
+ {
+ T &t = tris[i];
+ V &v1 = verts[t.vert[0]], &v2 = verts[t.vert[1]], &v3 = verts[t.vert[2]];
+ vec norm;
+ norm.cross(vec(v2.pos).sub(v1.pos), vec(v3.pos).sub(v1.pos));
+ if(!areaweight) norm.normalize();
+ v1.norm.add(norm);
+ v2.norm.add(norm);
+ v3.norm.add(norm);
+ }
+ vec *norms = new vec[numverts];
+ for(int i=0;i<numverts;++i)norms[i]=vec(0,0,0);
+ //~memclear(norms, numverts);
+ loopi(numverts)
+ {
+ V &v = verts[i];
+ norms[i].add(v.norm);
+ if(next[i] >= 0)
+ {
+ float vlimit = limit*v.norm.magnitude();
+ for(int j = next[i]; j >= 0; j = next[j])
+ {
+ V &o = verts[j];
+ if(v.norm.dot(o.norm) >= vlimit*o.norm.magnitude())
+ {
+ norms[i].add(o.norm);
+ norms[j].add(v.norm);
+ }
+ }
+ }
+ }
+ loopi(numverts) verts[i].norm = norms[i].normalize();
+ delete[] next;
+ delete[] norms;
+ }
+
+ template<class V, class T> void buildnorms(V *verts, int numverts, T *tris, int numtris, bool areaweight)
+ {
+ loopi(numverts) verts[i].norm = vec(0, 0, 0);
+ loopi(numtris)
+ {
+ T &t = tris[i];
+ V &v1 = verts[t.vert[0]], &v2 = verts[t.vert[1]], &v3 = verts[t.vert[2]];
+ vec norm;
+ norm.cross(vec(v2.pos).sub(v1.pos), vec(v3.pos).sub(v1.pos));
+ if(!areaweight) norm.normalize();
+ v1.norm.add(norm);
+ v2.norm.add(norm);
+ v3.norm.add(norm);
+ }
+ loopi(numverts) verts[i].norm.normalize();
+ }
+
+ template<class V, class T> void buildnorms(V *verts, int numverts, T *tris, int numtris, bool areaweight, int numframes)
+ {
+ if(!numverts) return;
+ loopi(numframes) buildnorms(&verts[i*numverts], numverts, tris, numtris, areaweight);
+ }
+
+ static inline void fixqtangent(quat &q, float bt)
+ {
+ static const float bias = -1.5f/65535, biasscale = sqrtf(1 - bias*bias);
+ if(bt < 0)
+ {
+ if(q.w >= 0) q.neg();
+ if(q.w > bias) { q.mul3(biasscale); q.w = bias; }
+ }
+ else if(q.w < 0) q.neg();
+ }
+
+ template<class V> static inline void calctangent(V &v, const vec &n, const vec &t, float bt)
+ {
+ matrix3 m;
+ m.c = n;
+ m.a = t;
+ m.b.cross(m.c, m.a);
+ quat q(m);
+ fixqtangent(q, bt);
+ v.tangent = q;
+ }
+
+ template<class B, class V, class TC, class T> void calctangents(B *bumpverts, V *verts, TC *tcverts, int numverts, T *tris, int numtris, bool areaweight)
+ {
+ vec *tangent = new vec[2*numverts], *bitangent = tangent+numverts;
+ for(int i=0;i<2*numverts;++i)tangent[i]=vec(0,0,0);
+ //~memclear(tangent, 2*numverts);
+ loopi(numtris)
+ {
+ const T &t = tris[i];
+ const vec &e0 = verts[t.vert[0]].pos;
+ vec e1 = vec(verts[t.vert[1]].pos).sub(e0), e2 = vec(verts[t.vert[2]].pos).sub(e0);
+
+ const vec2 &tc0 = tcverts[t.vert[0]].tc,
+ &tc1 = tcverts[t.vert[1]].tc,
+ &tc2 = tcverts[t.vert[2]].tc;
+ float u1 = tc1.x - tc0.x, v1 = tc1.y - tc0.y,
+ u2 = tc2.x - tc0.x, v2 = tc2.y - tc0.y;
+ vec u(e2), v(e2);
+ u.mul(v1).sub(vec(e1).mul(v2));
+ v.mul(u1).sub(vec(e1).mul(u2));
+
+ if(vec().cross(e2, e1).dot(vec().cross(v, u)) >= 0)
+ {
+ u.neg();
+ v.neg();
+ }
+
+ if(!areaweight)
+ {
+ u.normalize();
+ v.normalize();
+ }
+
+ loopj(3)
+ {
+ tangent[t.vert[j]].sub(u);
+ bitangent[t.vert[j]].add(v);
+ }
+ }
+ loopi(numverts)
+ {
+ const vec &n = verts[i].norm,
+ &t = tangent[i],
+ &bt = bitangent[i];
+ B &bv = bumpverts[i];
+ matrix3 m;
+ m.c = n;
+ (m.a = t).project(m.c).normalize();
+ m.b.cross(m.c, m.a);
+ quat q(m);
+ fixqtangent(q, m.b.dot(bt));
+ bv.tangent = q;
+ }
+ delete[] tangent;
+ }
+
+ template<class B, class V, class TC, class T> void calctangents(B *bumpverts, V *verts, TC *tcverts, int numverts, T *tris, int numtris, bool areaweight, int numframes)
+ {
+ loopi(numframes) calctangents(&bumpverts[i*numverts], &verts[i*numverts], tcverts, numverts, tris, numtris, areaweight);
+ }
+ };
+
+ struct meshgroup
+ {
+ meshgroup *next;
+ int shared;
+ char *name;
+ vector<mesh *> meshes;
+
+ meshgroup() : next(NULL), shared(0), name(NULL)
+ {
+ }
+
+ virtual ~meshgroup()
+ {
+ DELETEA(name);
+ meshes.deletecontents();
+ DELETEP(next);
+ }
+
+ virtual int findtag(const char *name) { return -1; }
+ virtual void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n) {}
+
+ void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m)
+ {
+ loopv(meshes) meshes[i]->calcbb(bbmin, bbmax, m);
+ }
+
+ void genBIH(vector<skin> &skins, vector<BIH::mesh> &bih, const matrix4x3 &t)
+ {
+ loopv(meshes) meshes[i]->genBIH(skins[i], bih, t);
+ }
+
+ virtual void *animkey() { return this; }
+ virtual int totalframes() const { return 1; }
+ bool hasframe(int i) const { return i>=0 && i<totalframes(); }
+ bool hasframes(int i, int n) const { return i>=0 && i+n<=totalframes(); }
+ int clipframes(int i, int n) const { return min(n, totalframes() - i); }
+
+ virtual void cleanup() {}
+ virtual void preload(part *p) {}
+ virtual void render(const animstate *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p) {}
+
+ void bindpos(GLuint ebuf, GLuint vbuf, void *v, int stride)
+ {
+ if(lastebuf!=ebuf)
+ {
+ gle::bindebo(ebuf);
+ lastebuf = ebuf;
+ }
+ if(lastvbuf!=vbuf)
+ {
+ gle::bindvbo(vbuf);
+ if(!lastvbuf) gle::enablevertex();
+ gle::vertexpointer(stride, v);
+ lastvbuf = vbuf;
+ }
+ }
+
+ void bindtc(void *v, int stride)
+ {
+ if(!enabletc)
+ {
+ gle::enabletexcoord0();
+ enabletc = true;
+ }
+ if(lasttcbuf!=lastvbuf)
+ {
+ gle::texcoord0pointer(stride, v);
+ lasttcbuf = lastvbuf;
+ }
+ }
+
+ void bindnormals(void *v, int stride)
+ {
+ if(!enablenormals)
+ {
+ gle::enablenormal();
+ enablenormals = true;
+ }
+ if(lastnbuf!=lastvbuf)
+ {
+ gle::normalpointer(stride, v);
+ lastnbuf = lastvbuf;
+ }
+ }
+
+ void bindtangents(void *v, int stride)
+ {
+ if(!enabletangents)
+ {
+ gle::enabletangent();
+ enabletangents = true;
+ }
+ if(lastxbuf!=lastvbuf)
+ {
+ gle::tangentpointer(stride, v, GL_SHORT);
+ lastxbuf = lastvbuf;
+ }
+ }
+
+ void bindbones(void *wv, void *bv, int stride)
+ {
+ if(!enablebones)
+ {
+ gle::enableboneweight();
+ gle::enableboneindex();
+ enablebones = true;
+ }
+ if(lastbbuf!=lastvbuf)
+ {
+ gle::boneweightpointer(stride, wv);
+ gle::boneindexpointer(stride, bv);
+ lastbbuf = lastvbuf;
+ }
+ }
+ };
+
+ virtual meshgroup *loadmeshes(const char *name, va_list args) { return NULL; }
+
+ meshgroup *sharemeshes(const char *name, ...)
+ {
+ static hashnameset<meshgroup *> meshgroups;
+ if(!meshgroups.access(name))
+ {
+ va_list args;
+ va_start(args, name);
+ meshgroup *group = loadmeshes(name, args);
+ va_end(args);
+ if(!group) return NULL;
+ meshgroups.add(group);
+ }
+ return meshgroups[name];
+ }
+
+ struct linkedpart
+ {
+ part *p;
+ int tag, anim, basetime;
+ vec translate;
+ vec *pos;
+ matrix4 matrix;
+
+ linkedpart() : p(NULL), tag(-1), anim(-1), basetime(0), translate(0, 0, 0), pos(NULL) {}
+ };
+
+ struct part
+ {
+ animmodel *model;
+ int index;
+ meshgroup *meshes;
+ vector<linkedpart> links;
+ vector<skin> skins;
+ vector<animspec> *anims[MAXANIMPARTS];
+ int numanimparts;
+ float pitchscale, pitchoffset, pitchmin, pitchmax;
+ vec translate;
+
+ part(animmodel *model, int index = 0) : model(model), index(index), meshes(NULL), numanimparts(1), pitchscale(1), pitchoffset(0), pitchmin(0), pitchmax(0), translate(0, 0, 0)
+ {
+ loopk(MAXANIMPARTS) anims[k] = NULL;
+ }
+ virtual ~part()
+ {
+ loopk(MAXANIMPARTS) DELETEA(anims[k]);
+ }
+
+ virtual void cleanup()
+ {
+ if(meshes) meshes->cleanup();
+ loopv(skins) skins[i].cleanup();
+ }
+
+ void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m)
+ {
+ matrix4x3 t = m;
+ t.scale(model->scale);
+ t.translate(translate);
+ meshes->calcbb(bbmin, bbmax, t);
+ loopv(links)
+ {
+ matrix4x3 n;
+ meshes->concattagtransform(this, links[i].tag, m, n);
+ n.translate(links[i].translate, model->scale);
+ links[i].p->calcbb(bbmin, bbmax, n);
+ }
+ }
+
+ void genBIH(vector<BIH::mesh> &bih, const matrix4x3 &m)
+ {
+ matrix4x3 t = m;
+ t.scale(model->scale);
+ t.translate(translate);
+ meshes->genBIH(skins, bih, t);
+ loopv(links)
+ {
+ matrix4x3 n;
+ meshes->concattagtransform(this, links[i].tag, m, n);
+ n.translate(links[i].translate, model->scale);
+ links[i].p->genBIH(bih, n);
+ }
+ }
+
+ bool link(part *p, const char *tag, const vec &translate = vec(0, 0, 0), int anim = -1, int basetime = 0, vec *pos = NULL)
+ {
+ int i = meshes ? meshes->findtag(tag) : -1;
+ if(i<0)
+ {
+ loopv(links) if(links[i].p && links[i].p->link(p, tag, translate, anim, basetime, pos)) return true;
+ return false;
+ }
+ linkedpart &l = links.add();
+ l.p = p;
+ l.tag = i;
+ l.anim = anim;
+ l.basetime = basetime;
+ l.translate = translate;
+ l.pos = pos;
+ return true;
+ }
+
+ bool unlink(part *p)
+ {
+ loopvrev(links) if(links[i].p==p) { links.remove(i, 1); return true; }
+ loopv(links) if(links[i].p && links[i].p->unlink(p)) return true;
+ return false;
+ }
+
+ void initskins(Texture *tex = notexture, Texture *masks = notexture, int limit = 0)
+ {
+ if(!limit)
+ {
+ if(!meshes) return;
+ limit = meshes->meshes.length();
+ }
+ while(skins.length() < limit)
+ {
+ skin &s = skins.add();
+ s.owner = this;
+ s.tex = tex;
+ s.masks = masks;
+ }
+ }
+
+ bool envmapped()
+ {
+ loopv(skins) if(skins[i].envmapped()) return true;
+ return false;
+ }
+
+ bool tangents()
+ {
+ loopv(skins) if(skins[i].tangents()) return true;
+ return false;
+ }
+
+ void preloadBIH()
+ {
+ loopv(skins) skins[i].preloadBIH();
+ }
+
+ void preloadshaders(bool force)
+ {
+ loopv(skins) skins[i].preloadshader(force);
+ }
+
+ void preloadmeshes()
+ {
+ if(meshes) meshes->preload(this);
+ }
+
+ virtual void getdefaultanim(animinfo &info, int anim, uint varseed, dynent *d)
+ {
+ (void) anim; (void) varseed; (void) d;
+ info.frame = 0;
+ info.range = 1;
+ }
+
+ bool calcanim(int animpart, int anim, int basetime, int basetime2, dynent *d, int interp, animinfo &info, int &aitime)
+ {
+ uint varseed = uint((size_t)d);
+ info.anim = anim;
+ info.basetime = basetime;
+ info.varseed = varseed;
+ info.speed = anim&ANIM_SETSPEED ? basetime2 : 100.0f;
+ if((anim&ANIM_INDEX)==ANIM_ALL)
+ {
+ info.frame = 0;
+ info.range = meshes->totalframes();
+ }
+ else
+ {
+ animspec *spec = NULL;
+ if(anims[animpart])
+ {
+ int primaryidx = anim&ANIM_INDEX;
+ if(primaryidx < NUMANIMS)
+ {
+ vector<animspec> &primary = anims[animpart][primaryidx];
+ if(primary.length()) spec = &primary[uint(varseed + basetime)%primary.length()];
+ }
+ if((anim>>ANIM_SECONDARY)&(ANIM_INDEX|ANIM_DIR))
+ {
+ int secondaryidx = (anim>>ANIM_SECONDARY)&ANIM_INDEX;
+ if(secondaryidx < NUMANIMS)
+ {
+ vector<animspec> &secondary = anims[animpart][secondaryidx];
+ if(secondary.length())
+ {
+ animspec &spec2 = secondary[uint(varseed + basetime2)%secondary.length()];
+ if(!spec || spec2.priority > spec->priority)
+ {
+ spec = &spec2;
+ info.anim >>= ANIM_SECONDARY;
+ info.basetime = basetime2;
+ }
+ }
+ }
+ }
+ }
+ if(spec)
+ {
+ info.frame = spec->frame;
+ info.range = spec->range;
+ if(spec->speed>0) info.speed = 1000.0f/spec->speed;
+ }
+ else getdefaultanim(info, anim, uint(varseed + info.basetime), d);
+ }
+
+ info.anim &= (1<<ANIM_SECONDARY)-1;
+ info.anim |= anim&ANIM_FLAGS;
+ if((info.anim&ANIM_CLAMP) != ANIM_CLAMP)
+ {
+ if(info.anim&(ANIM_LOOP|ANIM_START|ANIM_END))
+ {
+ info.anim &= ~ANIM_SETTIME;
+ if(!info.basetime) info.basetime = -((int)(size_t)d&0xFFF);
+ }
+ if(info.anim&(ANIM_START|ANIM_END))
+ {
+ if(info.anim&ANIM_END) info.frame += info.range-1;
+ info.range = 1;
+ }
+ }
+
+ if(!meshes->hasframes(info.frame, info.range))
+ {
+ if(!meshes->hasframe(info.frame)) return false;
+ info.range = meshes->clipframes(info.frame, info.range);
+ }
+
+ if(d && interp>=0)
+ {
+ animinterpinfo &ai = d->animinterp[interp];
+ if((info.anim&ANIM_CLAMP)==ANIM_CLAMP) aitime = min(aitime, int(info.range*info.speed*0.5e-3f));
+ void *ak = meshes->animkey();
+ if(d->ragdoll && !(anim&ANIM_RAGDOLL))
+ {
+ ai.prev.range = ai.cur.range = 0;
+ ai.lastswitch = -1;
+ }
+ else if(ai.lastmodel!=ak || ai.lastswitch<0 || lastmillis-d->lastrendered>aitime)
+ {
+ ai.prev = ai.cur = info;
+ ai.lastswitch = lastmillis-aitime*2;
+ }
+ else if(ai.cur!=info)
+ {
+ if(lastmillis-ai.lastswitch>aitime/2) ai.prev = ai.cur;
+ ai.cur = info;
+ ai.lastswitch = lastmillis;
+ }
+ else if(info.anim&ANIM_SETTIME) ai.cur.basetime = info.basetime;
+ ai.lastmodel = ak;
+ }
+ return true;
+ }
+
+ void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d)
+ {
+ animstate as[MAXANIMPARTS];
+ render(anim, basetime, basetime2, pitch, axis, forward, d, as);
+ }
+
+ void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, animstate *as)
+ {
+ if(!(anim&ANIM_REUSE)) loopi(numanimparts)
+ {
+ animinfo info;
+ int interp = d && index+numanimparts<=MAXANIMPARTS ? index+i : -1, aitime = animationinterpolationtime;
+ if(!calcanim(i, anim, basetime, basetime2, d, interp, info, aitime)) return;
+ animstate &p = as[i];
+ p.owner = this;
+ p.cur.setframes(info);
+ p.interp = 1;
+ if(interp>=0 && d->animinterp[interp].prev.range>0)
+ {
+ int diff = lastmillis-d->animinterp[interp].lastswitch;
+ if(diff<aitime)
+ {
+ p.prev.setframes(d->animinterp[interp].prev);
+ p.interp = diff/float(aitime);
+ }
+ }
+ }
+
+ vec oaxis, oforward;
+ matrixstack[matrixpos].transposedtransformnormal(axis, oaxis);
+ float pitchamount = pitchscale*pitch + pitchoffset;
+ if((pitchmin || pitchmax) && pitchmin <= pitchmax) pitchamount = clamp(pitchamount, pitchmin, pitchmax);
+ if(as->cur.anim&ANIM_NOPITCH || (as->interp < 1 && as->prev.anim&ANIM_NOPITCH))
+ pitchamount *= (as->cur.anim&ANIM_NOPITCH ? 0 : as->interp) + (as->interp < 1 && as->prev.anim&ANIM_NOPITCH ? 0 : 1-as->interp);
+ if(pitchamount)
+ {
+ ++matrixpos;
+ matrixstack[matrixpos] = matrixstack[matrixpos-1];
+ matrixstack[matrixpos].rotate(pitchamount*RAD, oaxis);
+ }
+ matrixstack[matrixpos].transposedtransformnormal(forward, oforward);
+
+ if(!(anim&ANIM_NORENDER))
+ {
+ matrix4 modelmatrix;
+ modelmatrix.mul(shadowmapping ? shadowmatrix : camprojmatrix, matrixstack[matrixpos]);
+ if(model->scale!=1) modelmatrix.scale(model->scale);
+ if(!translate.iszero()) modelmatrix.translate(translate);
+ GLOBALPARAM(modelmatrix, modelmatrix);
+
+ if(!(anim&ANIM_NOSKIN))
+ {
+ if(envmapped()) GLOBALPARAM(modelworld, matrix3(matrixstack[matrixpos]));
+
+ vec odir, ocampos;
+ matrixstack[matrixpos].transposedtransformnormal(lightdir, odir);
+ GLOBALPARAM(lightdir, odir);
+ matrixstack[matrixpos].transposedtransform(camera1->o, ocampos);
+ ocampos.div(model->scale).sub(translate);
+ GLOBALPARAM(modelcamera, ocampos);
+ }
+ }
+
+ meshes->render(as, pitch, oaxis, oforward, d, this);
+
+ if(!(anim&ANIM_REUSE))
+ {
+ loopv(links)
+ {
+ linkedpart &link = links[i];
+ link.matrix.translate(links[i].translate, model->scale);
+
+ matrixpos++;
+ matrixstack[matrixpos].mul(matrixstack[matrixpos-1], link.matrix);
+
+ if(link.pos) *link.pos = matrixstack[matrixpos].gettranslation();
+
+ if(!link.p)
+ {
+ matrixpos--;
+ continue;
+ }
+
+ int nanim = anim, nbasetime = basetime, nbasetime2 = basetime2;
+ if(link.anim>=0)
+ {
+ nanim = link.anim | (anim&ANIM_FLAGS);
+ nbasetime = link.basetime;
+ nbasetime2 = 0;
+ }
+ link.p->render(nanim, nbasetime, nbasetime2, pitch, axis, forward, d);
+
+ matrixpos--;
+ }
+ }
+
+ if(pitchamount) matrixpos--;
+ }
+
+ void setanim(int animpart, int num, int frame, int range, float speed, int priority = 0)
+ {
+ if(animpart<0 || animpart>=MAXANIMPARTS) return;
+ if(frame<0 || range<=0 || !meshes || !meshes->hasframes(frame, range))
+ {
+ conoutf(CON_ERROR, "invalid frame %d, range %d in model %s", frame, range, model->name);
+ return;
+ }
+ if(!anims[animpart]) anims[animpart] = new vector<animspec>[NUMANIMS];
+ animspec &spec = anims[animpart][num].add();
+ spec.frame = frame;
+ spec.range = range;
+ spec.speed = speed;
+ spec.priority = priority;
+ }
+
+ virtual void loaded()
+ {
+ meshes->shared++;
+ loopv(skins) skins[i].setkey();
+ }
+ };
+
+ enum
+ {
+ LINK_TAG = 0,
+ LINK_COOP,
+ LINK_REUSE
+ };
+
+ virtual int linktype(animmodel *m) const { (void) m; return LINK_TAG; }
+
+ void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, modelattach *a)
+ {
+ int numtags = 0;
+ if(a)
+ {
+ int index = parts.last()->index + parts.last()->numanimparts;
+ for(int i = 0; a[i].tag; i++)
+ {
+ numtags++;
+
+ animmodel *m = (animmodel *)a[i].m;
+ if(!m)
+ {
+ if(a[i].pos) link(NULL, a[i].tag, vec(0, 0, 0), 0, 0, a[i].pos);
+ continue;
+ }
+ part *p = m->parts[0];
+ switch(linktype(m))
+ {
+ case LINK_TAG:
+ p->index = link(p, a[i].tag, vec(0, 0, 0), a[i].anim, a[i].basetime, a[i].pos) ? index : -1;
+ break;
+
+ case LINK_COOP:
+ p->index = index;
+ break;
+
+ default:
+ continue;
+ }
+ index += p->numanimparts;
+ }
+ }
+
+ animstate as[MAXANIMPARTS];
+ parts[0]->render(anim, basetime, basetime2, pitch, axis, forward, d, as);
+
+ if(a) for(int i = numtags-1; i >= 0; i--)
+ {
+ animmodel *m = (animmodel *)a[i].m;
+ if(!m)
+ {
+ if(a[i].pos) unlink(NULL);
+ continue;
+ }
+ part *p = m->parts[0];
+ switch(linktype(m))
+ {
+ case LINK_TAG:
+ if(p->index >= 0) unlink(p);
+ p->index = 0;
+ break;
+
+ case LINK_COOP:
+ p->render(anim, basetime, basetime2, pitch, axis, forward, d);
+ p->index = 0;
+ break;
+
+ case LINK_REUSE:
+ p->render(anim | ANIM_REUSE, basetime, basetime2, pitch, axis, forward, d, as);
+ break;
+ }
+ }
+ }
+
+ void render(int anim, int basetime, int basetime2, const vec &o, float yaw, float pitch, dynent *d, modelattach *a, const vec &color, const vec &dir, float trans)
+ {
+ yaw += spinyaw*lastmillis/1000.0f;
+ pitch += offsetpitch + spinpitch*lastmillis/1000.0f;
+
+ vec axis(0, -1, 0), forward(1, 0, 0);
+
+ matrixpos = 0;
+ matrixstack[0].identity();
+ if(!d || !d->ragdoll || anim&ANIM_RAGDOLL)
+ {
+ matrixstack[0].settranslation(o);
+ matrixstack[0].rotate_around_z(yaw*RAD);
+ matrixstack[0].transformnormal(vec(axis), axis);
+ matrixstack[0].transformnormal(vec(forward), forward);
+ if(offsetyaw) matrixstack[0].rotate_around_z(offsetyaw*RAD);
+ }
+ else pitch = 0;
+
+ if(anim&ANIM_NORENDER)
+ {
+ render(anim, basetime, basetime2, pitch, axis, forward, d, a);
+ if(d) d->lastrendered = lastmillis;
+ return;
+ }
+
+ if(!(anim&ANIM_NOSKIN))
+ {
+ transparent = trans;
+ lightdir = dir;
+ lightcolor = color;
+
+ if(envmapped())
+ {
+ setupenvmap:
+ closestenvmaptex = lookupenvmap(closestenvmap(o));
+ GLOBALPARAM(lightdirworld, dir);
+ }
+ else if(a) for(int i = 0; a[i].tag; i++) if(a[i].m && a[i].m->envmapped()) goto setupenvmap;
+ }
+
+ if(depthoffset && !enabledepthoffset)
+ {
+ enablepolygonoffset(GL_POLYGON_OFFSET_FILL);
+ enabledepthoffset = true;
+ }
+
+ if(transparent<1)
+ {
+ if(anim&ANIM_GHOST)
+ {
+ glDepthFunc(GL_GREATER);
+ glDepthMask(GL_FALSE);
+ }
+ else if(alphadepth)
+ {
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ render(anim|ANIM_NOSKIN, basetime, basetime2, pitch, axis, forward, d, a);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, fading ? GL_FALSE : GL_TRUE);
+
+ glDepthFunc(GL_LEQUAL);
+ }
+
+ if(!enablealphablend)
+ {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ enablealphablend = true;
+ }
+ }
+
+ render(anim, basetime, basetime2, pitch, axis, forward, d, a);
+
+ if(transparent<1 && (alphadepth || anim&ANIM_GHOST))
+ {
+ glDepthFunc(GL_LESS);
+ if(anim&ANIM_GHOST) glDepthMask(GL_TRUE);
+ }
+
+ if(d) d->lastrendered = lastmillis;
+ }
+
+ vector<part *> parts;
+
+ animmodel(const char *name) : model(name)
+ {
+ }
+
+ ~animmodel()
+ {
+ parts.deletecontents();
+ }
+
+ void cleanup()
+ {
+ loopv(parts) parts[i]->cleanup();
+ }
+
+ virtual void flushpart() {}
+
+ part &addpart()
+ {
+ flushpart();
+ part *p = new part(this, parts.length());
+ parts.add(p);
+ return *p;
+ }
+
+ void initmatrix(matrix4x3 &m)
+ {
+ m.identity();
+ if(offsetyaw) m.rotate_around_z(offsetyaw*RAD);
+ if(offsetpitch) m.rotate_around_y(-offsetpitch*RAD);
+ }
+
+ void genBIH(vector<BIH::mesh> &bih)
+ {
+ if(parts.empty()) return;
+ matrix4x3 m;
+ initmatrix(m);
+ parts[0]->genBIH(bih, m);
+ }
+
+ void preloadBIH()
+ {
+ model::preloadBIH();
+ if(bih) loopv(parts) parts[i]->preloadBIH();
+ }
+
+ BIH *setBIH()
+ {
+ if(bih) return bih;
+ vector<BIH::mesh> meshes;
+ genBIH(meshes);
+ bih = new BIH(meshes);
+ return bih;
+ }
+
+ bool link(part *p, const char *tag, const vec &translate = vec(0, 0, 0), int anim = -1, int basetime = 0, vec *pos = NULL)
+ {
+ if(parts.empty()) return false;
+ return parts[0]->link(p, tag, translate, anim, basetime, pos);
+ }
+
+ bool unlink(part *p)
+ {
+ if(parts.empty()) return false;
+ return parts[0]->unlink(p);
+ }
+
+ bool envmapped()
+ {
+ loopv(parts) if(parts[i]->envmapped()) return true;
+ return false;
+ }
+
+ virtual bool flipy() const { return false; }
+ virtual bool loadconfig() { return false; }
+ virtual bool loaddefaultparts() { return false; }
+ virtual void startload() {}
+ virtual void endload() {}
+
+ bool load()
+ {
+ startload();
+ bool success = loadconfig() && parts.length(); // configured model, will call the model commands below
+ if(!success)
+ success = loaddefaultparts(); // model without configuration, try default tris and skin
+ flushpart();
+ endload();
+ if(flipy()) translate.y = -translate.y;
+
+ if(!success) return false;
+ loopv(parts) if(!parts[i]->meshes) return false;
+
+ loaded();
+ return true;
+ }
+
+ void preloadshaders(bool force)
+ {
+ loopv(parts) parts[i]->preloadshaders(force);
+ }
+
+ void preloadmeshes()
+ {
+ loopv(parts) parts[i]->preloadmeshes();
+ }
+
+ void setshader(Shader *shader)
+ {
+ if(parts.empty()) loaddefaultparts();
+ loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].shader = shader;
+ }
+
+ void setenvmap(float envmapmin, float envmapmax, Texture *envmap)
+ {
+ if(parts.empty()) loaddefaultparts();
+ loopv(parts) loopvj(parts[i]->skins)
+ {
+ skin &s = parts[i]->skins[j];
+ if(envmapmax)
+ {
+ s.envmapmin = envmapmin;
+ s.envmapmax = envmapmax;
+ }
+ if(envmap) s.envmap = envmap;
+ }
+ }
+
+ void setspec(float spec)
+ {
+ if(parts.empty()) loaddefaultparts();
+ loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].spec = spec;
+ }
+
+ void setambient(float ambient)
+ {
+ if(parts.empty()) loaddefaultparts();
+ loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].ambient = ambient;
+ }
+
+ void setglow(float glow, float delta, float pulse)
+ {
+ if(parts.empty()) loaddefaultparts();
+ loopv(parts) loopvj(parts[i]->skins)
+ {
+ skin &s = parts[i]->skins[j];
+ s.glow = glow;
+ s.glowdelta = delta;
+ s.glowpulse = pulse;
+ }
+ }
+
+ void setglare(float specglare, float glowglare)
+ {
+ if(parts.empty()) loaddefaultparts();
+ loopv(parts) loopvj(parts[i]->skins)
+ {
+ skin &s = parts[i]->skins[j];
+ s.specglare = specglare;
+ s.glowglare = glowglare;
+ }
+ }
+
+ void setalphatest(float alphatest)
+ {
+ if(parts.empty()) loaddefaultparts();
+ loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].alphatest = alphatest;
+ }
+
+ void setalphablend(bool alphablend)
+ {
+ if(parts.empty()) loaddefaultparts();
+ loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].alphablend = alphablend;
+ }
+
+ void setfullbright(float fullbright)
+ {
+ if(parts.empty()) loaddefaultparts();
+ loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].fullbright = fullbright;
+ }
+
+ void setcullface(bool cullface)
+ {
+ if(parts.empty()) loaddefaultparts();
+ loopv(parts) loopvj(parts[i]->skins) parts[i]->skins[j].cullface = cullface;
+ }
+
+ void calcbb(vec &center, vec &radius)
+ {
+ if(parts.empty()) return;
+ vec bbmin(1e16f, 1e16f, 1e16f), bbmax(-1e16f, -1e16f, -1e16f);
+ matrix4x3 m;
+ initmatrix(m);
+ parts[0]->calcbb(bbmin, bbmax, m);
+ radius = bbmax;
+ radius.sub(bbmin);
+ radius.mul(0.5f);
+ center = bbmin;
+ center.add(radius);
+ }
+
+ virtual void loaded()
+ {
+ scale /= 4;
+ if(parts.length()) parts[0]->translate = translate;
+ loopv(parts) parts[i]->loaded();
+ }
+
+ static bool enabletc, enablealphablend, enablecullface, enablenormals, enabletangents, enablebones, enabledepthoffset;
+ static vec lightdir, lightcolor;
+ static float transparent, lastalphatest;
+ static GLuint lastvbuf, lasttcbuf, lastnbuf, lastxbuf, lastbbuf, lastebuf, lastenvmaptex, closestenvmaptex;
+ static Texture *lasttex, *lastmasks, *lastnormalmap;
+ static int matrixpos;
+ static matrix4 matrixstack[64];
+
+ void startrender()
+ {
+ enabletc = enablealphablend = enablenormals = enabletangents = enablebones = enabledepthoffset = false;
+ enablecullface = true;
+ lastalphatest = -1;
+ lastvbuf = lasttcbuf = lastxbuf = lastnbuf = lastbbuf = lastebuf = lastenvmaptex = closestenvmaptex = 0;
+ lasttex = lastmasks = lastnormalmap = NULL;
+ transparent = 1;
+ shaderparamskey::invalidate();
+ }
+
+ static void disablebones()
+ {
+ gle::disableboneweight();
+ gle::disableboneindex();
+ enablebones = false;
+ }
+
+ static void disabletangents()
+ {
+ gle::disabletangent();
+ enabletangents = false;
+ }
+
+ static void disabletc()
+ {
+ gle::disabletexcoord0();
+ enabletc = false;
+ }
+
+ static void disablenormals()
+ {
+ gle::disablenormal();
+ enablenormals = false;
+ }
+
+ static void disablevbo()
+ {
+ if(lastebuf) gle::clearebo();
+ if(lastvbuf)
+ {
+ gle::clearvbo();
+ gle::disablevertex();
+ }
+ if(enabletc) disabletc();
+ if(enablenormals) disablenormals();
+ if(enabletangents) disabletangents();
+ if(enablebones) disablebones();
+ lastvbuf = lasttcbuf = lastxbuf = lastnbuf = lastbbuf = lastebuf = 0;
+ }
+
+ void endrender()
+ {
+ if(lastvbuf || lastebuf) disablevbo();
+ if(enablealphablend) glDisable(GL_BLEND);
+ if(!enablecullface) glEnable(GL_CULL_FACE);
+ if(enabledepthoffset) disablepolygonoffset(GL_POLYGON_OFFSET_FILL);
+ }
};
bool animmodel::enabletc = false, animmodel::enablealphablend = false,
- animmodel::enablecullface = true,
- animmodel::enablenormals = false, animmodel::enabletangents = false, animmodel::enablebones = false, animmodel::enabledepthoffset = false;
+ animmodel::enablecullface = true,
+ animmodel::enablenormals = false, animmodel::enabletangents = false, animmodel::enablebones = false, animmodel::enabledepthoffset = false;
vec animmodel::lightdir(0, 0, 1), animmodel::lightcolor(1, 1, 1);
float animmodel::transparent = 1, animmodel::lastalphatest = -1;
GLuint animmodel::lastvbuf = 0, animmodel::lasttcbuf = 0, animmodel::lastnbuf = 0, animmodel::lastxbuf = 0, animmodel::lastbbuf = 0,
- animmodel::lastebuf = 0, animmodel::lastenvmaptex = 0, animmodel::closestenvmaptex = 0;
+ animmodel::lastebuf = 0, animmodel::lastenvmaptex = 0, animmodel::closestenvmaptex = 0;
Texture *animmodel::lasttex = NULL, *animmodel::lastmasks = NULL, *animmodel::lastnormalmap = NULL;
int animmodel::matrixpos = 0;
matrix4 animmodel::matrixstack[64];
static inline uint hthash(const animmodel::shaderparams &k)
{
- return memhash(&k, sizeof(k));
+ return memhash(&k, sizeof(k));
}
static inline bool htcmp(const animmodel::shaderparams &x, const animmodel::shaderparams &y)
{
- return !memcmp(&x, &y, sizeof(animmodel::shaderparams));
+ return !memcmp(&x, &y, sizeof(animmodel::shaderparams));
}
hashtable<animmodel::shaderparams, animmodel::shaderparamskey> animmodel::shaderparamskey::keys;
@@ -1431,35 +1432,35 @@ int animmodel::shaderparamskey::firstversion = 0, animmodel::shaderparamskey::la
template<class MDL, class BASE> struct modelloader : BASE
{
- static MDL *loading;
- static string dir;
-
- modelloader(const char *name) : BASE(name) {}
-
- static bool animated() { return true; }
- static bool multiparted() { return true; }
- static bool multimeshed() { return true; }
-
- void startload()
- {
- loading = (MDL *)this;
- }
-
- void endload()
- {
- loading = NULL;
- }
-
- bool loadconfig()
- {
- formatstring(dir, "packages/models/%s", BASE::name);
- defformatstring(cfgname, "packages/models/%s/%s.cfg", BASE::name, MDL::formatname());
-
- identflags &= ~IDF_PERSIST;
- bool success = execfile(cfgname, false);
- identflags |= IDF_PERSIST;
- return success;
- }
+ static MDL *loading;
+ static string dir;
+
+ modelloader(const char *name) : BASE(name) {}
+
+ static bool animated() { return true; }
+ static bool multiparted() { return true; }
+ static bool multimeshed() { return true; }
+
+ void startload()
+ {
+ loading = (MDL *)this;
+ }
+
+ void endload()
+ {
+ loading = NULL;
+ }
+
+ bool loadconfig()
+ {
+ formatstring(dir, "packages/models/%s", BASE::name);
+ defformatstring(cfgname, "packages/models/%s/%s.cfg", BASE::name, MDL::formatname());
+
+ identflags &= ~IDF_PERSIST;
+ bool success = execfile(cfgname, false);
+ identflags |= IDF_PERSIST;
+ return success;
+ }
};
template<class MDL, class BASE> MDL *modelloader<MDL, BASE>::loading = NULL;
@@ -1467,154 +1468,154 @@ template<class MDL, class BASE> string modelloader<MDL, BASE>::dir = {'\0'}; //
template<class MDL, class MESH> struct modelcommands
{
- typedef struct MDL::part part;
- typedef struct MDL::skin skin;
-
- static void setdir(char *name)
- {
- if(!MDL::loading) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
- formatstring(MDL::dir, "packages/models/%s", name);
- }
-
- #define loopmeshes(meshname, m, body) \
- if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } \
- part &mdl = *MDL::loading->parts.last(); \
- if(!mdl.meshes) return; \
- loopv(mdl.meshes->meshes) \
- { \
- MESH &m = *(MESH *)mdl.meshes->meshes[i]; \
- if(!strcmp(meshname, "*") || (m.name && !strcmp(m.name, meshname))) \
- { \
- body; \
- } \
- }
-
- #define loopskins(meshname, s, body) loopmeshes(meshname, m, { skin &s = mdl.skins[i]; body; })
-
- static void setskin(char *meshname, char *tex, char *masks, float *envmapmax, float *envmapmin)
- {
- loopskins(meshname, s,
- s.tex = textureload(makerelpath(MDL::dir, tex), 0, true, false);
- if(*masks)
- {
- s.masks = textureload(makerelpath(MDL::dir, masks), 0, true, false);
- s.envmapmax = *envmapmax;
- s.envmapmin = *envmapmin;
- }
- );
- }
-
- static void setspec(char *meshname, int *percent)
- {
- float spec = 1.0f;
- if(*percent>0) spec = *percent/100.0f;
- else if(*percent<0) spec = 0.0f;
- loopskins(meshname, s, s.spec = spec);
- }
-
- static void setambient(char *meshname, int *percent)
- {
- float ambient = 0.3f;
- if(*percent>0) ambient = *percent/100.0f;
- else if(*percent<0) ambient = 0.0f;
- loopskins(meshname, s, s.ambient = ambient);
- }
-
- static void setglow(char *meshname, int *percent, int *delta, float *pulse)
- {
- float glow = 3.0f, glowdelta = *delta/100.0f, glowpulse = *pulse > 0 ? *pulse/1000.0f : 0;
- if(*percent>0) glow = *percent/100.0f;
- else if(*percent<0) glow = 0.0f;
- glowdelta -= glow;
- loopskins(meshname, s, { s.glow = glow; s.glowdelta = glowdelta; s.glowpulse = glowpulse; });
- }
-
- static void setglare(char *meshname, float *specglare, float *glowglare)
- {
- loopskins(meshname, s, { s.specglare = *specglare; s.glowglare = *glowglare; });
- }
-
- static void setalphatest(char *meshname, float *cutoff)
- {
- loopskins(meshname, s, s.alphatest = max(0.0f, min(1.0f, *cutoff)));
- }
-
- static void setalphablend(char *meshname, int *blend)
- {
- loopskins(meshname, s, s.alphablend = *blend!=0);
- }
-
- static void setcullface(char *meshname, int *cullface)
- {
- loopskins(meshname, s, s.cullface = *cullface!=0);
- }
-
- static void setenvmap(char *meshname, char *envmap)
- {
- Texture *tex = cubemapload(envmap);
- loopskins(meshname, s, s.envmap = tex);
- }
-
- static void setbumpmap(char *meshname, char *normalmapfile)
- {
- Texture *normalmaptex = textureload(makerelpath(MDL::dir, normalmapfile), 0, true, false);
- loopskins(meshname, s, s.normalmap = normalmaptex);
- }
-
- static void setfullbright(char *meshname, float *fullbright)
- {
- loopskins(meshname, s, s.fullbright = *fullbright);
- }
-
- static void setshader(char *meshname, char *shader)
- {
- loopskins(meshname, s, s.shader = lookupshaderbyname(shader));
- }
-
- static void setscroll(char *meshname, float *scrollu, float *scrollv)
- {
- loopskins(meshname, s, { s.scrollu = *scrollu; s.scrollv = *scrollv; });
- }
-
- static void setnoclip(char *meshname, int *noclip)
- {
- loopmeshes(meshname, m, m.noclip = *noclip!=0);
- }
-
- static void setlink(int *parent, int *child, char *tagname, float *x, float *y, float *z)
- {
- if(!MDL::loading) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
- if(!MDL::loading->parts.inrange(*parent) || !MDL::loading->parts.inrange(*child)) { conoutf(CON_ERROR, "no models loaded to link"); return; }
- if(!MDL::loading->parts[*parent]->link(MDL::loading->parts[*child], tagname, vec(*x, *y, *z))) conoutf(CON_ERROR, "could not link model %s", MDL::loading->name);
- }
-
- template<class F> void modelcommand(F *fun, const char *suffix, const char *args)
- {
- defformatstring(name, "%s%s", MDL::formatname(), suffix);
- addcommand(newstring(name), (void (*)())fun, args);
- }
-
- modelcommands()
- {
- modelcommand(setdir, "dir", "s");
- if(MDL::multimeshed())
- {
- modelcommand(setskin, "skin", "sssff");
- modelcommand(setspec, "spec", "si");
- modelcommand(setambient, "ambient", "si");
- modelcommand(setglow, "glow", "siif");
- modelcommand(setglare, "glare", "sff");
- modelcommand(setalphatest, "alphatest", "sf");
- modelcommand(setalphablend, "alphablend", "si");
- modelcommand(setcullface, "cullface", "si");
- modelcommand(setenvmap, "envmap", "ss");
- modelcommand(setbumpmap, "bumpmap", "ss");
- modelcommand(setfullbright, "fullbright", "sf");
- modelcommand(setshader, "shader", "ss");
- modelcommand(setscroll, "scroll", "sff");
- modelcommand(setnoclip, "noclip", "si");
- }
- if(MDL::multiparted()) modelcommand(setlink, "link", "iisfff");
- }
+ typedef struct MDL::part part;
+ typedef struct MDL::skin skin;
+
+ static void setdir(char *name)
+ {
+ if(!MDL::loading) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+ formatstring(MDL::dir, "packages/models/%s", name);
+ }
+
+ #define loopmeshes(meshname, m, body) \
+ if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; } \
+ part &mdl = *MDL::loading->parts.last(); \
+ if(!mdl.meshes) return; \
+ loopv(mdl.meshes->meshes) \
+ { \
+ MESH &m = *(MESH *)mdl.meshes->meshes[i]; \
+ if(!strcmp(meshname, "*") || (m.name && !strcmp(m.name, meshname))) \
+ { \
+ body; \
+ } \
+ }
+
+ #define loopskins(meshname, s, body) loopmeshes(meshname, m, { skin &s = mdl.skins[i]; body; })
+
+ static void setskin(char *meshname, char *tex, char *masks, float *envmapmax, float *envmapmin)
+ {
+ loopskins(meshname, s,
+ s.tex = textureload(makerelpath(MDL::dir, tex), 0, true, false);
+ if(*masks)
+ {
+ s.masks = textureload(makerelpath(MDL::dir, masks), 0, true, false);
+ s.envmapmax = *envmapmax;
+ s.envmapmin = *envmapmin;
+ }
+ );
+ }
+
+ static void setspec(char *meshname, int *percent)
+ {
+ float spec = 1.0f;
+ if(*percent>0) spec = *percent/100.0f;
+ else if(*percent<0) spec = 0.0f;
+ loopskins(meshname, s, s.spec = spec);
+ }
+
+ static void setambient(char *meshname, int *percent)
+ {
+ float ambient = 0.3f;
+ if(*percent>0) ambient = *percent/100.0f;
+ else if(*percent<0) ambient = 0.0f;
+ loopskins(meshname, s, s.ambient = ambient);
+ }
+
+ static void setglow(char *meshname, int *percent, int *delta, float *pulse)
+ {
+ float glow = 3.0f, glowdelta = *delta/100.0f, glowpulse = *pulse > 0 ? *pulse/1000.0f : 0;
+ if(*percent>0) glow = *percent/100.0f;
+ else if(*percent<0) glow = 0.0f;
+ glowdelta -= glow;
+ loopskins(meshname, s, { s.glow = glow; s.glowdelta = glowdelta; s.glowpulse = glowpulse; });
+ }
+
+ static void setglare(char *meshname, float *specglare, float *glowglare)
+ {
+ loopskins(meshname, s, { s.specglare = *specglare; s.glowglare = *glowglare; });
+ }
+
+ static void setalphatest(char *meshname, float *cutoff)
+ {
+ loopskins(meshname, s, s.alphatest = max(0.0f, min(1.0f, *cutoff)));
+ }
+
+ static void setalphablend(char *meshname, int *blend)
+ {
+ loopskins(meshname, s, s.alphablend = *blend!=0);
+ }
+
+ static void setcullface(char *meshname, int *cullface)
+ {
+ loopskins(meshname, s, s.cullface = *cullface!=0);
+ }
+
+ static void setenvmap(char *meshname, char *envmap)
+ {
+ Texture *tex = cubemapload(envmap);
+ loopskins(meshname, s, s.envmap = tex);
+ }
+
+ static void setbumpmap(char *meshname, char *normalmapfile)
+ {
+ Texture *normalmaptex = textureload(makerelpath(MDL::dir, normalmapfile), 0, true, false);
+ loopskins(meshname, s, s.normalmap = normalmaptex);
+ }
+
+ static void setfullbright(char *meshname, float *fullbright)
+ {
+ loopskins(meshname, s, s.fullbright = *fullbright);
+ }
+
+ static void setshader(char *meshname, char *shader)
+ {
+ loopskins(meshname, s, s.shader = lookupshaderbyname(shader));
+ }
+
+ static void setscroll(char *meshname, float *scrollu, float *scrollv)
+ {
+ loopskins(meshname, s, { s.scrollu = *scrollu; s.scrollv = *scrollv; });
+ }
+
+ static void setnoclip(char *meshname, int *noclip)
+ {
+ loopmeshes(meshname, m, m.noclip = *noclip!=0);
+ }
+
+ static void setlink(int *parent, int *child, char *tagname, float *x, float *y, float *z)
+ {
+ if(!MDL::loading) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+ if(!MDL::loading->parts.inrange(*parent) || !MDL::loading->parts.inrange(*child)) { conoutf(CON_ERROR, "no models loaded to link"); return; }
+ if(!MDL::loading->parts[*parent]->link(MDL::loading->parts[*child], tagname, vec(*x, *y, *z))) conoutf(CON_ERROR, "could not link model %s", MDL::loading->name);
+ }
+
+ template<class F> void modelcommand(F *fun, const char *suffix, const char *args)
+ {
+ defformatstring(name, "%s%s", MDL::formatname(), suffix);
+ addcommand(newstring(name), (void (*)())fun, args);
+ }
+
+ modelcommands()
+ {
+ modelcommand(setdir, "dir", "s");
+ if(MDL::multimeshed())
+ {
+ modelcommand(setskin, "skin", "sssff");
+ modelcommand(setspec, "spec", "si");
+ modelcommand(setambient, "ambient", "si");
+ modelcommand(setglow, "glow", "siif");
+ modelcommand(setglare, "glare", "sff");
+ modelcommand(setalphatest, "alphatest", "sf");
+ modelcommand(setalphablend, "alphablend", "si");
+ modelcommand(setcullface, "cullface", "si");
+ modelcommand(setenvmap, "envmap", "ss");
+ modelcommand(setbumpmap, "bumpmap", "ss");
+ modelcommand(setfullbright, "fullbright", "sf");
+ modelcommand(setshader, "shader", "ss");
+ modelcommand(setscroll, "scroll", "sff");
+ modelcommand(setnoclip, "noclip", "si");
+ }
+ if(MDL::multiparted()) modelcommand(setlink, "link", "iisfff");
+ }
};
diff --git a/src/engine/bih.cpp b/src/engine/bih.cpp
index e735f38..c983805 100644
--- a/src/engine/bih.cpp
+++ b/src/engine/bih.cpp
@@ -2,329 +2,329 @@
bool BIH::triintersect(const mesh &m, int tidx, const vec &mo, const vec &mray, float maxdist, float &dist, int mode)
{
- const tri &t = m.tris[tidx];
- vec a = m.getpos(t.vert[0]), b = m.getpos(t.vert[1]).sub(a), c = m.getpos(t.vert[2]).sub(a),
- n = vec().cross(b, c), r = vec(a).sub(mo), e = vec().cross(r, mray);
- float det = mray.dot(n), v, w, f;
- if(det >= 0)
- {
- if(!(mode&RAY_SHADOW) && m.flags&MESH_CULLFACE) return false;
- v = e.dot(c);
- if(v < 0 || v > det) return false;
- w = -e.dot(b);
- if(w < 0 || v + w > det) return false;
- f = r.dot(n)*m.scale;
- if(f < 0 || f > maxdist*det || !det) return false;
- }
- else
- {
- v = e.dot(c);
- if(v > 0 || v < det) return false;
- w = -e.dot(b);
- if(w > 0 || v + w < det) return false;
- f = r.dot(n)*m.scale;
- if(f > 0 || f < maxdist*det) return false;
- }
- float invdet = 1/det;
- if(m.flags&MESH_ALPHA && (mode&RAY_ALPHAPOLY)==RAY_ALPHAPOLY && (m.tex->alphamask || (lightmapping <= 1 && loadalphamask(m.tex))))
- {
- vec2 at = m.gettc(t.vert[0]), bt = m.gettc(t.vert[1]).sub(at).mul(v*invdet), ct = m.gettc(t.vert[2]).sub(at).mul(w*invdet);
- at.add(bt).add(ct);
- int si = clamp(int(m.tex->xs * at.x), 0, m.tex->xs-1),
- ti = clamp(int(m.tex->ys * at.y), 0, m.tex->ys-1);
- if(!(m.tex->alphamask[ti*((m.tex->xs+7)/8) + si/8] & (1<<(si%8)))) return false;
- }
- dist = f*invdet;
- return true;
+ const tri &t = m.tris[tidx];
+ vec a = m.getpos(t.vert[0]), b = m.getpos(t.vert[1]).sub(a), c = m.getpos(t.vert[2]).sub(a),
+ n = vec().cross(b, c), r = vec(a).sub(mo), e = vec().cross(r, mray);
+ float det = mray.dot(n), v, w, f;
+ if(det >= 0)
+ {
+ if(!(mode&RAY_SHADOW) && m.flags&MESH_CULLFACE) return false;
+ v = e.dot(c);
+ if(v < 0 || v > det) return false;
+ w = -e.dot(b);
+ if(w < 0 || v + w > det) return false;
+ f = r.dot(n)*m.scale;
+ if(f < 0 || f > maxdist*det || !det) return false;
+ }
+ else
+ {
+ v = e.dot(c);
+ if(v > 0 || v < det) return false;
+ w = -e.dot(b);
+ if(w > 0 || v + w < det) return false;
+ f = r.dot(n)*m.scale;
+ if(f > 0 || f < maxdist*det) return false;
+ }
+ float invdet = 1/det;
+ if(m.flags&MESH_ALPHA && (mode&RAY_ALPHAPOLY)==RAY_ALPHAPOLY && (m.tex->alphamask || (lightmapping <= 1 && loadalphamask(m.tex))))
+ {
+ vec2 at = m.gettc(t.vert[0]), bt = m.gettc(t.vert[1]).sub(at).mul(v*invdet), ct = m.gettc(t.vert[2]).sub(at).mul(w*invdet);
+ at.add(bt).add(ct);
+ int si = clamp(int(m.tex->xs * at.x), 0, m.tex->xs-1),
+ ti = clamp(int(m.tex->ys * at.y), 0, m.tex->ys-1);
+ if(!(m.tex->alphamask[ti*((m.tex->xs+7)/8) + si/8] & (1<<(si%8)))) return false;
+ }
+ dist = f*invdet;
+ return true;
}
struct traversestate
{
- BIH::node *node;
- float tmin, tmax;
+ BIH::node *node;
+ float tmin, tmax;
};
inline bool BIH::traverse(const mesh &m, const vec &o, const vec &ray, const vec &invray, float maxdist, float &dist, int mode, node *curnode, float tmin, float tmax)
{
- traversestate stack[128];
- int stacksize = 0;
- ivec order(ray.x>0 ? 0 : 1, ray.y>0 ? 0 : 1, ray.z>0 ? 0 : 1);
- vec mo = m.invxform.transform(o), mray = m.invxformnorm.transform(ray);
- for(;;)
- {
- int axis = curnode->axis();
- int nearidx = order[axis], faridx = nearidx^1;
- float nearsplit = (curnode->split[nearidx] - o[axis])*invray[axis],
- farsplit = (curnode->split[faridx] - o[axis])*invray[axis];
+ traversestate stack[128];
+ int stacksize = 0;
+ ivec order(ray.x>0 ? 0 : 1, ray.y>0 ? 0 : 1, ray.z>0 ? 0 : 1);
+ vec mo = m.invxform.transform(o), mray = m.invxformnorm.transform(ray);
+ for(;;)
+ {
+ int axis = curnode->axis();
+ int nearidx = order[axis], faridx = nearidx^1;
+ float nearsplit = (curnode->split[nearidx] - o[axis])*invray[axis],
+ farsplit = (curnode->split[faridx] - o[axis])*invray[axis];
- if(nearsplit <= tmin)
- {
- if(farsplit < tmax)
- {
- if(!curnode->isleaf(faridx))
- {
- curnode += curnode->childindex(faridx);
- tmin = max(tmin, farsplit);
- continue;
- }
- else if(triintersect(m, curnode->childindex(faridx), mo, mray, maxdist, dist, mode)) return true;
- }
- }
- else if(curnode->isleaf(nearidx))
- {
- if(triintersect(m, curnode->childindex(nearidx), mo, mray, maxdist, dist, mode)) return true;
- if(farsplit < tmax)
- {
- if(!curnode->isleaf(faridx))
- {
- curnode += curnode->childindex(faridx);
- tmin = max(tmin, farsplit);
- continue;
- }
- else if(triintersect(m, curnode->childindex(faridx), mo, mray, maxdist, dist, mode)) return true;
- }
- }
- else
- {
- if(farsplit < tmax)
- {
- if(!curnode->isleaf(faridx))
- {
- if(stacksize < int(sizeof(stack)/sizeof(stack[0])))
- {
- traversestate &save = stack[stacksize++];
- save.node = curnode + curnode->childindex(faridx);
- save.tmin = max(tmin, farsplit);
- save.tmax = tmax;
- }
- else
- {
- if(traverse(m, o, ray, invray, maxdist, dist, mode, curnode + curnode->childindex(nearidx), tmin, min(tmax, nearsplit))) return true;
- curnode += curnode->childindex(faridx);
- tmin = max(tmin, farsplit);
- continue;
- }
- }
- else if(triintersect(m, curnode->childindex(faridx), mo, mray, maxdist, dist, mode)) return true;
- }
- curnode += curnode->childindex(nearidx);
- tmax = min(tmax, nearsplit);
- continue;
- }
- if(stacksize <= 0) return false;
- traversestate &restore = stack[--stacksize];
- curnode = restore.node;
- tmin = restore.tmin;
- tmax = restore.tmax;
- }
+ if(nearsplit <= tmin)
+ {
+ if(farsplit < tmax)
+ {
+ if(!curnode->isleaf(faridx))
+ {
+ curnode += curnode->childindex(faridx);
+ tmin = max(tmin, farsplit);
+ continue;
+ }
+ else if(triintersect(m, curnode->childindex(faridx), mo, mray, maxdist, dist, mode)) return true;
+ }
+ }
+ else if(curnode->isleaf(nearidx))
+ {
+ if(triintersect(m, curnode->childindex(nearidx), mo, mray, maxdist, dist, mode)) return true;
+ if(farsplit < tmax)
+ {
+ if(!curnode->isleaf(faridx))
+ {
+ curnode += curnode->childindex(faridx);
+ tmin = max(tmin, farsplit);
+ continue;
+ }
+ else if(triintersect(m, curnode->childindex(faridx), mo, mray, maxdist, dist, mode)) return true;
+ }
+ }
+ else
+ {
+ if(farsplit < tmax)
+ {
+ if(!curnode->isleaf(faridx))
+ {
+ if(stacksize < int(sizeof(stack)/sizeof(stack[0])))
+ {
+ traversestate &save = stack[stacksize++];
+ save.node = curnode + curnode->childindex(faridx);
+ save.tmin = max(tmin, farsplit);
+ save.tmax = tmax;
+ }
+ else
+ {
+ if(traverse(m, o, ray, invray, maxdist, dist, mode, curnode + curnode->childindex(nearidx), tmin, min(tmax, nearsplit))) return true;
+ curnode += curnode->childindex(faridx);
+ tmin = max(tmin, farsplit);
+ continue;
+ }
+ }
+ else if(triintersect(m, curnode->childindex(faridx), mo, mray, maxdist, dist, mode)) return true;
+ }
+ curnode += curnode->childindex(nearidx);
+ tmax = min(tmax, nearsplit);
+ continue;
+ }
+ if(stacksize <= 0) return false;
+ traversestate &restore = stack[--stacksize];
+ curnode = restore.node;
+ tmin = restore.tmin;
+ tmax = restore.tmax;
+ }
}
inline bool BIH::traverse(const vec &o, const vec &ray, float maxdist, float &dist, int mode)
{
- vec invray(ray.x ? 1/ray.x : 1e16f, ray.y ? 1/ray.y : 1e16f, ray.z ? 1/ray.z : 1e16f);
- loopi(nummeshes)
- {
- mesh &m = meshes[i];
- if(!(mode&RAY_SHADOW) && m.flags&MESH_NOCLIP) continue;
- float t1 = (m.bbmin.x - o.x)*invray.x,
- t2 = (m.bbmax.x - o.x)*invray.x,
- tmin, tmax;
- if(invray.x > 0) { tmin = t1; tmax = t2; } else { tmin = t2; tmax = t1; }
- t1 = (m.bbmin.y - o.y)*invray.y;
- t2 = (m.bbmax.y - o.y)*invray.y;
- if(invray.y > 0) { tmin = max(tmin, t1); tmax = min(tmax, t2); } else { tmin = max(tmin, t2); tmax = min(tmax, t1); }
- t1 = (m.bbmin.z - o.z)*invray.z;
- t2 = (m.bbmax.z - o.z)*invray.z;
- if(invray.z > 0) { tmin = max(tmin, t1); tmax = min(tmax, t2); } else { tmin = max(tmin, t2); tmax = min(tmax, t1); }
- tmax = min(tmax, maxdist);
- if(tmin < tmax && traverse(m, o, ray, invray, maxdist, dist, mode, m.nodes, tmin, tmax)) return true;
- }
- return false;
+ vec invray(ray.x ? 1/ray.x : 1e16f, ray.y ? 1/ray.y : 1e16f, ray.z ? 1/ray.z : 1e16f);
+ loopi(nummeshes)
+ {
+ mesh &m = meshes[i];
+ if(!(mode&RAY_SHADOW) && m.flags&MESH_NOCLIP) continue;
+ float t1 = (m.bbmin.x - o.x)*invray.x,
+ t2 = (m.bbmax.x - o.x)*invray.x,
+ tmin, tmax;
+ if(invray.x > 0) { tmin = t1; tmax = t2; } else { tmin = t2; tmax = t1; }
+ t1 = (m.bbmin.y - o.y)*invray.y;
+ t2 = (m.bbmax.y - o.y)*invray.y;
+ if(invray.y > 0) { tmin = max(tmin, t1); tmax = min(tmax, t2); } else { tmin = max(tmin, t2); tmax = min(tmax, t1); }
+ t1 = (m.bbmin.z - o.z)*invray.z;
+ t2 = (m.bbmax.z - o.z)*invray.z;
+ if(invray.z > 0) { tmin = max(tmin, t1); tmax = min(tmax, t2); } else { tmin = max(tmin, t2); tmax = min(tmax, t1); }
+ tmax = min(tmax, maxdist);
+ if(tmin < tmax && traverse(m, o, ray, invray, maxdist, dist, mode, m.nodes, tmin, tmax)) return true;
+ }
+ return false;
}
void BIH::build(mesh &m, ushort *indices, int numindices, const ivec &vmin, const ivec &vmax)
{
- int axis = 2;
- loopk(2) if(vmax[k] - vmin[k] > vmax[axis] - vmin[axis]) axis = k;
+ int axis = 2;
+ loopk(2) if(vmax[k] - vmin[k] > vmax[axis] - vmin[axis]) axis = k;
- ivec leftmin, leftmax, rightmin, rightmax;
- int splitleft, splitright;
- int left, right;
- loopk(3)
- {
- leftmin = rightmin = ivec(INT_MAX, INT_MAX, INT_MAX);
- leftmax = rightmax = ivec(INT_MIN, INT_MIN, INT_MIN);
- int split = (vmax[axis] + vmin[axis])/2;
- for(left = 0, right = numindices, splitleft = SHRT_MIN, splitright = SHRT_MAX; left < right;)
- {
- const tribb &tri = m.tribbs[indices[left]];
- ivec trimin = ivec(tri.center).sub(ivec(tri.radius)),
- trimax = ivec(tri.center).add(ivec(tri.radius));
- int amin = trimin[axis], amax = trimax[axis];
- if(max(split - amin, 0) > max(amax - split, 0))
- {
- ++left;
- splitleft = max(splitleft, amax);
- leftmin.min(trimin);
- leftmax.max(trimax);
- }
- else
- {
- --right;
- swap(indices[left], indices[right]);
- splitright = min(splitright, amin);
- rightmin.min(trimin);
- rightmax.max(trimax);
- }
- }
- if(left > 0 && right < numindices) break;
- axis = (axis+1)%3;
- }
+ ivec leftmin, leftmax, rightmin, rightmax;
+ int splitleft, splitright;
+ int left, right;
+ loopk(3)
+ {
+ leftmin = rightmin = ivec(INT_MAX, INT_MAX, INT_MAX);
+ leftmax = rightmax = ivec(INT_MIN, INT_MIN, INT_MIN);
+ int split = (vmax[axis] + vmin[axis])/2;
+ for(left = 0, right = numindices, splitleft = SHRT_MIN, splitright = SHRT_MAX; left < right;)
+ {
+ const tribb &tri = m.tribbs[indices[left]];
+ ivec trimin = ivec(tri.center).sub(ivec(tri.radius)),
+ trimax = ivec(tri.center).add(ivec(tri.radius));
+ int amin = trimin[axis], amax = trimax[axis];
+ if(max(split - amin, 0) > max(amax - split, 0))
+ {
+ ++left;
+ splitleft = max(splitleft, amax);
+ leftmin.min(trimin);
+ leftmax.max(trimax);
+ }
+ else
+ {
+ --right;
+ swap(indices[left], indices[right]);
+ splitright = min(splitright, amin);
+ rightmin.min(trimin);
+ rightmax.max(trimax);
+ }
+ }
+ if(left > 0 && right < numindices) break;
+ axis = (axis+1)%3;
+ }
- if(!left || right==numindices)
- {
- leftmin = rightmin = ivec(INT_MAX, INT_MAX, INT_MAX);
- leftmax = rightmax = ivec(INT_MIN, INT_MIN, INT_MIN);
- left = right = numindices/2;
- splitleft = SHRT_MIN;
- splitright = SHRT_MAX;
- loopi(numindices)
- {
- const tribb &tri = m.tribbs[indices[i]];
- ivec trimin = ivec(tri.center).sub(ivec(tri.radius)),
- trimax = ivec(tri.center).add(ivec(tri.radius));
- if(i < left)
- {
- splitleft = max(splitleft, trimax[axis]);
- leftmin.min(trimin);
- leftmax.max(trimax);
- }
- else
- {
- splitright = min(splitright, trimin[axis]);
- rightmin.min(trimin);
- rightmax.max(trimax);
- }
- }
- }
+ if(!left || right==numindices)
+ {
+ leftmin = rightmin = ivec(INT_MAX, INT_MAX, INT_MAX);
+ leftmax = rightmax = ivec(INT_MIN, INT_MIN, INT_MIN);
+ left = right = numindices/2;
+ splitleft = SHRT_MIN;
+ splitright = SHRT_MAX;
+ loopi(numindices)
+ {
+ const tribb &tri = m.tribbs[indices[i]];
+ ivec trimin = ivec(tri.center).sub(ivec(tri.radius)),
+ trimax = ivec(tri.center).add(ivec(tri.radius));
+ if(i < left)
+ {
+ splitleft = max(splitleft, trimax[axis]);
+ leftmin.min(trimin);
+ leftmax.max(trimax);
+ }
+ else
+ {
+ splitright = min(splitright, trimin[axis]);
+ rightmin.min(trimin);
+ rightmax.max(trimax);
+ }
+ }
+ }
- int offset = m.numnodes++;
- node &curnode = m.nodes[offset];
- curnode.split[0] = short(splitleft);
- curnode.split[1] = short(splitright);
+ int offset = m.numnodes++;
+ node &curnode = m.nodes[offset];
+ curnode.split[0] = short(splitleft);
+ curnode.split[1] = short(splitright);
- if(left==1) curnode.child[0] = (axis<<14) | indices[0];
- else
- {
- curnode.child[0] = (axis<<14) | (m.numnodes - offset);
- build(m, indices, left, leftmin, leftmax);
- }
+ if(left==1) curnode.child[0] = (axis<<14) | indices[0];
+ else
+ {
+ curnode.child[0] = (axis<<14) | (m.numnodes - offset);
+ build(m, indices, left, leftmin, leftmax);
+ }
- if(numindices-right==1) curnode.child[1] = (1<<15) | (left==1 ? 1<<14 : 0) | indices[right];
- else
- {
- curnode.child[1] = (left==1 ? 1<<14 : 0) | (m.numnodes - offset);
- build(m, &indices[right], numindices-right, rightmin, rightmax);
- }
+ if(numindices-right==1) curnode.child[1] = (1<<15) | (left==1 ? 1<<14 : 0) | indices[right];
+ else
+ {
+ curnode.child[1] = (left==1 ? 1<<14 : 0) | (m.numnodes - offset);
+ build(m, &indices[right], numindices-right, rightmin, rightmax);
+ }
}
BIH::BIH(vector<mesh> &buildmeshes)
: meshes(NULL), nummeshes(0), nodes(NULL), numnodes(0), tribbs(NULL), numtris(0), bbmin(1e16f, 1e16f, 1e16f), bbmax(-1e16f, -1e16f, -1e16f), center(0, 0, 0), radius(0), entradius(0)
{
- if(buildmeshes.empty()) return;
- loopv(buildmeshes) numtris += buildmeshes[i].numtris;
- if(!numtris) return;
+ if(buildmeshes.empty()) return;
+ loopv(buildmeshes) numtris += buildmeshes[i].numtris;
+ if(!numtris) return;
- nummeshes = buildmeshes.length();
- meshes = new mesh[nummeshes];
- memcpy(meshes, buildmeshes.getbuf(), sizeof(mesh)*buildmeshes.length());
- tribbs = new tribb[numtris];
- tribb *dsttri = tribbs;
- loopi(nummeshes)
- {
- mesh &m = meshes[i];
- m.scale = m.xform.a.magnitude();
- m.invscale = 1/m.scale;
- m.xformnorm = matrix3(m.xform);
- m.xformnorm.normalize();
- m.invxform.invert(m.xform);
- m.invxformnorm = matrix3(m.invxform);
- m.invxformnorm.normalize();
- m.tribbs = dsttri;
- const tri *srctri = m.tris;
- vec mmin(1e16f, 1e16f, 1e16f), mmax(-1e16f, -1e16f, -1e16f);
- loopj(m.numtris)
- {
- vec s0 = m.getpos(srctri->vert[0]), s1 = m.getpos(srctri->vert[1]), s2 = m.getpos(srctri->vert[2]),
- v0 = m.xform.transform(s0), v1 = m.xform.transform(s1), v2 = m.xform.transform(s2),
- vmin = vec(v0).min(v1).min(v2),
- vmax = vec(v0).max(v1).max(v2);
- mmin.min(vmin);
- mmax.max(vmax);
- ivec imin = ivec::floor(vmin), imax = ivec::ceil(vmax);
- dsttri->center = svec(ivec(imin).add(imax).div(2));
- dsttri->radius = svec(ivec(imax).sub(imin).add(1).div(2));
- ++srctri;
- ++dsttri;
- }
- loopk(3) if(fabs(mmax[k] - mmin[k]) < 0.125f)
- {
- float mid = (mmin[k] + mmax[k]) / 2;
- mmin[k] = mid - 0.0625f;
- mmax[k] = mid + 0.0625f;
- }
- m.bbmin = mmin;
- m.bbmax = mmax;
- bbmin.min(mmin);
- bbmax.max(mmax);
- }
+ nummeshes = buildmeshes.length();
+ meshes = new mesh[nummeshes];
+ memcpy(meshes, buildmeshes.getbuf(), sizeof(mesh)*buildmeshes.length());
+ tribbs = new tribb[numtris];
+ tribb *dsttri = tribbs;
+ loopi(nummeshes)
+ {
+ mesh &m = meshes[i];
+ m.scale = m.xform.a.magnitude();
+ m.invscale = 1/m.scale;
+ m.xformnorm = matrix3(m.xform);
+ m.xformnorm.normalize();
+ m.invxform.invert(m.xform);
+ m.invxformnorm = matrix3(m.invxform);
+ m.invxformnorm.normalize();
+ m.tribbs = dsttri;
+ const tri *srctri = m.tris;
+ vec mmin(1e16f, 1e16f, 1e16f), mmax(-1e16f, -1e16f, -1e16f);
+ loopj(m.numtris)
+ {
+ vec s0 = m.getpos(srctri->vert[0]), s1 = m.getpos(srctri->vert[1]), s2 = m.getpos(srctri->vert[2]),
+ v0 = m.xform.transform(s0), v1 = m.xform.transform(s1), v2 = m.xform.transform(s2),
+ vmin = vec(v0).min(v1).min(v2),
+ vmax = vec(v0).max(v1).max(v2);
+ mmin.min(vmin);
+ mmax.max(vmax);
+ ivec imin = ivec::floor(vmin), imax = ivec::ceil(vmax);
+ dsttri->center = svec(ivec(imin).add(imax).div(2));
+ dsttri->radius = svec(ivec(imax).sub(imin).add(1).div(2));
+ ++srctri;
+ ++dsttri;
+ }
+ loopk(3) if(fabs(mmax[k] - mmin[k]) < 0.125f)
+ {
+ float mid = (mmin[k] + mmax[k]) / 2;
+ mmin[k] = mid - 0.0625f;
+ mmax[k] = mid + 0.0625f;
+ }
+ m.bbmin = mmin;
+ m.bbmax = mmax;
+ bbmin.min(mmin);
+ bbmax.max(mmax);
+ }
- center = vec(bbmin).add(bbmax).mul(0.5f);
- radius = vec(bbmax).sub(bbmin).mul(0.5f).magnitude();
- entradius = max(bbmin.squaredlen(), bbmax.squaredlen());
+ center = vec(bbmin).add(bbmax).mul(0.5f);
+ radius = vec(bbmax).sub(bbmin).mul(0.5f).magnitude();
+ entradius = max(bbmin.squaredlen(), bbmax.squaredlen());
- nodes = new node[numtris];
- node *curnode = nodes;
- ushort *indices = new ushort[numtris];
- loopi(nummeshes)
- {
- mesh &m = meshes[i];
- m.nodes = curnode;
- loopj(m.numtris) indices[j] = j;
- build(m, indices, m.numtris, ivec::floor(m.bbmin), ivec::ceil(m.bbmax));
- curnode += m.numnodes;
- }
- delete[] indices;
- numnodes = int(curnode - nodes);
+ nodes = new node[numtris];
+ node *curnode = nodes;
+ ushort *indices = new ushort[numtris];
+ loopi(nummeshes)
+ {
+ mesh &m = meshes[i];
+ m.nodes = curnode;
+ loopj(m.numtris) indices[j] = j;
+ build(m, indices, m.numtris, ivec::floor(m.bbmin), ivec::ceil(m.bbmax));
+ curnode += m.numnodes;
+ }
+ delete[] indices;
+ numnodes = int(curnode - nodes);
}
BIH::~BIH()
{
- delete[] meshes;
- delete[] nodes;
- delete[] tribbs;
+ delete[] meshes;
+ delete[] nodes;
+ delete[] tribbs;
}
bool mmintersect(const extentity &e, const vec &o, const vec &ray, float maxdist, int mode, float &dist)
{
- model *m = loadmapmodel(e.attr2);
- if(!m) return false;
- if(mode&RAY_SHADOW)
- {
- if(!m->shadow || e.flags&EF_NOSHADOW) return false;
- }
- else if((mode&RAY_ENTS)!=RAY_ENTS && (!m->collide || e.flags&EF_NOCOLLIDE)) return false;
- if(!m->bih && (lightmapping > 1 || !m->setBIH())) return false;
- vec mo = vec(o).sub(e.o), mray(ray);
- float v = mo.dot(mray), inside = m->bih->entradius - mo.squaredlen();
- if((inside < 0 && v > 0) || inside + v*v < 0) return false;
- int yaw = e.attr1;
- if(yaw != 0)
- {
- const vec2 &rot = sincosmod360(-yaw);
- mo.rotate_around_z(rot);
- mray.rotate_around_z(rot);
- }
- return m->bih->traverse(mo, mray, maxdist ? maxdist : 1e16f, dist, mode);
+ model *m = loadmapmodel(e.attr2);
+ if(!m) return false;
+ if(mode&RAY_SHADOW)
+ {
+ if(!m->shadow || e.flags&EF_NOSHADOW) return false;
+ }
+ else if((mode&RAY_ENTS)!=RAY_ENTS && (!m->collide || e.flags&EF_NOCOLLIDE)) return false;
+ if(!m->bih && (lightmapping > 1 || !m->setBIH())) return false;
+ vec mo = vec(o).sub(e.o), mray(ray);
+ float v = mo.dot(mray), inside = m->bih->entradius - mo.squaredlen();
+ if((inside < 0 && v > 0) || inside + v*v < 0) return false;
+ int yaw = e.attr1;
+ if(yaw != 0)
+ {
+ const vec2 &rot = sincosmod360(-yaw);
+ mo.rotate_around_z(rot);
+ mray.rotate_around_z(rot);
+ }
+ return m->bih->traverse(mo, mray, maxdist ? maxdist : 1e16f, dist, mode);
}
diff --git a/src/engine/bih.h b/src/engine/bih.h
index 971e64b..16da278 100644
--- a/src/engine/bih.h
+++ b/src/engine/bih.h
@@ -1,78 +1,78 @@
struct BIH
{
- struct node
- {
- short split[2];
- ushort child[2];
-
- int axis() const { return child[0]>>14; }
- int childindex(int which) const { return child[which]&0x3FFF; }
- bool isleaf(int which) const { return (child[1]&(1<<(14+which)))!=0; }
- };
-
- struct tri
- {
- ushort vert[3];
- };
-
- struct tribb
- {
- svec center, radius;
-
- bool outside(const ivec &bo, const ivec &br) const
- {
- return abs(bo.x - center.x) > br.x + radius.x ||
- abs(bo.y - center.y) > br.y + radius.y ||
- abs(bo.z - center.z) > br.z + radius.z;
- }
- };
-
- enum { MESH_NOCLIP = 1<<0, MESH_ALPHA = 1<<1, MESH_CULLFACE = 1<<2 };
-
- struct mesh
- {
- enum { MAXTRIS = 1<<14 };
-
- matrix4x3 xform, invxform;
- matrix3 xformnorm, invxformnorm;
- float scale, invscale;
- node *nodes;
- int numnodes;
- const tri *tris;
- const tribb *tribbs;
- int numtris;
- const uchar *pos, *tc;
- int posstride, tcstride;
- Texture *tex;
- int flags;
- vec bbmin, bbmax;
-
- mesh() : numnodes(0), numtris(0), tex(NULL), flags(0) {}
-
- vec getpos(int i) const { return *(const vec *)(pos + i*posstride); }
- vec2 gettc(int i) const { return *(const vec2 *)(tc + i*tcstride); }
- };
-
- mesh *meshes;
- int nummeshes;
- node *nodes;
- int numnodes;
- tribb *tribbs;
- int numtris;
- vec bbmin, bbmax, center;
- float radius, entradius;
-
- BIH(vector<mesh> &buildmeshes);
-
- ~BIH();
-
- void build(mesh &m, ushort *indices, int numindices, const ivec &vmin, const ivec &vmax);
-
- bool traverse(const vec &o, const vec &ray, float maxdist, float &dist, int mode);
- bool traverse(const mesh &m, const vec &o, const vec &ray, const vec &invray, float maxdist, float &dist, int mode, node *curnode, float tmin, float tmax);
- bool triintersect(const mesh &m, int tidx, const vec &mo, const vec &mray, float maxdist, float &dist, int mode);
-
- void preload();
+ struct node
+ {
+ short split[2];
+ ushort child[2];
+
+ int axis() const { return child[0]>>14; }
+ int childindex(int which) const { return child[which]&0x3FFF; }
+ bool isleaf(int which) const { return (child[1]&(1<<(14+which)))!=0; }
+ };
+
+ struct tri
+ {
+ ushort vert[3];
+ };
+
+ struct tribb
+ {
+ svec center, radius;
+
+ bool outside(const ivec &bo, const ivec &br) const
+ {
+ return abs(bo.x - center.x) > br.x + radius.x ||
+ abs(bo.y - center.y) > br.y + radius.y ||
+ abs(bo.z - center.z) > br.z + radius.z;
+ }
+ };
+
+ enum { MESH_NOCLIP = 1<<0, MESH_ALPHA = 1<<1, MESH_CULLFACE = 1<<2 };
+
+ struct mesh
+ {
+ enum { MAXTRIS = 1<<14 };
+
+ matrix4x3 xform, invxform;
+ matrix3 xformnorm, invxformnorm;
+ float scale, invscale;
+ node *nodes;
+ int numnodes;
+ const tri *tris;
+ const tribb *tribbs;
+ int numtris;
+ const uchar *pos, *tc;
+ int posstride, tcstride;
+ Texture *tex;
+ int flags;
+ vec bbmin, bbmax;
+
+ mesh() : numnodes(0), numtris(0), tex(NULL), flags(0) {}
+
+ vec getpos(int i) const { return *(const vec *)(pos + i*posstride); }
+ vec2 gettc(int i) const { return *(const vec2 *)(tc + i*tcstride); }
+ };
+
+ mesh *meshes;
+ int nummeshes;
+ node *nodes;
+ int numnodes;
+ tribb *tribbs;
+ int numtris;
+ vec bbmin, bbmax, center;
+ float radius, entradius;
+
+ BIH(vector<mesh> &buildmeshes);
+
+ ~BIH();
+
+ void build(mesh &m, ushort *indices, int numindices, const ivec &vmin, const ivec &vmax);
+
+ bool traverse(const vec &o, const vec &ray, float maxdist, float &dist, int mode);
+ bool traverse(const mesh &m, const vec &o, const vec &ray, const vec &invray, float maxdist, float &dist, int mode, node *curnode, float tmin, float tmax);
+ bool triintersect(const mesh &m, int tidx, const vec &mo, const vec &mray, float maxdist, float &dist, int mode);
+
+ void preload();
};
extern bool mmintersect(const extentity &e, const vec &o, const vec &ray, float maxdist, int mode, float &dist);
diff --git a/src/engine/blend.cpp b/src/engine/blend.cpp
index 16f21c2..4d83ef6 100644
--- a/src/engine/blend.cpp
+++ b/src/engine/blend.cpp
@@ -2,9 +2,9 @@
enum
{
- BM_BRANCH = 0,
- BM_SOLID,
- BM_IMAGE
+ BM_BRANCH = 0,
+ BM_SOLID,
+ BM_IMAGE
};
struct BlendMapBranch;
@@ -13,35 +13,35 @@ struct BlendMapImage;
struct BlendMapNode
{
- union
- {
- BlendMapBranch *branch;
- BlendMapSolid *solid;
- BlendMapImage *image;
- };
+ union
+ {
+ BlendMapBranch *branch;
+ BlendMapSolid *solid;
+ BlendMapImage *image;
+ };
- void cleanup(int type);
- void splitsolid(uchar &type, uchar val);
+ void cleanup(int type);
+ void splitsolid(uchar &type, uchar val);
};
struct BlendMapBranch
{
- uchar type[4];
- BlendMapNode children[4];
+ uchar type[4];
+ BlendMapNode children[4];
- ~BlendMapBranch()
- {
- loopi(4) children[i].cleanup(type[i]);
- }
+ ~BlendMapBranch()
+ {
+ loopi(4) children[i].cleanup(type[i]);
+ }
- uchar shrink(BlendMapNode &child, int quadrant);
+ uchar shrink(BlendMapNode &child, int quadrant);
};
struct BlendMapSolid
{
- uchar val;
+ uchar val;
- BlendMapSolid(uchar val) : val(val) {}
+ BlendMapSolid(uchar val) : val(val) {}
};
#define BM_SCALE 1
@@ -49,76 +49,76 @@ struct BlendMapSolid
struct BlendMapImage
{
- uchar data[BM_IMAGE_SIZE*BM_IMAGE_SIZE];
+ uchar data[BM_IMAGE_SIZE*BM_IMAGE_SIZE];
};
void BlendMapNode::cleanup(int type)
{
- switch(type)
- {
- case BM_BRANCH: delete branch; break;
- case BM_IMAGE: delete image; break;
- }
+ switch(type)
+ {
+ case BM_BRANCH: delete branch; break;
+ case BM_IMAGE: delete image; break;
+ }
}
#define DEFBMSOLIDS(n) n, n+1, n+2, n+3, n+4, n+5, n+6, n+7, n+8, n+9, n+10, n+11, n+12, n+13, n+14, n+15
-static BlendMapSolid bmsolids[256] =
+static BlendMapSolid bmsolids[256] =
{
- DEFBMSOLIDS(0x00), DEFBMSOLIDS(0x10), DEFBMSOLIDS(0x20), DEFBMSOLIDS(0x30),
- DEFBMSOLIDS(0x40), DEFBMSOLIDS(0x50), DEFBMSOLIDS(0x60), DEFBMSOLIDS(0x70),
- DEFBMSOLIDS(0x80), DEFBMSOLIDS(0x90), DEFBMSOLIDS(0xA0), DEFBMSOLIDS(0xB0),
- DEFBMSOLIDS(0xC0), DEFBMSOLIDS(0xD0), DEFBMSOLIDS(0xE0), DEFBMSOLIDS(0xF0),
+ DEFBMSOLIDS(0x00), DEFBMSOLIDS(0x10), DEFBMSOLIDS(0x20), DEFBMSOLIDS(0x30),
+ DEFBMSOLIDS(0x40), DEFBMSOLIDS(0x50), DEFBMSOLIDS(0x60), DEFBMSOLIDS(0x70),
+ DEFBMSOLIDS(0x80), DEFBMSOLIDS(0x90), DEFBMSOLIDS(0xA0), DEFBMSOLIDS(0xB0),
+ DEFBMSOLIDS(0xC0), DEFBMSOLIDS(0xD0), DEFBMSOLIDS(0xE0), DEFBMSOLIDS(0xF0),
};
void BlendMapNode::splitsolid(uchar &type, uchar val)
{
- cleanup(type);
- type = BM_BRANCH;
- branch = new BlendMapBranch;
- loopi(4)
- {
- branch->type[i] = BM_SOLID;
- branch->children[i].solid = &bmsolids[val];
- }
+ cleanup(type);
+ type = BM_BRANCH;
+ branch = new BlendMapBranch;
+ loopi(4)
+ {
+ branch->type[i] = BM_SOLID;
+ branch->children[i].solid = &bmsolids[val];
+ }
}
uchar BlendMapBranch::shrink(BlendMapNode &child, int quadrant)
{
- uchar childtype = type[quadrant];
- child = children[quadrant];
- type[quadrant] = BM_SOLID;
- children[quadrant].solid = &bmsolids[0];
- return childtype;
+ uchar childtype = type[quadrant];
+ child = children[quadrant];
+ type[quadrant] = BM_SOLID;
+ children[quadrant].solid = &bmsolids[0];
+ return childtype;
}
struct BlendMapRoot : BlendMapNode
{
- uchar type;
+ uchar type;
- BlendMapRoot() : type(BM_SOLID) { solid = &bmsolids[0xFF]; }
- BlendMapRoot(uchar type, const BlendMapNode &node) : BlendMapNode(node), type(type) {}
+ BlendMapRoot() : type(BM_SOLID) { solid = &bmsolids[0xFF]; }
+ BlendMapRoot(uchar type, const BlendMapNode &node) : BlendMapNode(node), type(type) {}
- void cleanup() { BlendMapNode::cleanup(type); }
+ void cleanup() { BlendMapNode::cleanup(type); }
- void shrink(int quadrant)
- {
- if(type == BM_BRANCH)
- {
- BlendMapRoot oldroot = *this;
- type = branch->shrink(*this, quadrant);
- oldroot.cleanup();
- }
- }
+ void shrink(int quadrant)
+ {
+ if(type == BM_BRANCH)
+ {
+ BlendMapRoot oldroot = *this;
+ type = branch->shrink(*this, quadrant);
+ oldroot.cleanup();
+ }
+ }
};
static BlendMapRoot blendmap;
struct BlendMapCache
{
- BlendMapRoot node;
- int scale;
- ivec2 origin;
+ BlendMapRoot node;
+ int scale;
+ ivec2 origin;
};
BlendMapCache *newblendmapcache() { return new BlendMapCache; }
@@ -127,430 +127,430 @@ void freeblendmapcache(BlendMapCache *&cache) { delete cache; cache = NULL; }
bool setblendmaporigin(BlendMapCache *cache, const ivec &o, int size)
{
- if(blendmap.type!=BM_BRANCH)
- {
- cache->node = blendmap;
- cache->scale = worldscale-BM_SCALE;
- cache->origin = ivec2(0, 0);
- return cache->node.solid!=&bmsolids[0xFF];
- }
-
- BlendMapBranch *bm = blendmap.branch;
- int bmscale = worldscale-BM_SCALE, bmsize = 1<<bmscale,
- x = o.x>>BM_SCALE, y = o.y>>BM_SCALE,
- x1 = max(x-1, 0), y1 = max(y-1, 0),
- x2 = min(((o.x + size + (1<<BM_SCALE)-1)>>BM_SCALE) + 1, bmsize),
- y2 = min(((o.y + size + (1<<BM_SCALE)-1)>>BM_SCALE) + 1, bmsize),
- diff = (x1^x2)|(y1^y2);
- if(diff < bmsize) while(!(diff&(1<<(bmscale-1))))
- {
- bmscale--;
- int n = (((y1>>bmscale)&1)<<1) | ((x1>>bmscale)&1);
- if(bm->type[n]!=BM_BRANCH)
- {
- cache->node = BlendMapRoot(bm->type[n], bm->children[n]);
- cache->scale = bmscale;
- cache->origin = ivec2(x1&(~0U<<bmscale), y1&(~0U<<bmscale));
- return cache->node.solid!=&bmsolids[0xFF];
- }
- bm = bm->children[n].branch;
- }
-
- cache->node.type = BM_BRANCH;
- cache->node.branch = bm;
- cache->scale = bmscale;
- cache->origin = ivec2(x1&(~0U<<bmscale), y1&(~0U<<bmscale));
- return true;
+ if(blendmap.type!=BM_BRANCH)
+ {
+ cache->node = blendmap;
+ cache->scale = worldscale-BM_SCALE;
+ cache->origin = ivec2(0, 0);
+ return cache->node.solid!=&bmsolids[0xFF];
+ }
+
+ BlendMapBranch *bm = blendmap.branch;
+ int bmscale = worldscale-BM_SCALE, bmsize = 1<<bmscale,
+ x = o.x>>BM_SCALE, y = o.y>>BM_SCALE,
+ x1 = max(x-1, 0), y1 = max(y-1, 0),
+ x2 = min(((o.x + size + (1<<BM_SCALE)-1)>>BM_SCALE) + 1, bmsize),
+ y2 = min(((o.y + size + (1<<BM_SCALE)-1)>>BM_SCALE) + 1, bmsize),
+ diff = (x1^x2)|(y1^y2);
+ if(diff < bmsize) while(!(diff&(1<<(bmscale-1))))
+ {
+ bmscale--;
+ int n = (((y1>>bmscale)&1)<<1) | ((x1>>bmscale)&1);
+ if(bm->type[n]!=BM_BRANCH)
+ {
+ cache->node = BlendMapRoot(bm->type[n], bm->children[n]);
+ cache->scale = bmscale;
+ cache->origin = ivec2(x1&(~0U<<bmscale), y1&(~0U<<bmscale));
+ return cache->node.solid!=&bmsolids[0xFF];
+ }
+ bm = bm->children[n].branch;
+ }
+
+ cache->node.type = BM_BRANCH;
+ cache->node.branch = bm;
+ cache->scale = bmscale;
+ cache->origin = ivec2(x1&(~0U<<bmscale), y1&(~0U<<bmscale));
+ return true;
}
bool hasblendmap(BlendMapCache *cache)
{
- return cache->node.solid!=&bmsolids[0xFF];
+ return cache->node.solid!=&bmsolids[0xFF];
}
static uchar lookupblendmap(int x, int y, BlendMapBranch *bm, int bmscale)
{
- for(;;)
- {
- bmscale--;
- int n = (((y>>bmscale)&1)<<1) | ((x>>bmscale)&1);
- switch(bm->type[n])
- {
- case BM_SOLID: return bm->children[n].solid->val;
- case BM_IMAGE: return bm->children[n].image->data[(y&((1<<bmscale)-1))*BM_IMAGE_SIZE + (x&((1<<bmscale)-1))];
- }
- bm = bm->children[n].branch;
- }
-}
-
+ for(;;)
+ {
+ bmscale--;
+ int n = (((y>>bmscale)&1)<<1) | ((x>>bmscale)&1);
+ switch(bm->type[n])
+ {
+ case BM_SOLID: return bm->children[n].solid->val;
+ case BM_IMAGE: return bm->children[n].image->data[(y&((1<<bmscale)-1))*BM_IMAGE_SIZE + (x&((1<<bmscale)-1))];
+ }
+ bm = bm->children[n].branch;
+ }
+}
+
uchar lookupblendmap(BlendMapCache *cache, const vec &pos)
{
- if(cache->node.type==BM_SOLID) return cache->node.solid->val;
-
- uchar vals[4], *val = vals;
- float bx = pos.x/(1<<BM_SCALE) - 0.5f, by = pos.y/(1<<BM_SCALE) - 0.5f;
- int ix = (int)floor(bx), iy = (int)floor(by),
- rx = ix-cache->origin.x, ry = iy-cache->origin.y;
- loop(vy, 2) loop(vx, 2)
- {
- int cx = clamp(rx+vx, 0, (1<<cache->scale)-1), cy = clamp(ry+vy, 0, (1<<cache->scale)-1);
- if(cache->node.type==BM_IMAGE)
- *val++ = cache->node.image->data[cy*BM_IMAGE_SIZE + cx];
- else *val++ = lookupblendmap(cx, cy, cache->node.branch, cache->scale);
- }
- float fx = bx - ix, fy = by - iy;
- return uchar((1-fy)*((1-fx)*vals[0] + fx*vals[1]) +
- fy*((1-fx)*vals[2] + fx*vals[3]));
+ if(cache->node.type==BM_SOLID) return cache->node.solid->val;
+
+ uchar vals[4], *val = vals;
+ float bx = pos.x/(1<<BM_SCALE) - 0.5f, by = pos.y/(1<<BM_SCALE) - 0.5f;
+ int ix = (int)floor(bx), iy = (int)floor(by),
+ rx = ix-cache->origin.x, ry = iy-cache->origin.y;
+ loop(vy, 2) loop(vx, 2)
+ {
+ int cx = clamp(rx+vx, 0, (1<<cache->scale)-1), cy = clamp(ry+vy, 0, (1<<cache->scale)-1);
+ if(cache->node.type==BM_IMAGE)
+ *val++ = cache->node.image->data[cy*BM_IMAGE_SIZE + cx];
+ else *val++ = lookupblendmap(cx, cy, cache->node.branch, cache->scale);
+ }
+ float fx = bx - ix, fy = by - iy;
+ return uchar((1-fy)*((1-fx)*vals[0] + fx*vals[1]) +
+ fy*((1-fx)*vals[2] + fx*vals[3]));
}
static void fillblendmap(uchar &type, BlendMapNode &node, int size, uchar val, int x1, int y1, int x2, int y2)
{
- if(max(x1, y1) <= 0 && min(x2, y2) >= size)
- {
- node.cleanup(type);
- type = BM_SOLID;
- node.solid = &bmsolids[val];
- return;
- }
-
- if(type==BM_BRANCH)
- {
- size /= 2;
- if(y1 < size)
- {
- if(x1 < size) fillblendmap(node.branch->type[0], node.branch->children[0], size, val,
- x1, y1, min(x2, size), min(y2, size));
- if(x2 > size) fillblendmap(node.branch->type[1], node.branch->children[1], size, val,
- max(x1-size, 0), y1, x2-size, min(y2, size));
- }
- if(y2 > size)
- {
- if(x1 < size) fillblendmap(node.branch->type[2], node.branch->children[2], size, val,
- x1, max(y1-size, 0), min(x2, size), y2-size);
- if(x2 > size) fillblendmap(node.branch->type[3], node.branch->children[3], size, val,
- max(x1-size, 0), max(y1-size, 0), x2-size, y2-size);
- }
- loopi(4) if(node.branch->type[i]!=BM_SOLID || node.branch->children[i].solid->val!=val) return;
- node.cleanup(type);
- type = BM_SOLID;
- node.solid = &bmsolids[val];
- return;
- }
- else if(type==BM_SOLID)
- {
- uchar oldval = node.solid->val;
- if(oldval==val) return;
-
- if(size > BM_IMAGE_SIZE)
- {
- node.splitsolid(type, oldval);
- fillblendmap(type, node, size, val, x1, y1, x2, y2);
- return;
- }
-
- type = BM_IMAGE;
- node.image = new BlendMapImage;
- memset(node.image->data, oldval, sizeof(node.image->data));
- }
-
- uchar *dst = &node.image->data[y1*BM_IMAGE_SIZE + x1];
- loopi(y2-y1)
- {
- memset(dst, val, x2-x1);
- dst += BM_IMAGE_SIZE;
- }
+ if(max(x1, y1) <= 0 && min(x2, y2) >= size)
+ {
+ node.cleanup(type);
+ type = BM_SOLID;
+ node.solid = &bmsolids[val];
+ return;
+ }
+
+ if(type==BM_BRANCH)
+ {
+ size /= 2;
+ if(y1 < size)
+ {
+ if(x1 < size) fillblendmap(node.branch->type[0], node.branch->children[0], size, val,
+ x1, y1, min(x2, size), min(y2, size));
+ if(x2 > size) fillblendmap(node.branch->type[1], node.branch->children[1], size, val,
+ max(x1-size, 0), y1, x2-size, min(y2, size));
+ }
+ if(y2 > size)
+ {
+ if(x1 < size) fillblendmap(node.branch->type[2], node.branch->children[2], size, val,
+ x1, max(y1-size, 0), min(x2, size), y2-size);
+ if(x2 > size) fillblendmap(node.branch->type[3], node.branch->children[3], size, val,
+ max(x1-size, 0), max(y1-size, 0), x2-size, y2-size);
+ }
+ loopi(4) if(node.branch->type[i]!=BM_SOLID || node.branch->children[i].solid->val!=val) return;
+ node.cleanup(type);
+ type = BM_SOLID;
+ node.solid = &bmsolids[val];
+ return;
+ }
+ else if(type==BM_SOLID)
+ {
+ uchar oldval = node.solid->val;
+ if(oldval==val) return;
+
+ if(size > BM_IMAGE_SIZE)
+ {
+ node.splitsolid(type, oldval);
+ fillblendmap(type, node, size, val, x1, y1, x2, y2);
+ return;
+ }
+
+ type = BM_IMAGE;
+ node.image = new BlendMapImage;
+ memset(node.image->data, oldval, sizeof(node.image->data));
+ }
+
+ uchar *dst = &node.image->data[y1*BM_IMAGE_SIZE + x1];
+ loopi(y2-y1)
+ {
+ memset(dst, val, x2-x1);
+ dst += BM_IMAGE_SIZE;
+ }
}
void fillblendmap(int x, int y, int w, int h, uchar val)
{
- int bmsize = worldsize>>BM_SCALE,
- x1 = clamp(x, 0, bmsize),
- y1 = clamp(y, 0, bmsize),
- x2 = clamp(x+w, 0, bmsize),
- y2 = clamp(y+h, 0, bmsize);
- if(max(x1, y1) >= bmsize || min(x2, y2) <= 0 || x1>=x2 || y1>=y2) return;
- fillblendmap(blendmap.type, blendmap, bmsize, val, x1, y1, x2, y2);
+ int bmsize = worldsize>>BM_SCALE,
+ x1 = clamp(x, 0, bmsize),
+ y1 = clamp(y, 0, bmsize),
+ x2 = clamp(x+w, 0, bmsize),
+ y2 = clamp(y+h, 0, bmsize);
+ if(max(x1, y1) >= bmsize || min(x2, y2) <= 0 || x1>=x2 || y1>=y2) return;
+ fillblendmap(blendmap.type, blendmap, bmsize, val, x1, y1, x2, y2);
}
static void invertblendmap(uchar &type, BlendMapNode &node, int size, int x1, int y1, int x2, int y2)
{
- if(type==BM_BRANCH)
- {
- size /= 2;
- if(y1 < size)
- {
- if(x1 < size) invertblendmap(node.branch->type[0], node.branch->children[0], size,
- x1, y1, min(x2, size), min(y2, size));
- if(x2 > size) invertblendmap(node.branch->type[1], node.branch->children[1], size,
- max(x1-size, 0), y1, x2-size, min(y2, size));
- }
- if(y2 > size)
- {
- if(x1 < size) invertblendmap(node.branch->type[2], node.branch->children[2], size,
- x1, max(y1-size, 0), min(x2, size), y2-size);
- if(x2 > size) invertblendmap(node.branch->type[3], node.branch->children[3], size,
- max(x1-size, 0), max(y1-size, 0), x2-size, y2-size);
- }
- return;
- }
- else if(type==BM_SOLID)
- {
- fillblendmap(type, node, size, 255-node.solid->val, x1, y1, x2, y2);
- }
- else if(type==BM_IMAGE)
- {
- uchar *dst = &node.image->data[y1*BM_IMAGE_SIZE + x1];
- loopi(y2-y1)
- {
- loopj(x2-x1) dst[j] = 255-dst[j];
- dst += BM_IMAGE_SIZE;
- }
- }
+ if(type==BM_BRANCH)
+ {
+ size /= 2;
+ if(y1 < size)
+ {
+ if(x1 < size) invertblendmap(node.branch->type[0], node.branch->children[0], size,
+ x1, y1, min(x2, size), min(y2, size));
+ if(x2 > size) invertblendmap(node.branch->type[1], node.branch->children[1], size,
+ max(x1-size, 0), y1, x2-size, min(y2, size));
+ }
+ if(y2 > size)
+ {
+ if(x1 < size) invertblendmap(node.branch->type[2], node.branch->children[2], size,
+ x1, max(y1-size, 0), min(x2, size), y2-size);
+ if(x2 > size) invertblendmap(node.branch->type[3], node.branch->children[3], size,
+ max(x1-size, 0), max(y1-size, 0), x2-size, y2-size);
+ }
+ return;
+ }
+ else if(type==BM_SOLID)
+ {
+ fillblendmap(type, node, size, 255-node.solid->val, x1, y1, x2, y2);
+ }
+ else if(type==BM_IMAGE)
+ {
+ uchar *dst = &node.image->data[y1*BM_IMAGE_SIZE + x1];
+ loopi(y2-y1)
+ {
+ loopj(x2-x1) dst[j] = 255-dst[j];
+ dst += BM_IMAGE_SIZE;
+ }
+ }
}
void invertblendmap(int x, int y, int w, int h)
{
- int bmsize = worldsize>>BM_SCALE,
- x1 = clamp(x, 0, bmsize),
- y1 = clamp(y, 0, bmsize),
- x2 = clamp(x+w, 0, bmsize),
- y2 = clamp(y+h, 0, bmsize);
- if(max(x1, y1) >= bmsize || min(x2, y2) <= 0 || x1>=x2 || y1>=y2) return;
- invertblendmap(blendmap.type, blendmap, bmsize, x1, y1, x2, y2);
+ int bmsize = worldsize>>BM_SCALE,
+ x1 = clamp(x, 0, bmsize),
+ y1 = clamp(y, 0, bmsize),
+ x2 = clamp(x+w, 0, bmsize),
+ y2 = clamp(y+h, 0, bmsize);
+ if(max(x1, y1) >= bmsize || min(x2, y2) <= 0 || x1>=x2 || y1>=y2) return;
+ invertblendmap(blendmap.type, blendmap, bmsize, x1, y1, x2, y2);
}
static void optimizeblendmap(uchar &type, BlendMapNode &node)
{
- switch(type)
- {
- case BM_IMAGE:
- {
- uint val = node.image->data[0];
- val |= val<<8;
- val |= val<<16;
- for(uint *data = (uint *)node.image->data, *end = &data[sizeof(node.image->data)/sizeof(uint)]; data < end; data++)
- if(*data != val) return;
- node.cleanup(type);
- type = BM_SOLID;
- node.solid = &bmsolids[val&0xFF];
- break;
- }
- case BM_BRANCH:
- {
- loopi(4) optimizeblendmap(node.branch->type[i], node.branch->children[i]);
- if(node.branch->type[3]!=BM_SOLID) return;
- uint val = node.branch->children[3].solid->val;
- loopi(3) if(node.branch->type[i]!=BM_SOLID || node.branch->children[i].solid->val != val) return;
- node.cleanup(type);
- type = BM_SOLID;
- node.solid = &bmsolids[val];
- break;
- }
- }
+ switch(type)
+ {
+ case BM_IMAGE:
+ {
+ uint val = node.image->data[0];
+ val |= val<<8;
+ val |= val<<16;
+ for(uint *data = (uint *)node.image->data, *end = &data[sizeof(node.image->data)/sizeof(uint)]; data < end; data++)
+ if(*data != val) return;
+ node.cleanup(type);
+ type = BM_SOLID;
+ node.solid = &bmsolids[val&0xFF];
+ break;
+ }
+ case BM_BRANCH:
+ {
+ loopi(4) optimizeblendmap(node.branch->type[i], node.branch->children[i]);
+ if(node.branch->type[3]!=BM_SOLID) return;
+ uint val = node.branch->children[3].solid->val;
+ loopi(3) if(node.branch->type[i]!=BM_SOLID || node.branch->children[i].solid->val != val) return;
+ node.cleanup(type);
+ type = BM_SOLID;
+ node.solid = &bmsolids[val];
+ break;
+ }
+ }
}
void optimizeblendmap()
{
- optimizeblendmap(blendmap.type, blendmap);
+ optimizeblendmap(blendmap.type, blendmap);
}
VARF(blendpaintmode, 0, 0, 5,
{
- if(!blendpaintmode) stoppaintblendmap();
+ if(!blendpaintmode) stoppaintblendmap();
});
static void blitblendmap(uchar &type, BlendMapNode &node, int bmx, int bmy, int bmsize, uchar *src, int sx, int sy, int sw, int sh, int smode)
{
- if(type==BM_BRANCH)
- {
- bmsize /= 2;
- if(sy < bmy + bmsize)
- {
- if(sx < bmx + bmsize) blitblendmap(node.branch->type[0], node.branch->children[0], bmx, bmy, bmsize, src, sx, sy, sw, sh, smode);
- if(sx + sw > bmx + bmsize) blitblendmap(node.branch->type[1], node.branch->children[1], bmx+bmsize, bmy, bmsize, src, sx, sy, sw, sh, smode);
- }
- if(sy + sh > bmy + bmsize)
- {
- if(sx < bmx + bmsize) blitblendmap(node.branch->type[2], node.branch->children[2], bmx, bmy+bmsize, bmsize, src, sx, sy, sw, sh, smode);
- if(sx + sw > bmx + bmsize) blitblendmap(node.branch->type[3], node.branch->children[3], bmx+bmsize, bmy+bmsize, bmsize, src, sx, sy, sw, sh, smode);
- }
- return;
- }
- if(type==BM_SOLID)
- {
- uchar val = node.solid->val;
- if(bmsize > BM_IMAGE_SIZE)
- {
- node.splitsolid(type, val);
- blitblendmap(type, node, bmx, bmy, bmsize, src, sx, sy, sw, sh, smode);
- return;
- }
-
- type = BM_IMAGE;
- node.image = new BlendMapImage;
- memset(node.image->data, val, sizeof(node.image->data));
- }
-
- int x1 = clamp(sx - bmx, 0, bmsize), y1 = clamp(sy - bmy, 0, bmsize),
- x2 = clamp(sx+sw - bmx, 0, bmsize), y2 = clamp(sy+sh - bmy, 0, bmsize);
- uchar *dst = &node.image->data[y1*BM_IMAGE_SIZE + x1];
- src += max(bmy - sy, 0)*sw + max(bmx - sx, 0);
- loopi(y2-y1)
- {
- switch(smode)
- {
- case 1:
- memcpy(dst, src, x2 - x1);
- break;
-
- case 2:
- loopi(x2 - x1) dst[i] = min(dst[i], src[i]);
- break;
-
- case 3:
- loopi(x2 - x1) dst[i] = max(dst[i], src[i]);
- break;
-
- case 4:
- loopi(x2 - x1) dst[i] = min(dst[i], uchar(0xFF - src[i]));
- break;
-
- case 5:
- loopi(x2 - x1) dst[i] = max(dst[i], uchar(0xFF - src[i]));
- break;
- }
- dst += BM_IMAGE_SIZE;
- src += sw;
- }
+ if(type==BM_BRANCH)
+ {
+ bmsize /= 2;
+ if(sy < bmy + bmsize)
+ {
+ if(sx < bmx + bmsize) blitblendmap(node.branch->type[0], node.branch->children[0], bmx, bmy, bmsize, src, sx, sy, sw, sh, smode);
+ if(sx + sw > bmx + bmsize) blitblendmap(node.branch->type[1], node.branch->children[1], bmx+bmsize, bmy, bmsize, src, sx, sy, sw, sh, smode);
+ }
+ if(sy + sh > bmy + bmsize)
+ {
+ if(sx < bmx + bmsize) blitblendmap(node.branch->type[2], node.branch->children[2], bmx, bmy+bmsize, bmsize, src, sx, sy, sw, sh, smode);
+ if(sx + sw > bmx + bmsize) blitblendmap(node.branch->type[3], node.branch->children[3], bmx+bmsize, bmy+bmsize, bmsize, src, sx, sy, sw, sh, smode);
+ }
+ return;
+ }
+ if(type==BM_SOLID)
+ {
+ uchar val = node.solid->val;
+ if(bmsize > BM_IMAGE_SIZE)
+ {
+ node.splitsolid(type, val);
+ blitblendmap(type, node, bmx, bmy, bmsize, src, sx, sy, sw, sh, smode);
+ return;
+ }
+
+ type = BM_IMAGE;
+ node.image = new BlendMapImage;
+ memset(node.image->data, val, sizeof(node.image->data));
+ }
+
+ int x1 = clamp(sx - bmx, 0, bmsize), y1 = clamp(sy - bmy, 0, bmsize),
+ x2 = clamp(sx+sw - bmx, 0, bmsize), y2 = clamp(sy+sh - bmy, 0, bmsize);
+ uchar *dst = &node.image->data[y1*BM_IMAGE_SIZE + x1];
+ src += max(bmy - sy, 0)*sw + max(bmx - sx, 0);
+ loopi(y2-y1)
+ {
+ switch(smode)
+ {
+ case 1:
+ memcpy(dst, src, x2 - x1);
+ break;
+
+ case 2:
+ loopi(x2 - x1) dst[i] = min(dst[i], src[i]);
+ break;
+
+ case 3:
+ loopi(x2 - x1) dst[i] = max(dst[i], src[i]);
+ break;
+
+ case 4:
+ loopi(x2 - x1) dst[i] = min(dst[i], uchar(0xFF - src[i]));
+ break;
+
+ case 5:
+ loopi(x2 - x1) dst[i] = max(dst[i], uchar(0xFF - src[i]));
+ break;
+ }
+ dst += BM_IMAGE_SIZE;
+ src += sw;
+ }
}
void blitblendmap(uchar *src, int sx, int sy, int sw, int sh, int smode)
{
- int bmsize = worldsize>>BM_SCALE;
- if(max(sx, sy) >= bmsize || min(sx+sw, sy+sh) <= 0 || min(sw, sh) <= 0) return;
- blitblendmap(blendmap.type, blendmap, 0, 0, bmsize, src, sx, sy, sw, sh, smode);
+ int bmsize = worldsize>>BM_SCALE;
+ if(max(sx, sy) >= bmsize || min(sx+sw, sy+sh) <= 0 || min(sw, sh) <= 0) return;
+ blitblendmap(blendmap.type, blendmap, 0, 0, bmsize, src, sx, sy, sw, sh, smode);
}
-
+
void resetblendmap()
{
- blendmap.cleanup();
- blendmap.type = BM_SOLID;
- blendmap.solid = &bmsolids[0xFF];
+ blendmap.cleanup();
+ blendmap.type = BM_SOLID;
+ blendmap.solid = &bmsolids[0xFF];
}
void enlargeblendmap()
{
- if(blendmap.type == BM_SOLID) return;
- BlendMapBranch *branch = new BlendMapBranch;
- branch->type[0] = blendmap.type;
- branch->children[0] = blendmap;
- loopi(3)
- {
- branch->type[i+1] = BM_SOLID;
- branch->children[i+1].solid = &bmsolids[0xFF];
- }
- blendmap.type = BM_BRANCH;
- blendmap.branch = branch;
+ if(blendmap.type == BM_SOLID) return;
+ BlendMapBranch *branch = new BlendMapBranch;
+ branch->type[0] = blendmap.type;
+ branch->children[0] = blendmap;
+ loopi(3)
+ {
+ branch->type[i+1] = BM_SOLID;
+ branch->children[i+1].solid = &bmsolids[0xFF];
+ }
+ blendmap.type = BM_BRANCH;
+ blendmap.branch = branch;
}
void shrinkblendmap(int octant)
{
- blendmap.shrink(octant&3);
+ blendmap.shrink(octant&3);
}
void moveblendmap(uchar type, BlendMapNode &node, int size, int x, int y, int dx, int dy)
{
- if(type == BM_BRANCH)
- {
- size /= 2;
- moveblendmap(node.branch->type[0], node.branch->children[0], size, x, y, dx, dy);
- moveblendmap(node.branch->type[1], node.branch->children[1], size, x + size, y, dx, dy);
- moveblendmap(node.branch->type[2], node.branch->children[2], size, x, y + size, dx, dy);
- moveblendmap(node.branch->type[3], node.branch->children[3], size, x + size, y + size, dx, dy);
- return;
- }
- else if(type == BM_SOLID)
- {
- fillblendmap(x+dx, y+dy, size, size, node.solid->val);
- }
- else if(type == BM_IMAGE)
- {
- blitblendmap(node.image->data, x+dx, y+dy, size, size, 1);
- }
+ if(type == BM_BRANCH)
+ {
+ size /= 2;
+ moveblendmap(node.branch->type[0], node.branch->children[0], size, x, y, dx, dy);
+ moveblendmap(node.branch->type[1], node.branch->children[1], size, x + size, y, dx, dy);
+ moveblendmap(node.branch->type[2], node.branch->children[2], size, x, y + size, dx, dy);
+ moveblendmap(node.branch->type[3], node.branch->children[3], size, x + size, y + size, dx, dy);
+ return;
+ }
+ else if(type == BM_SOLID)
+ {
+ fillblendmap(x+dx, y+dy, size, size, node.solid->val);
+ }
+ else if(type == BM_IMAGE)
+ {
+ blitblendmap(node.image->data, x+dx, y+dy, size, size, 1);
+ }
}
void moveblendmap(int dx, int dy)
{
- BlendMapRoot old = blendmap;
- blendmap.type = BM_SOLID;
- blendmap.solid = &bmsolids[0xFF];
- moveblendmap(old.type, old, worldsize>>BM_SCALE, 0, 0, dx, dy);
- old.cleanup();
+ BlendMapRoot old = blendmap;
+ blendmap.type = BM_SOLID;
+ blendmap.solid = &bmsolids[0xFF];
+ moveblendmap(old.type, old, worldsize>>BM_SCALE, 0, 0, dx, dy);
+ old.cleanup();
}
-
+
struct BlendBrush
{
- char *name;
- int w, h;
- uchar *data;
- GLuint tex;
-
- BlendBrush(const char *name, int w, int h) :
- name(newstring(name)), w(w), h(h), data(new uchar[w*h]), tex(0)
- {}
-
- ~BlendBrush()
- {
- cleanup();
- delete[] name;
- if(data) delete[] data;
- }
-
- void cleanup()
- {
- if(tex) { glDeleteTextures(1, &tex); tex = 0; }
- }
-
- void gentex()
- {
- if(!tex) glGenTextures(1, &tex);
- uchar *buf = new uchar[2*w*h];
- uchar *dst = buf, *src = data;
- loopi(h)
- {
- loopj(w) *dst++ = 255 - *src++;
- }
- createtexture(tex, w, h, buf, 3, 1, hasTRG ? GL_R8 : GL_LUMINANCE8);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
- GLfloat border[4] = { 0, 0, 0, 0 };
- glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
- delete[] buf;
- }
-
- void reorient(bool flipx, bool flipy, bool swapxy)
- {
- uchar *rdata = new uchar[w*h];
- int stridex = 1, stridey = 1;
- if(swapxy) stridex *= h; else stridey *= w;
- uchar *src = data, *dst = rdata;
- if(flipx) { dst += (w-1)*stridex; stridex = -stridex; }
- if(flipy) { dst += (h-1)*stridey; stridey = -stridey; }
- loopi(h)
- {
- uchar *curdst = dst;
- loopj(w)
- {
- *curdst = *src++;
- curdst += stridex;
- }
- dst += stridey;
- }
- if(swapxy) swap(w, h);
- delete[] data;
- data = rdata;
- if(tex) gentex();
- }
+ char *name;
+ int w, h;
+ uchar *data;
+ GLuint tex;
+
+ BlendBrush(const char *name, int w, int h) :
+ name(newstring(name)), w(w), h(h), data(new uchar[w*h]), tex(0)
+ {}
+
+ ~BlendBrush()
+ {
+ cleanup();
+ delete[] name;
+ if(data) delete[] data;
+ }
+
+ void cleanup()
+ {
+ if(tex) { glDeleteTextures(1, &tex); tex = 0; }
+ }
+
+ void gentex()
+ {
+ if(!tex) glGenTextures(1, &tex);
+ uchar *buf = new uchar[2*w*h];
+ uchar *dst = buf, *src = data;
+ loopi(h)
+ {
+ loopj(w) *dst++ = 255 - *src++;
+ }
+ createtexture(tex, w, h, buf, 3, 1, hasTRG ? GL_R8 : GL_LUMINANCE8);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ GLfloat border[4] = { 0, 0, 0, 0 };
+ glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
+ delete[] buf;
+ }
+
+ void reorient(bool flipx, bool flipy, bool swapxy)
+ {
+ uchar *rdata = new uchar[w*h];
+ int stridex = 1, stridey = 1;
+ if(swapxy) stridex *= h; else stridey *= w;
+ uchar *src = data, *dst = rdata;
+ if(flipx) { dst += (w-1)*stridex; stridex = -stridex; }
+ if(flipy) { dst += (h-1)*stridey; stridey = -stridey; }
+ loopi(h)
+ {
+ uchar *curdst = dst;
+ loopj(w)
+ {
+ *curdst = *src++;
+ curdst += stridex;
+ }
+ dst += stridey;
+ }
+ if(swapxy) swap(w, h);
+ delete[] data;
+ data = rdata;
+ if(tex) gentex();
+ }
};
static vector<BlendBrush *> brushes;
@@ -558,73 +558,73 @@ static int curbrush = -1;
void cleanupblendmap()
{
- loopv(brushes) brushes[i]->cleanup();
+ loopv(brushes) brushes[i]->cleanup();
}
void clearblendbrushes()
{
- while(brushes.length()) delete brushes.pop();
- curbrush = -1;
+ while(brushes.length()) delete brushes.pop();
+ curbrush = -1;
}
void delblendbrush(const char *name)
{
- loopv(brushes) if(!strcmp(brushes[i]->name, name))
- {
- delete brushes[i];
- brushes.remove(i--);
- }
- curbrush = brushes.empty() ? -1 : clamp(curbrush, 0, brushes.length()-1);
+ loopv(brushes) if(!strcmp(brushes[i]->name, name))
+ {
+ delete brushes[i];
+ brushes.remove(i--);
+ }
+ curbrush = brushes.empty() ? -1 : clamp(curbrush, 0, brushes.length()-1);
}
void addblendbrush(const char *name, const char *imgname)
{
- delblendbrush(name);
+ delblendbrush(name);
- ImageData s;
- if(!loadimage(imgname, s)) { conoutf(CON_ERROR, "could not load blend brush image %s", imgname); return; }
- if(max(s.w, s.h) > (1<<12))
- {
- conoutf(CON_ERROR, "blend brush image size exceeded %dx%d pixels: %s", 1<<12, 1<<12, imgname);
- return;
- }
-
- BlendBrush *brush = new BlendBrush(name, s.w, s.h);
+ ImageData s;
+ if(!loadimage(imgname, s)) { conoutf(CON_ERROR, "could not load blend brush image %s", imgname); return; }
+ if(max(s.w, s.h) > (1<<12))
+ {
+ conoutf(CON_ERROR, "blend brush image size exceeded %dx%d pixels: %s", 1<<12, 1<<12, imgname);
+ return;
+ }
- uchar *dst = brush->data, *srcrow = s.data;
- loopi(s.h)
- {
- for(uchar *src = srcrow, *end = &srcrow[s.w*s.bpp]; src < end; src += s.bpp)
- *dst++ = src[0];
- srcrow += s.pitch;
- }
+ BlendBrush *brush = new BlendBrush(name, s.w, s.h);
- brushes.add(brush);
- if(curbrush < 0) curbrush = 0;
- else if(curbrush >= brushes.length()) curbrush = brushes.length()-1;
+ uchar *dst = brush->data, *srcrow = s.data;
+ loopi(s.h)
+ {
+ for(uchar *src = srcrow, *end = &srcrow[s.w*s.bpp]; src < end; src += s.bpp)
+ *dst++ = src[0];
+ srcrow += s.pitch;
+ }
+
+ brushes.add(brush);
+ if(curbrush < 0) curbrush = 0;
+ else if(curbrush >= brushes.length()) curbrush = brushes.length()-1;
}
void nextblendbrush(int *dir)
{
- curbrush += *dir < 0 ? -1 : 1;
- if(brushes.empty()) curbrush = -1;
- else if(!brushes.inrange(curbrush)) curbrush = *dir < 0 ? brushes.length()-1 : 0;
+ curbrush += *dir < 0 ? -1 : 1;
+ if(brushes.empty()) curbrush = -1;
+ else if(!brushes.inrange(curbrush)) curbrush = *dir < 0 ? brushes.length()-1 : 0;
}
void setblendbrush(const char *name)
{
- loopv(brushes) if(!strcmp(brushes[i]->name, name)) { curbrush = i; break; }
+ loopv(brushes) if(!strcmp(brushes[i]->name, name)) { curbrush = i; break; }
}
void getblendbrushname(int *n)
{
- result(brushes.inrange(*n) ? brushes[*n]->name : "");
+ result(brushes.inrange(*n) ? brushes[*n]->name : "");
}
void curblendbrush()
{
- intret(curbrush);
+ intret(curbrush);
}
COMMAND(clearblendbrushes, "");
@@ -639,40 +639,40 @@ extern int nompedit;
bool canpaintblendmap(bool brush = true, bool sel = false, bool msg = true)
{
- if(noedit(!sel, msg) || (nompedit && multiplayer())) return false;
- if(!blendpaintmode)
- {
- if(msg) conoutf(CON_ERROR, "operation only allowed in blend paint mode");
- return false;
- }
- if(brush && !brushes.inrange(curbrush))
- {
- if(msg) conoutf(CON_ERROR, "no blend brush selected");
- return false;
- }
- return true;
+ if(noedit(!sel, msg) || (nompedit && multiplayer())) return false;
+ if(!blendpaintmode)
+ {
+ if(msg) conoutf(CON_ERROR, "operation only allowed in blend paint mode");
+ return false;
+ }
+ if(brush && !brushes.inrange(curbrush))
+ {
+ if(msg) conoutf(CON_ERROR, "no blend brush selected");
+ return false;
+ }
+ return true;
}
void rotateblendbrush(int *val)
{
- if(!canpaintblendmap()) return;
- BlendBrush *brush = brushes[curbrush];
- const texrotation &r = texrotations[*val < 0 ? 3 : clamp(*val, 1, 7)];
- brush->reorient(r.flipx, r.flipy, r.swapxy);
+ if(!canpaintblendmap()) return;
+ BlendBrush *brush = brushes[curbrush];
+ const texrotation &r = texrotations[*val < 0 ? 3 : clamp(*val, 1, 7)];
+ brush->reorient(r.flipx, r.flipy, r.swapxy);
}
COMMAND(rotateblendbrush, "i");
void paintblendmap(bool msg)
{
- if(!canpaintblendmap(true, false, msg)) return;
+ if(!canpaintblendmap(true, false, msg)) return;
- BlendBrush *brush = brushes[curbrush];
- int x = (int)floor(clamp(worldpos.x, 0.0f, float(worldsize))/(1<<BM_SCALE) - 0.5f*brush->w),
- y = (int)floor(clamp(worldpos.y, 0.0f, float(worldsize))/(1<<BM_SCALE) - 0.5f*brush->h);
- blitblendmap(brush->data, x, y, brush->w, brush->h, blendpaintmode);
- previewblends(ivec((x-1)<<BM_SCALE, (y-1)<<BM_SCALE, 0),
- ivec((x+brush->w+1)<<BM_SCALE, (y+brush->h+1)<<BM_SCALE, worldsize));
+ BlendBrush *brush = brushes[curbrush];
+ int x = (int)floor(clamp(worldpos.x, 0.0f, float(worldsize))/(1<<BM_SCALE) - 0.5f*brush->w),
+ y = (int)floor(clamp(worldpos.y, 0.0f, float(worldsize))/(1<<BM_SCALE) - 0.5f*brush->h);
+ blitblendmap(brush->data, x, y, brush->w, brush->h, blendpaintmode);
+ previewblends(ivec((x-1)<<BM_SCALE, (y-1)<<BM_SCALE, 0),
+ ivec((x+brush->w+1)<<BM_SCALE, (y+brush->h+1)<<BM_SCALE, worldsize));
}
VAR(paintblendmapdelay, 1, 500, 3000);
@@ -682,181 +682,181 @@ int paintingblendmap = 0, lastpaintblendmap = 0;
void stoppaintblendmap()
{
- paintingblendmap = 0;
- lastpaintblendmap = 0;
+ paintingblendmap = 0;
+ lastpaintblendmap = 0;
}
void trypaintblendmap()
{
- if(!paintingblendmap || totalmillis - paintingblendmap < paintblendmapdelay) return;
- if(lastpaintblendmap)
- {
- int diff = totalmillis - lastpaintblendmap;
- if(diff < paintblendmapinterval) return;
- lastpaintblendmap = (diff - diff%paintblendmapinterval) + lastpaintblendmap;
- }
- else lastpaintblendmap = totalmillis;
- paintblendmap(false);
+ if(!paintingblendmap || totalmillis - paintingblendmap < paintblendmapdelay) return;
+ if(lastpaintblendmap)
+ {
+ int diff = totalmillis - lastpaintblendmap;
+ if(diff < paintblendmapinterval) return;
+ lastpaintblendmap = (diff - diff%paintblendmapinterval) + lastpaintblendmap;
+ }
+ else lastpaintblendmap = totalmillis;
+ paintblendmap(false);
}
ICOMMAND(paintblendmap, "D", (int *isdown),
{
- if(*isdown)
- {
- if(!paintingblendmap) { paintblendmap(true); paintingblendmap = totalmillis; }
- }
- else stoppaintblendmap();
+ if(*isdown)
+ {
+ if(!paintingblendmap) { paintblendmap(true); paintingblendmap = totalmillis; }
+ }
+ else stoppaintblendmap();
});
-
+
void clearblendmapsel()
{
- if(noedit(false) || (nompedit && multiplayer())) return;
- extern selinfo sel;
- int x1 = sel.o.x>>BM_SCALE, y1 = sel.o.y>>BM_SCALE,
- x2 = (sel.o.x+sel.s.x*sel.grid+(1<<BM_SCALE)-1)>>BM_SCALE,
- y2 = (sel.o.y+sel.s.y*sel.grid+(1<<BM_SCALE)-1)>>BM_SCALE;
- fillblendmap(x1, y1, x2-x1, y2-y1, 0xFF);
- previewblends(ivec(x1<<BM_SCALE, y1<<BM_SCALE, 0),
- ivec(x2<<BM_SCALE, y2<<BM_SCALE, worldsize));
+ if(noedit(false) || (nompedit && multiplayer())) return;
+ extern selinfo sel;
+ int x1 = sel.o.x>>BM_SCALE, y1 = sel.o.y>>BM_SCALE,
+ x2 = (sel.o.x+sel.s.x*sel.grid+(1<<BM_SCALE)-1)>>BM_SCALE,
+ y2 = (sel.o.y+sel.s.y*sel.grid+(1<<BM_SCALE)-1)>>BM_SCALE;
+ fillblendmap(x1, y1, x2-x1, y2-y1, 0xFF);
+ previewblends(ivec(x1<<BM_SCALE, y1<<BM_SCALE, 0),
+ ivec(x2<<BM_SCALE, y2<<BM_SCALE, worldsize));
}
COMMAND(clearblendmapsel, "");
void invertblendmapsel()
{
- if(noedit(false) || (nompedit && multiplayer())) return;
- extern selinfo sel;
- int x1 = sel.o.x>>BM_SCALE, y1 = sel.o.y>>BM_SCALE,
- x2 = (sel.o.x+sel.s.x*sel.grid+(1<<BM_SCALE)-1)>>BM_SCALE,
- y2 = (sel.o.y+sel.s.y*sel.grid+(1<<BM_SCALE)-1)>>BM_SCALE;
- invertblendmap(x1, y1, x2-x1, y2-y1);
- previewblends(ivec(x1<<BM_SCALE, y1<<BM_SCALE, 0),
- ivec(x2<<BM_SCALE, y2<<BM_SCALE, worldsize));
+ if(noedit(false) || (nompedit && multiplayer())) return;
+ extern selinfo sel;
+ int x1 = sel.o.x>>BM_SCALE, y1 = sel.o.y>>BM_SCALE,
+ x2 = (sel.o.x+sel.s.x*sel.grid+(1<<BM_SCALE)-1)>>BM_SCALE,
+ y2 = (sel.o.y+sel.s.y*sel.grid+(1<<BM_SCALE)-1)>>BM_SCALE;
+ invertblendmap(x1, y1, x2-x1, y2-y1);
+ previewblends(ivec(x1<<BM_SCALE, y1<<BM_SCALE, 0),
+ ivec(x2<<BM_SCALE, y2<<BM_SCALE, worldsize));
}
COMMAND(invertblendmapsel, "");
void invertblendmap()
{
- if(noedit(false) || (nompedit && multiplayer())) return;
- invertblendmap(0, 0, worldsize>>BM_SCALE, worldsize>>BM_SCALE);
- previewblends(ivec(0, 0, 0), ivec(worldsize, worldsize, worldsize));
+ if(noedit(false) || (nompedit && multiplayer())) return;
+ invertblendmap(0, 0, worldsize>>BM_SCALE, worldsize>>BM_SCALE);
+ previewblends(ivec(0, 0, 0), ivec(worldsize, worldsize, worldsize));
}
COMMAND(invertblendmap, "");
void showblendmap()
{
- if(noedit(true) || (nompedit && multiplayer())) return;
- previewblends(ivec(0, 0, 0), ivec(worldsize, worldsize, worldsize));
+ if(noedit(true) || (nompedit && multiplayer())) return;
+ previewblends(ivec(0, 0, 0), ivec(worldsize, worldsize, worldsize));
}
COMMAND(showblendmap, "");
COMMAND(optimizeblendmap, "");
ICOMMAND(clearblendmap, "", (),
{
- if(noedit(true) || (nompedit && multiplayer())) return;
- resetblendmap();
- showblendmap();
+ if(noedit(true) || (nompedit && multiplayer())) return;
+ resetblendmap();
+ showblendmap();
});
ICOMMAND(moveblendmap, "ii", (int *dx, int *dy),
{
- if(noedit(true) || (nompedit && multiplayer())) return;
- if(*dx%(BM_IMAGE_SIZE<<BM_SCALE) || *dy%(BM_IMAGE_SIZE<<BM_SCALE))
- {
- conoutf(CON_ERROR, "blendmap movement must be in multiples of %d", BM_IMAGE_SIZE<<BM_SCALE);
- return;
- }
- if(*dx <= -worldsize || *dx >= worldsize || *dy <= -worldsize || *dy >= worldsize)
- resetblendmap();
- else
- moveblendmap(*dx>>BM_SCALE, *dy>>BM_SCALE);
- showblendmap();
+ if(noedit(true) || (nompedit && multiplayer())) return;
+ if(*dx%(BM_IMAGE_SIZE<<BM_SCALE) || *dy%(BM_IMAGE_SIZE<<BM_SCALE))
+ {
+ conoutf(CON_ERROR, "blendmap movement must be in multiples of %d", BM_IMAGE_SIZE<<BM_SCALE);
+ return;
+ }
+ if(*dx <= -worldsize || *dx >= worldsize || *dy <= -worldsize || *dy >= worldsize)
+ resetblendmap();
+ else
+ moveblendmap(*dx>>BM_SCALE, *dy>>BM_SCALE);
+ showblendmap();
});
void renderblendbrush()
{
- if(!blendpaintmode || !brushes.inrange(curbrush)) return;
+ if(!blendpaintmode || !brushes.inrange(curbrush)) return;
- BlendBrush *brush = brushes[curbrush];
- int x1 = (int)floor(clamp(worldpos.x, 0.0f, float(worldsize))/(1<<BM_SCALE) - 0.5f*brush->w) << BM_SCALE,
- y1 = (int)floor(clamp(worldpos.y, 0.0f, float(worldsize))/(1<<BM_SCALE) - 0.5f*brush->h) << BM_SCALE,
- x2 = x1 + (brush->w << BM_SCALE),
- y2 = y1 + (brush->h << BM_SCALE);
+ BlendBrush *brush = brushes[curbrush];
+ int x1 = (int)floor(clamp(worldpos.x, 0.0f, float(worldsize))/(1<<BM_SCALE) - 0.5f*brush->w) << BM_SCALE,
+ y1 = (int)floor(clamp(worldpos.y, 0.0f, float(worldsize))/(1<<BM_SCALE) - 0.5f*brush->h) << BM_SCALE,
+ x2 = x1 + (brush->w << BM_SCALE),
+ y2 = y1 + (brush->h << BM_SCALE);
- if(max(x1, y1) >= worldsize || min(x2, y2) <= 0 || x1>=x2 || y1>=y2) return;
+ if(max(x1, y1) >= worldsize || min(x2, y2) <= 0 || x1>=x2 || y1>=y2) return;
- if(!brush->tex) brush->gentex();
- renderblendbrush(brush->tex, x1, y1, x2 - x1, y2 - y1);
+ if(!brush->tex) brush->gentex();
+ renderblendbrush(brush->tex, x1, y1, x2 - x1, y2 - y1);
}
bool loadblendmap(stream *f, uchar &type, BlendMapNode &node)
{
- type = f->getchar();
- switch(type)
- {
- case BM_SOLID:
- {
- int val = f->getchar();
- if(val<0 || val>0xFF) return false;
- node.solid = &bmsolids[val];
- break;
- }
-
- case BM_IMAGE:
- node.image = new BlendMapImage;
- if(f->read(node.image->data, sizeof(node.image->data)) != sizeof(node.image->data))
- return false;
- break;
-
- case BM_BRANCH:
- node.branch = new BlendMapBranch;
- loopi(4) { node.branch->type[i] = BM_SOLID; node.branch->children[i].solid = &bmsolids[0xFF]; }
- loopi(4) if(!loadblendmap(f, node.branch->type[i], node.branch->children[i]))
- return false;
- break;
-
- default:
- type = BM_SOLID;
- node.solid = &bmsolids[0xFF];
- return false;
- }
- return true;
+ type = f->getchar();
+ switch(type)
+ {
+ case BM_SOLID:
+ {
+ int val = f->getchar();
+ if(val<0 || val>0xFF) return false;
+ node.solid = &bmsolids[val];
+ break;
+ }
+
+ case BM_IMAGE:
+ node.image = new BlendMapImage;
+ if(f->read(node.image->data, sizeof(node.image->data)) != sizeof(node.image->data))
+ return false;
+ break;
+
+ case BM_BRANCH:
+ node.branch = new BlendMapBranch;
+ loopi(4) { node.branch->type[i] = BM_SOLID; node.branch->children[i].solid = &bmsolids[0xFF]; }
+ loopi(4) if(!loadblendmap(f, node.branch->type[i], node.branch->children[i]))
+ return false;
+ break;
+
+ default:
+ type = BM_SOLID;
+ node.solid = &bmsolids[0xFF];
+ return false;
+ }
+ return true;
}
bool loadblendmap(stream *f, int info)
{
- resetblendmap();
- return loadblendmap(f, blendmap.type, blendmap);
+ resetblendmap();
+ return loadblendmap(f, blendmap.type, blendmap);
}
void saveblendmap(stream *f, uchar type, BlendMapNode &node)
{
- f->putchar(type);
- switch(type)
- {
- case BM_SOLID:
- f->putchar(node.solid->val);
- break;
-
- case BM_IMAGE:
- f->write(node.image->data, sizeof(node.image->data));
- break;
+ f->putchar(type);
+ switch(type)
+ {
+ case BM_SOLID:
+ f->putchar(node.solid->val);
+ break;
- case BM_BRANCH:
- loopi(4) saveblendmap(f, node.branch->type[i], node.branch->children[i]);
- break;
- }
+ case BM_IMAGE:
+ f->write(node.image->data, sizeof(node.image->data));
+ break;
+
+ case BM_BRANCH:
+ loopi(4) saveblendmap(f, node.branch->type[i], node.branch->children[i]);
+ break;
+ }
}
void saveblendmap(stream *f)
{
- saveblendmap(f, blendmap.type, blendmap);
+ saveblendmap(f, blendmap.type, blendmap);
}
uchar shouldsaveblendmap()
{
- return blendmap.solid!=&bmsolids[0xFF] ? 1 : 0;
+ return blendmap.solid!=&bmsolids[0xFF] ? 1 : 0;
}
-
+
diff --git a/src/engine/client.cpp b/src/engine/client.cpp
index 3cfef8e..bd46b24 100644
--- a/src/engine/client.cpp
+++ b/src/engine/client.cpp
@@ -8,9 +8,9 @@ int connmillis = 0, connattempts = 0, discmillis = 0;
bool multiplayer(bool msg)
{
- bool val = curpeer || hasnonlocalclients();
- if(val && msg) conoutf(CON_ERROR, "operation not available in multiplayer");
- return val;
+ bool val = curpeer || hasnonlocalclients();
+ if(val && msg) conoutf(CON_ERROR, "operation not available in multiplayer");
+ return val;
}
void setrate(int rate)
@@ -24,160 +24,160 @@ VARF(rate, 0, 0, 1024, setrate(rate));
void throttle();
VARF(throttle_interval, 0, 5, 30, throttle());
-VARF(throttle_accel, 0, 2, 32, throttle());
-VARF(throttle_decel, 0, 2, 32, throttle());
+VARF(throttle_accel, 0, 2, 32, throttle());
+VARF(throttle_decel, 0, 2, 32, throttle());
void throttle()
{
- if(!curpeer) return;
- ASSERT(ENET_PEER_PACKET_THROTTLE_SCALE==32);
- enet_peer_throttle_configure(curpeer, throttle_interval*1000, throttle_accel, throttle_decel);
+ if(!curpeer) return;
+ ASSERT(ENET_PEER_PACKET_THROTTLE_SCALE==32);
+ enet_peer_throttle_configure(curpeer, throttle_interval*1000, throttle_accel, throttle_decel);
}
bool isconnected(bool attempt, bool local)
{
- return curpeer || (attempt && connpeer) || (local && haslocalclients());
+ return curpeer || (attempt && connpeer) || (local && haslocalclients());
}
ICOMMAND(isconnected, "bb", (int *attempt, int *local), intret(isconnected(*attempt > 0, *local != 0) ? 1 : 0));
const ENetAddress *connectedpeer()
{
- return curpeer ? &curpeer->address : NULL;
+ return curpeer ? &curpeer->address : NULL;
}
ICOMMAND(connectedip, "", (),
{
- const ENetAddress *address = connectedpeer();
- string hostname;
- result(address && enet_address_get_host_ip(address, hostname, sizeof(hostname)) >= 0 ? hostname : "");
+ const ENetAddress *address = connectedpeer();
+ string hostname;
+ result(address && enet_address_get_host_ip(address, hostname, sizeof(hostname)) >= 0 ? hostname : "");
});
ICOMMAND(connectedport, "", (),
{
- const ENetAddress *address = connectedpeer();
- intret(address ? address->port : -1);
+ const ENetAddress *address = connectedpeer();
+ intret(address ? address->port : -1);
});
void abortconnect()
{
- if(!connpeer) return;
- game::connectfail();
- if(connpeer->state!=ENET_PEER_STATE_DISCONNECTED) enet_peer_reset(connpeer);
- connpeer = NULL;
- if(curpeer) return;
- enet_host_destroy(clienthost);
- clienthost = NULL;
+ if(!connpeer) return;
+ game::connectfail();
+ if(connpeer->state!=ENET_PEER_STATE_DISCONNECTED) enet_peer_reset(connpeer);
+ connpeer = NULL;
+ if(curpeer) return;
+ enet_host_destroy(clienthost);
+ clienthost = NULL;
}
SVARP(connectname, "");
VARP(connectport, 0, 0, 0xFFFF);
void connectserv(const char *servername, int serverport, const char *serverpassword)
-{
- if(connpeer)
- {
- conoutf("aborting connection attempt");
- abortconnect();
- }
-
- if(serverport <= 0) serverport = server::serverport();
-
- ENetAddress address;
- address.port = serverport;
-
- if(servername)
- {
- if(strcmp(servername, connectname)) setsvar("connectname", servername);
- if(serverport != connectport) setvar("connectport", serverport);
- addserver(servername, serverport, serverpassword && serverpassword[0] ? serverpassword : NULL);
- conoutf("attempting to connect to %s:%d", servername, serverport);
- if(!resolverwait(servername, &address))
- {
- conoutf(CON_ERROR, "\f3could not resolve server %s", servername);
- return;
- }
- }
- else
- {
- setsvar("connectname", "");
- setvar("connectport", 0);
- conoutf("attempting to connect over LAN");
- address.host = ENET_HOST_BROADCAST;
- }
-
- if(!clienthost)
- {
- clienthost = enet_host_create(NULL, 2, server::numchannels(), rate*1024, rate*1024);
- if(!clienthost)
- {
- conoutf(CON_ERROR, "\f3could not connect to server");
- return;
- }
- clienthost->duplicatePeers = 0;
- }
-
- connpeer = enet_host_connect(clienthost, &address, server::numchannels(), 0);
- enet_host_flush(clienthost);
- connmillis = totalmillis;
- connattempts = 0;
-
- game::connectattempt(servername ? servername : "", serverpassword ? serverpassword : "", address);
+{
+ if(connpeer)
+ {
+ conoutf("aborting connection attempt");
+ abortconnect();
+ }
+
+ if(serverport <= 0) serverport = server::serverport();
+
+ ENetAddress address;
+ address.port = serverport;
+
+ if(servername)
+ {
+ if(strcmp(servername, connectname)) setsvar("connectname", servername);
+ if(serverport != connectport) setvar("connectport", serverport);
+ addserver(servername, serverport, serverpassword && serverpassword[0] ? serverpassword : NULL);
+ conoutf("attempting to connect to %s:%d", servername, serverport);
+ if(!resolverwait(servername, &address))
+ {
+ conoutf(CON_ERROR, "\f3could not resolve server %s", servername);
+ return;
+ }
+ }
+ else
+ {
+ setsvar("connectname", "");
+ setvar("connectport", 0);
+ conoutf("attempting to connect over LAN");
+ address.host = ENET_HOST_BROADCAST;
+ }
+
+ if(!clienthost)
+ {
+ clienthost = enet_host_create(NULL, 2, server::numchannels(), rate*1024, rate*1024);
+ if(!clienthost)
+ {
+ conoutf(CON_ERROR, "\f3could not connect to server");
+ return;
+ }
+ clienthost->duplicatePeers = 0;
+ }
+
+ connpeer = enet_host_connect(clienthost, &address, server::numchannels(), 0);
+ enet_host_flush(clienthost);
+ connmillis = totalmillis;
+ connattempts = 0;
+
+ game::connectattempt(servername ? servername : "", serverpassword ? serverpassword : "", address);
}
void reconnect(const char *serverpassword)
{
- if(!connectname[0] || connectport <= 0)
- {
- conoutf(CON_ERROR, "no previous connection");
- return;
- }
+ if(!connectname[0] || connectport <= 0)
+ {
+ conoutf(CON_ERROR, "no previous connection");
+ return;
+ }
- connectserv(connectname, connectport, serverpassword);
+ connectserv(connectname, connectport, serverpassword);
}
void disconnect(bool async, bool cleanup)
{
- if(curpeer)
- {
- if(!discmillis)
- {
- enet_peer_disconnect(curpeer, DISC_NONE);
- enet_host_flush(clienthost);
- discmillis = totalmillis;
- }
- if(curpeer->state!=ENET_PEER_STATE_DISCONNECTED)
- {
- if(async) return;
- enet_peer_reset(curpeer);
- }
- curpeer = NULL;
- discmillis = 0;
- conoutf("disconnected");
- game::gamedisconnect(cleanup);
- mainmenu = 1;
- }
- if(!connpeer && clienthost)
- {
- enet_host_destroy(clienthost);
- clienthost = NULL;
- }
+ if(curpeer)
+ {
+ if(!discmillis)
+ {
+ enet_peer_disconnect(curpeer, DISC_NONE);
+ enet_host_flush(clienthost);
+ discmillis = totalmillis;
+ }
+ if(curpeer->state!=ENET_PEER_STATE_DISCONNECTED)
+ {
+ if(async) return;
+ enet_peer_reset(curpeer);
+ }
+ curpeer = NULL;
+ discmillis = 0;
+ conoutf("disconnected");
+ game::gamedisconnect(cleanup);
+ mainmenu = 1;
+ }
+ if(!connpeer && clienthost)
+ {
+ enet_host_destroy(clienthost);
+ clienthost = NULL;
+ }
}
void trydisconnect(bool local)
{
- if(connpeer)
- {
- conoutf("aborting connection attempt");
- abortconnect();
- }
- else if(curpeer)
- {
- conoutf("attempting to disconnect...");
- disconnect(!discmillis);
- }
- else if(local && haslocalclients()) localdisconnect();
- else conoutf(CON_WARN, "not connected");
+ if(connpeer)
+ {
+ conoutf("aborting connection attempt");
+ abortconnect();
+ }
+ else if(curpeer)
+ {
+ conoutf("attempting to disconnect...");
+ disconnect(!discmillis);
+ }
+ else if(local && haslocalclients()) localdisconnect();
+ else conoutf(CON_WARN, "not connected");
}
ICOMMAND(connect, "sis", (char *name, int *port, char *pw), connectserv(name, *port, pw));
@@ -189,86 +189,86 @@ ICOMMAND(localdisconnect, "", (), { if(haslocalclients()) localdisconnect(); });
void sendclientpacket(ENetPacket *packet, int chan)
{
- if(curpeer) enet_peer_send(curpeer, chan, packet);
- else localclienttoserver(chan, packet);
+ if(curpeer) enet_peer_send(curpeer, chan, packet);
+ else localclienttoserver(chan, packet);
}
void flushclient()
{
- if(clienthost) enet_host_flush(clienthost);
+ if(clienthost) enet_host_flush(clienthost);
}
void neterr(const char *s, bool disc)
{
- conoutf(CON_ERROR, "\f3illegal network message (%s)", s);
- if(disc) disconnect();
+ conoutf(CON_ERROR, "\f3illegal network message (%s)", s);
+ if(disc) disconnect();
}
void localservertoclient(int chan, ENetPacket *packet) // processes any updates from the server
{
- packetbuf p(packet);
- game::parsepacketclient(chan, p);
+ packetbuf p(packet);
+ game::parsepacketclient(chan, p);
}
void clientkeepalive() { if(clienthost) enet_host_service(clienthost, NULL, 0); }
-void gets2c() // get updates from the server
+void gets2c() // get updates from the server
{
- ENetEvent event;
- if(!clienthost) return;
- if(connpeer && totalmillis/3000 > connmillis/3000)
- {
- conoutf("attempting to connect...");
- connmillis = totalmillis;
- ++connattempts;
- if(connattempts > 3)
- {
- conoutf(CON_ERROR, "\f3could not connect to server");
- abortconnect();
- return;
- }
- }
- while(clienthost && enet_host_service(clienthost, &event, 0)>0)
- switch(event.type)
- {
- case ENET_EVENT_TYPE_CONNECT:
- disconnect(false, false);
- localdisconnect(false);
- curpeer = connpeer;
- connpeer = NULL;
- conoutf("connected to server");
- throttle();
- if(rate) setrate(rate);
- game::gameconnect(true);
- break;
-
- case ENET_EVENT_TYPE_RECEIVE:
- if(discmillis) conoutf("attempting to disconnect...");
- else localservertoclient(event.channelID, event.packet);
- enet_packet_destroy(event.packet);
- break;
-
- case ENET_EVENT_TYPE_DISCONNECT:
- if(event.data>=DISC_NUM) event.data = DISC_NONE;
- if(event.peer==connpeer)
- {
- conoutf(CON_ERROR, "\f3could not connect to server");
- abortconnect();
- }
- else
- {
- if(!discmillis || event.data)
- {
- const char *msg = disconnectreason(event.data);
- if(msg) conoutf(CON_ERROR, "\f3server network error, disconnecting (%s) ...", msg);
- else conoutf(CON_ERROR, "\f3server network error, disconnecting...");
- }
- disconnect();
- }
- return;
-
- default:
- break;
- }
+ ENetEvent event;
+ if(!clienthost) return;
+ if(connpeer && totalmillis/3000 > connmillis/3000)
+ {
+ conoutf("attempting to connect...");
+ connmillis = totalmillis;
+ ++connattempts;
+ if(connattempts > 3)
+ {
+ conoutf(CON_ERROR, "\f3could not connect to server");
+ abortconnect();
+ return;
+ }
+ }
+ while(clienthost && enet_host_service(clienthost, &event, 0)>0)
+ switch(event.type)
+ {
+ case ENET_EVENT_TYPE_CONNECT:
+ disconnect(false, false);
+ localdisconnect(false);
+ curpeer = connpeer;
+ connpeer = NULL;
+ conoutf("connected to server");
+ throttle();
+ if(rate) setrate(rate);
+ game::gameconnect(true);
+ break;
+
+ case ENET_EVENT_TYPE_RECEIVE:
+ if(discmillis) conoutf("attempting to disconnect...");
+ else localservertoclient(event.channelID, event.packet);
+ enet_packet_destroy(event.packet);
+ break;
+
+ case ENET_EVENT_TYPE_DISCONNECT:
+ if(event.data>=DISC_NUM) event.data = DISC_NONE;
+ if(event.peer==connpeer)
+ {
+ conoutf(CON_ERROR, "\f3could not connect to server");
+ abortconnect();
+ }
+ else
+ {
+ if(!discmillis || event.data)
+ {
+ const char *msg = disconnectreason(event.data);
+ if(msg) conoutf(CON_ERROR, "\f3server network error, disconnecting (%s) ...", msg);
+ else conoutf(CON_ERROR, "\f3server network error, disconnecting...");
+ }
+ disconnect();
+ }
+ return;
+
+ default:
+ break;
+ }
}
diff --git a/src/engine/command.cpp b/src/engine/command.cpp
index 0029e4d..012b8c1 100644
--- a/src/engine/command.cpp
+++ b/src/engine/command.cpp
@@ -11,186 +11,186 @@ int identflags = 0;
enum
{
- MAXARGS = 25,
- MAXCOMARGS = 12
+ MAXARGS = 25,
+ MAXCOMARGS = 12
};
VARN(numargs, _numargs, MAXARGS, 0, 0);
static inline void freearg(tagval &v)
{
- switch(v.type)
- {
- case VAL_STR: delete[] v.s; break;
- case VAL_CODE: if(v.code[-1] == CODE_START) delete[] (uchar *)&v.code[-1]; break;
- }
+ switch(v.type)
+ {
+ case VAL_STR: delete[] v.s; break;
+ case VAL_CODE: if(v.code[-1] == CODE_START) delete[] (uchar *)&v.code[-1]; break;
+ }
}
static inline void forcenull(tagval &v)
{
- switch(v.type)
- {
- case VAL_NULL: return;
- }
- freearg(v);
- v.setnull();
+ switch(v.type)
+ {
+ case VAL_NULL: return;
+ }
+ freearg(v);
+ v.setnull();
}
static inline float forcefloat(tagval &v)
{
- float f = 0.0f;
- switch(v.type)
- {
- case VAL_INT: f = v.i; break;
- case VAL_STR: f = parsefloat(v.s); break;
- case VAL_MACRO: f = parsefloat(v.s); break;
- case VAL_FLOAT: return v.f;
- }
- freearg(v);
- v.setfloat(f);
- return f;
+ float f = 0.0f;
+ switch(v.type)
+ {
+ case VAL_INT: f = v.i; break;
+ case VAL_STR: f = parsefloat(v.s); break;
+ case VAL_MACRO: f = parsefloat(v.s); break;
+ case VAL_FLOAT: return v.f;
+ }
+ freearg(v);
+ v.setfloat(f);
+ return f;
}
static inline int forceint(tagval &v)
{
- int i = 0;
- switch(v.type)
- {
- case VAL_FLOAT: i = v.f; break;
- case VAL_STR: i = parseint(v.s); break;
- case VAL_MACRO: i = parseint(v.s); break;
- case VAL_INT: return v.i;
- }
- freearg(v);
- v.setint(i);
- return i;
+ int i = 0;
+ switch(v.type)
+ {
+ case VAL_FLOAT: i = v.f; break;
+ case VAL_STR: i = parseint(v.s); break;
+ case VAL_MACRO: i = parseint(v.s); break;
+ case VAL_INT: return v.i;
+ }
+ freearg(v);
+ v.setint(i);
+ return i;
}
static inline const char *forcestr(tagval &v)
{
- const char *s = "";
- switch(v.type)
- {
- case VAL_FLOAT: s = floatstr(v.f); break;
- case VAL_INT: s = intstr(v.i); break;
- case VAL_STR:
- [[fallthrough]];
- case VAL_MACRO: return v.s;
- }
- freearg(v);
- v.setstr(newstring(s));
- return s;
+ const char *s = "";
+ switch(v.type)
+ {
+ case VAL_FLOAT: s = floatstr(v.f); break;
+ case VAL_INT: s = intstr(v.i); break;
+ case VAL_STR:
+ [[fallthrough]];
+ case VAL_MACRO: return v.s;
+ }
+ freearg(v);
+ v.setstr(newstring(s));
+ return s;
}
static inline void forcearg(tagval &v, int type)
{
- switch(type)
- {
- case RET_STR: if(v.type != VAL_STR) forcestr(v); break;
- case RET_INT: if(v.type != VAL_INT) forceint(v); break;
- case RET_FLOAT: if(v.type != VAL_FLOAT) forcefloat(v); break;
- }
+ switch(type)
+ {
+ case RET_STR: if(v.type != VAL_STR) forcestr(v); break;
+ case RET_INT: if(v.type != VAL_INT) forceint(v); break;
+ case RET_FLOAT: if(v.type != VAL_FLOAT) forcefloat(v); break;
+ }
}
static inline ident *forceident(tagval &v)
{
- switch(v.type)
- {
- case VAL_IDENT: return v.id;
- case VAL_MACRO:
- {
- ident *id = newident(v.s, IDF_UNKNOWN);
- v.setident(id);
- return id;
- }
- case VAL_STR:
- {
- ident *id = newident(v.s, IDF_UNKNOWN);
- delete[] v.s;
- v.setident(id);
- return id;
- }
- }
- freearg(v);
- v.setident(dummyident);
- return dummyident;
+ switch(v.type)
+ {
+ case VAL_IDENT: return v.id;
+ case VAL_MACRO:
+ {
+ ident *id = newident(v.s, IDF_UNKNOWN);
+ v.setident(id);
+ return id;
+ }
+ case VAL_STR:
+ {
+ ident *id = newident(v.s, IDF_UNKNOWN);
+ delete[] v.s;
+ v.setident(id);
+ return id;
+ }
+ }
+ freearg(v);
+ v.setident(dummyident);
+ return dummyident;
}
void tagval::cleanup()
{
- freearg(*this);
+ freearg(*this);
}
static inline void freeargs(tagval *args, int &oldnum, int newnum)
{
- for(int i = newnum; i < oldnum; i++) freearg(args[i]);
- oldnum = newnum;
+ for(int i = newnum; i < oldnum; i++) freearg(args[i]);
+ oldnum = newnum;
}
static inline void cleancode(ident &id)
{
- if(id.code)
- {
- id.code[0] -= 0x100;
- if(int(id.code[0]) < 0x100) delete[] id.code;
- id.code = NULL;
- }
+ if(id.code)
+ {
+ id.code[0] -= 0x100;
+ if(int(id.code[0]) < 0x100) delete[] id.code;
+ id.code = NULL;
+ }
}
struct nullval : tagval
{
- nullval() { setnull(); }
+ nullval() { setnull(); }
} nullval;
tagval noret = nullval, *commandret = &noret;
void clear_command()
{
- enumerate(idents, ident, i,
- {
- if(i.type==ID_ALIAS)
- {
- DELETEA(i.name);
- i.forcenull();
- DELETEA(i.code);
- }
- });
+ enumerate(idents, ident, i,
+ {
+ if(i.type==ID_ALIAS)
+ {
+ DELETEA(i.name);
+ i.forcenull();
+ DELETEA(i.code);
+ }
+ });
}
void clearoverride(ident &i)
{
- if(!(i.flags&IDF_OVERRIDDEN)) return;
- switch(i.type)
- {
- case ID_ALIAS:
- if(i.valtype==VAL_STR)
- {
- if(!i.val.s[0]) break;
- delete[] i.val.s;
- }
- cleancode(i);
- i.valtype = VAL_STR;
- i.val.s = newstring("");
- break;
- case ID_VAR:
- *i.storage.i = i.overrideval.i;
- i.changed();
- break;
- case ID_FVAR:
- *i.storage.f = i.overrideval.f;
- i.changed();
- break;
- case ID_SVAR:
- delete[] *i.storage.s;
- *i.storage.s = i.overrideval.s;
- i.changed();
- break;
- }
- i.flags &= ~IDF_OVERRIDDEN;
+ if(!(i.flags&IDF_OVERRIDDEN)) return;
+ switch(i.type)
+ {
+ case ID_ALIAS:
+ if(i.valtype==VAL_STR)
+ {
+ if(!i.val.s[0]) break;
+ delete[] i.val.s;
+ }
+ cleancode(i);
+ i.valtype = VAL_STR;
+ i.val.s = newstring("");
+ break;
+ case ID_VAR:
+ *i.storage.i = i.overrideval.i;
+ i.changed();
+ break;
+ case ID_FVAR:
+ *i.storage.f = i.overrideval.f;
+ i.changed();
+ break;
+ case ID_SVAR:
+ delete[] *i.storage.s;
+ *i.storage.s = i.overrideval.s;
+ i.changed();
+ break;
+ }
+ i.flags &= ~IDF_OVERRIDDEN;
}
void clearoverrides()
{
- enumerate(idents, ident, i, clearoverride(i));
+ enumerate(idents, ident, i, clearoverride(i));
}
static bool initedidents = false;
@@ -198,32 +198,32 @@ static vector<ident> *identinits = NULL;
static inline ident *addident(const ident &id)
{
- if(!initedidents)
- {
- if(!identinits) identinits = new vector<ident>;
- identinits->add(id);
- return NULL;
- }
- ident &def = idents.access(id.name, id);
- def.index = identmap.length();
- return identmap.add(&def);
+ if(!initedidents)
+ {
+ if(!identinits) identinits = new vector<ident>;
+ identinits->add(id);
+ return NULL;
+ }
+ ident &def = idents.access(id.name, id);
+ def.index = identmap.length();
+ return identmap.add(&def);
}
static bool initidents()
{
- initedidents = true;
- for(int i = 0; i < MAXARGS; i++)
- {
- defformatstring(argname, "arg%d", i+1);
- newident(argname, IDF_ARG);
- }
- dummyident = newident("//dummy", IDF_UNKNOWN);
- if(identinits)
- {
- loopv(*identinits) addident((*identinits)[i]);
- DELETEP(identinits);
- }
- return true;
+ initedidents = true;
+ for(int i = 0; i < MAXARGS; i++)
+ {
+ defformatstring(argname, "arg%d", i+1);
+ newident(argname, IDF_ARG);
+ }
+ dummyident = newident("//dummy", IDF_UNKNOWN);
+ if(identinits)
+ {
+ loopv(*identinits) addident((*identinits)[i]);
+ DELETEP(identinits);
+ }
+ return true;
}
UNUSED static bool forceinitidents = initidents();
@@ -231,49 +231,49 @@ static const char *sourcefile = NULL, *sourcestr = NULL;
static const char *debugline(const char *p, const char *fmt)
{
- if(!sourcestr) return fmt;
- int num = 1;
- const char *line = sourcestr;
- for(;;)
- {
- const char *end = strchr(line, '\n');
- if(!end) end = line + strlen(line);
- if(p >= line && p <= end)
- {
- static string buf;
- if(sourcefile) formatstring(buf, "%s:%d: %s", sourcefile, num, fmt);
- else formatstring(buf, "%d: %s", num, fmt);
- return buf;
- }
- if(!*end) break;
- line = end + 1;
- num++;
- }
- return fmt;
+ if(!sourcestr) return fmt;
+ int num = 1;
+ const char *line = sourcestr;
+ for(;;)
+ {
+ const char *end = strchr(line, '\n');
+ if(!end) end = line + strlen(line);
+ if(p >= line && p <= end)
+ {
+ static string buf;
+ if(sourcefile) formatstring(buf, "%s:%d: %s", sourcefile, num, fmt);
+ else formatstring(buf, "%d: %s", num, fmt);
+ return buf;
+ }
+ if(!*end) break;
+ line = end + 1;
+ num++;
+ }
+ return fmt;
}
static struct identlink
{
- ident *id;
- identlink *next;
- int usedargs;
- identstack *argstack;
+ ident *id;
+ identlink *next;
+ int usedargs;
+ identstack *argstack;
} noalias = { NULL, NULL, (1<<MAXARGS)-1, NULL }, *aliasstack = &noalias;
VAR(dbgalias, 0, 4, 1000);
static void debugalias()
{
- if(!dbgalias) return;
- int total = 0, depth = 0;
- for(identlink *l = aliasstack; l != &noalias; l = l->next) total++;
- for(identlink *l = aliasstack; l != &noalias; l = l->next)
- {
- ident *id = l->id;
- ++depth;
- if(depth < dbgalias) conoutf(CON_ERROR, " %d) %s", total-depth+1, id->name);
- else if(l->next == &noalias) conoutf(CON_ERROR, depth == dbgalias ? " %d) %s" : " ..%d) %s", total-depth+1, id->name);
- }
+ if(!dbgalias) return;
+ int total = 0, depth = 0;
+ for(identlink *l = aliasstack; l != &noalias; l = l->next) total++;
+ for(identlink *l = aliasstack; l != &noalias; l = l->next)
+ {
+ ident *id = l->id;
+ ++depth;
+ if(depth < dbgalias) conoutf(CON_ERROR, " %d) %s", total-depth+1, id->name);
+ else if(l->next == &noalias) conoutf(CON_ERROR, depth == dbgalias ? " %d) %s" : " ..%d) %s", total-depth+1, id->name);
+ }
}
static int nodebug = 0;
@@ -282,294 +282,294 @@ static void debugcode(const char *fmt, ...) PRINTFARGS(1, 2);
static void debugcode(const char *fmt, ...)
{
- if(nodebug) return;
+ if(nodebug) return;
- va_list args;
- va_start(args, fmt);
- conoutfv(CON_ERROR, fmt, args);
- va_end(args);
+ va_list args;
+ va_start(args, fmt);
+ conoutfv(CON_ERROR, fmt, args);
+ va_end(args);
- debugalias();
+ debugalias();
}
static void debugcodeline(const char *p, const char *fmt, ...) PRINTFARGS(2, 3);
static void debugcodeline(const char *p, const char *fmt, ...)
{
- if(nodebug) return;
+ if(nodebug) return;
- va_list args;
- va_start(args, fmt);
- conoutfv(CON_ERROR, debugline(p, fmt), args);
- va_end(args);
+ va_list args;
+ va_start(args, fmt);
+ conoutfv(CON_ERROR, debugline(p, fmt), args);
+ va_end(args);
- debugalias();
+ debugalias();
}
ICOMMAND(nodebug, "e", (uint *body), { nodebug++; executeret(body, *commandret); nodebug--; });
void addident(ident *id)
{
- addident(*id);
+ addident(*id);
}
static inline void pusharg(ident &id, const tagval &v, identstack &stack)
{
- stack.val = id.val;
- stack.valtype = id.valtype;
- stack.next = id.stack;
- id.stack = &stack;
- id.setval(v);
- cleancode(id);
+ stack.val = id.val;
+ stack.valtype = id.valtype;
+ stack.next = id.stack;
+ id.stack = &stack;
+ id.setval(v);
+ cleancode(id);
}
static inline void poparg(ident &id)
{
- if(!id.stack) return;
- identstack *stack = id.stack;
- if(id.valtype == VAL_STR) delete[] id.val.s;
- id.setval(*stack);
- cleancode(id);
- id.stack = stack->next;
+ if(!id.stack) return;
+ identstack *stack = id.stack;
+ if(id.valtype == VAL_STR) delete[] id.val.s;
+ id.setval(*stack);
+ cleancode(id);
+ id.stack = stack->next;
}
ICOMMAND(push, "rte", (ident *id, tagval *v, uint *code),
{
- if(id->type != ID_ALIAS || id->index < MAXARGS) return;
- identstack stack;
- pusharg(*id, *v, stack);
- v->type = VAL_NULL;
- id->flags &= ~IDF_UNKNOWN;
- executeret(code, *commandret);
- poparg(*id);
+ if(id->type != ID_ALIAS || id->index < MAXARGS) return;
+ identstack stack;
+ pusharg(*id, *v, stack);
+ v->type = VAL_NULL;
+ id->flags &= ~IDF_UNKNOWN;
+ executeret(code, *commandret);
+ poparg(*id);
});
static inline void pushalias(ident &id, identstack &stack)
{
- if(id.type == ID_ALIAS && id.index >= MAXARGS)
- {
- pusharg(id, nullval, stack);
- id.flags &= ~IDF_UNKNOWN;
- }
+ if(id.type == ID_ALIAS && id.index >= MAXARGS)
+ {
+ pusharg(id, nullval, stack);
+ id.flags &= ~IDF_UNKNOWN;
+ }
}
static inline void popalias(ident &id)
{
- if(id.type == ID_ALIAS && id.index >= MAXARGS) poparg(id);
+ if(id.type == ID_ALIAS && id.index >= MAXARGS) poparg(id);
}
KEYWORD(local, ID_LOCAL);
static inline bool checknumber(const char *s)
{
- if(isdigit(s[0])) return true;
- else switch(s[0])
- {
- case '+': case '-': return isdigit(s[1]) || (s[1] == '.' && isdigit(s[2]));
- case '.': return isdigit(s[1]) != 0;
- default: return false;
- }
+ if(isdigit(s[0])) return true;
+ else switch(s[0])
+ {
+ case '+': case '-': return isdigit(s[1]) || (s[1] == '.' && isdigit(s[2]));
+ case '.': return isdigit(s[1]) != 0;
+ default: return false;
+ }
}
ident *newident(const char *name, int flags)
{
- ident *id = idents.access(name);
- if(!id)
- {
- if(checknumber(name))
- {
- debugcode("number %s is not a valid identifier name", name);
- return dummyident;
- }
- id = addident(ident(ID_ALIAS, newstring(name), flags));
- }
- return id;
+ ident *id = idents.access(name);
+ if(!id)
+ {
+ if(checknumber(name))
+ {
+ debugcode("number %s is not a valid identifier name", name);
+ return dummyident;
+ }
+ id = addident(ident(ID_ALIAS, newstring(name), flags));
+ }
+ return id;
}
ident *writeident(const char *name, int flags)
{
- ident *id = newident(name, flags);
- if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index)))
- {
- pusharg(*id, nullval, aliasstack->argstack[id->index]);
- aliasstack->usedargs |= 1<<id->index;
- }
- return id;
+ ident *id = newident(name, flags);
+ if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index)))
+ {
+ pusharg(*id, nullval, aliasstack->argstack[id->index]);
+ aliasstack->usedargs |= 1<<id->index;
+ }
+ return id;
}
ident *readident(const char *name)
{
- ident *id = idents.access(name);
- if(id && id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index)))
- return NULL;
- return id;
+ ident *id = idents.access(name);
+ if(id && id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index)))
+ return NULL;
+ return id;
}
void resetvar(char *name)
{
- ident *id = idents.access(name);
- if(!id) return;
- if(id->flags&IDF_READONLY) debugcode("variable %s is read-only", id->name);
- else clearoverride(*id);
+ ident *id = idents.access(name);
+ if(!id) return;
+ if(id->flags&IDF_READONLY) debugcode("variable %s is read-only", id->name);
+ else clearoverride(*id);
}
COMMAND(resetvar, "s");
static inline void setarg(ident &id, tagval &v)
{
- if(aliasstack->usedargs&(1<<id.index))
- {
- if(id.valtype == VAL_STR) delete[] id.val.s;
- id.setval(v);
- cleancode(id);
- }
- else
- {
- pusharg(id, v, aliasstack->argstack[id.index]);
- aliasstack->usedargs |= 1<<id.index;
- }
+ if(aliasstack->usedargs&(1<<id.index))
+ {
+ if(id.valtype == VAL_STR) delete[] id.val.s;
+ id.setval(v);
+ cleancode(id);
+ }
+ else
+ {
+ pusharg(id, v, aliasstack->argstack[id.index]);
+ aliasstack->usedargs |= 1<<id.index;
+ }
}
static inline void setalias(ident &id, tagval &v)
{
- if(id.valtype == VAL_STR) delete[] id.val.s;
- id.setval(v);
- cleancode(id);
- id.flags = (id.flags & identflags) | identflags;
+ if(id.valtype == VAL_STR) delete[] id.val.s;
+ id.setval(v);
+ cleancode(id);
+ id.flags = (id.flags & identflags) | identflags;
}
static void setalias(const char *name, tagval &v)
{
- ident *id = idents.access(name);
- if(id)
- {
- if(id->type == ID_ALIAS)
- {
- if(id->index < MAXARGS) setarg(*id, v); else setalias(*id, v);
- }
- else
- {
- debugcode("cannot redefine builtin %s with an alias", id->name);
- freearg(v);
- }
- }
- else if(checknumber(name))
- {
- debugcode("cannot alias number %s", name);
- freearg(v);
- }
- else
- {
- addident(ident(ID_ALIAS, newstring(name), v, identflags));
- }
+ ident *id = idents.access(name);
+ if(id)
+ {
+ if(id->type == ID_ALIAS)
+ {
+ if(id->index < MAXARGS) setarg(*id, v); else setalias(*id, v);
+ }
+ else
+ {
+ debugcode("cannot redefine builtin %s with an alias", id->name);
+ freearg(v);
+ }
+ }
+ else if(checknumber(name))
+ {
+ debugcode("cannot alias number %s", name);
+ freearg(v);
+ }
+ else
+ {
+ addident(ident(ID_ALIAS, newstring(name), v, identflags));
+ }
}
void alias(const char *name, const char *str)
{
- tagval v;
- v.setstr(newstring(str));
- setalias(name, v);
+ tagval v;
+ v.setstr(newstring(str));
+ setalias(name, v);
}
void alias(const char *name, tagval &v)
{
- setalias(name, v);
+ setalias(name, v);
}
ICOMMAND(alias, "st", (const char *name, tagval *v),
{
- setalias(name, *v);
- v->type = VAL_NULL;
+ setalias(name, *v);
+ v->type = VAL_NULL;
});
// variable's and commands are registered through globals, see cube.h
int variable(const char *name, int min, int cur, int max, int *storage, identfun fun, int flags)
{
- addident(ident(ID_VAR, name, min, max, storage, (void *)fun, flags));
- return cur;
+ addident(ident(ID_VAR, name, min, max, storage, (void *)fun, flags));
+ return cur;
}
float fvariable(const char *name, float min, float cur, float max, float *storage, identfun fun, int flags)
{
- addident(ident(ID_FVAR, name, min, max, storage, (void *)fun, flags));
- return cur;
+ addident(ident(ID_FVAR, name, min, max, storage, (void *)fun, flags));
+ return cur;
}
char *svariable(const char *name, const char *cur, char **storage, identfun fun, int flags)
{
- addident(ident(ID_SVAR, name, storage, (void *)fun, flags));
- return newstring(cur);
+ addident(ident(ID_SVAR, name, storage, (void *)fun, flags));
+ return newstring(cur);
}
#define _GETVAR(id, vartype, name, retval) \
- ident *id = idents.access(name); \
- if(!id || id->type!=vartype) return retval;
+ ident *id = idents.access(name); \
+ if(!id || id->type!=vartype) return retval;
#define GETVAR(id, name, retval) _GETVAR(id, ID_VAR, name, retval)
#define OVERRIDEVAR(errorval, saveval, resetval, clearval) \
- if(identflags&IDF_OVERRIDDEN || id->flags&IDF_OVERRIDE) \
- { \
- if(id->flags&IDF_PERSIST) \
- { \
- debugcode("cannot override persistent variable %s", id->name); \
- errorval; \
- } \
- if(!(id->flags&IDF_OVERRIDDEN)) { saveval; id->flags |= IDF_OVERRIDDEN; } \
- else { clearval; } \
- } \
- else \
- { \
- if(id->flags&IDF_OVERRIDDEN) { resetval; id->flags &= ~IDF_OVERRIDDEN; } \
- clearval; \
- }
+ if(identflags&IDF_OVERRIDDEN || id->flags&IDF_OVERRIDE) \
+ { \
+ if(id->flags&IDF_PERSIST) \
+ { \
+ debugcode("cannot override persistent variable %s", id->name); \
+ errorval; \
+ } \
+ if(!(id->flags&IDF_OVERRIDDEN)) { saveval; id->flags |= IDF_OVERRIDDEN; } \
+ else { clearval; } \
+ } \
+ else \
+ { \
+ if(id->flags&IDF_OVERRIDDEN) { resetval; id->flags &= ~IDF_OVERRIDDEN; } \
+ clearval; \
+ }
void setvar(const char *name, int i, bool dofunc, bool doclamp)
{
- GETVAR(id, name, );
- OVERRIDEVAR(return, id->overrideval.i = *id->storage.i, , )
- if(doclamp) *id->storage.i = clamp(i, id->minval, id->maxval);
- else *id->storage.i = i;
- if(dofunc) id->changed();
+ GETVAR(id, name, );
+ OVERRIDEVAR(return, id->overrideval.i = *id->storage.i, , )
+ if(doclamp) *id->storage.i = clamp(i, id->minval, id->maxval);
+ else *id->storage.i = i;
+ if(dofunc) id->changed();
}
void setfvar(const char *name, float f, bool dofunc, bool doclamp)
{
- _GETVAR(id, ID_FVAR, name, );
- OVERRIDEVAR(return, id->overrideval.f = *id->storage.f, , );
- if(doclamp) *id->storage.f = clamp(f, id->minvalf, id->maxvalf);
- else *id->storage.f = f;
- if(dofunc) id->changed();
+ _GETVAR(id, ID_FVAR, name, );
+ OVERRIDEVAR(return, id->overrideval.f = *id->storage.f, , );
+ if(doclamp) *id->storage.f = clamp(f, id->minvalf, id->maxvalf);
+ else *id->storage.f = f;
+ if(dofunc) id->changed();
}
void setsvar(const char *name, const char *str, bool dofunc)
{
- _GETVAR(id, ID_SVAR, name, );
- OVERRIDEVAR(return, id->overrideval.s = *id->storage.s, delete[] id->overrideval.s, delete[] *id->storage.s);
- *id->storage.s = newstring(str);
- if(dofunc) id->changed();
+ _GETVAR(id, ID_SVAR, name, );
+ OVERRIDEVAR(return, id->overrideval.s = *id->storage.s, delete[] id->overrideval.s, delete[] *id->storage.s);
+ *id->storage.s = newstring(str);
+ if(dofunc) id->changed();
}
int getvar(const char *name)
{
- GETVAR(id, name, 0);
- return *id->storage.i;
+ GETVAR(id, name, 0);
+ return *id->storage.i;
}
int getvarmin(const char *name)
{
- GETVAR(id, name, 0);
- return id->minval;
+ GETVAR(id, name, 0);
+ return id->minval;
}
int getvarmax(const char *name)
{
- GETVAR(id, name, 0);
- return id->maxval;
+ GETVAR(id, name, 0);
+ return id->maxval;
}
float getfvarmin(const char *name)
{
- _GETVAR(id, ID_FVAR, name, 0);
- return id->minvalf;
+ _GETVAR(id, ID_FVAR, name, 0);
+ return id->minvalf;
}
float getfvarmax(const char *name)
{
- _GETVAR(id, ID_FVAR, name, 0);
- return id->maxvalf;
+ _GETVAR(id, ID_FVAR, name, 0);
+ return id->maxvalf;
}
ICOMMAND(getvarmin, "s", (char *s), intret(getvarmin(s)));
@@ -582,433 +582,433 @@ ident *getident(const char *name) { return idents.access(name); }
void touchvar(const char *name)
{
- ident *id = idents.access(name);
- if(id) switch(id->type)
- {
- case ID_VAR:
- case ID_FVAR:
- case ID_SVAR:
- id->changed();
- break;
- }
+ ident *id = idents.access(name);
+ if(id) switch(id->type)
+ {
+ case ID_VAR:
+ case ID_FVAR:
+ case ID_SVAR:
+ id->changed();
+ break;
+ }
}
const char *getalias(const char *name)
{
- ident *i = idents.access(name);
- return i && i->type==ID_ALIAS && (i->index >= MAXARGS || aliasstack->usedargs&(1<<i->index)) ? i->getstr() : "";
+ ident *i = idents.access(name);
+ return i && i->type==ID_ALIAS && (i->index >= MAXARGS || aliasstack->usedargs&(1<<i->index)) ? i->getstr() : "";
}
ICOMMAND(getalias, "s", (char *s), result(getalias(s)));
int clampvar(ident *id, int val, int minval, int maxval)
{
- if(val < minval) val = minval;
- else if(val > maxval) val = maxval;
- else return val;
- debugcode(id->flags&IDF_HEX ?
- (minval <= 255 ? "valid range for %s is %d..0x%X" : "valid range for %s is 0x%X..0x%X") :
- "valid range for %s is %d..%d",
- id->name, minval, maxval);
- return val;
+ if(val < minval) val = minval;
+ else if(val > maxval) val = maxval;
+ else return val;
+ debugcode(id->flags&IDF_HEX ?
+ (minval <= 255 ? "valid range for %s is %d..0x%X" : "valid range for %s is 0x%X..0x%X") :
+ "valid range for %s is %d..%d",
+ id->name, minval, maxval);
+ return val;
}
void setvarchecked(ident *id, int val)
{
- if(id->flags&IDF_READONLY) debugcode("variable %s is read-only", id->name);
+ if(id->flags&IDF_READONLY) debugcode("variable %s is read-only", id->name);
#ifndef STANDALONE
- else if(!(id->flags&IDF_OVERRIDE) || identflags&IDF_OVERRIDDEN || game::allowedittoggle())
+ else if(!(id->flags&IDF_OVERRIDE) || identflags&IDF_OVERRIDDEN || game::allowedittoggle())
#else
- else
+ else
#endif
- {
- OVERRIDEVAR(return, id->overrideval.i = *id->storage.i, , )
- if(val < id->minval || val > id->maxval) val = clampvar(id, val, id->minval, id->maxval);
- *id->storage.i = val;
- id->changed(); // call trigger function if available
+ {
+ OVERRIDEVAR(return, id->overrideval.i = *id->storage.i, , )
+ if(val < id->minval || val > id->maxval) val = clampvar(id, val, id->minval, id->maxval);
+ *id->storage.i = val;
+ id->changed(); // call trigger function if available
#ifndef STANDALONE
- if(id->flags&IDF_OVERRIDE && !(identflags&IDF_OVERRIDDEN)) game::vartrigger(id);
+ if(id->flags&IDF_OVERRIDE && !(identflags&IDF_OVERRIDDEN)) game::vartrigger(id);
#endif
- }
+ }
}
static inline void setvarchecked(ident *id, tagval *args, int numargs)
{
- int val = forceint(args[0]);
- if(id->flags&IDF_HEX && numargs > 1)
- {
- val = (val << 16) | (forceint(args[1])<<8);
- if(numargs > 2) val |= forceint(args[2]);
- }
- setvarchecked(id, val);
+ int val = forceint(args[0]);
+ if(id->flags&IDF_HEX && numargs > 1)
+ {
+ val = (val << 16) | (forceint(args[1])<<8);
+ if(numargs > 2) val |= forceint(args[2]);
+ }
+ setvarchecked(id, val);
}
float clampfvar(ident *id, float val, float minval, float maxval)
{
- if(val < minval) val = minval;
- else if(val > maxval) val = maxval;
- else return val;
- debugcode("valid range for %s is %s..%s", id->name, floatstr(minval), floatstr(maxval));
- return val;
+ if(val < minval) val = minval;
+ else if(val > maxval) val = maxval;
+ else return val;
+ debugcode("valid range for %s is %s..%s", id->name, floatstr(minval), floatstr(maxval));
+ return val;
}
void setfvarchecked(ident *id, float val)
{
- if(id->flags&IDF_READONLY) debugcode("variable %s is read-only", id->name);
+ if(id->flags&IDF_READONLY) debugcode("variable %s is read-only", id->name);
#ifndef STANDALONE
- else if(!(id->flags&IDF_OVERRIDE) || identflags&IDF_OVERRIDDEN || game::allowedittoggle())
+ else if(!(id->flags&IDF_OVERRIDE) || identflags&IDF_OVERRIDDEN || game::allowedittoggle())
#else
- else
+ else
#endif
- {
- OVERRIDEVAR(return, id->overrideval.f = *id->storage.f, , );
- if(val < id->minvalf || val > id->maxvalf) val = clampfvar(id, val, id->minvalf, id->maxvalf);
- *id->storage.f = val;
- id->changed();
+ {
+ OVERRIDEVAR(return, id->overrideval.f = *id->storage.f, , );
+ if(val < id->minvalf || val > id->maxvalf) val = clampfvar(id, val, id->minvalf, id->maxvalf);
+ *id->storage.f = val;
+ id->changed();
#ifndef STANDALONE
- if(id->flags&IDF_OVERRIDE && !(identflags&IDF_OVERRIDDEN)) game::vartrigger(id);
+ if(id->flags&IDF_OVERRIDE && !(identflags&IDF_OVERRIDDEN)) game::vartrigger(id);
#endif
- }
+ }
}
void setsvarchecked(ident *id, const char *val)
{
- if(id->flags&IDF_READONLY) debugcode("variable %s is read-only", id->name);
+ if(id->flags&IDF_READONLY) debugcode("variable %s is read-only", id->name);
#ifndef STANDALONE
- else if(!(id->flags&IDF_OVERRIDE) || identflags&IDF_OVERRIDDEN || game::allowedittoggle())
+ else if(!(id->flags&IDF_OVERRIDE) || identflags&IDF_OVERRIDDEN || game::allowedittoggle())
#else
- else
+ else
#endif
- {
- OVERRIDEVAR(return, id->overrideval.s = *id->storage.s, delete[] id->overrideval.s, delete[] *id->storage.s);
- *id->storage.s = newstring(val);
- id->changed();
+ {
+ OVERRIDEVAR(return, id->overrideval.s = *id->storage.s, delete[] id->overrideval.s, delete[] *id->storage.s);
+ *id->storage.s = newstring(val);
+ id->changed();
#ifndef STANDALONE
- if(id->flags&IDF_OVERRIDE && !(identflags&IDF_OVERRIDDEN)) game::vartrigger(id);
+ if(id->flags&IDF_OVERRIDE && !(identflags&IDF_OVERRIDDEN)) game::vartrigger(id);
#endif
- }
+ }
}
ICOMMAND(set, "rt", (ident *id, tagval *v),
{
- switch(id->type)
- {
- case ID_ALIAS:
- if(id->index < MAXARGS) setarg(*id, *v); else setalias(*id, *v);
- v->type = VAL_NULL;
- break;
- case ID_VAR:
- setvarchecked(id, forceint(*v));
- break;
- case ID_FVAR:
- setfvarchecked(id, forcefloat(*v));
- break;
- case ID_SVAR:
- setsvarchecked(id, forcestr(*v));
- break;
- case ID_COMMAND:
- if(id->flags&IDF_EMUVAR)
- {
- execute(id, v, 1);
- v->type = VAL_NULL;
- break;
- }
- // fall through
- default:
- debugcode("cannot redefine builtin %s with an alias", id->name);
- break;
- }
+ switch(id->type)
+ {
+ case ID_ALIAS:
+ if(id->index < MAXARGS) setarg(*id, *v); else setalias(*id, *v);
+ v->type = VAL_NULL;
+ break;
+ case ID_VAR:
+ setvarchecked(id, forceint(*v));
+ break;
+ case ID_FVAR:
+ setfvarchecked(id, forcefloat(*v));
+ break;
+ case ID_SVAR:
+ setsvarchecked(id, forcestr(*v));
+ break;
+ case ID_COMMAND:
+ if(id->flags&IDF_EMUVAR)
+ {
+ execute(id, v, 1);
+ v->type = VAL_NULL;
+ break;
+ }
+ // fall through
+ default:
+ debugcode("cannot redefine builtin %s with an alias", id->name);
+ break;
+ }
});
bool addcommand(const char *name, identfun fun, const char *args)
{
- uint argmask = 0;
- int numargs = 0, flags = 0;
- bool limit = true;
- for(const char *fmt = args; *fmt; fmt++) switch(*fmt)
- {
- case 'i': case 'b': case 'f': case 't': case 'N': case 'D': if(numargs < MAXARGS) numargs++; break;
- case '$': flags |= IDF_EMUVAR; // fall through
- case 's': case 'e': case 'r': if(numargs < MAXARGS) { argmask |= 1<<numargs; numargs++; } break;
- case '1': case '2': case '3': case '4': if(numargs < MAXARGS) fmt -= *fmt-'0'+1; break;
- case 'C': case 'V': limit = false; break;
- default: fatal("builtin %s declared with illegal type: %s", name, args); break;
- }
- if(limit && numargs > MAXCOMARGS) fatal("builtin %s declared with too many args: %d", name, numargs);
- addident(ident(ID_COMMAND, name, args, argmask, numargs, (void *)fun, flags));
- return false;
+ uint argmask = 0;
+ int numargs = 0, flags = 0;
+ bool limit = true;
+ for(const char *fmt = args; *fmt; fmt++) switch(*fmt)
+ {
+ case 'i': case 'b': case 'f': case 't': case 'N': case 'D': if(numargs < MAXARGS) numargs++; break;
+ case '$': flags |= IDF_EMUVAR; // fall through
+ case 's': case 'e': case 'r': if(numargs < MAXARGS) { argmask |= 1<<numargs; numargs++; } break;
+ case '1': case '2': case '3': case '4': if(numargs < MAXARGS) fmt -= *fmt-'0'+1; break;
+ case 'C': case 'V': limit = false; break;
+ default: fatal("builtin %s declared with illegal type: %s", name, args); break;
+ }
+ if(limit && numargs > MAXCOMARGS) fatal("builtin %s declared with too many args: %d", name, numargs);
+ addident(ident(ID_COMMAND, name, args, argmask, numargs, (void *)fun, flags));
+ return false;
}
bool addkeyword(int type, const char *name)
{
- addident(ident(type, name, "", 0, 0, NULL));
- return true;
+ addident(ident(type, name, "", 0, 0, NULL));
+ return true;
}
const char *parsestring(const char *p)
{
- for(; *p; p++) switch(*p)
- {
- case '\r':
- case '\n':
- case '\"':
- return p;
- case '^':
- if(*++p) break;
- return p;
- }
- return p;
+ for(; *p; p++) switch(*p)
+ {
+ case '\r':
+ case '\n':
+ case '\"':
+ return p;
+ case '^':
+ if(*++p) break;
+ return p;
+ }
+ return p;
}
int unescapestring(char *dst, const char *src, const char *end)
{
- char *start = dst;
- while(src < end)
- {
- int c = *src++;
- if(c == '^')
- {
- if(src >= end) break;
- int e = *src++;
- switch(e)
- {
- case 'n': *dst++ = '\n'; break;
- case 't': *dst++ = '\t'; break;
- case 'f': *dst++ = '\f'; break;
- default: *dst++ = e; break;
- }
- }
- else *dst++ = c;
- }
- return dst - start;
+ char *start = dst;
+ while(src < end)
+ {
+ int c = *src++;
+ if(c == '^')
+ {
+ if(src >= end) break;
+ int e = *src++;
+ switch(e)
+ {
+ case 'n': *dst++ = '\n'; break;
+ case 't': *dst++ = '\t'; break;
+ case 'f': *dst++ = '\f'; break;
+ default: *dst++ = e; break;
+ }
+ }
+ else *dst++ = c;
+ }
+ return dst - start;
}
static char *conc(vector<char> &buf, tagval *v, int n, bool space, const char *prefix = NULL, int prefixlen = 0)
{
- if(prefix)
- {
- buf.put(prefix, prefixlen);
- if(space && n) buf.add(' ');
- }
- loopi(n)
- {
- const char *s = "";
- int len = 0;
- switch(v[i].type)
- {
- case VAL_INT: s = intstr(v[i].i); break;
- case VAL_FLOAT: s = floatstr(v[i].f); break;
- case VAL_STR: s = v[i].s; break;
- case VAL_MACRO: s = v[i].s; len = v[i].code[-1]>>8; goto haslen;
- }
- len = int(strlen(s));
- haslen:
- buf.put(s, len);
- if(i == n-1) break;
- if(space) buf.add(' ');
- }
- buf.add('\0');
- return buf.getbuf();
+ if(prefix)
+ {
+ buf.put(prefix, prefixlen);
+ if(space && n) buf.add(' ');
+ }
+ loopi(n)
+ {
+ const char *s = "";
+ int len = 0;
+ switch(v[i].type)
+ {
+ case VAL_INT: s = intstr(v[i].i); break;
+ case VAL_FLOAT: s = floatstr(v[i].f); break;
+ case VAL_STR: s = v[i].s; break;
+ case VAL_MACRO: s = v[i].s; len = v[i].code[-1]>>8; goto haslen;
+ }
+ len = int(strlen(s));
+ haslen:
+ buf.put(s, len);
+ if(i == n-1) break;
+ if(space) buf.add(' ');
+ }
+ buf.add('\0');
+ return buf.getbuf();
}
static char *conc(tagval *v, int n, bool space, const char *prefix, int prefixlen)
{
- static int vlen[MAXARGS];
- static char numbuf[3*MAXSTRLEN];
- int len = prefixlen, numlen = 0, i = 0;
- for(; i < n; i++) switch(v[i].type)
- {
- case VAL_MACRO: len += (vlen[i] = v[i].code[-1]>>8); break;
- case VAL_STR: len += (vlen[i] = int(strlen(v[i].s))); break;
- case VAL_INT:
- if(numlen + MAXSTRLEN > int(sizeof(numbuf))) goto overflow;
- intformat(&numbuf[numlen], v[i].i);
- numlen += (vlen[i] = strlen(&numbuf[numlen]));
- break;
- case VAL_FLOAT:
- if(numlen + MAXSTRLEN > int(sizeof(numbuf))) goto overflow;
- floatformat(&numbuf[numlen], v[i].f);
- numlen += (vlen[i] = strlen(&numbuf[numlen]));
- break;
- default: vlen[i] = 0; break;
- }
+ static int vlen[MAXARGS];
+ static char numbuf[3*MAXSTRLEN];
+ int len = prefixlen, numlen = 0, i = 0;
+ for(; i < n; i++) switch(v[i].type)
+ {
+ case VAL_MACRO: len += (vlen[i] = v[i].code[-1]>>8); break;
+ case VAL_STR: len += (vlen[i] = int(strlen(v[i].s))); break;
+ case VAL_INT:
+ if(numlen + MAXSTRLEN > int(sizeof(numbuf))) goto overflow;
+ intformat(&numbuf[numlen], v[i].i);
+ numlen += (vlen[i] = strlen(&numbuf[numlen]));
+ break;
+ case VAL_FLOAT:
+ if(numlen + MAXSTRLEN > int(sizeof(numbuf))) goto overflow;
+ floatformat(&numbuf[numlen], v[i].f);
+ numlen += (vlen[i] = strlen(&numbuf[numlen]));
+ break;
+ default: vlen[i] = 0; break;
+ }
overflow:
- if(space) len += max(prefix ? i : i-1, 0);
- char *buf = newstring(len + numlen);
- int offset = 0, numoffset = 0;
- if(prefix)
- {
- memcpy(buf, prefix, prefixlen);
- offset += prefixlen;
- if(space && i) buf[offset++] = ' ';
- }
- loopj(i)
- {
- if(v[j].type == VAL_INT || v[j].type == VAL_FLOAT)
- {
- memcpy(&buf[offset], &numbuf[numoffset], vlen[j]);
- numoffset += vlen[j];
- }
- else if(vlen[j]) memcpy(&buf[offset], v[j].s, vlen[j]);
- offset += vlen[j];
- if(j==i-1) break;
- if(space) buf[offset++] = ' ';
- }
- buf[offset] = '\0';
- if(i < n)
- {
- char *morebuf = conc(&v[i], n-i, space, buf, offset);
- delete[] buf;
- return morebuf;
- }
- return buf;
+ if(space) len += max(prefix ? i : i-1, 0);
+ char *buf = newstring(len + numlen);
+ int offset = 0, numoffset = 0;
+ if(prefix)
+ {
+ memcpy(buf, prefix, prefixlen);
+ offset += prefixlen;
+ if(space && i) buf[offset++] = ' ';
+ }
+ loopj(i)
+ {
+ if(v[j].type == VAL_INT || v[j].type == VAL_FLOAT)
+ {
+ memcpy(&buf[offset], &numbuf[numoffset], vlen[j]);
+ numoffset += vlen[j];
+ }
+ else if(vlen[j]) memcpy(&buf[offset], v[j].s, vlen[j]);
+ offset += vlen[j];
+ if(j==i-1) break;
+ if(space) buf[offset++] = ' ';
+ }
+ buf[offset] = '\0';
+ if(i < n)
+ {
+ char *morebuf = conc(&v[i], n-i, space, buf, offset);
+ delete[] buf;
+ return morebuf;
+ }
+ return buf;
}
static inline char *conc(tagval *v, int n, bool space)
{
- return conc(v, n, space, NULL, 0);
+ return conc(v, n, space, NULL, 0);
}
static inline char *conc(tagval *v, int n, bool space, const char *prefix)
{
- return conc(v, n, space, prefix, strlen(prefix));
+ return conc(v, n, space, prefix, strlen(prefix));
}
static inline void skipcomments(const char *&p)
{
- for(;;)
- {
- p += strspn(p, " \t\r");
- if(p[0]!='/' || p[1]!='/') break;
- p += strcspn(p, "\n\0");
- }
+ for(;;)
+ {
+ p += strspn(p, " \t\r");
+ if(p[0]!='/' || p[1]!='/') break;
+ p += strcspn(p, "\n\0");
+ }
}
static inline char *cutstring(const char *&p, int &len)
{
- p++;
- const char *end = parsestring(p);
- char *s = newstring(end - p);
- len = unescapestring(s, p, end);
- s[len] = '\0';
- p = end;
- if(*p=='\"') p++;
- return s;
+ p++;
+ const char *end = parsestring(p);
+ char *s = newstring(end - p);
+ len = unescapestring(s, p, end);
+ s[len] = '\0';
+ p = end;
+ if(*p=='\"') p++;
+ return s;
}
static inline const char *parseword(const char *p)
{
- const int maxbrak = 100;
- static char brakstack[maxbrak];
- int brakdepth = 0;
- for(;; p++)
- {
- p += strcspn(p, "\"/;()[] \t\r\n\0");
- switch(p[0])
- {
- case '"': case ';': case ' ': case '\t': case '\r': case '\n': case '\0': return p;
- case '/': if(p[1] == '/') return p; break;
- case '[': case '(': if(brakdepth >= maxbrak) return p; brakstack[brakdepth++] = p[0]; break;
- case ']': if(brakdepth <= 0 || brakstack[--brakdepth] != '[') return p; break;
- case ')': if(brakdepth <= 0 || brakstack[--brakdepth] != '(') return p; break;
- }
- }
- return p;
+ const int maxbrak = 100;
+ static char brakstack[maxbrak];
+ int brakdepth = 0;
+ for(;; p++)
+ {
+ p += strcspn(p, "\"/;()[] \t\r\n\0");
+ switch(p[0])
+ {
+ case '"': case ';': case ' ': case '\t': case '\r': case '\n': case '\0': return p;
+ case '/': if(p[1] == '/') return p; break;
+ case '[': case '(': if(brakdepth >= maxbrak) return p; brakstack[brakdepth++] = p[0]; break;
+ case ']': if(brakdepth <= 0 || brakstack[--brakdepth] != '[') return p; break;
+ case ')': if(brakdepth <= 0 || brakstack[--brakdepth] != '(') return p; break;
+ }
+ }
+ return p;
}
static inline char *cutword(const char *&p, int &len)
{
- const char *word = p;
- p = parseword(p);
- len = p-word;
- if(!len) return NULL;
- return newstring(word, len);
+ const char *word = p;
+ p = parseword(p);
+ len = p-word;
+ if(!len) return NULL;
+ return newstring(word, len);
}
static inline void compilestr(vector<uint> &code, const char *word, int len, bool macro = false)
{
- if(len <= 3 && !macro)
- {
- uint op = CODE_VALI|RET_STR;
- for(int i = 0; i < len; i++) op |= uint(uchar(word[i]))<<((i+1)*8);
- code.add(op);
- return;
- }
- code.add((macro ? CODE_MACRO : CODE_VAL|RET_STR)|(len<<8));
- code.put((const uint *)word, len/sizeof(uint));
- size_t endlen = len%sizeof(uint);
- union
- {
- char c[sizeof(uint)];
- uint u;
- } end;
- end.u = 0;
- memcpy(end.c, word + len - endlen, endlen);
- code.add(end.u);
+ if(len <= 3 && !macro)
+ {
+ uint op = CODE_VALI|RET_STR;
+ for(int i = 0; i < len; i++) op |= uint(uchar(word[i]))<<((i+1)*8);
+ code.add(op);
+ return;
+ }
+ code.add((macro ? CODE_MACRO : CODE_VAL|RET_STR)|(len<<8));
+ code.put((const uint *)word, len/sizeof(uint));
+ size_t endlen = len%sizeof(uint);
+ union
+ {
+ char c[sizeof(uint)];
+ uint u;
+ } end;
+ end.u = 0;
+ memcpy(end.c, word + len - endlen, endlen);
+ code.add(end.u);
}
static inline void compilestr(vector<uint> &code, const char *word = NULL)
{
- if(!word) { code.add(CODE_VALI|RET_STR); return; }
- compilestr(code, word, int(strlen(word)));
+ if(!word) { code.add(CODE_VALI|RET_STR); return; }
+ compilestr(code, word, int(strlen(word)));
}
static inline void compileint(vector<uint> &code, int i)
{
- if(i >= -0x800000 && i <= 0x7FFFFF)
- code.add(CODE_VALI|RET_INT|(i<<8));
- else
- {
- code.add(CODE_VAL|RET_INT);
- code.add(i);
- }
+ if(i >= -0x800000 && i <= 0x7FFFFF)
+ code.add(CODE_VALI|RET_INT|(i<<8));
+ else
+ {
+ code.add(CODE_VAL|RET_INT);
+ code.add(i);
+ }
}
static inline void compilenull(vector<uint> &code)
{
- code.add(CODE_VALI|RET_NULL);
+ code.add(CODE_VALI|RET_NULL);
}
static inline void compileblock(vector<uint> &code)
{
- int start = code.length();
- code.add(CODE_BLOCK);
- code.add(CODE_OFFSET|((start+2)<<8));
- code.add(CODE_EXIT);
- code[start] |= uint(code.length() - (start + 1))<<8;
+ int start = code.length();
+ code.add(CODE_BLOCK);
+ code.add(CODE_OFFSET|((start+2)<<8));
+ code.add(CODE_EXIT);
+ code[start] |= uint(code.length() - (start + 1))<<8;
}
static inline void compileident(vector<uint> &code, ident *id)
{
- code.add((id->index < MAXARGS ? CODE_IDENTARG : CODE_IDENT)|(id->index<<8));
+ code.add((id->index < MAXARGS ? CODE_IDENTARG : CODE_IDENT)|(id->index<<8));
}
static inline void compileident(vector<uint> &code, const char *word = NULL)
{
- compileident(code, word ? newident(word, IDF_UNKNOWN) : dummyident);
+ compileident(code, word ? newident(word, IDF_UNKNOWN) : dummyident);
}
static inline void compileint(vector<uint> &code, const char *word = NULL)
{
- return compileint(code, word ? parseint(word) : 0);
+ return compileint(code, word ? parseint(word) : 0);
}
static inline void compilefloat(vector<uint> &code, float f)
{
- if(int(f) == f && f >= -0x800000 && f <= 0x7FFFFF)
- code.add(CODE_VALI|RET_FLOAT|(int(f)<<8));
- else
- {
- union { float f; uint u; } conv;
- conv.f = f;
- code.add(CODE_VAL|RET_FLOAT);
- code.add(conv.u);
- }
+ if(int(f) == f && f >= -0x800000 && f <= 0x7FFFFF)
+ code.add(CODE_VALI|RET_FLOAT|(int(f)<<8));
+ else
+ {
+ union { float f; uint u; } conv;
+ conv.f = f;
+ code.add(CODE_VAL|RET_FLOAT);
+ code.add(conv.u);
+ }
}
static inline void compilefloat(vector<uint> &code, const char *word = NULL)
{
- return compilefloat(code, word ? parsefloat(word) : 0.0f);
+ return compilefloat(code, word ? parsefloat(word) : 0.0f);
}
static bool compilearg(vector<uint> &code, const char *&p, int wordtype);
@@ -1016,641 +1016,641 @@ static void compilestatements(vector<uint> &code, const char *&p, int rettype, i
static inline void compileval(vector<uint> &code, int wordtype, char *word, int wordlen)
{
- switch(wordtype)
- {
- case VAL_STR: compilestr(code, word, wordlen, true); break;
- case VAL_ANY: compilestr(code, word, wordlen); break;
- case VAL_FLOAT: compilefloat(code, word); break;
- case VAL_INT: compileint(code, word); break;
- case VAL_CODE:
- {
- int start = code.length();
- code.add(CODE_BLOCK);
- code.add(CODE_OFFSET|((start+2)<<8));
- const char *p = word;
- if(p) compilestatements(code, p, VAL_ANY);
- code.add(CODE_EXIT|RET_STR);
- code[start] |= uint(code.length() - (start + 1))<<8;
- break;
- }
- case VAL_IDENT: compileident(code, word); break;
- default:
- break;
- }
+ switch(wordtype)
+ {
+ case VAL_STR: compilestr(code, word, wordlen, true); break;
+ case VAL_ANY: compilestr(code, word, wordlen); break;
+ case VAL_FLOAT: compilefloat(code, word); break;
+ case VAL_INT: compileint(code, word); break;
+ case VAL_CODE:
+ {
+ int start = code.length();
+ code.add(CODE_BLOCK);
+ code.add(CODE_OFFSET|((start+2)<<8));
+ const char *p = word;
+ if(p) compilestatements(code, p, VAL_ANY);
+ code.add(CODE_EXIT|RET_STR);
+ code[start] |= uint(code.length() - (start + 1))<<8;
+ break;
+ }
+ case VAL_IDENT: compileident(code, word); break;
+ default:
+ break;
+ }
}
static bool compileword(vector<uint> &code, const char *&p, int wordtype, char *&word, int &wordlen);
static void compilelookup(vector<uint> &code, const char *&p, int ltype)
{
- char *lookup = NULL;
- int lookuplen = 0;
- switch(*++p)
- {
- case '(':
- case '[':
- if(!compileword(code, p, VAL_STR, lookup, lookuplen)) goto invalid;
- break;
- case '$':
- compilelookup(code, p, VAL_STR);
- break;
- case '\"':
- lookup = cutstring(p, lookuplen);
- goto lookupid;
- default:
- {
- lookup = cutword(p, lookuplen);
- if(!lookup) goto invalid;
- lookupid:
- ident *id = newident(lookup, IDF_UNKNOWN);
- if(id) switch(id->type)
- {
- case ID_VAR: code.add(CODE_IVAR|((ltype >= VAL_ANY ? VAL_INT : ltype)<<CODE_RET)|(id->index<<8)); goto done;
- case ID_FVAR: code.add(CODE_FVAR|((ltype >= VAL_ANY ? VAL_FLOAT : ltype)<<CODE_RET)|(id->index<<8)); goto done;
- case ID_SVAR: code.add(CODE_SVAR|((ltype >= VAL_ANY ? VAL_STR : ltype)<<CODE_RET)|(id->index<<8)); goto done;
- case ID_ALIAS: code.add((id->index < MAXARGS ? CODE_LOOKUPARG : CODE_LOOKUP)|((ltype >= VAL_ANY ? VAL_STR : ltype)<<CODE_RET)|(id->index<<8)); goto done;
- case ID_COMMAND:
- {
- int comtype = CODE_COM, numargs = 0;
- code.add(CODE_ENTER);
- for(const char *fmt = id->args; *fmt; fmt++) switch(*fmt)
- {
- case 's': compilestr(code, NULL, 0, true); numargs++; break;
- case 'i': compileint(code); numargs++; break;
- case 'b': compileint(code, INT_MIN); numargs++; break;
- case 'f': compilefloat(code); numargs++; break;
- case 't': compilenull(code); numargs++; break;
- case 'e': compileblock(code); numargs++; break;
- case 'r': compileident(code); numargs++; break;
- case '$': compileident(code, id); numargs++; break;
- case 'N': compileint(code, -1); numargs++; break;
+ char *lookup = NULL;
+ int lookuplen = 0;
+ switch(*++p)
+ {
+ case '(':
+ case '[':
+ if(!compileword(code, p, VAL_STR, lookup, lookuplen)) goto invalid;
+ break;
+ case '$':
+ compilelookup(code, p, VAL_STR);
+ break;
+ case '\"':
+ lookup = cutstring(p, lookuplen);
+ goto lookupid;
+ default:
+ {
+ lookup = cutword(p, lookuplen);
+ if(!lookup) goto invalid;
+ lookupid:
+ ident *id = newident(lookup, IDF_UNKNOWN);
+ if(id) switch(id->type)
+ {
+ case ID_VAR: code.add(CODE_IVAR|((ltype >= VAL_ANY ? VAL_INT : ltype)<<CODE_RET)|(id->index<<8)); goto done;
+ case ID_FVAR: code.add(CODE_FVAR|((ltype >= VAL_ANY ? VAL_FLOAT : ltype)<<CODE_RET)|(id->index<<8)); goto done;
+ case ID_SVAR: code.add(CODE_SVAR|((ltype >= VAL_ANY ? VAL_STR : ltype)<<CODE_RET)|(id->index<<8)); goto done;
+ case ID_ALIAS: code.add((id->index < MAXARGS ? CODE_LOOKUPARG : CODE_LOOKUP)|((ltype >= VAL_ANY ? VAL_STR : ltype)<<CODE_RET)|(id->index<<8)); goto done;
+ case ID_COMMAND:
+ {
+ int comtype = CODE_COM, numargs = 0;
+ code.add(CODE_ENTER);
+ for(const char *fmt = id->args; *fmt; fmt++) switch(*fmt)
+ {
+ case 's': compilestr(code, NULL, 0, true); numargs++; break;
+ case 'i': compileint(code); numargs++; break;
+ case 'b': compileint(code, INT_MIN); numargs++; break;
+ case 'f': compilefloat(code); numargs++; break;
+ case 't': compilenull(code); numargs++; break;
+ case 'e': compileblock(code); numargs++; break;
+ case 'r': compileident(code); numargs++; break;
+ case '$': compileident(code, id); numargs++; break;
+ case 'N': compileint(code, -1); numargs++; break;
#ifndef STANDALONE
- case 'D': comtype = CODE_COMD; numargs++; break;
+ case 'D': comtype = CODE_COMD; numargs++; break;
#endif
- case 'C': comtype = CODE_COMC; numargs = 1; goto endfmt;
- case 'V': comtype = CODE_COMV; numargs = 2; goto endfmt;
- case '1': case '2': case '3': case '4': break;
- }
- endfmt:
- code.add(comtype|(ltype < VAL_ANY ? ltype<<CODE_RET : 0)|(id->index<<8));
- code.add(CODE_EXIT|(ltype < VAL_ANY ? ltype<<CODE_RET : 0));
- goto done;
- }
- default: goto invalid;
- }
- compilestr(code, lookup, lookuplen, true);
- break;
- }
- }
- code.add(CODE_LOOKUPU|((ltype < VAL_ANY ? ltype<<CODE_RET : 0)));
+ case 'C': comtype = CODE_COMC; numargs = 1; goto endfmt;
+ case 'V': comtype = CODE_COMV; numargs = 2; goto endfmt;
+ case '1': case '2': case '3': case '4': break;
+ }
+ endfmt:
+ code.add(comtype|(ltype < VAL_ANY ? ltype<<CODE_RET : 0)|(id->index<<8));
+ code.add(CODE_EXIT|(ltype < VAL_ANY ? ltype<<CODE_RET : 0));
+ goto done;
+ }
+ default: goto invalid;
+ }
+ compilestr(code, lookup, lookuplen, true);
+ break;
+ }
+ }
+ code.add(CODE_LOOKUPU|((ltype < VAL_ANY ? ltype<<CODE_RET : 0)));
done:
- delete[] lookup;
- switch(ltype)
- {
- case VAL_CODE: code.add(CODE_COMPILE); break;
- case VAL_IDENT: code.add(CODE_IDENTU); break;
- }
- return;
+ delete[] lookup;
+ switch(ltype)
+ {
+ case VAL_CODE: code.add(CODE_COMPILE); break;
+ case VAL_IDENT: code.add(CODE_IDENTU); break;
+ }
+ return;
invalid:
- switch(ltype)
- {
- case VAL_NULL: case VAL_ANY: compilenull(code); break;
- default: compileval(code, ltype, NULL, 0); break;
- }
+ switch(ltype)
+ {
+ case VAL_NULL: case VAL_ANY: compilenull(code); break;
+ default: compileval(code, ltype, NULL, 0); break;
+ }
}
static bool compileblockstr(vector<uint> &code, const char *str, const char *end, bool macro)
{
- int start = code.length();
- code.add(macro ? CODE_MACRO : CODE_VAL|RET_STR);
- char *buf = (char *)code.reserve((end-str)/sizeof(uint)+1).buf;
- int len = 0;
- while(str < end)
- {
- int n = strcspn(str, "\r/\"@]\0");
- memcpy(&buf[len], str, n);
- len += n;
- str += n;
- switch(*str)
- {
- case '\r': str++; break;
- case '\"':
- {
- const char *start = str;
- str = parsestring(str+1);
- if(*str=='\"') str++;
- memcpy(&buf[len], start, str-start);
- len += str-start;
- break;
- }
- case '/':
- if(str[1] == '/')
- {
- size_t comment = strcspn(str, "\n\0");
- if (iscubepunct(str[2]))
- {
- memcpy(&buf[len], str, comment);
- len += comment;
- }
- str += comment;
- }
- else buf[len++] = *str++;
- break;
- case '@':
- [[fallthrough]];
- case ']':
- if(str < end) { buf[len++] = *str++; break; }
- [[fallthrough]];
- case '\0': goto done;
- }
- }
+ int start = code.length();
+ code.add(macro ? CODE_MACRO : CODE_VAL|RET_STR);
+ char *buf = (char *)code.reserve((end-str)/sizeof(uint)+1).buf;
+ int len = 0;
+ while(str < end)
+ {
+ int n = strcspn(str, "\r/\"@]\0");
+ memcpy(&buf[len], str, n);
+ len += n;
+ str += n;
+ switch(*str)
+ {
+ case '\r': str++; break;
+ case '\"':
+ {
+ const char *start = str;
+ str = parsestring(str+1);
+ if(*str=='\"') str++;
+ memcpy(&buf[len], start, str-start);
+ len += str-start;
+ break;
+ }
+ case '/':
+ if(str[1] == '/')
+ {
+ size_t comment = strcspn(str, "\n\0");
+ if (iscubepunct(str[2]))
+ {
+ memcpy(&buf[len], str, comment);
+ len += comment;
+ }
+ str += comment;
+ }
+ else buf[len++] = *str++;
+ break;
+ case '@':
+ [[fallthrough]];
+ case ']':
+ if(str < end) { buf[len++] = *str++; break; }
+ [[fallthrough]];
+ case '\0': goto done;
+ }
+ }
done:
- memset(&buf[len], '\0', sizeof(uint)-len%sizeof(uint));
- code.advance(len/sizeof(uint)+1);
- code[start] |= len<<8;
- return true;
+ memset(&buf[len], '\0', sizeof(uint)-len%sizeof(uint));
+ code.advance(len/sizeof(uint)+1);
+ code[start] |= len<<8;
+ return true;
}
static bool compileblocksub(vector<uint> &code, const char *&p)
{
- char *lookup = NULL;
- int lookuplen = 0;
- switch(*p)
- {
- case '(':
- if(!compilearg(code, p, VAL_STR)) return false;
- break;
- case '[':
- if(!compilearg(code, p, VAL_STR)) return false;
- code.add(CODE_LOOKUPU|RET_STR);
- break;
- case '\"':
- lookup = cutstring(p, lookuplen);
- goto lookupid;
- default:
- {
- {
- const char *start = p;
- while(iscubealnum(*p) || *p=='_') p++;
- lookuplen = p-start;
- if(!lookuplen) return false;
- lookup = newstring(start, lookuplen);
- }
- lookupid:
- ident *id = newident(lookup, IDF_UNKNOWN);
- if(id) switch(id->type)
- {
- case ID_VAR: code.add(CODE_IVAR|RET_STR|(id->index<<8)); goto done;
- case ID_FVAR: code.add(CODE_FVAR|RET_STR|(id->index<<8)); goto done;
- case ID_SVAR: code.add(CODE_SVAR|RET_STR|(id->index<<8)); goto done;
- case ID_ALIAS: code.add((id->index < MAXARGS ? CODE_LOOKUPARG : CODE_LOOKUP)|RET_STR|(id->index<<8)); goto done;
- }
- compilestr(code, lookup, lookuplen, true);
- code.add(CODE_LOOKUPU|RET_STR);
- done:
- delete[] lookup;
- break;
- }
- }
- return true;
+ char *lookup = NULL;
+ int lookuplen = 0;
+ switch(*p)
+ {
+ case '(':
+ if(!compilearg(code, p, VAL_STR)) return false;
+ break;
+ case '[':
+ if(!compilearg(code, p, VAL_STR)) return false;
+ code.add(CODE_LOOKUPU|RET_STR);
+ break;
+ case '\"':
+ lookup = cutstring(p, lookuplen);
+ goto lookupid;
+ default:
+ {
+ {
+ const char *start = p;
+ while(iscubealnum(*p) || *p=='_') p++;
+ lookuplen = p-start;
+ if(!lookuplen) return false;
+ lookup = newstring(start, lookuplen);
+ }
+ lookupid:
+ ident *id = newident(lookup, IDF_UNKNOWN);
+ if(id) switch(id->type)
+ {
+ case ID_VAR: code.add(CODE_IVAR|RET_STR|(id->index<<8)); goto done;
+ case ID_FVAR: code.add(CODE_FVAR|RET_STR|(id->index<<8)); goto done;
+ case ID_SVAR: code.add(CODE_SVAR|RET_STR|(id->index<<8)); goto done;
+ case ID_ALIAS: code.add((id->index < MAXARGS ? CODE_LOOKUPARG : CODE_LOOKUP)|RET_STR|(id->index<<8)); goto done;
+ }
+ compilestr(code, lookup, lookuplen, true);
+ code.add(CODE_LOOKUPU|RET_STR);
+ done:
+ delete[] lookup;
+ break;
+ }
+ }
+ return true;
}
static void compileblock(vector<uint> &code, const char *&p, int wordtype)
{
- const char *line = p, *start = p;
- int concs = 0;
- for(int brak = 1; brak;)
- {
- p += strcspn(p, "@\"/[]\0");
- int c = *p++;
- switch(c)
- {
- case '\0':
- debugcodeline(line, "missing \"]\"");
- p--;
- goto done;
- case '\"':
- p = parsestring(p);
- if(*p=='\"') p++;
- break;
- case '/':
- if(*p=='/') p += strcspn(p, "\n\0");
- break;
- case '[': brak++; break;
- case ']': brak--; break;
- case '@':
- {
- const char *esc = p;
- while(*p == '@') p++;
- int level = p - (esc - 1);
- if(brak > level) continue;
- else if(brak < level) debugcodeline(line, "too many @s");
- if(!concs) code.add(CODE_ENTER);
- if(concs + 2 > MAXARGS)
- {
- code.add(CODE_CONCW|RET_STR|(concs<<8));
- concs = 1;
- }
- if(compileblockstr(code, start, esc-1, true)) concs++;
- if(compileblocksub(code, p)) concs++;
- if(!concs) code.pop();
- else start = p;
- break;
- }
- }
- }
+ const char *line = p, *start = p;
+ int concs = 0;
+ for(int brak = 1; brak;)
+ {
+ p += strcspn(p, "@\"/[]\0");
+ int c = *p++;
+ switch(c)
+ {
+ case '\0':
+ debugcodeline(line, "missing \"]\"");
+ p--;
+ goto done;
+ case '\"':
+ p = parsestring(p);
+ if(*p=='\"') p++;
+ break;
+ case '/':
+ if(*p=='/') p += strcspn(p, "\n\0");
+ break;
+ case '[': brak++; break;
+ case ']': brak--; break;
+ case '@':
+ {
+ const char *esc = p;
+ while(*p == '@') p++;
+ int level = p - (esc - 1);
+ if(brak > level) continue;
+ else if(brak < level) debugcodeline(line, "too many @s");
+ if(!concs) code.add(CODE_ENTER);
+ if(concs + 2 > MAXARGS)
+ {
+ code.add(CODE_CONCW|RET_STR|(concs<<8));
+ concs = 1;
+ }
+ if(compileblockstr(code, start, esc-1, true)) concs++;
+ if(compileblocksub(code, p)) concs++;
+ if(!concs) code.pop();
+ else start = p;
+ break;
+ }
+ }
+ }
done:
- if(p-1 > start)
- {
- if(!concs) switch(wordtype)
- {
- case VAL_CODE:
- {
- p = start;
- int inst = code.length();
- code.add(CODE_BLOCK);
- code.add(CODE_OFFSET|((inst+2)<<8));
- compilestatements(code, p, VAL_ANY, ']');
- code.add(CODE_EXIT);
- code[inst] |= uint(code.length() - (inst + 1))<<8;
- return;
- }
- case VAL_IDENT:
- {
- char *name = newstring(start, p-1-start);
- compileident(code, name);
- delete[] name;
- return;
- }
- }
- compileblockstr(code, start, p-1, concs > 0);
- if(concs > 1) concs++;
- }
- if(concs)
- {
- code.add(CODE_CONCM|(wordtype < VAL_ANY ? wordtype<<CODE_RET : RET_STR)|(concs<<8));
- code.add(CODE_EXIT|(wordtype < VAL_ANY ? wordtype<<CODE_RET : RET_STR));
- }
- switch(wordtype)
- {
- case VAL_CODE: if(!concs && p-1 <= start) compileblock(code); else code.add(CODE_COMPILE); break;
- case VAL_IDENT: if(!concs && p-1 <= start) compileident(code); else code.add(CODE_IDENTU); break;
- case VAL_STR: case VAL_NULL: case VAL_ANY:
- if(!concs && p-1 <= start) compilestr(code);
- break;
- default:
- if(!concs)
- {
- if(p-1 <= start) compileval(code, wordtype, NULL, 0);
- else code.add(CODE_FORCE|(wordtype<<CODE_RET));
- }
- break;
- }
+ if(p-1 > start)
+ {
+ if(!concs) switch(wordtype)
+ {
+ case VAL_CODE:
+ {
+ p = start;
+ int inst = code.length();
+ code.add(CODE_BLOCK);
+ code.add(CODE_OFFSET|((inst+2)<<8));
+ compilestatements(code, p, VAL_ANY, ']');
+ code.add(CODE_EXIT);
+ code[inst] |= uint(code.length() - (inst + 1))<<8;
+ return;
+ }
+ case VAL_IDENT:
+ {
+ char *name = newstring(start, p-1-start);
+ compileident(code, name);
+ delete[] name;
+ return;
+ }
+ }
+ compileblockstr(code, start, p-1, concs > 0);
+ if(concs > 1) concs++;
+ }
+ if(concs)
+ {
+ code.add(CODE_CONCM|(wordtype < VAL_ANY ? wordtype<<CODE_RET : RET_STR)|(concs<<8));
+ code.add(CODE_EXIT|(wordtype < VAL_ANY ? wordtype<<CODE_RET : RET_STR));
+ }
+ switch(wordtype)
+ {
+ case VAL_CODE: if(!concs && p-1 <= start) compileblock(code); else code.add(CODE_COMPILE); break;
+ case VAL_IDENT: if(!concs && p-1 <= start) compileident(code); else code.add(CODE_IDENTU); break;
+ case VAL_STR: case VAL_NULL: case VAL_ANY:
+ if(!concs && p-1 <= start) compilestr(code);
+ break;
+ default:
+ if(!concs)
+ {
+ if(p-1 <= start) compileval(code, wordtype, NULL, 0);
+ else code.add(CODE_FORCE|(wordtype<<CODE_RET));
+ }
+ break;
+ }
}
static bool compileword(vector<uint> &code, const char *&p, int wordtype, char *&word, int &wordlen)
{
- skipcomments(p);
- switch(*p)
- {
- case '\"': word = cutstring(p, wordlen); break;
- case '$': compilelookup(code, p, wordtype); return true;
- case '(':
- p++;
- code.add(CODE_ENTER);
- compilestatements(code, p, VAL_ANY, ')');
- code.add(CODE_EXIT|(wordtype < VAL_ANY ? wordtype<<CODE_RET : 0));
- switch(wordtype)
- {
- case VAL_CODE: code.add(CODE_COMPILE); break;
- case VAL_IDENT: code.add(CODE_IDENTU); break;
- }
- return true;
- case '[':
- p++;
- compileblock(code, p, wordtype);
- return true;
- default: word = cutword(p, wordlen); break;
- }
- return word!=NULL;
+ skipcomments(p);
+ switch(*p)
+ {
+ case '\"': word = cutstring(p, wordlen); break;
+ case '$': compilelookup(code, p, wordtype); return true;
+ case '(':
+ p++;
+ code.add(CODE_ENTER);
+ compilestatements(code, p, VAL_ANY, ')');
+ code.add(CODE_EXIT|(wordtype < VAL_ANY ? wordtype<<CODE_RET : 0));
+ switch(wordtype)
+ {
+ case VAL_CODE: code.add(CODE_COMPILE); break;
+ case VAL_IDENT: code.add(CODE_IDENTU); break;
+ }
+ return true;
+ case '[':
+ p++;
+ compileblock(code, p, wordtype);
+ return true;
+ default: word = cutword(p, wordlen); break;
+ }
+ return word!=NULL;
}
static inline bool compilearg(vector<uint> &code, const char *&p, int wordtype)
{
- char *word = NULL;
- int wordlen = 0;
- bool more = compileword(code, p, wordtype, word, wordlen);
- if(!more) return false;
- if(word)
- {
- compileval(code, wordtype, word, wordlen);
- delete[] word;
- }
- return true;
+ char *word = NULL;
+ int wordlen = 0;
+ bool more = compileword(code, p, wordtype, word, wordlen);
+ if(!more) return false;
+ if(word)
+ {
+ compileval(code, wordtype, word, wordlen);
+ delete[] word;
+ }
+ return true;
}
static void compilestatements(vector<uint> &code, const char *&p, int rettype, int brak)
{
- const char *line = p;
- char *idname = NULL;
- int idlen = 0;
- ident *id = NULL;
- int numargs = 0;
- for(;;)
- {
- skipcomments(p);
- idname = NULL;
- bool more = compileword(code, p, VAL_ANY, idname, idlen);
- if(!more) goto endstatement;
- skipcomments(p);
- if(p[0] == '=') switch(p[1])
- {
- case '/':
- if(p[2] != '/') break;
- [[fallthrough]];
- case ';':
- [[fallthrough]];
- case ' ':
- [[fallthrough]];
- case '\t':
- [[fallthrough]];
- case '\r':
- [[fallthrough]];
- case '\n':
- [[fallthrough]];
- case '\0':
- p++;
- if(idname)
- {
- id = newident(idname, IDF_UNKNOWN);
- if(id) switch(id->type)
- {
- case ID_ALIAS:
- if(!(more = compilearg(code, p, VAL_ANY))) compilestr(code);
- code.add((id->index < MAXARGS ? CODE_ALIASARG : CODE_ALIAS)|(id->index<<8));
- goto endcommand;
- case ID_VAR:
- if(!(more = compilearg(code, p, VAL_INT))) compileint(code);
- code.add(CODE_IVAR1|(id->index<<8));
- goto endcommand;
- case ID_FVAR:
- if(!(more = compilearg(code, p, VAL_FLOAT))) compilefloat(code);
- code.add(CODE_FVAR1|(id->index<<8));
- goto endcommand;
- case ID_SVAR:
- if(!(more = compilearg(code, p, VAL_STR))) compilestr(code);
- code.add(CODE_SVAR1|(id->index<<8));
- goto endcommand;
- case ID_COMMAND:
- if(id->flags&IDF_EMUVAR) goto compilecommand;
- break;
- }
- compilestr(code, idname, idlen, true);
- delete[] idname;
- }
- if(!(more = compilearg(code, p, VAL_ANY))) compilestr(code);
- code.add(CODE_ALIASU);
- goto endstatement;
- }
- compilecommand:
- numargs = 0;
- if(!idname)
- {
- noid:
- while(numargs < MAXARGS && (more = compilearg(code, p, VAL_ANY))) numargs++;
- code.add(CODE_CALLU);
- }
- else
- {
- id = idents.access(idname);
- if(!id)
- {
- if(!checknumber(idname)) { compilestr(code, idname, idlen); delete[] idname; goto noid; }
- char *end = idname;
- int val = int(strtoul(idname, &end, 0));
- if(*end) compilestr(code, idname, idlen);
- else compileint(code, val);
- code.add(CODE_RESULT);
- }
- else switch(id->type)
- {
- case ID_ALIAS:
- while(numargs < MAXARGS && (more = compilearg(code, p, VAL_ANY))) numargs++;
- code.add((id->index < MAXARGS ? CODE_CALLARG : CODE_CALL)|(id->index<<8));
- break;
- case ID_COMMAND:
- {
- int comtype = CODE_COM, fakeargs = 0;
- bool rep = false;
- for(const char *fmt = id->args; *fmt; fmt++) switch(*fmt)
- {
- case 's':
- if(more) more = compilearg(code, p, VAL_STR);
- if(!more)
- {
- if(rep) break;
- compilestr(code, NULL, 0, true);
- fakeargs++;
- }
- else if(!fmt[1])
- {
- int numconc = 0;
- while(numargs + numconc < MAXARGS && (more = compilearg(code, p, VAL_STR))) numconc++;
- if(numconc > 0) code.add(CODE_CONC|RET_STR|((numconc+1)<<8));
- }
- numargs++;
- break;
- case 'i': if(more) more = compilearg(code, p, VAL_INT); if(!more) { if(rep) break; compileint(code); fakeargs++; } numargs++; break;
- case 'b': if(more) more = compilearg(code, p, VAL_INT); if(!more) { if(rep) break; compileint(code, INT_MIN); fakeargs++; } numargs++; break;
- case 'f': if(more) more = compilearg(code, p, VAL_FLOAT); if(!more) { if(rep) break; compilefloat(code); fakeargs++; } numargs++; break;
- case 't': if(more) more = compilearg(code, p, VAL_ANY); if(!more) { if(rep) break; compilenull(code); fakeargs++; } numargs++; break;
- case 'e': if(more) more = compilearg(code, p, VAL_CODE); if(!more) { if(rep) break; compileblock(code); fakeargs++; } numargs++; break;
- case 'r': if(more) more = compilearg(code, p, VAL_IDENT); if(!more) { if(rep) break; compileident(code); fakeargs++; } numargs++; break;
- case '$': compileident(code, id); numargs++; break;
- case 'N': compileint(code, numargs-fakeargs); numargs++; break;
+ const char *line = p;
+ char *idname = NULL;
+ int idlen = 0;
+ ident *id = NULL;
+ int numargs = 0;
+ for(;;)
+ {
+ skipcomments(p);
+ idname = NULL;
+ bool more = compileword(code, p, VAL_ANY, idname, idlen);
+ if(!more) goto endstatement;
+ skipcomments(p);
+ if(p[0] == '=') switch(p[1])
+ {
+ case '/':
+ if(p[2] != '/') break;
+ [[fallthrough]];
+ case ';':
+ [[fallthrough]];
+ case ' ':
+ [[fallthrough]];
+ case '\t':
+ [[fallthrough]];
+ case '\r':
+ [[fallthrough]];
+ case '\n':
+ [[fallthrough]];
+ case '\0':
+ p++;
+ if(idname)
+ {
+ id = newident(idname, IDF_UNKNOWN);
+ if(id) switch(id->type)
+ {
+ case ID_ALIAS:
+ if(!(more = compilearg(code, p, VAL_ANY))) compilestr(code);
+ code.add((id->index < MAXARGS ? CODE_ALIASARG : CODE_ALIAS)|(id->index<<8));
+ goto endcommand;
+ case ID_VAR:
+ if(!(more = compilearg(code, p, VAL_INT))) compileint(code);
+ code.add(CODE_IVAR1|(id->index<<8));
+ goto endcommand;
+ case ID_FVAR:
+ if(!(more = compilearg(code, p, VAL_FLOAT))) compilefloat(code);
+ code.add(CODE_FVAR1|(id->index<<8));
+ goto endcommand;
+ case ID_SVAR:
+ if(!(more = compilearg(code, p, VAL_STR))) compilestr(code);
+ code.add(CODE_SVAR1|(id->index<<8));
+ goto endcommand;
+ case ID_COMMAND:
+ if(id->flags&IDF_EMUVAR) goto compilecommand;
+ break;
+ }
+ compilestr(code, idname, idlen, true);
+ delete[] idname;
+ }
+ if(!(more = compilearg(code, p, VAL_ANY))) compilestr(code);
+ code.add(CODE_ALIASU);
+ goto endstatement;
+ }
+ compilecommand:
+ numargs = 0;
+ if(!idname)
+ {
+ noid:
+ while(numargs < MAXARGS && (more = compilearg(code, p, VAL_ANY))) numargs++;
+ code.add(CODE_CALLU);
+ }
+ else
+ {
+ id = idents.access(idname);
+ if(!id)
+ {
+ if(!checknumber(idname)) { compilestr(code, idname, idlen); delete[] idname; goto noid; }
+ char *end = idname;
+ int val = int(strtoul(idname, &end, 0));
+ if(*end) compilestr(code, idname, idlen);
+ else compileint(code, val);
+ code.add(CODE_RESULT);
+ }
+ else switch(id->type)
+ {
+ case ID_ALIAS:
+ while(numargs < MAXARGS && (more = compilearg(code, p, VAL_ANY))) numargs++;
+ code.add((id->index < MAXARGS ? CODE_CALLARG : CODE_CALL)|(id->index<<8));
+ break;
+ case ID_COMMAND:
+ {
+ int comtype = CODE_COM, fakeargs = 0;
+ bool rep = false;
+ for(const char *fmt = id->args; *fmt; fmt++) switch(*fmt)
+ {
+ case 's':
+ if(more) more = compilearg(code, p, VAL_STR);
+ if(!more)
+ {
+ if(rep) break;
+ compilestr(code, NULL, 0, true);
+ fakeargs++;
+ }
+ else if(!fmt[1])
+ {
+ int numconc = 0;
+ while(numargs + numconc < MAXARGS && (more = compilearg(code, p, VAL_STR))) numconc++;
+ if(numconc > 0) code.add(CODE_CONC|RET_STR|((numconc+1)<<8));
+ }
+ numargs++;
+ break;
+ case 'i': if(more) more = compilearg(code, p, VAL_INT); if(!more) { if(rep) break; compileint(code); fakeargs++; } numargs++; break;
+ case 'b': if(more) more = compilearg(code, p, VAL_INT); if(!more) { if(rep) break; compileint(code, INT_MIN); fakeargs++; } numargs++; break;
+ case 'f': if(more) more = compilearg(code, p, VAL_FLOAT); if(!more) { if(rep) break; compilefloat(code); fakeargs++; } numargs++; break;
+ case 't': if(more) more = compilearg(code, p, VAL_ANY); if(!more) { if(rep) break; compilenull(code); fakeargs++; } numargs++; break;
+ case 'e': if(more) more = compilearg(code, p, VAL_CODE); if(!more) { if(rep) break; compileblock(code); fakeargs++; } numargs++; break;
+ case 'r': if(more) more = compilearg(code, p, VAL_IDENT); if(!more) { if(rep) break; compileident(code); fakeargs++; } numargs++; break;
+ case '$': compileident(code, id); numargs++; break;
+ case 'N': compileint(code, numargs-fakeargs); numargs++; break;
#ifndef STANDALONE
- case 'D': comtype = CODE_COMD; numargs++; break;
+ case 'D': comtype = CODE_COMD; numargs++; break;
#endif
- case 'C': comtype = CODE_COMC; if(more) while(numargs < MAXARGS && (more = compilearg(code, p, VAL_ANY))) numargs++; numargs = 1; goto endfmt;
- case 'V': comtype = CODE_COMV; if(more) while(numargs < MAXARGS && (more = compilearg(code, p, VAL_ANY))) numargs++; numargs = 2; goto endfmt;
- case '1': case '2': case '3': case '4':
- if(more && numargs < MAXARGS)
- {
- int numrep = *fmt-'0'+1;
- fmt -= numrep;
- rep = true;
- }
- else for(; numargs > MAXARGS; numargs--) code.add(CODE_POP);
- break;
- }
- endfmt:
- code.add(comtype|(rettype < VAL_ANY ? rettype<<CODE_RET : 0)|(id->index<<8));
- break;
- }
- case ID_LOCAL:
- if(more) while(numargs < MAXARGS && (more = compilearg(code, p, VAL_IDENT))) numargs++;
- if(more) while((more = compilearg(code, p, VAL_ANY))) code.add(CODE_POP);
- code.add(CODE_LOCAL);
- break;
- case ID_VAR:
- if(!(more = compilearg(code, p, VAL_INT))) code.add(CODE_PRINT|(id->index<<8));
- else if(!(id->flags&IDF_HEX) || !(more = compilearg(code, p, VAL_INT))) code.add(CODE_IVAR1|(id->index<<8));
- else if(!(more = compilearg(code, p, VAL_INT))) code.add(CODE_IVAR2|(id->index<<8));
- else code.add(CODE_IVAR3|(id->index<<8));
- break;
- case ID_FVAR:
- if(!(more = compilearg(code, p, VAL_FLOAT))) code.add(CODE_PRINT|(id->index<<8));
- else code.add(CODE_FVAR1|(id->index<<8));
- break;
- case ID_SVAR:
- if(!(more = compilearg(code, p, VAL_STR))) code.add(CODE_PRINT|(id->index<<8));
- else
- {
- int numconc = 0;
- while(numconc+1 < MAXARGS && (more = compilearg(code, p, VAL_ANY))) numconc++;
- if(numconc > 0) code.add(CODE_CONC|RET_STR|((numconc+1)<<8));
- code.add(CODE_SVAR1|(id->index<<8));
- }
- break;
- }
- endcommand:
- delete[] idname;
- }
- endstatement:
- if(more) while(compilearg(code, p, VAL_ANY)) code.add(CODE_POP);
- p += strcspn(p, ")];/\n\0");
- int c = *p++;
- switch(c)
- {
- case '\0':
- if(c != brak) debugcodeline(line, "missing \"%c\"", brak);
- p--;
- return;
-
- case ')':
- case ']':
- if(c == brak) return;
- debugcodeline(line, "unexpected \"%c\"", c);
- break;
-
- case '/':
- if(*p == '/') p += strcspn(p, "\n\0");
- goto endstatement;
- }
- }
+ case 'C': comtype = CODE_COMC; if(more) while(numargs < MAXARGS && (more = compilearg(code, p, VAL_ANY))) numargs++; numargs = 1; goto endfmt;
+ case 'V': comtype = CODE_COMV; if(more) while(numargs < MAXARGS && (more = compilearg(code, p, VAL_ANY))) numargs++; numargs = 2; goto endfmt;
+ case '1': case '2': case '3': case '4':
+ if(more && numargs < MAXARGS)
+ {
+ int numrep = *fmt-'0'+1;
+ fmt -= numrep;
+ rep = true;
+ }
+ else for(; numargs > MAXARGS; numargs--) code.add(CODE_POP);
+ break;
+ }
+ endfmt:
+ code.add(comtype|(rettype < VAL_ANY ? rettype<<CODE_RET : 0)|(id->index<<8));
+ break;
+ }
+ case ID_LOCAL:
+ if(more) while(numargs < MAXARGS && (more = compilearg(code, p, VAL_IDENT))) numargs++;
+ if(more) while((more = compilearg(code, p, VAL_ANY))) code.add(CODE_POP);
+ code.add(CODE_LOCAL);
+ break;
+ case ID_VAR:
+ if(!(more = compilearg(code, p, VAL_INT))) code.add(CODE_PRINT|(id->index<<8));
+ else if(!(id->flags&IDF_HEX) || !(more = compilearg(code, p, VAL_INT))) code.add(CODE_IVAR1|(id->index<<8));
+ else if(!(more = compilearg(code, p, VAL_INT))) code.add(CODE_IVAR2|(id->index<<8));
+ else code.add(CODE_IVAR3|(id->index<<8));
+ break;
+ case ID_FVAR:
+ if(!(more = compilearg(code, p, VAL_FLOAT))) code.add(CODE_PRINT|(id->index<<8));
+ else code.add(CODE_FVAR1|(id->index<<8));
+ break;
+ case ID_SVAR:
+ if(!(more = compilearg(code, p, VAL_STR))) code.add(CODE_PRINT|(id->index<<8));
+ else
+ {
+ int numconc = 0;
+ while(numconc+1 < MAXARGS && (more = compilearg(code, p, VAL_ANY))) numconc++;
+ if(numconc > 0) code.add(CODE_CONC|RET_STR|((numconc+1)<<8));
+ code.add(CODE_SVAR1|(id->index<<8));
+ }
+ break;
+ }
+ endcommand:
+ delete[] idname;
+ }
+ endstatement:
+ if(more) while(compilearg(code, p, VAL_ANY)) code.add(CODE_POP);
+ p += strcspn(p, ")];/\n\0");
+ int c = *p++;
+ switch(c)
+ {
+ case '\0':
+ if(c != brak) debugcodeline(line, "missing \"%c\"", brak);
+ p--;
+ return;
+
+ case ')':
+ case ']':
+ if(c == brak) return;
+ debugcodeline(line, "unexpected \"%c\"", c);
+ break;
+
+ case '/':
+ if(*p == '/') p += strcspn(p, "\n\0");
+ goto endstatement;
+ }
+ }
}
static void compilemain(vector<uint> &code, const char *p, int rettype = VAL_ANY)
{
- code.add(CODE_START);
- compilestatements(code, p, VAL_ANY);
- code.add(CODE_EXIT|(rettype < VAL_ANY ? rettype<<CODE_RET : 0));
+ code.add(CODE_START);
+ compilestatements(code, p, VAL_ANY);
+ code.add(CODE_EXIT|(rettype < VAL_ANY ? rettype<<CODE_RET : 0));
}
uint *compilecode(const char *p)
{
- vector<uint> buf;
- buf.reserve(64);
- compilemain(buf, p);
- uint *code = new uint[buf.length()];
- memcpy(code, buf.getbuf(), buf.length()*sizeof(uint));
- code[0] += 0x100;
- return code;
+ vector<uint> buf;
+ buf.reserve(64);
+ compilemain(buf, p);
+ uint *code = new uint[buf.length()];
+ memcpy(code, buf.getbuf(), buf.length()*sizeof(uint));
+ code[0] += 0x100;
+ return code;
}
void keepcode(uint *code)
{
- if(!code) return;
- switch(*code&CODE_OP_MASK)
- {
- case CODE_START:
- *code += 0x100;
- return;
- }
- switch(code[-1]&CODE_OP_MASK)
- {
- case CODE_START:
- code[-1] += 0x100;
- break;
- case CODE_OFFSET:
- code -= int(code[-1]>>8);
- *code += 0x100;
- break;
- }
+ if(!code) return;
+ switch(*code&CODE_OP_MASK)
+ {
+ case CODE_START:
+ *code += 0x100;
+ return;
+ }
+ switch(code[-1]&CODE_OP_MASK)
+ {
+ case CODE_START:
+ code[-1] += 0x100;
+ break;
+ case CODE_OFFSET:
+ code -= int(code[-1]>>8);
+ *code += 0x100;
+ break;
+ }
}
void freecode(uint *code)
{
- if(!code) return;
- switch(*code&CODE_OP_MASK)
- {
- case CODE_START:
- *code -= 0x100;
- if(int(*code) < 0x100) delete[] code;
- return;
- }
- switch(code[-1]&CODE_OP_MASK)
- {
- case CODE_START:
- code[-1] -= 0x100;
- if(int(code[-1]) < 0x100) delete[] &code[-1];
- break;
- case CODE_OFFSET:
- code -= int(code[-1]>>8);
- *code -= 0x100;
- if(int(*code) < 0x100) delete[] code;
- break;
- }
+ if(!code) return;
+ switch(*code&CODE_OP_MASK)
+ {
+ case CODE_START:
+ *code -= 0x100;
+ if(int(*code) < 0x100) delete[] code;
+ return;
+ }
+ switch(code[-1]&CODE_OP_MASK)
+ {
+ case CODE_START:
+ code[-1] -= 0x100;
+ if(int(code[-1]) < 0x100) delete[] &code[-1];
+ break;
+ case CODE_OFFSET:
+ code -= int(code[-1]>>8);
+ *code -= 0x100;
+ if(int(*code) < 0x100) delete[] code;
+ break;
+ }
}
void printvar(ident *id, int i)
{
- if(i < 0) conoutf(CON_INFO, id->index, "%s = %d", id->name, i);
- else if(id->flags&IDF_HEX && id->maxval==0xFFFFFF)
- conoutf(CON_INFO, id->index, "%s = 0x%.6X (%d, %d, %d)", id->name, i, (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF);
- else
- conoutf(CON_INFO, id->index, id->flags&IDF_HEX ? "%s = 0x%X" : "%s = %d", id->name, i);
+ if(i < 0) conoutf(CON_INFO, id->index, "%s = %d", id->name, i);
+ else if(id->flags&IDF_HEX && id->maxval==0xFFFFFF)
+ conoutf(CON_INFO, id->index, "%s = 0x%.6X (%d, %d, %d)", id->name, i, (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF);
+ else
+ conoutf(CON_INFO, id->index, id->flags&IDF_HEX ? "%s = 0x%X" : "%s = %d", id->name, i);
}
void printfvar(ident *id, float f)
{
- conoutf(CON_INFO, id->index, "%s = %s", id->name, floatstr(f));
+ conoutf(CON_INFO, id->index, "%s = %s", id->name, floatstr(f));
}
void printsvar(ident *id, const char *s)
{
- conoutf(CON_INFO, id->index, strchr(s, '"') ? "%s = [%s]" : "%s = \"%s\"", id->name, s);
+ conoutf(CON_INFO, id->index, strchr(s, '"') ? "%s = [%s]" : "%s = \"%s\"", id->name, s);
}
template <class V>
static void printvar(ident *id, int type, V &val)
{
- switch(type)
- {
- case VAL_INT: printvar(id, val.getint()); break;
- case VAL_FLOAT: printfvar(id, val.getfloat()); break;
- default: printsvar(id, val.getstr()); break;
- }
+ switch(type)
+ {
+ case VAL_INT: printvar(id, val.getint()); break;
+ case VAL_FLOAT: printfvar(id, val.getfloat()); break;
+ default: printsvar(id, val.getstr()); break;
+ }
}
void printvar(ident *id)
{
- switch(id->type)
- {
- case ID_VAR: printvar(id, *id->storage.i); break;
- case ID_FVAR: printfvar(id, *id->storage.f); break;
- case ID_SVAR: printsvar(id, *id->storage.s); break;
- case ID_ALIAS: printvar(id, id->valtype, *id); break;
- case ID_COMMAND:
- if(id->flags&IDF_EMUVAR)
- {
- tagval result;
- executeret(id, NULL, 0, true, result);
- printvar(id, result.type, result);
- freearg(result);
- }
- break;
- }
+ switch(id->type)
+ {
+ case ID_VAR: printvar(id, *id->storage.i); break;
+ case ID_FVAR: printfvar(id, *id->storage.f); break;
+ case ID_SVAR: printsvar(id, *id->storage.s); break;
+ case ID_ALIAS: printvar(id, id->valtype, *id); break;
+ case ID_COMMAND:
+ if(id->flags&IDF_EMUVAR)
+ {
+ tagval result;
+ executeret(id, NULL, 0, true, result);
+ printvar(id, result.type, result);
+ freearg(result);
+ }
+ break;
+ }
}
ICOMMAND(printvar, "r", (ident *id), printvar(id));
@@ -1671,102 +1671,102 @@ typedef void (__cdecl *comfunv)(tagval *, int);
static const uint *skipcode(const uint *code, tagval &result)
{
- int depth = 0;
- for(;;)
- {
- uint op = *code++;
- switch(op&0xFF)
- {
- case CODE_MACRO:
- case CODE_VAL|RET_STR:
- {
- uint len = op>>8;
- code += len/sizeof(uint) + 1;
- continue;
- }
- case CODE_BLOCK:
- {
- uint len = op>>8;
- code += len;
- continue;
- }
- case CODE_ENTER:
- ++depth;
- continue;
- case CODE_EXIT|RET_NULL: case CODE_EXIT|RET_STR: case CODE_EXIT|RET_INT: case CODE_EXIT|RET_FLOAT:
- if(depth <= 0)
- {
- forcearg(result, op&CODE_RET_MASK);
- return code;
- }
- --depth;
- continue;
- }
- }
+ int depth = 0;
+ for(;;)
+ {
+ uint op = *code++;
+ switch(op&0xFF)
+ {
+ case CODE_MACRO:
+ case CODE_VAL|RET_STR:
+ {
+ uint len = op>>8;
+ code += len/sizeof(uint) + 1;
+ continue;
+ }
+ case CODE_BLOCK:
+ {
+ uint len = op>>8;
+ code += len;
+ continue;
+ }
+ case CODE_ENTER:
+ ++depth;
+ continue;
+ case CODE_EXIT|RET_NULL: case CODE_EXIT|RET_STR: case CODE_EXIT|RET_INT: case CODE_EXIT|RET_FLOAT:
+ if(depth <= 0)
+ {
+ forcearg(result, op&CODE_RET_MASK);
+ return code;
+ }
+ --depth;
+ continue;
+ }
+ }
}
static inline void callcommand(ident *id, tagval *args, int numargs, bool lookup = false)
{
- int i = -1, fakeargs = 0;
- bool rep = false;
- for(const char *fmt = id->args; *fmt; fmt++) switch(*fmt)
- {
- case 'i': if(++i >= numargs) { if(rep) break; args[i].setint(0); fakeargs++; } else forceint(args[i]); break;
- case 'b': if(++i >= numargs) { if(rep) break; args[i].setint(INT_MIN); fakeargs++; } else forceint(args[i]); break;
- case 'f': if(++i >= numargs) { if(rep) break; args[i].setfloat(0.0f); fakeargs++; } else forcefloat(args[i]); break;
- case 's': if(++i >= numargs) { if(rep) break; args[i].setstr(newstring("")); fakeargs++; } else forcestr(args[i]); break;
- case 't': if(++i >= numargs) { if(rep) break; args[i].setnull(); fakeargs++; } break;
- case 'e':
- if(++i >= numargs)
- {
- if(rep) break;
- static uint buf[2] = { CODE_START + 0x100, CODE_EXIT };
- args[i].setcode(buf);
- fakeargs++;
- }
- else
- {
- vector<uint> buf;
- buf.reserve(64);
- compilemain(buf, numargs <= i ? "" : args[i].getstr());
- freearg(args[i]);
- args[i].setcode(buf.getbuf()+1);
- buf.disown();
- }
- break;
- case 'r': if(++i >= numargs) { if(rep) break; args[i].setident(dummyident); fakeargs++; } else forceident(args[i]); break;
- case '$': if(++i < numargs) freearg(args[i]); args[i].setident(id); break;
- case 'N': if(++i < numargs) freearg(args[i]); args[i].setint(lookup ? -1 : i-fakeargs); break;
+ int i = -1, fakeargs = 0;
+ bool rep = false;
+ for(const char *fmt = id->args; *fmt; fmt++) switch(*fmt)
+ {
+ case 'i': if(++i >= numargs) { if(rep) break; args[i].setint(0); fakeargs++; } else forceint(args[i]); break;
+ case 'b': if(++i >= numargs) { if(rep) break; args[i].setint(INT_MIN); fakeargs++; } else forceint(args[i]); break;
+ case 'f': if(++i >= numargs) { if(rep) break; args[i].setfloat(0.0f); fakeargs++; } else forcefloat(args[i]); break;
+ case 's': if(++i >= numargs) { if(rep) break; args[i].setstr(newstring("")); fakeargs++; } else forcestr(args[i]); break;
+ case 't': if(++i >= numargs) { if(rep) break; args[i].setnull(); fakeargs++; } break;
+ case 'e':
+ if(++i >= numargs)
+ {
+ if(rep) break;
+ static uint buf[2] = { CODE_START + 0x100, CODE_EXIT };
+ args[i].setcode(buf);
+ fakeargs++;
+ }
+ else
+ {
+ vector<uint> buf;
+ buf.reserve(64);
+ compilemain(buf, numargs <= i ? "" : args[i].getstr());
+ freearg(args[i]);
+ args[i].setcode(buf.getbuf()+1);
+ buf.disown();
+ }
+ break;
+ case 'r': if(++i >= numargs) { if(rep) break; args[i].setident(dummyident); fakeargs++; } else forceident(args[i]); break;
+ case '$': if(++i < numargs) freearg(args[i]); args[i].setident(id); break;
+ case 'N': if(++i < numargs) freearg(args[i]); args[i].setint(lookup ? -1 : i-fakeargs); break;
#ifndef STANDALONE
- case 'D': if(++i < numargs) freearg(args[i]); args[i].setint(addreleaseaction(conc(args, i, true, id->name)) ? 1 : 0); fakeargs++; break;
+ case 'D': if(++i < numargs) freearg(args[i]); args[i].setint(addreleaseaction(conc(args, i, true, id->name)) ? 1 : 0); fakeargs++; break;
#endif
- case 'C': { i = max(i+1, numargs); vector<char> buf; ((comfun1)id->fun)(conc(buf, args, i, true)); goto cleanup; }
- case 'V': i = max(i+1, numargs); ((comfunv)id->fun)(args, i); goto cleanup;
- case '1': case '2': case '3': case '4': if(i+1 < numargs) { fmt -= *fmt-'0'+1; rep = true; } break;
- }
- #define ARG(n) (id->argmask&(1<<n) ? (void *)args[n].s : (void *)&args[n].i)
- #define CALLCOM(n) \
- switch(n) \
- { \
- case 0: ((comfun)id->fun)(); break; \
- case 1: ((comfun1)id->fun)(ARG(0)); break; \
- case 2: ((comfun2)id->fun)(ARG(0), ARG(1)); break; \
- case 3: ((comfun3)id->fun)(ARG(0), ARG(1), ARG(2)); break; \
- case 4: ((comfun4)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3)); break; \
- case 5: ((comfun5)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4)); break; \
- case 6: ((comfun6)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5)); break; \
- case 7: ((comfun7)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6)); break; \
- case 8: ((comfun8)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7)); break; \
- case 9: ((comfun9)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8)); break; \
- case 10: ((comfun10)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9)); break; \
- case 11: ((comfun11)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10)); break; \
- case 12: ((comfun12)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10), ARG(11)); break; \
- }
- ++i;
- CALLCOM(i)
+ case 'C': { i = max(i+1, numargs); vector<char> buf; ((comfun1)id->fun)(conc(buf, args, i, true)); goto cleanup; }
+ case 'V': i = max(i+1, numargs); ((comfunv)id->fun)(args, i); goto cleanup;
+ case '1': case '2': case '3': case '4': if(i+1 < numargs) { fmt -= *fmt-'0'+1; rep = true; } break;
+ }
+ #define ARG(n) (id->argmask&(1<<n) ? (void *)args[n].s : (void *)&args[n].i)
+ #define CALLCOM(n) \
+ switch(n) \
+ { \
+ case 0: ((comfun)id->fun)(); break; \
+ case 1: ((comfun1)id->fun)(ARG(0)); break; \
+ case 2: ((comfun2)id->fun)(ARG(0), ARG(1)); break; \
+ case 3: ((comfun3)id->fun)(ARG(0), ARG(1), ARG(2)); break; \
+ case 4: ((comfun4)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3)); break; \
+ case 5: ((comfun5)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4)); break; \
+ case 6: ((comfun6)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5)); break; \
+ case 7: ((comfun7)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6)); break; \
+ case 8: ((comfun8)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7)); break; \
+ case 9: ((comfun9)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8)); break; \
+ case 10: ((comfun10)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9)); break; \
+ case 11: ((comfun11)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10)); break; \
+ case 12: ((comfun12)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10), ARG(11)); break; \
+ }
+ ++i;
+ CALLCOM(i)
cleanup:
- loopk(i) freearg(args[k]);
- for(; i < numargs; i++) freearg(args[i]);
+ loopk(i) freearg(args[k]);
+ for(; i < numargs; i++) freearg(args[i]);
}
#define MAXRUNDEPTH 255
@@ -1774,725 +1774,725 @@ static int rundepth = 0;
static const uint *runcode(const uint *code, tagval &result)
{
- result.setnull();
- if(rundepth >= MAXRUNDEPTH)
- {
- debugcode("exceeded recursion limit");
- return skipcode(code, result);
- }
- ++rundepth;
- ident *id = NULL;
- int numargs = 0;
- tagval args[MAXARGS+1], *prevret = commandret;
- commandret = &result;
- for(;;)
- {
- uint op = *code++;
- switch(op&0xFF)
- {
- case CODE_START: case CODE_OFFSET: continue;
-
- case CODE_POP:
- freearg(args[--numargs]);
- continue;
- case CODE_ENTER:
- code = runcode(code, args[numargs++]);
- continue;
- case CODE_EXIT|RET_NULL: case CODE_EXIT|RET_STR: case CODE_EXIT|RET_INT: case CODE_EXIT|RET_FLOAT:
- forcearg(result, op&CODE_RET_MASK);
- goto exit;
- case CODE_PRINT:
- printvar(identmap[op>>8]);
- continue;
- case CODE_LOCAL:
- {
- identstack locals[MAXARGS];
- freearg(result);
- loopi(numargs) pushalias(*args[i].id, locals[i]);
- code = runcode(code, result);
- loopi(numargs) popalias(*args[i].id);
- goto exit;
- }
-
- case CODE_MACRO:
- {
- uint len = op>>8;
- args[numargs++].setmacro(code);
- code += len/sizeof(uint) + 1;
- continue;
- }
-
- case CODE_VAL|RET_STR:
- {
- uint len = op>>8;
- args[numargs++].setstr(newstring((const char *)code, len));
- code += len/sizeof(uint) + 1;
- continue;
- }
- case CODE_VALI|RET_STR:
- {
- char s[4] = { char((op>>8)&0xFF), char((op>>16)&0xFF), char((op>>24)&0xFF), '\0' };
- args[numargs++].setstr(newstring(s));
- continue;
- }
- case CODE_VAL|RET_NULL:
- case CODE_VALI|RET_NULL: args[numargs++].setnull(); continue;
- case CODE_VAL|RET_INT: args[numargs++].setint(int(*code++)); continue;
- case CODE_VALI|RET_INT: args[numargs++].setint(int(op)>>8); continue;
- case CODE_VAL|RET_FLOAT: args[numargs++].setfloat(*(const float *)code++); continue;
- case CODE_VALI|RET_FLOAT: args[numargs++].setfloat(float(int(op)>>8)); continue;
-
- case CODE_FORCE|RET_STR: forcestr(args[numargs-1]); continue;
- case CODE_FORCE|RET_INT: forceint(args[numargs-1]); continue;
- case CODE_FORCE|RET_FLOAT: forcefloat(args[numargs-1]); continue;
-
- case CODE_RESULT|RET_NULL: case CODE_RESULT|RET_STR: case CODE_RESULT|RET_INT: case CODE_RESULT|RET_FLOAT:
- litval:
- freearg(result);
- result = args[0];
- forcearg(result, op&CODE_RET_MASK);
- args[0].setnull();
- freeargs(args, numargs, 0);
- continue;
-
- case CODE_BLOCK:
- {
- uint len = op>>8;
- args[numargs++].setcode(code+1);
- code += len;
- continue;
- }
- case CODE_COMPILE:
- {
- tagval &arg = args[numargs-1];
- vector<uint> buf;
- switch(arg.type)
- {
- case VAL_INT: buf.reserve(8); buf.add(CODE_START); compileint(buf, arg.i); buf.add(CODE_RESULT); buf.add(CODE_EXIT); break;
- case VAL_FLOAT: buf.reserve(8); buf.add(CODE_START); compilefloat(buf, arg.f); buf.add(CODE_RESULT); buf.add(CODE_EXIT); break;
- case VAL_STR: case VAL_MACRO: buf.reserve(64); compilemain(buf, arg.s); freearg(arg); break;
- default: buf.reserve(8); buf.add(CODE_START); compilenull(buf); buf.add(CODE_RESULT); buf.add(CODE_EXIT); break;
- }
- arg.setcode(buf.getbuf()+1);
- buf.disown();
- continue;
- }
-
- case CODE_IDENT:
- args[numargs++].setident(identmap[op>>8]);
- continue;
- case CODE_IDENTARG:
- {
- ident *id = identmap[op>>8];
- if(!(aliasstack->usedargs&(1<<id->index)))
- {
- pusharg(*id, nullval, aliasstack->argstack[id->index]);
- aliasstack->usedargs |= 1<<id->index;
- }
- args[numargs++].setident(id);
- continue;
- }
- case CODE_IDENTU:
- {
- tagval &arg = args[numargs-1];
- ident *id = arg.type == VAL_STR || arg.type == VAL_MACRO ? newident(arg.s, IDF_UNKNOWN) : dummyident;
- if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index)))
- {
- pusharg(*id, nullval, aliasstack->argstack[id->index]);
- aliasstack->usedargs |= 1<<id->index;
- }
- freearg(arg);
- arg.setident(id);
- continue;
- }
-
- case CODE_LOOKUPU|RET_STR:
- #define LOOKUPU(aval, sval, ival, fval, nval) { \
- tagval &arg = args[numargs-1]; \
- if(arg.type != VAL_STR && arg.type != VAL_MACRO) continue; \
- id = idents.access(arg.s); \
- if(id) switch(id->type) \
- { \
- case ID_ALIAS: \
- if(id->flags&IDF_UNKNOWN) break; \
- freearg(arg); \
- if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index))) { nval; continue; } \
- aval; \
- continue; \
- case ID_SVAR: freearg(arg); sval; continue; \
- case ID_VAR: freearg(arg); ival; continue; \
- case ID_FVAR: freearg(arg); fval; continue; \
- case ID_COMMAND: \
- { \
- freearg(arg); \
- arg.setnull(); \
- commandret = &arg; \
- tagval buf[MAXARGS]; \
- callcommand(id, buf, 0, true); \
- forcearg(arg, op&CODE_RET_MASK); \
- commandret = &result; \
- continue; \
- } \
- default: freearg(arg); nval; continue; \
- } \
- debugcode("unknown alias lookup: %s", arg.s); \
- freearg(arg); \
- nval; \
- continue; \
- }
- LOOKUPU(arg.setstr(newstring(id->getstr())),
- arg.setstr(newstring(*id->storage.s)),
- arg.setstr(newstring(intstr(*id->storage.i))),
- arg.setstr(newstring(floatstr(*id->storage.f))),
- arg.setstr(newstring("")));
- case CODE_LOOKUP|RET_STR:
- #define LOOKUP(aval) { \
- id = identmap[op>>8]; \
- if(id->flags&IDF_UNKNOWN) debugcode("unknown alias lookup: %s", id->name); \
- aval; \
- continue; \
- }
- LOOKUP(args[numargs++].setstr(newstring(id->getstr())));
- case CODE_LOOKUPARG|RET_STR:
- #define LOOKUPARG(aval, nval) { \
- id = identmap[op>>8]; \
- if(!(aliasstack->usedargs&(1<<id->index))) { nval; continue; } \
- aval; \
- continue; \
- }
- LOOKUPARG(args[numargs++].setstr(newstring(id->getstr())), args[numargs++].setstr(newstring("")));
- case CODE_LOOKUPU|RET_INT:
- LOOKUPU(arg.setint(id->getint()),
- arg.setint(parseint(*id->storage.s)),
- arg.setint(*id->storage.i),
- arg.setint(int(*id->storage.f)),
- arg.setint(0));
- case CODE_LOOKUP|RET_INT:
- LOOKUP(args[numargs++].setint(id->getint()));
- case CODE_LOOKUPARG|RET_INT:
- LOOKUPARG(args[numargs++].setint(id->getint()), args[numargs++].setint(0));
- case CODE_LOOKUPU|RET_FLOAT:
- LOOKUPU(arg.setfloat(id->getfloat()),
- arg.setfloat(parsefloat(*id->storage.s)),
- arg.setfloat(float(*id->storage.i)),
- arg.setfloat(*id->storage.f),
- arg.setfloat(0.0f));
- case CODE_LOOKUP|RET_FLOAT:
- LOOKUP(args[numargs++].setfloat(id->getfloat()));
- case CODE_LOOKUPARG|RET_FLOAT:
- LOOKUPARG(args[numargs++].setfloat(id->getfloat()), args[numargs++].setfloat(0.0f));
- case CODE_LOOKUPU|RET_NULL:
- LOOKUPU(id->getval(arg),
- arg.setstr(newstring(*id->storage.s)),
- arg.setint(*id->storage.i),
- arg.setfloat(*id->storage.f),
- arg.setnull());
- case CODE_LOOKUP|RET_NULL:
- LOOKUP(id->getval(args[numargs++]));
- case CODE_LOOKUPARG|RET_NULL:
- LOOKUPARG(id->getval(args[numargs++]), args[numargs++].setnull());
-
- case CODE_SVAR|RET_STR: case CODE_SVAR|RET_NULL: args[numargs++].setstr(newstring(*identmap[op>>8]->storage.s)); continue;
- case CODE_SVAR|RET_INT: args[numargs++].setint(parseint(*identmap[op>>8]->storage.s)); continue;
- case CODE_SVAR|RET_FLOAT: args[numargs++].setfloat(parsefloat(*identmap[op>>8]->storage.s)); continue;
- case CODE_SVAR1: setsvarchecked(identmap[op>>8], args[0].s); freeargs(args, numargs, 0); continue;
-
- case CODE_IVAR|RET_INT: case CODE_IVAR|RET_NULL: args[numargs++].setint(*identmap[op>>8]->storage.i); continue;
- case CODE_IVAR|RET_STR: args[numargs++].setstr(newstring(intstr(*identmap[op>>8]->storage.i))); continue;
- case CODE_IVAR|RET_FLOAT: args[numargs++].setfloat(float(*identmap[op>>8]->storage.i)); continue;
- case CODE_IVAR1: setvarchecked(identmap[op>>8], args[0].i); numargs = 0; continue;
- case CODE_IVAR2: setvarchecked(identmap[op>>8], (args[0].i<<16)|(args[1].i<<8)); numargs = 0; continue;
- case CODE_IVAR3: setvarchecked(identmap[op>>8], (args[0].i<<16)|(args[1].i<<8)|args[2].i); numargs = 0; continue;
-
- case CODE_FVAR|RET_FLOAT: case CODE_FVAR|RET_NULL: args[numargs++].setfloat(*identmap[op>>8]->storage.f); continue;
- case CODE_FVAR|RET_STR: args[numargs++].setstr(newstring(floatstr(*identmap[op>>8]->storage.f))); continue;
- case CODE_FVAR|RET_INT: args[numargs++].setint(int(*identmap[op>>8]->storage.f)); continue;
- case CODE_FVAR1: setfvarchecked(identmap[op>>8], args[0].f); numargs = 0; continue;
-
- case CODE_COM|RET_NULL: case CODE_COM|RET_STR: case CODE_COM|RET_FLOAT: case CODE_COM|RET_INT:
- id = identmap[op>>8];
+ result.setnull();
+ if(rundepth >= MAXRUNDEPTH)
+ {
+ debugcode("exceeded recursion limit");
+ return skipcode(code, result);
+ }
+ ++rundepth;
+ ident *id = NULL;
+ int numargs = 0;
+ tagval args[MAXARGS+1], *prevret = commandret;
+ commandret = &result;
+ for(;;)
+ {
+ uint op = *code++;
+ switch(op&0xFF)
+ {
+ case CODE_START: case CODE_OFFSET: continue;
+
+ case CODE_POP:
+ freearg(args[--numargs]);
+ continue;
+ case CODE_ENTER:
+ code = runcode(code, args[numargs++]);
+ continue;
+ case CODE_EXIT|RET_NULL: case CODE_EXIT|RET_STR: case CODE_EXIT|RET_INT: case CODE_EXIT|RET_FLOAT:
+ forcearg(result, op&CODE_RET_MASK);
+ goto exit;
+ case CODE_PRINT:
+ printvar(identmap[op>>8]);
+ continue;
+ case CODE_LOCAL:
+ {
+ identstack locals[MAXARGS];
+ freearg(result);
+ loopi(numargs) pushalias(*args[i].id, locals[i]);
+ code = runcode(code, result);
+ loopi(numargs) popalias(*args[i].id);
+ goto exit;
+ }
+
+ case CODE_MACRO:
+ {
+ uint len = op>>8;
+ args[numargs++].setmacro(code);
+ code += len/sizeof(uint) + 1;
+ continue;
+ }
+
+ case CODE_VAL|RET_STR:
+ {
+ uint len = op>>8;
+ args[numargs++].setstr(newstring((const char *)code, len));
+ code += len/sizeof(uint) + 1;
+ continue;
+ }
+ case CODE_VALI|RET_STR:
+ {
+ char s[4] = { char((op>>8)&0xFF), char((op>>16)&0xFF), char((op>>24)&0xFF), '\0' };
+ args[numargs++].setstr(newstring(s));
+ continue;
+ }
+ case CODE_VAL|RET_NULL:
+ case CODE_VALI|RET_NULL: args[numargs++].setnull(); continue;
+ case CODE_VAL|RET_INT: args[numargs++].setint(int(*code++)); continue;
+ case CODE_VALI|RET_INT: args[numargs++].setint(int(op)>>8); continue;
+ case CODE_VAL|RET_FLOAT: args[numargs++].setfloat(*(const float *)code++); continue;
+ case CODE_VALI|RET_FLOAT: args[numargs++].setfloat(float(int(op)>>8)); continue;
+
+ case CODE_FORCE|RET_STR: forcestr(args[numargs-1]); continue;
+ case CODE_FORCE|RET_INT: forceint(args[numargs-1]); continue;
+ case CODE_FORCE|RET_FLOAT: forcefloat(args[numargs-1]); continue;
+
+ case CODE_RESULT|RET_NULL: case CODE_RESULT|RET_STR: case CODE_RESULT|RET_INT: case CODE_RESULT|RET_FLOAT:
+ litval:
+ freearg(result);
+ result = args[0];
+ forcearg(result, op&CODE_RET_MASK);
+ args[0].setnull();
+ freeargs(args, numargs, 0);
+ continue;
+
+ case CODE_BLOCK:
+ {
+ uint len = op>>8;
+ args[numargs++].setcode(code+1);
+ code += len;
+ continue;
+ }
+ case CODE_COMPILE:
+ {
+ tagval &arg = args[numargs-1];
+ vector<uint> buf;
+ switch(arg.type)
+ {
+ case VAL_INT: buf.reserve(8); buf.add(CODE_START); compileint(buf, arg.i); buf.add(CODE_RESULT); buf.add(CODE_EXIT); break;
+ case VAL_FLOAT: buf.reserve(8); buf.add(CODE_START); compilefloat(buf, arg.f); buf.add(CODE_RESULT); buf.add(CODE_EXIT); break;
+ case VAL_STR: case VAL_MACRO: buf.reserve(64); compilemain(buf, arg.s); freearg(arg); break;
+ default: buf.reserve(8); buf.add(CODE_START); compilenull(buf); buf.add(CODE_RESULT); buf.add(CODE_EXIT); break;
+ }
+ arg.setcode(buf.getbuf()+1);
+ buf.disown();
+ continue;
+ }
+
+ case CODE_IDENT:
+ args[numargs++].setident(identmap[op>>8]);
+ continue;
+ case CODE_IDENTARG:
+ {
+ ident *id = identmap[op>>8];
+ if(!(aliasstack->usedargs&(1<<id->index)))
+ {
+ pusharg(*id, nullval, aliasstack->argstack[id->index]);
+ aliasstack->usedargs |= 1<<id->index;
+ }
+ args[numargs++].setident(id);
+ continue;
+ }
+ case CODE_IDENTU:
+ {
+ tagval &arg = args[numargs-1];
+ ident *id = arg.type == VAL_STR || arg.type == VAL_MACRO ? newident(arg.s, IDF_UNKNOWN) : dummyident;
+ if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index)))
+ {
+ pusharg(*id, nullval, aliasstack->argstack[id->index]);
+ aliasstack->usedargs |= 1<<id->index;
+ }
+ freearg(arg);
+ arg.setident(id);
+ continue;
+ }
+
+ case CODE_LOOKUPU|RET_STR:
+ #define LOOKUPU(aval, sval, ival, fval, nval) { \
+ tagval &arg = args[numargs-1]; \
+ if(arg.type != VAL_STR && arg.type != VAL_MACRO) continue; \
+ id = idents.access(arg.s); \
+ if(id) switch(id->type) \
+ { \
+ case ID_ALIAS: \
+ if(id->flags&IDF_UNKNOWN) break; \
+ freearg(arg); \
+ if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index))) { nval; continue; } \
+ aval; \
+ continue; \
+ case ID_SVAR: freearg(arg); sval; continue; \
+ case ID_VAR: freearg(arg); ival; continue; \
+ case ID_FVAR: freearg(arg); fval; continue; \
+ case ID_COMMAND: \
+ { \
+ freearg(arg); \
+ arg.setnull(); \
+ commandret = &arg; \
+ tagval buf[MAXARGS]; \
+ callcommand(id, buf, 0, true); \
+ forcearg(arg, op&CODE_RET_MASK); \
+ commandret = &result; \
+ continue; \
+ } \
+ default: freearg(arg); nval; continue; \
+ } \
+ debugcode("unknown alias lookup: %s", arg.s); \
+ freearg(arg); \
+ nval; \
+ continue; \
+ }
+ LOOKUPU(arg.setstr(newstring(id->getstr())),
+ arg.setstr(newstring(*id->storage.s)),
+ arg.setstr(newstring(intstr(*id->storage.i))),
+ arg.setstr(newstring(floatstr(*id->storage.f))),
+ arg.setstr(newstring("")));
+ case CODE_LOOKUP|RET_STR:
+ #define LOOKUP(aval) { \
+ id = identmap[op>>8]; \
+ if(id->flags&IDF_UNKNOWN) debugcode("unknown alias lookup: %s", id->name); \
+ aval; \
+ continue; \
+ }
+ LOOKUP(args[numargs++].setstr(newstring(id->getstr())));
+ case CODE_LOOKUPARG|RET_STR:
+ #define LOOKUPARG(aval, nval) { \
+ id = identmap[op>>8]; \
+ if(!(aliasstack->usedargs&(1<<id->index))) { nval; continue; } \
+ aval; \
+ continue; \
+ }
+ LOOKUPARG(args[numargs++].setstr(newstring(id->getstr())), args[numargs++].setstr(newstring("")));
+ case CODE_LOOKUPU|RET_INT:
+ LOOKUPU(arg.setint(id->getint()),
+ arg.setint(parseint(*id->storage.s)),
+ arg.setint(*id->storage.i),
+ arg.setint(int(*id->storage.f)),
+ arg.setint(0));
+ case CODE_LOOKUP|RET_INT:
+ LOOKUP(args[numargs++].setint(id->getint()));
+ case CODE_LOOKUPARG|RET_INT:
+ LOOKUPARG(args[numargs++].setint(id->getint()), args[numargs++].setint(0));
+ case CODE_LOOKUPU|RET_FLOAT:
+ LOOKUPU(arg.setfloat(id->getfloat()),
+ arg.setfloat(parsefloat(*id->storage.s)),
+ arg.setfloat(float(*id->storage.i)),
+ arg.setfloat(*id->storage.f),
+ arg.setfloat(0.0f));
+ case CODE_LOOKUP|RET_FLOAT:
+ LOOKUP(args[numargs++].setfloat(id->getfloat()));
+ case CODE_LOOKUPARG|RET_FLOAT:
+ LOOKUPARG(args[numargs++].setfloat(id->getfloat()), args[numargs++].setfloat(0.0f));
+ case CODE_LOOKUPU|RET_NULL:
+ LOOKUPU(id->getval(arg),
+ arg.setstr(newstring(*id->storage.s)),
+ arg.setint(*id->storage.i),
+ arg.setfloat(*id->storage.f),
+ arg.setnull());
+ case CODE_LOOKUP|RET_NULL:
+ LOOKUP(id->getval(args[numargs++]));
+ case CODE_LOOKUPARG|RET_NULL:
+ LOOKUPARG(id->getval(args[numargs++]), args[numargs++].setnull());
+
+ case CODE_SVAR|RET_STR: case CODE_SVAR|RET_NULL: args[numargs++].setstr(newstring(*identmap[op>>8]->storage.s)); continue;
+ case CODE_SVAR|RET_INT: args[numargs++].setint(parseint(*identmap[op>>8]->storage.s)); continue;
+ case CODE_SVAR|RET_FLOAT: args[numargs++].setfloat(parsefloat(*identmap[op>>8]->storage.s)); continue;
+ case CODE_SVAR1: setsvarchecked(identmap[op>>8], args[0].s); freeargs(args, numargs, 0); continue;
+
+ case CODE_IVAR|RET_INT: case CODE_IVAR|RET_NULL: args[numargs++].setint(*identmap[op>>8]->storage.i); continue;
+ case CODE_IVAR|RET_STR: args[numargs++].setstr(newstring(intstr(*identmap[op>>8]->storage.i))); continue;
+ case CODE_IVAR|RET_FLOAT: args[numargs++].setfloat(float(*identmap[op>>8]->storage.i)); continue;
+ case CODE_IVAR1: setvarchecked(identmap[op>>8], args[0].i); numargs = 0; continue;
+ case CODE_IVAR2: setvarchecked(identmap[op>>8], (args[0].i<<16)|(args[1].i<<8)); numargs = 0; continue;
+ case CODE_IVAR3: setvarchecked(identmap[op>>8], (args[0].i<<16)|(args[1].i<<8)|args[2].i); numargs = 0; continue;
+
+ case CODE_FVAR|RET_FLOAT: case CODE_FVAR|RET_NULL: args[numargs++].setfloat(*identmap[op>>8]->storage.f); continue;
+ case CODE_FVAR|RET_STR: args[numargs++].setstr(newstring(floatstr(*identmap[op>>8]->storage.f))); continue;
+ case CODE_FVAR|RET_INT: args[numargs++].setint(int(*identmap[op>>8]->storage.f)); continue;
+ case CODE_FVAR1: setfvarchecked(identmap[op>>8], args[0].f); numargs = 0; continue;
+
+ case CODE_COM|RET_NULL: case CODE_COM|RET_STR: case CODE_COM|RET_FLOAT: case CODE_COM|RET_INT:
+ id = identmap[op>>8];
#ifndef STANDALONE
- callcom:
+ callcom:
#endif
- forcenull(result);
- CALLCOM(numargs)
- forceresult:
- freeargs(args, numargs, 0);
- forcearg(result, op&CODE_RET_MASK);
- continue;
+ forcenull(result);
+ CALLCOM(numargs)
+ forceresult:
+ freeargs(args, numargs, 0);
+ forcearg(result, op&CODE_RET_MASK);
+ continue;
#ifndef STANDALONE
- case CODE_COMD|RET_NULL: case CODE_COMD|RET_STR: case CODE_COMD|RET_FLOAT: case CODE_COMD|RET_INT:
- id = identmap[op>>8];
- args[numargs].setint(addreleaseaction(conc(args, numargs, true, id->name)) ? 1 : 0);
- numargs++;
- goto callcom;
+ case CODE_COMD|RET_NULL: case CODE_COMD|RET_STR: case CODE_COMD|RET_FLOAT: case CODE_COMD|RET_INT:
+ id = identmap[op>>8];
+ args[numargs].setint(addreleaseaction(conc(args, numargs, true, id->name)) ? 1 : 0);
+ numargs++;
+ goto callcom;
#endif
- case CODE_COMV|RET_NULL: case CODE_COMV|RET_STR: case CODE_COMV|RET_FLOAT: case CODE_COMV|RET_INT:
- id = identmap[op>>8];
- forcenull(result);
- ((comfunv)id->fun)(args, numargs);
- goto forceresult;
- case CODE_COMC|RET_NULL: case CODE_COMC|RET_STR: case CODE_COMC|RET_FLOAT: case CODE_COMC|RET_INT:
- id = identmap[op>>8];
- forcenull(result);
- {
- vector<char> buf;
- buf.reserve(MAXSTRLEN);
- ((comfun1)id->fun)(conc(buf, args, numargs, true));
- }
- goto forceresult;
-
- case CODE_CONC|RET_NULL: case CODE_CONC|RET_STR: case CODE_CONC|RET_FLOAT: case CODE_CONC|RET_INT:
- case CODE_CONCW|RET_NULL: case CODE_CONCW|RET_STR: case CODE_CONCW|RET_FLOAT: case CODE_CONCW|RET_INT:
- {
- int numconc = op>>8;
- char *s = conc(&args[numargs-numconc], numconc, (op&CODE_OP_MASK)==CODE_CONC);
- freeargs(args, numargs, numargs-numconc);
- args[numargs++].setstr(s);
- forcearg(args[numargs-1], op&CODE_RET_MASK);
- continue;
- }
-
- case CODE_CONCM|RET_NULL: case CODE_CONCM|RET_STR: case CODE_CONCM|RET_FLOAT: case CODE_CONCM|RET_INT:
- {
- int numconc = op>>8;
- char *s = conc(&args[numargs-numconc], numconc, false);
- freeargs(args, numargs, numargs-numconc);
- result.setstr(s);
- forcearg(result, op&CODE_RET_MASK);
- continue;
- }
-
- case CODE_ALIAS:
- setalias(*identmap[op>>8], args[--numargs]);
- freeargs(args, numargs, 0);
- continue;
- case CODE_ALIASARG:
- setarg(*identmap[op>>8], args[--numargs]);
- freeargs(args, numargs, 0);
- continue;
- case CODE_ALIASU:
- forcestr(args[0]);
- setalias(args[0].s, args[--numargs]);
- freeargs(args, numargs, 0);
- continue;
-
- case CODE_CALL|RET_NULL: case CODE_CALL|RET_STR: case CODE_CALL|RET_FLOAT: case CODE_CALL|RET_INT:
- #define CALLALIAS(offset) { \
- identstack argstack[MAXARGS]; \
- for(int i = 0; i < numargs-offset; i++) \
- pusharg(*identmap[i], args[i+offset], argstack[i]); \
- int oldargs = _numargs, newargs = numargs-offset; \
- _numargs = newargs; \
- int oldflags = identflags; \
- identflags |= id->flags&IDF_OVERRIDDEN; \
- identlink aliaslink = { id, aliasstack, (1<<newargs)-1, argstack }; \
- aliasstack = &aliaslink; \
- if(!id->code) id->code = compilecode(id->getstr()); \
- uint *code = id->code; \
- code[0] += 0x100; \
- runcode(code+1, result); \
- code[0] -= 0x100; \
- if(int(code[0]) < 0x100) delete[] code; \
- aliasstack = aliaslink.next; \
- identflags = oldflags; \
- for(int i = 0; i < newargs; i++) \
- poparg(*identmap[i]); \
- for(int argmask = aliaslink.usedargs&(~0U<<newargs), i = newargs; argmask; i++) \
- if(argmask&(1<<i)) { poparg(*identmap[i]); argmask &= ~(1<<i); } \
- forcearg(result, op&CODE_RET_MASK); \
- _numargs = oldargs; \
- numargs = 0; \
- }
- forcenull(result);
- id = identmap[op>>8];
- if(id->flags&IDF_UNKNOWN)
- {
- debugcode("unknown command: %s", id->name);
- goto forceresult;
- }
- CALLALIAS(0);
- continue;
- case CODE_CALLARG|RET_NULL: case CODE_CALLARG|RET_STR: case CODE_CALLARG|RET_FLOAT: case CODE_CALLARG|RET_INT:
- forcenull(result);
- id = identmap[op>>8];
- if(!(aliasstack->usedargs&(1<<id->index))) goto forceresult;
- CALLALIAS(0);
- continue;
-
- case CODE_CALLU|RET_NULL: case CODE_CALLU|RET_STR: case CODE_CALLU|RET_FLOAT: case CODE_CALLU|RET_INT:
- if(args[0].type != VAL_STR) goto litval;
- id = idents.access(args[0].s);
- if(!id)
- {
- noid:
- if(checknumber(args[0].s)) goto litval;
- debugcode("unknown command: %s", args[0].s);
- forcenull(result);
- goto forceresult;
- }
- forcenull(result);
- switch(id->type)
- {
- case ID_COMMAND:
- freearg(args[0]);
- callcommand(id, args+1, numargs-1);
- forcearg(result, op&CODE_RET_MASK);
- numargs = 0;
- continue;
- case ID_LOCAL:
- {
- identstack locals[MAXARGS];
- freearg(args[0]);
- loopj(numargs-1) pushalias(*forceident(args[j+1]), locals[j]);
- code = runcode(code, result);
- loopj(numargs-1) popalias(*args[j+1].id);
- goto exit;
- }
- case ID_VAR:
- if(numargs <= 1) printvar(id); else setvarchecked(id, &args[1], numargs-1);
- goto forceresult;
- case ID_FVAR:
- if(numargs <= 1) printvar(id); else setfvarchecked(id, forcefloat(args[1]));
- goto forceresult;
- case ID_SVAR:
- if(numargs <= 1) printvar(id); else setsvarchecked(id, forcestr(args[1]));
- goto forceresult;
- case ID_ALIAS:
- if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index))) goto forceresult;
- if(id->valtype==VAL_NULL) goto noid;
- freearg(args[0]);
- CALLALIAS(1);
- continue;
- default:
- goto forceresult;
- }
- }
- }
+ case CODE_COMV|RET_NULL: case CODE_COMV|RET_STR: case CODE_COMV|RET_FLOAT: case CODE_COMV|RET_INT:
+ id = identmap[op>>8];
+ forcenull(result);
+ ((comfunv)id->fun)(args, numargs);
+ goto forceresult;
+ case CODE_COMC|RET_NULL: case CODE_COMC|RET_STR: case CODE_COMC|RET_FLOAT: case CODE_COMC|RET_INT:
+ id = identmap[op>>8];
+ forcenull(result);
+ {
+ vector<char> buf;
+ buf.reserve(MAXSTRLEN);
+ ((comfun1)id->fun)(conc(buf, args, numargs, true));
+ }
+ goto forceresult;
+
+ case CODE_CONC|RET_NULL: case CODE_CONC|RET_STR: case CODE_CONC|RET_FLOAT: case CODE_CONC|RET_INT:
+ case CODE_CONCW|RET_NULL: case CODE_CONCW|RET_STR: case CODE_CONCW|RET_FLOAT: case CODE_CONCW|RET_INT:
+ {
+ int numconc = op>>8;
+ char *s = conc(&args[numargs-numconc], numconc, (op&CODE_OP_MASK)==CODE_CONC);
+ freeargs(args, numargs, numargs-numconc);
+ args[numargs++].setstr(s);
+ forcearg(args[numargs-1], op&CODE_RET_MASK);
+ continue;
+ }
+
+ case CODE_CONCM|RET_NULL: case CODE_CONCM|RET_STR: case CODE_CONCM|RET_FLOAT: case CODE_CONCM|RET_INT:
+ {
+ int numconc = op>>8;
+ char *s = conc(&args[numargs-numconc], numconc, false);
+ freeargs(args, numargs, numargs-numconc);
+ result.setstr(s);
+ forcearg(result, op&CODE_RET_MASK);
+ continue;
+ }
+
+ case CODE_ALIAS:
+ setalias(*identmap[op>>8], args[--numargs]);
+ freeargs(args, numargs, 0);
+ continue;
+ case CODE_ALIASARG:
+ setarg(*identmap[op>>8], args[--numargs]);
+ freeargs(args, numargs, 0);
+ continue;
+ case CODE_ALIASU:
+ forcestr(args[0]);
+ setalias(args[0].s, args[--numargs]);
+ freeargs(args, numargs, 0);
+ continue;
+
+ case CODE_CALL|RET_NULL: case CODE_CALL|RET_STR: case CODE_CALL|RET_FLOAT: case CODE_CALL|RET_INT:
+ #define CALLALIAS(offset) { \
+ identstack argstack[MAXARGS]; \
+ for(int i = 0; i < numargs-offset; i++) \
+ pusharg(*identmap[i], args[i+offset], argstack[i]); \
+ int oldargs = _numargs, newargs = numargs-offset; \
+ _numargs = newargs; \
+ int oldflags = identflags; \
+ identflags |= id->flags&IDF_OVERRIDDEN; \
+ identlink aliaslink = { id, aliasstack, (1<<newargs)-1, argstack }; \
+ aliasstack = &aliaslink; \
+ if(!id->code) id->code = compilecode(id->getstr()); \
+ uint *code = id->code; \
+ code[0] += 0x100; \
+ runcode(code+1, result); \
+ code[0] -= 0x100; \
+ if(int(code[0]) < 0x100) delete[] code; \
+ aliasstack = aliaslink.next; \
+ identflags = oldflags; \
+ for(int i = 0; i < newargs; i++) \
+ poparg(*identmap[i]); \
+ for(int argmask = aliaslink.usedargs&(~0U<<newargs), i = newargs; argmask; i++) \
+ if(argmask&(1<<i)) { poparg(*identmap[i]); argmask &= ~(1<<i); } \
+ forcearg(result, op&CODE_RET_MASK); \
+ _numargs = oldargs; \
+ numargs = 0; \
+ }
+ forcenull(result);
+ id = identmap[op>>8];
+ if(id->flags&IDF_UNKNOWN)
+ {
+ debugcode("unknown command: %s", id->name);
+ goto forceresult;
+ }
+ CALLALIAS(0);
+ continue;
+ case CODE_CALLARG|RET_NULL: case CODE_CALLARG|RET_STR: case CODE_CALLARG|RET_FLOAT: case CODE_CALLARG|RET_INT:
+ forcenull(result);
+ id = identmap[op>>8];
+ if(!(aliasstack->usedargs&(1<<id->index))) goto forceresult;
+ CALLALIAS(0);
+ continue;
+
+ case CODE_CALLU|RET_NULL: case CODE_CALLU|RET_STR: case CODE_CALLU|RET_FLOAT: case CODE_CALLU|RET_INT:
+ if(args[0].type != VAL_STR) goto litval;
+ id = idents.access(args[0].s);
+ if(!id)
+ {
+ noid:
+ if(checknumber(args[0].s)) goto litval;
+ debugcode("unknown command: %s", args[0].s);
+ forcenull(result);
+ goto forceresult;
+ }
+ forcenull(result);
+ switch(id->type)
+ {
+ case ID_COMMAND:
+ freearg(args[0]);
+ callcommand(id, args+1, numargs-1);
+ forcearg(result, op&CODE_RET_MASK);
+ numargs = 0;
+ continue;
+ case ID_LOCAL:
+ {
+ identstack locals[MAXARGS];
+ freearg(args[0]);
+ loopj(numargs-1) pushalias(*forceident(args[j+1]), locals[j]);
+ code = runcode(code, result);
+ loopj(numargs-1) popalias(*args[j+1].id);
+ goto exit;
+ }
+ case ID_VAR:
+ if(numargs <= 1) printvar(id); else setvarchecked(id, &args[1], numargs-1);
+ goto forceresult;
+ case ID_FVAR:
+ if(numargs <= 1) printvar(id); else setfvarchecked(id, forcefloat(args[1]));
+ goto forceresult;
+ case ID_SVAR:
+ if(numargs <= 1) printvar(id); else setsvarchecked(id, forcestr(args[1]));
+ goto forceresult;
+ case ID_ALIAS:
+ if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index))) goto forceresult;
+ if(id->valtype==VAL_NULL) goto noid;
+ freearg(args[0]);
+ CALLALIAS(1);
+ continue;
+ default:
+ goto forceresult;
+ }
+ }
+ }
exit:
- commandret = prevret;
- --rundepth;
- return code;
+ commandret = prevret;
+ --rundepth;
+ return code;
}
void executeret(const uint *code, tagval &result)
{
- runcode(code, result);
+ runcode(code, result);
}
void executeret(const char *p, tagval &result)
{
- vector<uint> code;
- code.reserve(64);
- compilemain(code, p, VAL_ANY);
- runcode(code.getbuf()+1, result);
- if(int(code[0]) >= 0x100) code.disown();
+ vector<uint> code;
+ code.reserve(64);
+ compilemain(code, p, VAL_ANY);
+ runcode(code.getbuf()+1, result);
+ if(int(code[0]) >= 0x100) code.disown();
}
void executeret(ident *id, tagval *args, int numargs, bool lookup, tagval &result)
{
- result.setnull();
- ++rundepth;
- tagval *prevret = commandret;
- commandret = &result;
- if(rundepth > MAXRUNDEPTH) debugcode("exceeded recursion limit");
- else if(id) switch(id->type)
- {
- default:
- if(!id->fun) break;
- // fall-through
- case ID_COMMAND:
- if(numargs < id->numargs)
- {
- tagval buf[MAXARGS];
- memcpy(buf, args, numargs*sizeof(tagval));
- callcommand(id, buf, numargs, lookup);
- }
- else callcommand(id, args, numargs, lookup);
- numargs = 0;
- break;
- case ID_VAR:
- if(numargs <= 0) printvar(id); else setvarchecked(id, args, numargs);
- break;
- case ID_FVAR:
- if(numargs <= 0) printvar(id); else setfvarchecked(id, forcefloat(args[0]));
- break;
- case ID_SVAR:
- if(numargs <= 0) printvar(id); else setsvarchecked(id, forcestr(args[0]));
- break;
- case ID_ALIAS:
- if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index))) break;
- if(id->valtype==VAL_NULL) break;
- #define op RET_NULL
- CALLALIAS(0);
- #undef op
- break;
- }
- freeargs(args, numargs, 0);
- commandret = prevret;
- --rundepth;
+ result.setnull();
+ ++rundepth;
+ tagval *prevret = commandret;
+ commandret = &result;
+ if(rundepth > MAXRUNDEPTH) debugcode("exceeded recursion limit");
+ else if(id) switch(id->type)
+ {
+ default:
+ if(!id->fun) break;
+ // fall-through
+ case ID_COMMAND:
+ if(numargs < id->numargs)
+ {
+ tagval buf[MAXARGS];
+ memcpy(buf, args, numargs*sizeof(tagval));
+ callcommand(id, buf, numargs, lookup);
+ }
+ else callcommand(id, args, numargs, lookup);
+ numargs = 0;
+ break;
+ case ID_VAR:
+ if(numargs <= 0) printvar(id); else setvarchecked(id, args, numargs);
+ break;
+ case ID_FVAR:
+ if(numargs <= 0) printvar(id); else setfvarchecked(id, forcefloat(args[0]));
+ break;
+ case ID_SVAR:
+ if(numargs <= 0) printvar(id); else setsvarchecked(id, forcestr(args[0]));
+ break;
+ case ID_ALIAS:
+ if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index))) break;
+ if(id->valtype==VAL_NULL) break;
+ #define op RET_NULL
+ CALLALIAS(0);
+ #undef op
+ break;
+ }
+ freeargs(args, numargs, 0);
+ commandret = prevret;
+ --rundepth;
}
char *executestr(const uint *code)
{
- tagval result;
- runcode(code, result);
- if(result.type == VAL_NULL) return NULL;
- forcestr(result);
- return result.s;
+ tagval result;
+ runcode(code, result);
+ if(result.type == VAL_NULL) return NULL;
+ forcestr(result);
+ return result.s;
}
char *executestr(const char *p)
{
- tagval result;
- executeret(p, result);
- if(result.type == VAL_NULL) return NULL;
- forcestr(result);
- return result.s;
+ tagval result;
+ executeret(p, result);
+ if(result.type == VAL_NULL) return NULL;
+ forcestr(result);
+ return result.s;
}
char *executestr(ident *id, tagval *args, int numargs, bool lookup)
{
- tagval result;
- executeret(id, args, numargs, lookup, result);
- if(result.type == VAL_NULL) return NULL;
- forcestr(result);
- return result.s;
+ tagval result;
+ executeret(id, args, numargs, lookup, result);
+ if(result.type == VAL_NULL) return NULL;
+ forcestr(result);
+ return result.s;
}
char *execidentstr(const char *name, bool lookup)
{
- ident *id = idents.access(name);
- return id ? executestr(id, NULL, 0, lookup) : NULL;
+ ident *id = idents.access(name);
+ return id ? executestr(id, NULL, 0, lookup) : NULL;
}
int execute(const uint *code)
{
- tagval result;
- runcode(code, result);
- int i = result.getint();
- freearg(result);
- return i;
+ tagval result;
+ runcode(code, result);
+ int i = result.getint();
+ freearg(result);
+ return i;
}
int execute(const char *p)
{
- vector<uint> code;
- code.reserve(64);
- compilemain(code, p, VAL_INT);
- tagval result;
- runcode(code.getbuf()+1, result);
- if(int(code[0]) >= 0x100) code.disown();
- int i = result.getint();
- freearg(result);
- return i;
+ vector<uint> code;
+ code.reserve(64);
+ compilemain(code, p, VAL_INT);
+ tagval result;
+ runcode(code.getbuf()+1, result);
+ if(int(code[0]) >= 0x100) code.disown();
+ int i = result.getint();
+ freearg(result);
+ return i;
}
int execute(ident *id, tagval *args, int numargs, bool lookup)
{
- tagval result;
- executeret(id, args, numargs, lookup, result);
- int i = result.getint();
- freearg(result);
- return i;
+ tagval result;
+ executeret(id, args, numargs, lookup, result);
+ int i = result.getint();
+ freearg(result);
+ return i;
}
int execident(const char *name, int noid, bool lookup)
{
- ident *id = idents.access(name);
- return id ? execute(id, NULL, 0, lookup) : noid;
+ ident *id = idents.access(name);
+ return id ? execute(id, NULL, 0, lookup) : noid;
}
static inline bool getbool(const char *s)
{
- switch(s[0])
- {
- case '+': case '-':
- switch(s[1])
- {
- case '0': break;
- case '.': return !isdigit(s[2]) || parsefloat(s) != 0;
- default: return true;
- }
- // fall through
- case '0':
- {
- char *end;
- int val = int(strtoul((char *)s, &end, 0));
- if(val) return true;
- switch(*end)
- {
- case 'e': case '.': return parsefloat(s) != 0;
- default: return false;
- }
- }
- case '.': return !isdigit(s[1]) || parsefloat(s) != 0;
- case '\0': return false;
- default: return true;
- }
+ switch(s[0])
+ {
+ case '+': case '-':
+ switch(s[1])
+ {
+ case '0': break;
+ case '.': return !isdigit(s[2]) || parsefloat(s) != 0;
+ default: return true;
+ }
+ // fall through
+ case '0':
+ {
+ char *end;
+ int val = int(strtoul((char *)s, &end, 0));
+ if(val) return true;
+ switch(*end)
+ {
+ case 'e': case '.': return parsefloat(s) != 0;
+ default: return false;
+ }
+ }
+ case '.': return !isdigit(s[1]) || parsefloat(s) != 0;
+ case '\0': return false;
+ default: return true;
+ }
}
static inline bool getbool(const tagval &v)
{
- switch(v.type)
- {
- case VAL_FLOAT: return v.f!=0;
- case VAL_INT: return v.i!=0;
- case VAL_STR: case VAL_MACRO: return getbool(v.s);
- default: return false;
- }
+ switch(v.type)
+ {
+ case VAL_FLOAT: return v.f!=0;
+ case VAL_INT: return v.i!=0;
+ case VAL_STR: case VAL_MACRO: return getbool(v.s);
+ default: return false;
+ }
}
bool executebool(const uint *code)
{
- tagval result;
- runcode(code, result);
- bool b = getbool(result);
- freearg(result);
- return b;
+ tagval result;
+ runcode(code, result);
+ bool b = getbool(result);
+ freearg(result);
+ return b;
}
bool executebool(const char *p)
{
- tagval result;
- executeret(p, result);
- bool b = getbool(result);
- freearg(result);
- return b;
+ tagval result;
+ executeret(p, result);
+ bool b = getbool(result);
+ freearg(result);
+ return b;
}
bool executebool(ident *id, tagval *args, int numargs, bool lookup)
{
- tagval result;
- executeret(id, args, numargs, lookup, result);
- bool b = getbool(result);
- freearg(result);
- return b;
+ tagval result;
+ executeret(id, args, numargs, lookup, result);
+ bool b = getbool(result);
+ freearg(result);
+ return b;
}
bool execidentbool(const char *name, bool noid, bool lookup)
{
- ident *id = idents.access(name);
- return id ? executebool(id, NULL, 0, lookup) : noid;
+ ident *id = idents.access(name);
+ return id ? executebool(id, NULL, 0, lookup) : noid;
}
bool execfile(const char *cfgfile, bool msg)
{
- string s;
- copystring(s, cfgfile);
- char *buf = loadfile(path(s), NULL);
- if(!buf)
- {
- if(msg) conoutf(CON_ERROR, "could not read \"%s\"", cfgfile);
- return false;
- }
- const char *oldsourcefile = sourcefile, *oldsourcestr = sourcestr;
- sourcefile = cfgfile;
- sourcestr = buf;
- execute(buf);
- sourcefile = oldsourcefile;
- sourcestr = oldsourcestr;
- delete[] buf;
- return true;
+ string s;
+ copystring(s, cfgfile);
+ char *buf = loadfile(path(s), NULL);
+ if(!buf)
+ {
+ if(msg) conoutf(CON_ERROR, "could not read \"%s\"", cfgfile);
+ return false;
+ }
+ const char *oldsourcefile = sourcefile, *oldsourcestr = sourcestr;
+ sourcefile = cfgfile;
+ sourcestr = buf;
+ execute(buf);
+ sourcefile = oldsourcefile;
+ sourcestr = oldsourcestr;
+ delete[] buf;
+ return true;
}
ICOMMAND(exec, "sb", (char *file, int *msg), intret(execfile(file, *msg != 0) ? 1 : 0));
const char *escapestring(const char *s)
{
- static vector<char> strbuf[3];
- static int stridx = 0;
- stridx = (stridx + 1)%3;
- vector<char> &buf = strbuf[stridx];
- buf.setsize(0);
- buf.add('"');
- for(; *s; s++) switch(*s)
- {
- case '\n': buf.put("^n", 2); break;
- case '\t': buf.put("^t", 2); break;
- case '\f': buf.put("^f", 2); break;
- case '"': buf.put("^\"", 2); break;
- case '^': buf.put("^^", 2); break;
- default: buf.add(*s); break;
- }
- buf.put("\"\0", 2);
- return buf.getbuf();
+ static vector<char> strbuf[3];
+ static int stridx = 0;
+ stridx = (stridx + 1)%3;
+ vector<char> &buf = strbuf[stridx];
+ buf.setsize(0);
+ buf.add('"');
+ for(; *s; s++) switch(*s)
+ {
+ case '\n': buf.put("^n", 2); break;
+ case '\t': buf.put("^t", 2); break;
+ case '\f': buf.put("^f", 2); break;
+ case '"': buf.put("^\"", 2); break;
+ case '^': buf.put("^^", 2); break;
+ default: buf.add(*s); break;
+ }
+ buf.put("\"\0", 2);
+ return buf.getbuf();
}
ICOMMAND(escape, "s", (char *s), result(escapestring(s)));
ICOMMAND(unescape, "s", (char *s),
{
- int len = strlen(s);
- char *d = newstring(len);
- d[unescapestring(d, s, &s[len])] = '\0';
- stringret(d);
+ int len = strlen(s);
+ char *d = newstring(len);
+ d[unescapestring(d, s, &s[len])] = '\0';
+ stringret(d);
});
const char *escapeid(const char *s)
{
- const char *end = s + strcspn(s, "\"/;()[]@ \f\t\r\n\0");
- return *end ? escapestring(s) : s;
+ const char *end = s + strcspn(s, "\"/;()[]@ \f\t\r\n\0");
+ return *end ? escapestring(s) : s;
}
bool validateblock(const char *s)
{
- const int maxbrak = 100;
- static char brakstack[maxbrak];
- int brakdepth = 0;
- for(; *s; s++) switch(*s)
- {
- case '[': case '(': if(brakdepth >= maxbrak) return false; brakstack[brakdepth++] = *s; break;
- case ']': if(brakdepth <= 0 || brakstack[--brakdepth] != '[') return false; break;
- case ')': if(brakdepth <= 0 || brakstack[--brakdepth] != '(') return false; break;
- case '"': s = parsestring(s + 1); if(*s != '"') return false; break;
- case '/': if(s[1] == '/') return false; break;
- case '@': case '\f': return false;
- }
- return brakdepth == 0;
+ const int maxbrak = 100;
+ static char brakstack[maxbrak];
+ int brakdepth = 0;
+ for(; *s; s++) switch(*s)
+ {
+ case '[': case '(': if(brakdepth >= maxbrak) return false; brakstack[brakdepth++] = *s; break;
+ case ']': if(brakdepth <= 0 || brakstack[--brakdepth] != '[') return false; break;
+ case ')': if(brakdepth <= 0 || brakstack[--brakdepth] != '(') return false; break;
+ case '"': s = parsestring(s + 1); if(*s != '"') return false; break;
+ case '/': if(s[1] == '/') return false; break;
+ case '@': case '\f': return false;
+ }
+ return brakdepth == 0;
}
#ifndef STANDALONE
void writecfg(const char *name)
{
- stream *f = openutf8file(path(name && name[0] ? name : game::savedconfig(), true), "w");
- if(!f) return;
- f->printf("// automatically written on exit, DO NOT MODIFY\n// delete this file to have %s overwrite these settings\n// modify settings in game, or put settings in %s to override anything\n\n", game::defaultconfig(), game::autoexec());
- game::writeclientinfo(f);
- f->printf("\n");
- writecrosshairs(f);
- vector<ident *> ids;
- enumerate(idents, ident, id, ids.add(&id));
- ids.sortname();
- loopv(ids)
- {
- ident &id = *ids[i];
- if(id.flags&IDF_PERSIST) switch(id.type)
- {
- case ID_VAR: f->printf("%s %d\n", escapeid(id), *id.storage.i); break;
- case ID_FVAR: f->printf("%s %s\n", escapeid(id), floatstr(*id.storage.f)); break;
- case ID_SVAR: f->printf("%s %s\n", escapeid(id), escapestring(*id.storage.s)); break;
- }
- }
- f->printf("\n");
- writebinds(f);
- f->printf("\n");
- loopv(ids)
- {
- ident &id = *ids[i];
- if(id.type==ID_ALIAS && id.flags&IDF_PERSIST && !(id.flags&IDF_OVERRIDDEN)) switch(id.valtype)
- {
- case VAL_STR:
- if(!id.val.s[0]) break;
- if(!validateblock(id.val.s)) { f->printf("%s = %s\n", escapeid(id), escapestring(id.val.s)); break; }
- [[fallthrough]];
- case VAL_FLOAT:
- [[fallthrough]];
- case VAL_INT:
- f->printf("%s = [%s]\n", escapeid(id), id.getstr()); break;
- }
- }
- f->printf("\n");
- writecompletions(f);
- delete f;
+ stream *f = openutf8file(path(name && name[0] ? name : game::savedconfig(), true), "w");
+ if(!f) return;
+ f->printf("// automatically written on exit, DO NOT MODIFY\n// delete this file to have %s overwrite these settings\n// modify settings in game, or put settings in %s to override anything\n\n", game::defaultconfig(), game::autoexec());
+ game::writeclientinfo(f);
+ f->printf("\n");
+ writecrosshairs(f);
+ vector<ident *> ids;
+ enumerate(idents, ident, id, ids.add(&id));
+ ids.sortname();
+ loopv(ids)
+ {
+ ident &id = *ids[i];
+ if(id.flags&IDF_PERSIST) switch(id.type)
+ {
+ case ID_VAR: f->printf("%s %d\n", escapeid(id), *id.storage.i); break;
+ case ID_FVAR: f->printf("%s %s\n", escapeid(id), floatstr(*id.storage.f)); break;
+ case ID_SVAR: f->printf("%s %s\n", escapeid(id), escapestring(*id.storage.s)); break;
+ }
+ }
+ f->printf("\n");
+ writebinds(f);
+ f->printf("\n");
+ loopv(ids)
+ {
+ ident &id = *ids[i];
+ if(id.type==ID_ALIAS && id.flags&IDF_PERSIST && !(id.flags&IDF_OVERRIDDEN)) switch(id.valtype)
+ {
+ case VAL_STR:
+ if(!id.val.s[0]) break;
+ if(!validateblock(id.val.s)) { f->printf("%s = %s\n", escapeid(id), escapestring(id.val.s)); break; }
+ [[fallthrough]];
+ case VAL_FLOAT:
+ [[fallthrough]];
+ case VAL_INT:
+ f->printf("%s = [%s]\n", escapeid(id), id.getstr()); break;
+ }
+ }
+ f->printf("\n");
+ writecompletions(f);
+ delete f;
}
COMMAND(writecfg, "s");
@@ -2500,10 +2500,10 @@ COMMAND(writecfg, "s");
void changedvars()
{
- vector<ident *> ids;
- enumerate(idents, ident, id, if(id.flags&IDF_OVERRIDDEN) ids.add(&id));
- ids.sortname();
- loopv(ids) printvar(ids[i]);
+ vector<ident *> ids;
+ enumerate(idents, ident, id, if(id.flags&IDF_OVERRIDDEN) ids.add(&id));
+ ids.sortname();
+ loopv(ids) printvar(ids[i]);
}
COMMAND(changedvars, "");
@@ -2515,26 +2515,26 @@ static int retidx = 0;
const char *intstr(int v)
{
- retidx = (retidx + 1)%4;
- intformat(retbuf[retidx], v);
- return retbuf[retidx];
+ retidx = (retidx + 1)%4;
+ intformat(retbuf[retidx], v);
+ return retbuf[retidx];
}
void intret(int v)
{
- commandret->setint(v);
+ commandret->setint(v);
}
const char *floatstr(float v)
{
- retidx = (retidx + 1)%4;
- floatformat(retbuf[retidx], v);
- return retbuf[retidx];
+ retidx = (retidx + 1)%4;
+ floatformat(retbuf[retidx], v);
+ return retbuf[retidx];
}
void floatret(float v)
{
- commandret->setfloat(v);
+ commandret->setfloat(v);
}
#undef ICOMMANDNAME
@@ -2546,190 +2546,190 @@ ICOMMAND(?, "ttt", (tagval *cond, tagval *t, tagval *f), result(*(getbool(*cond)
ICOMMAND(pushif, "rte", (ident *id, tagval *v, uint *code),
{
- if(id->type != ID_ALIAS || id->index < MAXARGS) return;
- if(getbool(*v))
- {
- identstack stack;
- pusharg(*id, *v, stack);
- v->type = VAL_NULL;
- id->flags &= ~IDF_UNKNOWN;
- executeret(code, *commandret);
- poparg(*id);
- }
+ if(id->type != ID_ALIAS || id->index < MAXARGS) return;
+ if(getbool(*v))
+ {
+ identstack stack;
+ pusharg(*id, *v, stack);
+ v->type = VAL_NULL;
+ id->flags &= ~IDF_UNKNOWN;
+ executeret(code, *commandret);
+ poparg(*id);
+ }
});
void loopiter(ident *id, identstack &stack, const tagval &v)
{
- if(id->stack != &stack)
- {
- pusharg(*id, v, stack);
- id->flags &= ~IDF_UNKNOWN;
- }
- else
- {
- if(id->valtype == VAL_STR) delete[] id->val.s;
- cleancode(*id);
- id->setval(v);
- }
+ if(id->stack != &stack)
+ {
+ pusharg(*id, v, stack);
+ id->flags &= ~IDF_UNKNOWN;
+ }
+ else
+ {
+ if(id->valtype == VAL_STR) delete[] id->val.s;
+ cleancode(*id);
+ id->setval(v);
+ }
}
void loopend(ident *id, identstack &stack)
{
- if(id->stack == &stack) poparg(*id);
+ if(id->stack == &stack) poparg(*id);
}
static inline void setiter(ident &id, int i, identstack &stack)
{
- if(id.stack == &stack)
- {
- if(id.valtype != VAL_INT)
- {
- if(id.valtype == VAL_STR) delete[] id.val.s;
- cleancode(id);
- id.valtype = VAL_INT;
- }
- id.val.i = i;
- }
- else
- {
- tagval t;
- t.setint(i);
- pusharg(id, t, stack);
- id.flags &= ~IDF_UNKNOWN;
- }
+ if(id.stack == &stack)
+ {
+ if(id.valtype != VAL_INT)
+ {
+ if(id.valtype == VAL_STR) delete[] id.val.s;
+ cleancode(id);
+ id.valtype = VAL_INT;
+ }
+ id.val.i = i;
+ }
+ else
+ {
+ tagval t;
+ t.setint(i);
+ pusharg(id, t, stack);
+ id.flags &= ~IDF_UNKNOWN;
+ }
}
ICOMMAND(loop, "rie", (ident *id, int *n, uint *body),
{
- if(*n <= 0 || id->type!=ID_ALIAS) return;
- identstack stack;
- loopi(*n)
- {
- setiter(*id, i, stack);
- execute(body);
- }
- poparg(*id);
+ if(*n <= 0 || id->type!=ID_ALIAS) return;
+ identstack stack;
+ loopi(*n)
+ {
+ setiter(*id, i, stack);
+ execute(body);
+ }
+ poparg(*id);
});
ICOMMAND(loopwhile, "riee", (ident *id, int *n, uint *cond, uint *body),
{
- if(*n <= 0 || id->type!=ID_ALIAS) return;
- identstack stack;
- loopi(*n)
- {
- setiter(*id, i, stack);
- if(!executebool(cond)) break;
- execute(body);
- }
- poparg(*id);
+ if(*n <= 0 || id->type!=ID_ALIAS) return;
+ identstack stack;
+ loopi(*n)
+ {
+ setiter(*id, i, stack);
+ if(!executebool(cond)) break;
+ execute(body);
+ }
+ poparg(*id);
});
ICOMMAND(while, "ee", (uint *cond, uint *body), while(executebool(cond)) execute(body));
char *loopconc(ident *id, int n, uint *body, bool space)
{
- identstack stack;
- vector<char> s;
- loopi(n)
- {
- setiter(*id, i, stack);
- tagval v;
- executeret(body, v);
- const char *vstr = v.getstr();
- int len = strlen(vstr);
- if(space && i) s.add(' ');
- s.put(vstr, len);
- freearg(v);
- }
- if(n > 0) poparg(*id);
- s.add('\0');
- return newstring(s.getbuf(), s.length()-1);
+ identstack stack;
+ vector<char> s;
+ loopi(n)
+ {
+ setiter(*id, i, stack);
+ tagval v;
+ executeret(body, v);
+ const char *vstr = v.getstr();
+ int len = strlen(vstr);
+ if(space && i) s.add(' ');
+ s.put(vstr, len);
+ freearg(v);
+ }
+ if(n > 0) poparg(*id);
+ s.add('\0');
+ return newstring(s.getbuf(), s.length()-1);
}
ICOMMAND(loopconcat, "rie", (ident *id, int *n, uint *body),
{
- if(*n > 0 && id->type==ID_ALIAS) commandret->setstr(loopconc(id, *n, body, true));
+ if(*n > 0 && id->type==ID_ALIAS) commandret->setstr(loopconc(id, *n, body, true));
});
ICOMMAND(loopconcatword, "rie", (ident *id, int *n, uint *body),
{
- if(*n > 0 && id->type==ID_ALIAS) commandret->setstr(loopconc(id, *n, body, false));
+ if(*n > 0 && id->type==ID_ALIAS) commandret->setstr(loopconc(id, *n, body, false));
});
void concat(tagval *v, int n)
{
- commandret->setstr(conc(v, n, true));
+ commandret->setstr(conc(v, n, true));
}
COMMAND(concat, "V");
void concatword(tagval *v, int n)
{
- commandret->setstr(conc(v, n, false));
+ commandret->setstr(conc(v, n, false));
}
COMMAND(concatword, "V");
void append(ident *id, tagval *v, bool space)
{
- if(id->type != ID_ALIAS || v->type == VAL_NULL) return;
- if(id->valtype == VAL_NULL)
- {
- noprefix:
- if(id->index < MAXARGS) setarg(*id, *v); else setalias(*id, *v);
- v->type = VAL_NULL;
- }
- else
- {
- const char *prefix = id->getstr();
- if(!prefix[0]) goto noprefix;
- tagval r;
- r.setstr(conc(v, 1, space, prefix));
- if(id->index < MAXARGS) setarg(*id, r); else setalias(*id, r);
- }
+ if(id->type != ID_ALIAS || v->type == VAL_NULL) return;
+ if(id->valtype == VAL_NULL)
+ {
+ noprefix:
+ if(id->index < MAXARGS) setarg(*id, *v); else setalias(*id, *v);
+ v->type = VAL_NULL;
+ }
+ else
+ {
+ const char *prefix = id->getstr();
+ if(!prefix[0]) goto noprefix;
+ tagval r;
+ r.setstr(conc(v, 1, space, prefix));
+ if(id->index < MAXARGS) setarg(*id, r); else setalias(*id, r);
+ }
}
ICOMMAND(append, "rt", (ident *id, tagval *v), append(id, v, true));
ICOMMAND(appendword, "rt", (ident *id, tagval *v), append(id, v, false));
void result(tagval &v)
{
- *commandret = v;
- v.type = VAL_NULL;
+ *commandret = v;
+ v.type = VAL_NULL;
}
void stringret(char *s)
{
- commandret->setstr(s);
+ commandret->setstr(s);
}
void result(const char *s)
{
- commandret->setstr(newstring(s));
+ commandret->setstr(newstring(s));
}
ICOMMAND(result, "t", (tagval *v),
{
- *commandret = *v;
- v->type = VAL_NULL;
+ *commandret = *v;
+ v->type = VAL_NULL;
});
void format(tagval *args, int numargs)
{
- vector<char> s;
- const char *f = args[0].getstr();
- while(*f)
- {
- int c = *f++;
- if(c == '%')
- {
- int i = *f++;
- if(i >= '1' && i <= '9')
- {
- i -= '0';
- const char *sub = i < numargs ? args[i].getstr() : "";
- while(*sub) s.add(*sub++);
- }
- else s.add(i);
- }
- else s.add(c);
- }
- s.add('\0');
- result(s.getbuf());
+ vector<char> s;
+ const char *f = args[0].getstr();
+ while(*f)
+ {
+ int c = *f++;
+ if(c == '%')
+ {
+ int i = *f++;
+ if(i >= '1' && i <= '9')
+ {
+ i -= '0';
+ const char *sub = i < numargs ? args[i].getstr() : "";
+ while(*sub) s.add(*sub++);
+ }
+ else s.add(i);
+ }
+ else s.add(c);
+ }
+ s.add('\0');
+ result(s.getbuf());
}
COMMAND(format, "V");
@@ -2737,448 +2737,448 @@ static const char *liststart = NULL, *listend = NULL, *listquotestart = NULL, *l
static inline void skiplist(const char *&p)
{
- for(;;)
- {
- p += strspn(p, " \t\r\n");
- if(p[0]!='/' || p[1]!='/') break;
- p += strcspn(p, "\n\0");
- }
+ for(;;)
+ {
+ p += strspn(p, " \t\r\n");
+ if(p[0]!='/' || p[1]!='/') break;
+ p += strcspn(p, "\n\0");
+ }
}
static bool parselist(const char *&s, const char *&start = liststart, const char *&end = listend, const char *&quotestart = listquotestart, const char *&quoteend = listquoteend)
{
- skiplist(s);
- switch(*s)
- {
- case '"': quotestart = s++; start = s; s = parsestring(s); end = s; if(*s == '"') s++; quoteend = s; break;
- case '(': case '[':
- quotestart = s;
- start = s+1;
- for(int braktype = *s++, brak = 1;;)
- {
- s += strcspn(s, "\"/;()[]\0");
- int c = *s++;
- switch(c)
- {
- case '\0': s--; quoteend = end = s; return true;
- case '"': s = parsestring(s); if(*s == '"') s++; break;
- case '/': if(*s == '/') s += strcspn(s, "\n\0"); break;
- case '(': case '[': if(c == braktype) brak++; break;
- case ')': if(braktype == '(' && --brak <= 0) goto endblock; break;
- case ']': if(braktype == '[' && --brak <= 0) goto endblock; break;
- }
- }
- endblock:
- end = s-1;
- quoteend = s;
- break;
- case '\0': case ')': case ']': return false;
- default: quotestart = start = s; s = parseword(s); quoteend = end = s; break;
- }
- skiplist(s);
- if(*s == ';') s++;
- return true;
+ skiplist(s);
+ switch(*s)
+ {
+ case '"': quotestart = s++; start = s; s = parsestring(s); end = s; if(*s == '"') s++; quoteend = s; break;
+ case '(': case '[':
+ quotestart = s;
+ start = s+1;
+ for(int braktype = *s++, brak = 1;;)
+ {
+ s += strcspn(s, "\"/;()[]\0");
+ int c = *s++;
+ switch(c)
+ {
+ case '\0': s--; quoteend = end = s; return true;
+ case '"': s = parsestring(s); if(*s == '"') s++; break;
+ case '/': if(*s == '/') s += strcspn(s, "\n\0"); break;
+ case '(': case '[': if(c == braktype) brak++; break;
+ case ')': if(braktype == '(' && --brak <= 0) goto endblock; break;
+ case ']': if(braktype == '[' && --brak <= 0) goto endblock; break;
+ }
+ }
+ endblock:
+ end = s-1;
+ quoteend = s;
+ break;
+ case '\0': case ')': case ']': return false;
+ default: quotestart = start = s; s = parseword(s); quoteend = end = s; break;
+ }
+ skiplist(s);
+ if(*s == ';') s++;
+ return true;
}
void explodelist(const char *s, vector<char *> &elems, int limit)
{
- const char *start, *end;
- while((limit < 0 || elems.length() < limit) && parselist(s, start, end))
- elems.add(newstring(start, end-start));
+ const char *start, *end;
+ while((limit < 0 || elems.length() < limit) && parselist(s, start, end))
+ elems.add(newstring(start, end-start));
}
char *indexlist(const char *s, int pos)
{
- loopi(pos) if(!parselist(s)) return newstring("");
- const char *start, *end;
- return parselist(s, start, end) ? newstring(start, end-start) : newstring("");
+ loopi(pos) if(!parselist(s)) return newstring("");
+ const char *start, *end;
+ return parselist(s, start, end) ? newstring(start, end-start) : newstring("");
}
int listlen(const char *s)
{
- int n = 0;
- while(parselist(s)) n++;
- return n;
+ int n = 0;
+ while(parselist(s)) n++;
+ return n;
}
ICOMMAND(listlen, "s", (char *s), intret(listlen(s)));
void at(tagval *args, int numargs)
{
- if(!numargs) return;
- const char *start = args[0].getstr(), *end = start + strlen(start);
- for(int i = 1; i < numargs; i++)
- {
- const char *list = start;
- int pos = args[i].getint();
- for(; pos > 0; pos--) if(!parselist(list)) break;
- if(pos > 0 || !parselist(list, start, end)) start = end = "";
- }
- commandret->setstr(newstring(start, end-start));
+ if(!numargs) return;
+ const char *start = args[0].getstr(), *end = start + strlen(start);
+ for(int i = 1; i < numargs; i++)
+ {
+ const char *list = start;
+ int pos = args[i].getint();
+ for(; pos > 0; pos--) if(!parselist(list)) break;
+ if(pos > 0 || !parselist(list, start, end)) start = end = "";
+ }
+ commandret->setstr(newstring(start, end-start));
}
COMMAND(at, "si1V");
void substr(char *s, int *start, int *count, int *numargs)
{
- int len = strlen(s), offset = clamp(*start, 0, len);
- commandret->setstr(newstring(&s[offset], *numargs >= 3 ? clamp(*count, 0, len - offset) : len - offset));
+ int len = strlen(s), offset = clamp(*start, 0, len);
+ commandret->setstr(newstring(&s[offset], *numargs >= 3 ? clamp(*count, 0, len - offset) : len - offset));
}
COMMAND(substr, "siiN");
void chopstr(char *s, int *lim, char *ellipsis)
{
- int len = strlen(s), maxlen = abs(*lim);
- if(len > maxlen)
- {
- int elen = strlen(ellipsis);
- maxlen = max(maxlen, elen);
- char *chopped = newstring(maxlen);
- if(*lim < 0)
- {
- memcpy(chopped, ellipsis, elen);
- memcpy(&chopped[elen], &s[len - (maxlen - elen)], maxlen - elen);
- }
- else
- {
- memcpy(chopped, s, maxlen - elen);
- memcpy(&chopped[maxlen - elen], ellipsis, elen);
- }
- chopped[maxlen] = '\0';
- commandret->setstr(chopped);
- }
- else result(s);
+ int len = strlen(s), maxlen = abs(*lim);
+ if(len > maxlen)
+ {
+ int elen = strlen(ellipsis);
+ maxlen = max(maxlen, elen);
+ char *chopped = newstring(maxlen);
+ if(*lim < 0)
+ {
+ memcpy(chopped, ellipsis, elen);
+ memcpy(&chopped[elen], &s[len - (maxlen - elen)], maxlen - elen);
+ }
+ else
+ {
+ memcpy(chopped, s, maxlen - elen);
+ memcpy(&chopped[maxlen - elen], ellipsis, elen);
+ }
+ chopped[maxlen] = '\0';
+ commandret->setstr(chopped);
+ }
+ else result(s);
}
COMMAND(chopstr, "sis");
void sublist(const char *s, int *skip, int *count, int *numargs)
{
- int offset = max(*skip, 0), len = *numargs >= 3 ? max(*count, 0) : -1;
- loopi(offset) if(!parselist(s)) break;
- if(len < 0) { if(offset > 0) skiplist(s); commandret->setstr(newstring(s)); return; }
- const char *list = s, *start, *end, *qstart, *qend = s;
- if(len > 0 && parselist(s, start, end, list, qend)) while(--len > 0 && parselist(s, start, end, qstart, qend));
- commandret->setstr(newstring(list, qend - list));
+ int offset = max(*skip, 0), len = *numargs >= 3 ? max(*count, 0) : -1;
+ loopi(offset) if(!parselist(s)) break;
+ if(len < 0) { if(offset > 0) skiplist(s); commandret->setstr(newstring(s)); return; }
+ const char *list = s, *start, *end, *qstart, *qend = s;
+ if(len > 0 && parselist(s, start, end, list, qend)) while(--len > 0 && parselist(s, start, end, qstart, qend));
+ commandret->setstr(newstring(list, qend - list));
}
COMMAND(sublist, "siiN");
ICOMMAND(stripcolors, "s", (char *s),
{
- int len = strlen(s);
- char *d = newstring(len);
- filtertext(d, s, true, false, len);
- stringret(d);
+ int len = strlen(s);
+ char *d = newstring(len);
+ filtertext(d, s, true, false, len);
+ stringret(d);
});
static inline void setiter(ident &id, char *val, identstack &stack)
{
- if(id.stack == &stack)
- {
- if(id.valtype == VAL_STR) delete[] id.val.s;
- else id.valtype = VAL_STR;
- cleancode(id);
- id.val.s = val;
- }
- else
- {
- tagval t;
- t.setstr(val);
- pusharg(id, t, stack);
- id.flags &= ~IDF_UNKNOWN;
- }
+ if(id.stack == &stack)
+ {
+ if(id.valtype == VAL_STR) delete[] id.val.s;
+ else id.valtype = VAL_STR;
+ cleancode(id);
+ id.val.s = val;
+ }
+ else
+ {
+ tagval t;
+ t.setstr(val);
+ pusharg(id, t, stack);
+ id.flags &= ~IDF_UNKNOWN;
+ }
}
void listfind(ident *id, const char *list, const uint *body)
{
- if(id->type!=ID_ALIAS) { intret(-1); return; }
- identstack stack;
- int n = -1;
- for(const char *s = list, *start, *end; parselist(s, start, end);)
- {
- ++n;
- char *val = newstring(start, end-start);
- setiter(*id, val, stack);
- if(executebool(body)) { intret(n); goto found; }
- }
- intret(-1);
+ if(id->type!=ID_ALIAS) { intret(-1); return; }
+ identstack stack;
+ int n = -1;
+ for(const char *s = list, *start, *end; parselist(s, start, end);)
+ {
+ ++n;
+ char *val = newstring(start, end-start);
+ setiter(*id, val, stack);
+ if(executebool(body)) { intret(n); goto found; }
+ }
+ intret(-1);
found:
- if(n >= 0) poparg(*id);
+ if(n >= 0) poparg(*id);
}
COMMAND(listfind, "rse");
void looplist(ident *id, const char *list, const uint *body)
{
- if(id->type!=ID_ALIAS) return;
- identstack stack;
- int n = 0;
- for(const char *s = list, *start, *end; parselist(s, start, end); n++)
- {
- char *val = newstring(start, end-start);
- setiter(*id, val, stack);
- execute(body);
- }
- if(n) poparg(*id);
+ if(id->type!=ID_ALIAS) return;
+ identstack stack;
+ int n = 0;
+ for(const char *s = list, *start, *end; parselist(s, start, end); n++)
+ {
+ char *val = newstring(start, end-start);
+ setiter(*id, val, stack);
+ execute(body);
+ }
+ if(n) poparg(*id);
}
COMMAND(looplist, "rse");
void loopsublist(ident *id, const char *list, int *skip, int *count, const uint *body)
{
- if(id->type!=ID_ALIAS) return;
- identstack stack;
- int n = 0, offset = max(*skip, 0), len = *count < 0 ? INT_MAX : offset + *count;
- for(const char *s = list, *start, *end; parselist(s, start, end) && n < len; n++) if(n >= offset)
- {
- char *val = newstring(start, end-start);
- setiter(*id, val, stack);
- execute(body);
- }
- if(n) poparg(*id);
+ if(id->type!=ID_ALIAS) return;
+ identstack stack;
+ int n = 0, offset = max(*skip, 0), len = *count < 0 ? INT_MAX : offset + *count;
+ for(const char *s = list, *start, *end; parselist(s, start, end) && n < len; n++) if(n >= offset)
+ {
+ char *val = newstring(start, end-start);
+ setiter(*id, val, stack);
+ execute(body);
+ }
+ if(n) poparg(*id);
}
COMMAND(loopsublist, "rsiie");
void looplistconc(ident *id, const char *list, const uint *body, bool space)
{
- if(id->type!=ID_ALIAS) return;
- identstack stack;
- vector<char> r;
- int n = 0;
- for(const char *s = list, *start, *end; parselist(s, start, end); n++)
- {
- char *val = newstring(start, end-start);
- setiter(*id, val, stack);
-
- if(n && space) r.add(' ');
-
- tagval v;
- executeret(body, v);
- const char *vstr = v.getstr();
- int len = strlen(vstr);
- r.put(vstr, len);
- freearg(v);
- }
- if(n) poparg(*id);
- r.add('\0');
- commandret->setstr(newstring(r.getbuf(), r.length()-1));
+ if(id->type!=ID_ALIAS) return;
+ identstack stack;
+ vector<char> r;
+ int n = 0;
+ for(const char *s = list, *start, *end; parselist(s, start, end); n++)
+ {
+ char *val = newstring(start, end-start);
+ setiter(*id, val, stack);
+
+ if(n && space) r.add(' ');
+
+ tagval v;
+ executeret(body, v);
+ const char *vstr = v.getstr();
+ int len = strlen(vstr);
+ r.put(vstr, len);
+ freearg(v);
+ }
+ if(n) poparg(*id);
+ r.add('\0');
+ commandret->setstr(newstring(r.getbuf(), r.length()-1));
}
ICOMMAND(looplistconcat, "rse", (ident *id, char *list, uint *body), looplistconc(id, list, body, true));
ICOMMAND(looplistconcatword, "rse", (ident *id, char *list, uint *body), looplistconc(id, list, body, false));
void listfilter(ident *id, const char *list, const uint *body)
{
- if(id->type!=ID_ALIAS) return;
- identstack stack;
- vector<char> r;
- int n = 0;
- for(const char *s = list, *start, *end, *quotestart, *quoteend; parselist(s, start, end, quotestart, quoteend); n++)
- {
- char *val = newstring(start, end-start);
- setiter(*id, val, stack);
-
- if(executebool(body))
- {
- if(r.length()) r.add(' ');
- r.put(quotestart, quoteend-quotestart);
- }
- }
- if(n) poparg(*id);
- r.add('\0');
- commandret->setstr(newstring(r.getbuf(), r.length()-1));
+ if(id->type!=ID_ALIAS) return;
+ identstack stack;
+ vector<char> r;
+ int n = 0;
+ for(const char *s = list, *start, *end, *quotestart, *quoteend; parselist(s, start, end, quotestart, quoteend); n++)
+ {
+ char *val = newstring(start, end-start);
+ setiter(*id, val, stack);
+
+ if(executebool(body))
+ {
+ if(r.length()) r.add(' ');
+ r.put(quotestart, quoteend-quotestart);
+ }
+ }
+ if(n) poparg(*id);
+ r.add('\0');
+ commandret->setstr(newstring(r.getbuf(), r.length()-1));
}
COMMAND(listfilter, "rse");
void prettylist(const char *s, const char *conj)
{
- vector<char> p;
- const char *start, *end;
- for(int len = listlen(s), n = 0; parselist(s, start, end); n++)
- {
- p.put(start, end - start);
- if(n+1 < len)
- {
- if(len > 2 || !conj[0]) p.add(',');
- if(n+2 == len && conj[0])
- {
- p.add(' ');
- p.put(conj, strlen(conj));
- }
- p.add(' ');
- }
- }
- p.add('\0');
- result(p.getbuf());
+ vector<char> p;
+ const char *start, *end;
+ for(int len = listlen(s), n = 0; parselist(s, start, end); n++)
+ {
+ p.put(start, end - start);
+ if(n+1 < len)
+ {
+ if(len > 2 || !conj[0]) p.add(',');
+ if(n+2 == len && conj[0])
+ {
+ p.add(' ');
+ p.put(conj, strlen(conj));
+ }
+ p.add(' ');
+ }
+ }
+ p.add('\0');
+ result(p.getbuf());
}
COMMAND(prettylist, "ss");
int listincludes(const char *list, const char *needle, int needlelen)
{
- int offset = 0;
- for(const char *s = list, *start, *end; parselist(s, start, end);)
- {
- int len = end - start;
- if(needlelen == len && !strncmp(needle, start, len)) return offset;
- offset++;
- }
- return -1;
+ int offset = 0;
+ for(const char *s = list, *start, *end; parselist(s, start, end);)
+ {
+ int len = end - start;
+ if(needlelen == len && !strncmp(needle, start, len)) return offset;
+ offset++;
+ }
+ return -1;
}
ICOMMAND(indexof, "ss", (char *list, char *elem), intret(listincludes(list, elem, strlen(elem))));
char *listdel(const char *s, const char *del)
{
- vector<char> p;
- for(const char *start, *end, *qstart, *qend; parselist(s, start, end, qstart, qend);)
- {
- if(listincludes(del, start, end-start) < 0)
- {
- if(!p.empty()) p.add(' ');
- p.put(qstart, qend-qstart);
- }
- }
- p.add('\0');
- return newstring(p.getbuf(), p.length()-1);
+ vector<char> p;
+ for(const char *start, *end, *qstart, *qend; parselist(s, start, end, qstart, qend);)
+ {
+ if(listincludes(del, start, end-start) < 0)
+ {
+ if(!p.empty()) p.add(' ');
+ p.put(qstart, qend-qstart);
+ }
+ }
+ p.add('\0');
+ return newstring(p.getbuf(), p.length()-1);
}
ICOMMAND(listdel, "ss", (char *list, char *del), commandret->setstr(listdel(list, del)));
void listsplice(const char *s, const char *vals, int *skip, int *count)
{
- int offset = max(*skip, 0), len = max(*count, 0);
- const char *list = s, *start, *end, *qstart, *qend = s;
- loopi(offset) if(!parselist(s, start, end, qstart, qend)) break;
- vector<char> p;
- if(qend > list) p.put(list, qend-list);
- if(*vals)
- {
- if(!p.empty()) p.add(' ');
- p.put(vals, strlen(vals));
- }
- loopi(len) if(!parselist(s)) break;
- skiplist(s);
- switch(*s)
- {
- case '\0': case ')': case ']': break;
- default:
- if(!p.empty()) p.add(' ');
- p.put(s, strlen(s));
- break;
- }
- p.add('\0');
- commandret->setstr(newstring(p.getbuf(), p.length()-1));
+ int offset = max(*skip, 0), len = max(*count, 0);
+ const char *list = s, *start, *end, *qstart, *qend = s;
+ loopi(offset) if(!parselist(s, start, end, qstart, qend)) break;
+ vector<char> p;
+ if(qend > list) p.put(list, qend-list);
+ if(*vals)
+ {
+ if(!p.empty()) p.add(' ');
+ p.put(vals, strlen(vals));
+ }
+ loopi(len) if(!parselist(s)) break;
+ skiplist(s);
+ switch(*s)
+ {
+ case '\0': case ')': case ']': break;
+ default:
+ if(!p.empty()) p.add(' ');
+ p.put(s, strlen(s));
+ break;
+ }
+ p.add('\0');
+ commandret->setstr(newstring(p.getbuf(), p.length()-1));
}
COMMAND(listsplice, "ssii");
ICOMMAND(loopfiles, "rsse", (ident *id, char *dir, char *ext, uint *body),
{
- if(id->type!=ID_ALIAS) return;
- identstack stack;
- vector<char *> files;
- listfiles(dir, ext[0] ? ext : NULL, files);
- loopvrev(files)
- {
- char *file = files[i];
- bool redundant = false;
- loopj(i) if(!strcmp(files[j], file)) { redundant = true; break; }
- if(redundant) delete[] files.removeunordered(i);
- }
- loopv(files)
- {
- char *file = files[i];
- if(i)
- {
- if(id->valtype == VAL_STR) delete[] id->val.s;
- else id->valtype = VAL_STR;
- id->val.s = file;
- }
- else
- {
- tagval t;
- t.setstr(file);
- pusharg(*id, t, stack);
- id->flags &= ~IDF_UNKNOWN;
- }
- execute(body);
- }
- if(files.length()) poparg(*id);
+ if(id->type!=ID_ALIAS) return;
+ identstack stack;
+ vector<char *> files;
+ listfiles(dir, ext[0] ? ext : NULL, files);
+ loopvrev(files)
+ {
+ char *file = files[i];
+ bool redundant = false;
+ loopj(i) if(!strcmp(files[j], file)) { redundant = true; break; }
+ if(redundant) delete[] files.removeunordered(i);
+ }
+ loopv(files)
+ {
+ char *file = files[i];
+ if(i)
+ {
+ if(id->valtype == VAL_STR) delete[] id->val.s;
+ else id->valtype = VAL_STR;
+ id->val.s = file;
+ }
+ else
+ {
+ tagval t;
+ t.setstr(file);
+ pusharg(*id, t, stack);
+ id->flags &= ~IDF_UNKNOWN;
+ }
+ execute(body);
+ }
+ if(files.length()) poparg(*id);
});
void findfile_(char *name)
{
- string fname;
- copystring(fname, name);
- path(fname);
- intret(
+ string fname;
+ copystring(fname, name);
+ path(fname);
+ intret(
#ifndef STANDALONE
- findzipfile(fname) ||
+ findzipfile(fname) ||
#endif
- fileexists(fname, "e") || findfile(fname, "e") ? 1 : 0
- );
+ fileexists(fname, "e") || findfile(fname, "e") ? 1 : 0
+ );
}
COMMANDN(findfile, findfile_, "s");
struct sortitem
{
- const char *str, *quotestart, *quoteend;
+ const char *str, *quotestart, *quoteend;
};
struct sortfun
{
- ident *x, *y;
- uint *body;
-
- bool operator()(const sortitem &xval, const sortitem &yval)
- {
- if(x->valtype != VAL_MACRO) x->valtype = VAL_MACRO;
- cleancode(*x);
- x->val.code = (const uint *)xval.str;
- if(y->valtype != VAL_MACRO) y->valtype = VAL_MACRO;
- cleancode(*y);
- y->val.code = (const uint *)yval.str;
- return executebool(body);
- }
+ ident *x, *y;
+ uint *body;
+
+ bool operator()(const sortitem &xval, const sortitem &yval)
+ {
+ if(x->valtype != VAL_MACRO) x->valtype = VAL_MACRO;
+ cleancode(*x);
+ x->val.code = (const uint *)xval.str;
+ if(y->valtype != VAL_MACRO) y->valtype = VAL_MACRO;
+ cleancode(*y);
+ y->val.code = (const uint *)yval.str;
+ return executebool(body);
+ }
};
void sortlist(char *list, ident *x, ident *y, uint *body)
{
- if(x == y || x->type != ID_ALIAS || y->type != ID_ALIAS) return;
-
- vector<sortitem> items;
- int macrolen = strlen(list), total = 0;
- char *macros = newstring(list, macrolen);
- const char *curlist = list, *start, *end, *quotestart, *quoteend;
- while(parselist(curlist, start, end, quotestart, quoteend))
- {
- macros[end - list] = '\0';
- sortitem item = { &macros[start - list], quotestart, quoteend };
- items.add(item);
- total += int(quoteend - quotestart);
- }
-
- identstack xstack, ystack;
- pusharg(*x, nullval, xstack); x->flags &= ~IDF_UNKNOWN;
- pusharg(*y, nullval, ystack); y->flags &= ~IDF_UNKNOWN;
-
- sortfun f = { x, y, body };
- items.sort(f);
-
- poparg(*x);
- poparg(*y);
-
- char *sorted = macros;
- int sortedlen = total + max(items.length() - 1, 0);
- if(macrolen < sortedlen)
- {
- delete[] macros;
- sorted = newstring(sortedlen);
- }
-
- int offset = 0;
- loopv(items)
- {
- sortitem &item = items[i];
- int len = int(item.quoteend - item.quotestart);
- if(i) sorted[offset++] = ' ';
- memcpy(&sorted[offset], item.quotestart, len);
- offset += len;
- }
- sorted[offset] = '\0';
-
- commandret->setstr(sorted);
+ if(x == y || x->type != ID_ALIAS || y->type != ID_ALIAS) return;
+
+ vector<sortitem> items;
+ int macrolen = strlen(list), total = 0;
+ char *macros = newstring(list, macrolen);
+ const char *curlist = list, *start, *end, *quotestart, *quoteend;
+ while(parselist(curlist, start, end, quotestart, quoteend))
+ {
+ macros[end - list] = '\0';
+ sortitem item = { &macros[start - list], quotestart, quoteend };
+ items.add(item);
+ total += int(quoteend - quotestart);
+ }
+
+ identstack xstack, ystack;
+ pusharg(*x, nullval, xstack); x->flags &= ~IDF_UNKNOWN;
+ pusharg(*y, nullval, ystack); y->flags &= ~IDF_UNKNOWN;
+
+ sortfun f = { x, y, body };
+ items.sort(f);
+
+ poparg(*x);
+ poparg(*y);
+
+ char *sorted = macros;
+ int sortedlen = total + max(items.length() - 1, 0);
+ if(macrolen < sortedlen)
+ {
+ delete[] macros;
+ sorted = newstring(sortedlen);
+ }
+
+ int offset = 0;
+ loopv(items)
+ {
+ sortitem &item = items[i];
+ int len = int(item.quoteend - item.quotestart);
+ if(i) sorted[offset++] = ' ';
+ memcpy(&sorted[offset], item.quotestart, len);
+ offset += len;
+ }
+ sorted[offset] = '\0';
+
+ commandret->setstr(sorted);
}
COMMAND(sortlist, "srre");
@@ -3212,23 +3212,23 @@ ICOMMAND(<<, "ii", (int *a, int *b), intret(*b < 32 ? *a << max(*b, 0) : 0));
ICOMMAND(>>, "ii", (int *a, int *b), intret(*a >> clamp(*b, 0, 31)));
ICOMMAND(&&, "e1V", (tagval *args, int numargs),
{
- if(!numargs) intret(1);
- else loopi(numargs)
- {
- if(i) freearg(*commandret);
- executeret(args[i].code, *commandret);
- if(!getbool(*commandret)) break;
- }
+ if(!numargs) intret(1);
+ else loopi(numargs)
+ {
+ if(i) freearg(*commandret);
+ executeret(args[i].code, *commandret);
+ if(!getbool(*commandret)) break;
+ }
});
ICOMMAND(||, "e1V", (tagval *args, int numargs),
{
- if(!numargs) intret(0);
- else loopi(numargs)
- {
- if(i) freearg(*commandret);
- executeret(args[i].code, *commandret);
- if(getbool(*commandret)) break;
- }
+ if(!numargs) intret(0);
+ else loopi(numargs)
+ {
+ if(i) freearg(*commandret);
+ executeret(args[i].code, *commandret);
+ if(getbool(*commandret)) break;
+ }
});
ICOMMAND(div, "ii", (int *a, int *b), intret(*b ? *a / *b : 0));
@@ -3250,27 +3250,27 @@ ICOMMAND(log10, "f", (float *a), floatret(log10(*a)));
ICOMMAND(exp, "f", (float *a), floatret(exp(*a)));
ICOMMAND(min, "V", (tagval *args, int numargs),
{
- int val = numargs > 0 ? args[numargs - 1].getint() : 0;
- loopi(numargs - 1) val = min(val, args[i].getint());
- intret(val);
+ int val = numargs > 0 ? args[numargs - 1].getint() : 0;
+ loopi(numargs - 1) val = min(val, args[i].getint());
+ intret(val);
});
ICOMMAND(max, "V", (tagval *args, int numargs),
{
- int val = numargs > 0 ? args[numargs - 1].getint() : 0;
- loopi(numargs - 1) val = max(val, args[i].getint());
- intret(val);
+ int val = numargs > 0 ? args[numargs - 1].getint() : 0;
+ loopi(numargs - 1) val = max(val, args[i].getint());
+ intret(val);
});
ICOMMAND(minf, "V", (tagval *args, int numargs),
{
- float val = numargs > 0 ? args[numargs - 1].getfloat() : 0.0f;
- loopi(numargs - 1) val = min(val, args[i].getfloat());
- floatret(val);
+ float val = numargs > 0 ? args[numargs - 1].getfloat() : 0.0f;
+ loopi(numargs - 1) val = min(val, args[i].getfloat());
+ floatret(val);
});
ICOMMAND(maxf, "V", (tagval *args, int numargs),
{
- float val = numargs > 0 ? args[numargs - 1].getfloat() : 0.0f;
- loopi(numargs - 1) val = max(val, args[i].getfloat());
- floatret(val);
+ float val = numargs > 0 ? args[numargs - 1].getfloat() : 0.0f;
+ loopi(numargs - 1) val = max(val, args[i].getfloat());
+ floatret(val);
});
ICOMMAND(abs, "i", (int *n), intret(abs(*n)));
ICOMMAND(absf, "f", (float *n), floatret(fabs(*n)));
@@ -3279,50 +3279,50 @@ ICOMMAND(floor, "f", (float *n), floatret(floor(*n)));
ICOMMAND(ceil, "f", (float *n), floatret(ceil(*n)));
ICOMMAND(round, "ff", (float *n, float *k),
{
- double step = *k;
- double r = *n;
- if(step > 0)
- {
- r += step * (r < 0 ? -0.5 : 0.5);
- r -= fmod(r, step);
- }
- else r = r < 0 ? ceil(r - 0.5) : floor(r + 0.5);
- floatret(float(r));
+ double step = *k;
+ double r = *n;
+ if(step > 0)
+ {
+ r += step * (r < 0 ? -0.5 : 0.5);
+ r -= fmod(r, step);
+ }
+ else r = r < 0 ? ceil(r - 0.5) : floor(r + 0.5);
+ floatret(float(r));
});
ICOMMAND(cond, "ee2V", (tagval *args, int numargs),
{
- for(int i = 0; i < numargs; i += 2)
- {
- if(i+1 < numargs)
- {
- if(executebool(args[i].code))
- {
- executeret(args[i+1].code, *commandret);
- break;
- }
- }
- else
- {
- executeret(args[i].code, *commandret);
- break;
- }
- }
+ for(int i = 0; i < numargs; i += 2)
+ {
+ if(i+1 < numargs)
+ {
+ if(executebool(args[i].code))
+ {
+ executeret(args[i+1].code, *commandret);
+ break;
+ }
+ }
+ else
+ {
+ executeret(args[i].code, *commandret);
+ break;
+ }
+ }
});
#define CASECOMMAND(name, fmt, type, acc, compare) \
- ICOMMAND(name, fmt "te2V", (tagval *args, int numargs), \
- { \
- type val = acc; \
- int i; \
- for(i = 1; i+1 < numargs; i += 2) \
- { \
- if(compare) \
- { \
- executeret(args[i+1].code, *commandret); \
- return; \
- } \
- } \
- })
+ ICOMMAND(name, fmt "te2V", (tagval *args, int numargs), \
+ { \
+ type val = acc; \
+ int i; \
+ for(i = 1; i+1 < numargs; i += 2) \
+ { \
+ if(compare) \
+ { \
+ executeret(args[i+1].code, *commandret); \
+ return; \
+ } \
+ } \
+ })
CASECOMMAND(case, "i", int, args[0].getint(), args[i].type == VAL_NULL || args[i].getint() == val);
CASECOMMAND(casef, "f", float, args[0].getfloat(), args[i].type == VAL_NULL || args[i].getfloat() == val);
CASECOMMAND(cases, "s", const char *, args[0].getstr(), args[i].type == VAL_NULL || !strcmp(args[i].getstr(), val));
@@ -3330,19 +3330,19 @@ CASECOMMAND(cases, "s", const char *, args[0].getstr(), args[i].type == VAL_NULL
ICOMMAND(rnd, "ii", (int *a, int *b), intret(*a - *b > 0 ? rnd(*a - *b) + *b : *b));
ICOMMAND(rndstr, "i", (int *len),
{
- int n = clamp(*len, 0, 10000);
- char *s = newstring(n);
- for(int i = 0; i < n;)
- {
- uint r = randomMT();
- for(int j = min(i + 4, n); i < j; i++)
- {
- s[i] = (r%255) + 1;
- r /= 255;
- }
- }
- s[n] = '\0';
- stringret(s);
+ int n = clamp(*len, 0, 10000);
+ char *s = newstring(n);
+ for(int i = 0; i < n;)
+ {
+ uint r = randomMT();
+ for(int j = min(i + 4, n); i < j; i++)
+ {
+ s[i] = (r%255) + 1;
+ r /= 255;
+ }
+ }
+ s[n] = '\0';
+ stringret(s);
});
ICOMMAND(strcmp, "ss", (char *a, char *b), intret(strcmp(a,b)==0));
@@ -3363,80 +3363,80 @@ ICOMMAND(unistr, "i", (int *i), { char *s = newstring(1); s[0] = uni2cube(*i); s
int naturalsort(const char *a, const char *b)
{
- for(;;)
- {
- int ac = *a, bc = *b;
- if(!ac) return bc ? -1 : 0;
- else if(!bc) return 1;
- else if(isdigit(ac) && isdigit(bc))
- {
- while(*a == '0') a++;
- while(*b == '0') b++;
- const char *a0 = a, *b0 = b;
- while(isdigit(*a)) a++;
- while(isdigit(*b)) b++;
- int alen = a - a0, blen = b - b0;
- if(alen != blen) return alen - blen;
- int n = memcmp(a0, b0, alen);
- if(n < 0) return -1;
- else if(n > 0) return 1;
- }
- else if(ac != bc) return ac - bc;
- else { ++a; ++b; }
- }
+ for(;;)
+ {
+ int ac = *a, bc = *b;
+ if(!ac) return bc ? -1 : 0;
+ else if(!bc) return 1;
+ else if(isdigit(ac) && isdigit(bc))
+ {
+ while(*a == '0') a++;
+ while(*b == '0') b++;
+ const char *a0 = a, *b0 = b;
+ while(isdigit(*a)) a++;
+ while(isdigit(*b)) b++;
+ int alen = a - a0, blen = b - b0;
+ if(alen != blen) return alen - blen;
+ int n = memcmp(a0, b0, alen);
+ if(n < 0) return -1;
+ else if(n > 0) return 1;
+ }
+ else if(ac != bc) return ac - bc;
+ else { ++a; ++b; }
+ }
}
ICOMMAND(naturalsort, "ss", (char *a, char *b), intret(naturalsort(a,b)<=0));
#define STRMAPCOMMAND(name, map) \
- ICOMMAND(name, "s", (char *s), \
- { \
- int len = strlen(s); \
- char *m = newstring(len); \
- loopi(len) m[i] = map(s[i]); \
- m[len] = '\0'; \
- stringret(m); \
- })
+ ICOMMAND(name, "s", (char *s), \
+ { \
+ int len = strlen(s); \
+ char *m = newstring(len); \
+ loopi(len) m[i] = map(s[i]); \
+ m[len] = '\0'; \
+ stringret(m); \
+ })
STRMAPCOMMAND(strlower, cubelower);
STRMAPCOMMAND(strupper, cubeupper);
char *strreplace(const char *s, const char *oldval, const char *newval)
{
- vector<char> buf;
-
- int oldlen = strlen(oldval);
- if(!oldlen) return newstring(s);
- for(;;)
- {
- const char *found = strstr(s, oldval);
- if(found)
- {
- while(s < found) buf.add(*s++);
- for(const char *n = newval; *n; n++) buf.add(*n);
- s = found + oldlen;
- }
- else
- {
- while(*s) buf.add(*s++);
- buf.add('\0');
- return newstring(buf.getbuf(), buf.length());
- }
- }
+ vector<char> buf;
+
+ int oldlen = strlen(oldval);
+ if(!oldlen) return newstring(s);
+ for(;;)
+ {
+ const char *found = strstr(s, oldval);
+ if(found)
+ {
+ while(s < found) buf.add(*s++);
+ for(const char *n = newval; *n; n++) buf.add(*n);
+ s = found + oldlen;
+ }
+ else
+ {
+ while(*s) buf.add(*s++);
+ buf.add('\0');
+ return newstring(buf.getbuf(), buf.length());
+ }
+ }
}
ICOMMAND(strreplace, "sss", (char *s, char *o, char *n), commandret->setstr(strreplace(s, o, n)));
void strsplice(const char *s, const char *vals, int *skip, int *count)
{
- int slen = strlen(s), vlen = strlen(vals),
- offset = clamp(*skip, 0, slen),
- len = clamp(*count, 0, slen - offset);
- char *p = newstring(slen - len + vlen);
- if(offset) memcpy(p, s, offset);
- if(vlen) memcpy(&p[offset], vals, vlen);
- if(offset + len < slen) memcpy(&p[offset + vlen], &s[offset + len], slen - (offset + len));
- p[slen - len + vlen] = '\0';
- commandret->setstr(p);
+ int slen = strlen(s), vlen = strlen(vals),
+ offset = clamp(*skip, 0, slen),
+ len = clamp(*count, 0, slen - offset);
+ char *p = newstring(slen - len + vlen);
+ if(offset) memcpy(p, s, offset);
+ if(vlen) memcpy(&p[offset], vals, vlen);
+ if(offset + len < slen) memcpy(&p[offset + vlen], &s[offset + len], slen - (offset + len));
+ p[slen - len + vlen] = '\0';
+ commandret->setstr(p);
}
COMMAND(strsplice, "ssii");
@@ -3445,55 +3445,55 @@ ICOMMAND(getmillis, "i", (int *total), intret(*total ? totalmillis : lastmillis)
struct sleepcmd
{
- int delay, millis, flags;
- char *command;
+ int delay, millis, flags;
+ char *command;
};
vector<sleepcmd> sleepcmds;
void addsleep(int *msec, char *cmd)
{
- sleepcmd &s = sleepcmds.add();
- s.delay = max(*msec, 1);
- s.millis = lastmillis;
- s.command = newstring(cmd);
- s.flags = identflags;
+ sleepcmd &s = sleepcmds.add();
+ s.delay = max(*msec, 1);
+ s.millis = lastmillis;
+ s.command = newstring(cmd);
+ s.flags = identflags;
}
COMMANDN(sleep, addsleep, "is");
void checksleep(int millis)
{
- loopv(sleepcmds)
- {
- sleepcmd &s = sleepcmds[i];
- if(millis - s.millis >= s.delay)
- {
- char *cmd = s.command; // execute might create more sleep commands
- s.command = NULL;
- int oldflags = identflags;
- identflags = s.flags;
- execute(cmd);
- identflags = oldflags;
- delete[] cmd;
- if(sleepcmds.inrange(i) && !sleepcmds[i].command) sleepcmds.remove(i--);
- }
- }
+ loopv(sleepcmds)
+ {
+ sleepcmd &s = sleepcmds[i];
+ if(millis - s.millis >= s.delay)
+ {
+ char *cmd = s.command; // execute might create more sleep commands
+ s.command = NULL;
+ int oldflags = identflags;
+ identflags = s.flags;
+ execute(cmd);
+ identflags = oldflags;
+ delete[] cmd;
+ if(sleepcmds.inrange(i) && !sleepcmds[i].command) sleepcmds.remove(i--);
+ }
+ }
}
void clearsleep(bool clearoverrides)
{
- int len = 0;
- loopv(sleepcmds) if(sleepcmds[i].command)
- {
- if(clearoverrides && !(sleepcmds[i].flags&IDF_OVERRIDDEN)) sleepcmds[len++] = sleepcmds[i];
- else delete[] sleepcmds[i].command;
- }
- sleepcmds.shrink(len);
+ int len = 0;
+ loopv(sleepcmds) if(sleepcmds[i].command)
+ {
+ if(clearoverrides && !(sleepcmds[i].flags&IDF_OVERRIDDEN)) sleepcmds[len++] = sleepcmds[i];
+ else delete[] sleepcmds[i].command;
+ }
+ sleepcmds.shrink(len);
}
void clearsleep_(int *clearoverrides)
{
- clearsleep(*clearoverrides!=0 || identflags&IDF_OVERRIDDEN);
+ clearsleep(*clearoverrides!=0 || identflags&IDF_OVERRIDDEN);
}
COMMANDN(clearsleep, clearsleep_, "i");
diff --git a/src/engine/console.cpp b/src/engine/console.cpp
index d6e83a1..c979215 100644
--- a/src/engine/console.cpp
+++ b/src/engine/console.cpp
@@ -18,33 +18,33 @@ VARFP(maxcon, 10, 200, MAXCONLINES, { while(conlines.length() > maxcon) delete[]
VARP(contags, 0, 3, 3);
-void conline(int type, const char *sf) // add a line to the console buffer
-{
- char *buf = NULL;
- if(type&CON_TAG_MASK) for(int i = conlines.length()-1; i >= max(conlines.length()-contags, 0); i--)
- {
- int prev = conlines.removing(i).type;
- if(!(prev&CON_TAG_MASK)) break;
- if(type == prev)
- {
- buf = conlines.remove(i).line;
- break;
- }
- }
- if(!buf) buf = conlines.length() >= maxcon ? conlines.remove().line : newstring("", CONSTRLEN-1);
- cline &cl = conlines.add();
- cl.line = buf;
- cl.type = type;
- cl.outtime = totalmillis; // for how long to keep line on screen
- copystring(cl.line, sf, CONSTRLEN);
+void conline(int type, const char *sf) // add a line to the console buffer
+{
+ char *buf = NULL;
+ if(type&CON_TAG_MASK) for(int i = conlines.length()-1; i >= max(conlines.length()-contags, 0); i--)
+ {
+ int prev = conlines.removing(i).type;
+ if(!(prev&CON_TAG_MASK)) break;
+ if(type == prev)
+ {
+ buf = conlines.remove(i).line;
+ break;
+ }
+ }
+ if(!buf) buf = conlines.length() >= maxcon ? conlines.remove().line : newstring("", CONSTRLEN-1);
+ cline &cl = conlines.add();
+ cl.line = buf;
+ cl.type = type;
+ cl.outtime = totalmillis; // for how long to keep line on screen
+ copystring(cl.line, sf, CONSTRLEN);
}
void conoutfv(int type, const char *fmt, va_list args)
{
- static char buf[CONSTRLEN];
- vformatstring(buf, fmt, args, sizeof(buf));
- conline(type, buf);
- logoutf("%s", buf);
+ static char buf[CONSTRLEN];
+ vformatstring(buf, fmt, args, sizeof(buf));
+ conline(type, buf);
+ logoutf("%s", buf);
}
VAR(fullconsole, 0, 0, 1);
@@ -52,14 +52,14 @@ ICOMMAND(toggleconsole, "", (), { fullconsole ^= 1; });
int rendercommand(int x, int y, int w)
{
- if(commandmillis < 0) return 0;
+ if(commandmillis < 0) return 0;
- defformatstring(s, "%s %s", commandprompt ? commandprompt : ">", commandbuf);
- int width, height;
- text_bounds(s, width, height, w);
- y -= height;
- draw_text(s, x, y, 0xFF, 0xFF, 0xFF, 0xFF, (commandpos>=0) ? (commandpos+1+(commandprompt?strlen(commandprompt):1)) : strlen(s), w);
- return height;
+ defformatstring(s, "%s %s", commandprompt ? commandprompt : ">", commandbuf);
+ int width, height;
+ text_bounds(s, width, height, w);
+ y -= height;
+ draw_text(s, x, y, 0xFF, 0xFF, 0xFF, 0xFF, (commandpos>=0) ? (commandpos+1+(commandprompt?strlen(commandprompt):1)) : strlen(s), w);
+ return height;
}
VARP(consize, 0, 5, 100);
@@ -76,19 +76,19 @@ int conskip = 0, miniconskip = 0;
void setconskip(int &skip, int filter, int n)
{
- filter &= CON_FLAGS;
- int offset = abs(n), dir = n < 0 ? -1 : 1;
- skip = clamp(skip, 0, conlines.length()-1);
- while(offset)
- {
- skip += dir;
- if(!conlines.inrange(skip))
- {
- skip = clamp(skip, 0, conlines.length()-1);
- return;
- }
- if(conlines[skip].type&filter) --offset;
- }
+ filter &= CON_FLAGS;
+ int offset = abs(n), dir = n < 0 ? -1 : 1;
+ skip = clamp(skip, 0, conlines.length()-1);
+ while(offset)
+ {
+ skip += dir;
+ if(!conlines.inrange(skip))
+ {
+ skip = clamp(skip, 0, conlines.length()-1);
+ return;
+ }
+ if(conlines[skip].type&filter) --offset;
+ }
}
ICOMMAND(conskip, "i", (int *n), setconskip(conskip, fullconsole ? fullconfilter : confilter, *n));
@@ -98,94 +98,94 @@ ICOMMAND(clearconsole, "", (), { while(conlines.length()) delete[] conlines.pop(
int drawconlines(int conskip, int confade, int conwidth, int conheight, int conoff, int filter, int y = 0, int dir = 1)
{
- filter &= CON_FLAGS;
- int numl = conlines.length(), offset = min(conskip, numl);
-
- if(confade)
- {
- if(!conskip)
- {
- numl = 0;
- loopvrev(conlines) if(totalmillis-conlines[i].outtime < confade*1000) { numl = i+1; break; }
- }
- else offset--;
- }
-
- int totalheight = 0;
- loopi(numl) //determine visible height
- {
- // shuffle backwards to fill if necessary
- int idx = offset+i < numl ? offset+i : --offset;
- if(!(conlines[idx].type&filter)) continue;
- char *line = conlines[idx].line;
- int width, height;
- text_bounds(line, width, height, conwidth);
- if(totalheight + height > conheight) { numl = i; if(offset == idx) ++offset; break; }
- totalheight += height;
- }
- if(dir > 0) y = conoff;
- loopi(numl)
- {
- int idx = offset + (dir > 0 ? numl-i-1 : i);
- if(!(conlines[idx].type&filter)) continue;
- char *line = conlines[idx].line;
- int width, height;
- text_bounds(line, width, height, conwidth);
- if(dir <= 0) y -= height;
- draw_text(line, conoff, y, 0xFF, 0xFF, 0xFF, 0xFF, -1, conwidth);
- if(dir > 0) y += height;
- }
- return y+conoff;
-}
-
-int renderconsole(int w, int h, int abovehud) // render buffer taking into account time & scrolling
-{
- int conpad = fullconsole ? 0 : FONTH/4,
- conoff = fullconsole ? FONTH : FONTH/3,
- conheight = min(fullconsole ? ((h*fullconsize/100)/FONTH)*FONTH : FONTH*consize, h - 2*(conpad + conoff)),
- conwidth = w - 2*(conpad + conoff) - (fullconsole ? 0 : game::clipconsole(w, h));
-
- extern void consolebox(int x1, int y1, int x2, int y2);
- if(fullconsole) consolebox(conpad, conpad, conwidth+conpad+2*conoff, conheight+conpad+2*conoff);
-
- int y = drawconlines(conskip, fullconsole ? 0 : confade, conwidth, conheight, conpad+conoff, fullconsole ? fullconfilter : confilter);
- if(!fullconsole && (miniconsize && miniconwidth))
- drawconlines(miniconskip, miniconfade, (miniconwidth*(w - 2*(conpad + conoff)))/100, min(FONTH*miniconsize, abovehud - y), conpad+conoff, miniconfilter, abovehud, -1);
- return fullconsole ? conheight + 2*(conpad + conoff) : y;
+ filter &= CON_FLAGS;
+ int numl = conlines.length(), offset = min(conskip, numl);
+
+ if(confade)
+ {
+ if(!conskip)
+ {
+ numl = 0;
+ loopvrev(conlines) if(totalmillis-conlines[i].outtime < confade*1000) { numl = i+1; break; }
+ }
+ else offset--;
+ }
+
+ int totalheight = 0;
+ loopi(numl) //determine visible height
+ {
+ // shuffle backwards to fill if necessary
+ int idx = offset+i < numl ? offset+i : --offset;
+ if(!(conlines[idx].type&filter)) continue;
+ char *line = conlines[idx].line;
+ int width, height;
+ text_bounds(line, width, height, conwidth);
+ if(totalheight + height > conheight) { numl = i; if(offset == idx) ++offset; break; }
+ totalheight += height;
+ }
+ if(dir > 0) y = conoff;
+ loopi(numl)
+ {
+ int idx = offset + (dir > 0 ? numl-i-1 : i);
+ if(!(conlines[idx].type&filter)) continue;
+ char *line = conlines[idx].line;
+ int width, height;
+ text_bounds(line, width, height, conwidth);
+ if(dir <= 0) y -= height;
+ draw_text(line, conoff, y, 0xFF, 0xFF, 0xFF, 0xFF, -1, conwidth);
+ if(dir > 0) y += height;
+ }
+ return y+conoff;
+}
+
+int renderconsole(int w, int h, int abovehud) // render buffer taking into account time & scrolling
+{
+ int conpad = fullconsole ? 0 : FONTH/4,
+ conoff = fullconsole ? FONTH : FONTH/3,
+ conheight = min(fullconsole ? ((h*fullconsize/100)/FONTH)*FONTH : FONTH*consize, h - 2*(conpad + conoff)),
+ conwidth = w - 2*(conpad + conoff) - (fullconsole ? 0 : game::clipconsole(w, h));
+
+ extern void consolebox(int x1, int y1, int x2, int y2);
+ if(fullconsole) consolebox(conpad, conpad, conwidth+conpad+2*conoff, conheight+conpad+2*conoff);
+
+ int y = drawconlines(conskip, fullconsole ? 0 : confade, conwidth, conheight, conpad+conoff, fullconsole ? fullconfilter : confilter);
+ if(!fullconsole && (miniconsize && miniconwidth))
+ drawconlines(miniconskip, miniconfade, (miniconwidth*(w - 2*(conpad + conoff)))/100, min(FONTH*miniconsize, abovehud - y), conpad+conoff, miniconfilter, abovehud, -1);
+ return fullconsole ? conheight + 2*(conpad + conoff) : y;
}
// keymap is defined externally in keymap.cfg
struct keym
{
- enum
- {
- ACTION_DEFAULT = 0,
- ACTION_SPECTATOR,
- ACTION_EDITING,
- NUMACTIONS
- };
-
- int code;
- char *name;
- char *actions[NUMACTIONS];
- bool pressed;
-
- keym() : code(-1), name(NULL), pressed(false) { loopi(NUMACTIONS) actions[i] = newstring(""); }
- ~keym() { DELETEA(name); loopi(NUMACTIONS) DELETEA(actions[i]); }
+ enum
+ {
+ ACTION_DEFAULT = 0,
+ ACTION_SPECTATOR,
+ ACTION_EDITING,
+ NUMACTIONS
+ };
+
+ int code;
+ char *name;
+ char *actions[NUMACTIONS];
+ bool pressed;
+
+ keym() : code(-1), name(NULL), pressed(false) { loopi(NUMACTIONS) actions[i] = newstring(""); }
+ ~keym() { DELETEA(name); loopi(NUMACTIONS) DELETEA(actions[i]); }
};
hashtable<int, keym> keyms(128);
void keymap(int *code, char *key)
{
- if(identflags&IDF_OVERRIDDEN) { conoutf(CON_ERROR, "cannot override keymap %d", *code); return; }
- keym &km = keyms[*code];
- km.code = *code;
- DELETEA(km.name);
- km.name = newstring(key);
+ if(identflags&IDF_OVERRIDDEN) { conoutf(CON_ERROR, "cannot override keymap %d", *code); return; }
+ keym &km = keyms[*code];
+ km.code = *code;
+ DELETEA(km.name);
+ km.name = newstring(key);
}
-
+
COMMAND(keymap, "is");
keym *keypressed = NULL;
@@ -193,83 +193,83 @@ char *keyaction = NULL;
const char *getkeyname(int code)
{
- keym *km = keyms.access(code);
- return km ? km->name : NULL;
+ keym *km = keyms.access(code);
+ return km ? km->name : NULL;
}
void searchbinds(char *action, int type)
{
- vector<char> names;
- enumerate(keyms, keym, km,
- {
- if(!strcmp(km.actions[type], action))
- {
- if(names.length()) names.add(' ');
- names.put(km.name, strlen(km.name));
- }
- });
- names.add('\0');
- result(names.getbuf());
+ vector<char> names;
+ enumerate(keyms, keym, km,
+ {
+ if(!strcmp(km.actions[type], action))
+ {
+ if(names.length()) names.add(' ');
+ names.put(km.name, strlen(km.name));
+ }
+ });
+ names.add('\0');
+ result(names.getbuf());
}
keym *findbind(char *key)
{
- enumerate(keyms, keym, km,
- {
- if(!strcasecmp(km.name, key)) return &km;
- });
- return NULL;
-}
-
+ enumerate(keyms, keym, km,
+ {
+ if(!strcasecmp(km.name, key)) return &km;
+ });
+ return NULL;
+}
+
void getbind(char *key, int type)
{
- keym *km = findbind(key);
- result(km ? km->actions[type] : "");
-}
+ keym *km = findbind(key);
+ result(km ? km->actions[type] : "");
+}
void bindkey(char *key, char *action, int state, const char *cmd)
{
- if(identflags&IDF_OVERRIDDEN) { conoutf(CON_ERROR, "cannot override %s \"%s\"", cmd, key); return; }
- keym *km = findbind(key);
- if(!km) { conoutf(CON_ERROR, "unknown key \"%s\"", key); return; }
- char *&binding = km->actions[state];
- if(!keypressed || keyaction!=binding) delete[] binding;
- // trim white-space to make searchbinds more reliable
- while(iscubespace(*action)) action++;
- int len = strlen(action);
- while(len>0 && iscubespace(action[len-1])) len--;
- binding = newstring(action, len);
+ if(identflags&IDF_OVERRIDDEN) { conoutf(CON_ERROR, "cannot override %s \"%s\"", cmd, key); return; }
+ keym *km = findbind(key);
+ if(!km) { conoutf(CON_ERROR, "unknown key \"%s\"", key); return; }
+ char *&binding = km->actions[state];
+ if(!keypressed || keyaction!=binding) delete[] binding;
+ // trim white-space to make searchbinds more reliable
+ while(iscubespace(*action)) action++;
+ int len = strlen(action);
+ while(len>0 && iscubespace(action[len-1])) len--;
+ binding = newstring(action, len);
}
-ICOMMAND(bind, "ss", (char *key, char *action), bindkey(key, action, keym::ACTION_DEFAULT, "bind"));
+ICOMMAND(bind, "ss", (char *key, char *action), bindkey(key, action, keym::ACTION_DEFAULT, "bind"));
ICOMMAND(specbind, "ss", (char *key, char *action), bindkey(key, action, keym::ACTION_SPECTATOR, "specbind"));
ICOMMAND(editbind, "ss", (char *key, char *action), bindkey(key, action, keym::ACTION_EDITING, "editbind"));
-ICOMMAND(getbind, "s", (char *key), getbind(key, keym::ACTION_DEFAULT));
+ICOMMAND(getbind, "s", (char *key), getbind(key, keym::ACTION_DEFAULT));
ICOMMAND(getspecbind, "s", (char *key), getbind(key, keym::ACTION_SPECTATOR));
ICOMMAND(geteditbind, "s", (char *key), getbind(key, keym::ACTION_EDITING));
-ICOMMAND(searchbinds, "s", (char *action), searchbinds(action, keym::ACTION_DEFAULT));
+ICOMMAND(searchbinds, "s", (char *action), searchbinds(action, keym::ACTION_DEFAULT));
ICOMMAND(searchspecbinds, "s", (char *action), searchbinds(action, keym::ACTION_SPECTATOR));
ICOMMAND(searcheditbinds, "s", (char *action), searchbinds(action, keym::ACTION_EDITING));
void inputcommand(char *init, char *action = NULL, char *prompt = NULL, char *flags = NULL) // turns input to the command line on or off
{
- commandmillis = init ? totalmillis : -1;
- textinput(commandmillis >= 0, TI_CONSOLE);
- keyrepeat(commandmillis >= 0, KR_CONSOLE);
- copystring(commandbuf, init ? init : "");
- DELETEA(commandaction);
- DELETEA(commandprompt);
- commandpos = -1;
- if(action && action[0]) commandaction = newstring(action);
- if(prompt && prompt[0]) commandprompt = newstring(prompt);
- commandflags = 0;
- if(flags) while(*flags) switch(*flags++)
- {
- case 'c': commandflags |= CF_COMPLETE; break;
- case 'x': commandflags |= CF_EXECUTE; break;
- case 's': commandflags |= CF_COMPLETE|CF_EXECUTE; break;
- }
- else if(init) commandflags |= CF_COMPLETE|CF_EXECUTE;
+ commandmillis = init ? totalmillis : -1;
+ textinput(commandmillis >= 0, TI_CONSOLE);
+ keyrepeat(commandmillis >= 0, KR_CONSOLE);
+ copystring(commandbuf, init ? init : "");
+ DELETEA(commandaction);
+ DELETEA(commandprompt);
+ commandpos = -1;
+ if(action && action[0]) commandaction = newstring(action);
+ if(prompt && prompt[0]) commandprompt = newstring(prompt);
+ commandflags = 0;
+ if(flags) while(*flags) switch(*flags++)
+ {
+ case 'c': commandflags |= CF_COMPLETE; break;
+ case 'x': commandflags |= CF_EXECUTE; break;
+ case 's': commandflags |= CF_COMPLETE|CF_EXECUTE; break;
+ }
+ else if(init) commandflags |= CF_COMPLETE|CF_EXECUTE;
}
ICOMMAND(saycommand, "C", (char *init), inputcommand(init));
@@ -277,66 +277,66 @@ COMMAND(inputcommand, "ssss");
void pasteconsole()
{
- if(!SDL_HasClipboardText()) return;
- char *cb = SDL_GetClipboardText();
- if(!cb) return;
- size_t cblen = strlen(cb),
- commandlen = strlen(commandbuf),
- decoded = decodeutf8((uchar *)&commandbuf[commandlen], sizeof(commandbuf)-1-commandlen, (const uchar *)cb, cblen);
- commandbuf[commandlen + decoded] = '\0';
- SDL_free(cb);
+ if(!SDL_HasClipboardText()) return;
+ char *cb = SDL_GetClipboardText();
+ if(!cb) return;
+ size_t cblen = strlen(cb),
+ commandlen = strlen(commandbuf),
+ decoded = decodeutf8((uchar *)&commandbuf[commandlen], sizeof(commandbuf)-1-commandlen, (const uchar *)cb, cblen);
+ commandbuf[commandlen + decoded] = '\0';
+ SDL_free(cb);
}
struct hline
{
- char *buf, *action, *prompt;
- int flags;
-
- hline() : buf(NULL), action(NULL), prompt(NULL), flags(0) {}
- ~hline()
- {
- DELETEA(buf);
- DELETEA(action);
- DELETEA(prompt);
- }
-
- void restore()
- {
- copystring(commandbuf, buf);
- if(commandpos >= (int)strlen(commandbuf)) commandpos = -1;
- DELETEA(commandaction);
- DELETEA(commandprompt);
- if(action) commandaction = newstring(action);
- if(prompt) commandprompt = newstring(prompt);
- commandflags = flags;
- }
-
- bool shouldsave()
- {
- return strcmp(commandbuf, buf) ||
- (commandaction ? !action || strcmp(commandaction, action) : action!=NULL) ||
- (commandprompt ? !prompt || strcmp(commandprompt, prompt) : prompt!=NULL) ||
- commandflags != flags;
- }
-
- void save()
- {
- buf = newstring(commandbuf);
- if(commandaction) action = newstring(commandaction);
- if(commandprompt) prompt = newstring(commandprompt);
- flags = commandflags;
- }
-
- void run()
- {
- if(flags&CF_EXECUTE && buf[0]=='/') execute(buf+1);
- else if(action)
- {
- alias("commandbuf", buf);
- execute(action);
- }
- else game::toserver(buf);
- }
+ char *buf, *action, *prompt;
+ int flags;
+
+ hline() : buf(NULL), action(NULL), prompt(NULL), flags(0) {}
+ ~hline()
+ {
+ DELETEA(buf);
+ DELETEA(action);
+ DELETEA(prompt);
+ }
+
+ void restore()
+ {
+ copystring(commandbuf, buf);
+ if(commandpos >= (int)strlen(commandbuf)) commandpos = -1;
+ DELETEA(commandaction);
+ DELETEA(commandprompt);
+ if(action) commandaction = newstring(action);
+ if(prompt) commandprompt = newstring(prompt);
+ commandflags = flags;
+ }
+
+ bool shouldsave()
+ {
+ return strcmp(commandbuf, buf) ||
+ (commandaction ? !action || strcmp(commandaction, action) : action!=NULL) ||
+ (commandprompt ? !prompt || strcmp(commandprompt, prompt) : prompt!=NULL) ||
+ commandflags != flags;
+ }
+
+ void save()
+ {
+ buf = newstring(commandbuf);
+ if(commandaction) action = newstring(commandaction);
+ if(commandprompt) prompt = newstring(commandprompt);
+ flags = commandflags;
+ }
+
+ void run()
+ {
+ if(flags&CF_EXECUTE && buf[0]=='/') execute(buf+1);
+ else if(action)
+ {
+ alias("commandbuf", buf);
+ execute(action);
+ }
+ else game::toserver(buf);
+ }
};
vector<hline *> history;
int histpos = 0;
@@ -345,250 +345,246 @@ VARP(maxhistory, 0, 1000, 10000);
void history_(int *n)
{
- static bool inhistory = false;
- if(!inhistory && history.inrange(*n))
- {
- inhistory = true;
- history[history.length()-*n-1]->run();
- inhistory = false;
- }
+ static bool inhistory = false;
+ if(!inhistory && history.inrange(*n))
+ {
+ inhistory = true;
+ history[history.length()-*n-1]->run();
+ inhistory = false;
+ }
}
COMMANDN(history, history_, "i");
struct releaseaction
{
- keym *key;
- char *action;
+ keym *key;
+ char *action;
};
vector<releaseaction> releaseactions;
const char *addreleaseaction(char *s)
{
- if(!keypressed) { delete[] s; return NULL; }
- releaseaction &ra = releaseactions.add();
- ra.key = keypressed;
- ra.action = s;
- return keypressed->name;
+ if(!keypressed) { delete[] s; return NULL; }
+ releaseaction &ra = releaseactions.add();
+ ra.key = keypressed;
+ ra.action = s;
+ return keypressed->name;
}
void onrelease(const char *s)
{
- addreleaseaction(newstring(s));
+ addreleaseaction(newstring(s));
}
COMMAND(onrelease, "s");
void execbind(keym &k, bool isdown)
{
- loopv(releaseactions)
- {
- releaseaction &ra = releaseactions[i];
- if(ra.key==&k)
- {
- if(!isdown) execute(ra.action);
- delete[] ra.action;
- releaseactions.remove(i--);
- }
- }
- if(isdown)
- {
- int state = keym::ACTION_DEFAULT;
- if(!mainmenu)
- {
- if(editmode) state = keym::ACTION_EDITING;
- else if(player->state==CS_SPECTATOR) state = keym::ACTION_SPECTATOR;
- }
- char *&action = k.actions[state][0] ? k.actions[state] : k.actions[keym::ACTION_DEFAULT];
- keyaction = action;
- keypressed = &k;
- execute(keyaction);
- keypressed = NULL;
- if(keyaction!=action) delete[] keyaction;
- }
- k.pressed = isdown;
+ loopv(releaseactions)
+ {
+ releaseaction &ra = releaseactions[i];
+ if(ra.key==&k)
+ {
+ if(!isdown) execute(ra.action);
+ delete[] ra.action;
+ releaseactions.remove(i--);
+ }
+ }
+ if(isdown)
+ {
+ int state = keym::ACTION_DEFAULT;
+ if(!mainmenu)
+ {
+ if(editmode) state = keym::ACTION_EDITING;
+ else if(player->state==CS_SPECTATOR) state = keym::ACTION_SPECTATOR;
+ }
+ char *&action = k.actions[state][0] ? k.actions[state] : k.actions[keym::ACTION_DEFAULT];
+ keyaction = action;
+ keypressed = &k;
+ execute(keyaction);
+ keypressed = NULL;
+ if(keyaction!=action) delete[] keyaction;
+ }
+ k.pressed = isdown;
}
bool consoleinput(const char *str, int len)
{
- if(commandmillis < 0) return false;
-
- resetcomplete();
- int cmdlen = (int)strlen(commandbuf), cmdspace = int(sizeof(commandbuf)) - (cmdlen+1);
- len = min(len, cmdspace);
- if(commandpos<0)
- {
- memcpy(&commandbuf[cmdlen], str, len);
- }
- else
- {
- memmove(&commandbuf[commandpos+len], &commandbuf[commandpos], cmdlen - commandpos);
- memcpy(&commandbuf[commandpos], str, len);
- commandpos += len;
- }
- commandbuf[cmdlen + len] = '\0';
-
- return true;
+ if(commandmillis < 0) return false;
+
+ resetcomplete();
+ int cmdlen = (int)strlen(commandbuf), cmdspace = int(sizeof(commandbuf)) - (cmdlen+1);
+ len = min(len, cmdspace);
+ if(commandpos<0)
+ {
+ memcpy(&commandbuf[cmdlen], str, len);
+ }
+ else
+ {
+ memmove(&commandbuf[commandpos+len], &commandbuf[commandpos], cmdlen - commandpos);
+ memcpy(&commandbuf[commandpos], str, len);
+ commandpos += len;
+ }
+ commandbuf[cmdlen + len] = '\0';
+
+ return true;
}
bool consolekey(int code, bool isdown)
{
- if(commandmillis < 0) return false;
-
- #ifdef __APPLE__
- #define MOD_KEYS (KMOD_LGUI|KMOD_RGUI)
- #else
- #define MOD_KEYS (KMOD_LCTRL|KMOD_RCTRL)
- #endif
-
- if(isdown)
- {
- switch(code)
- {
- case SDLK_RETURN:
- case SDLK_KP_ENTER:
- break;
-
- case SDLK_HOME:
- if(strlen(commandbuf)) commandpos = 0;
- break;
-
- case SDLK_END:
- commandpos = -1;
- break;
-
- case SDLK_DELETE:
- {
- int len = (int)strlen(commandbuf);
- if(commandpos<0) break;
- memmove(&commandbuf[commandpos], &commandbuf[commandpos+1], len - commandpos);
- resetcomplete();
- if(commandpos>=len-1) commandpos = -1;
- break;
- }
-
- case SDLK_BACKSPACE:
- {
- int len = (int)strlen(commandbuf), i = commandpos>=0 ? commandpos : len;
- if(i<1) break;
- memmove(&commandbuf[i-1], &commandbuf[i], len - i + 1);
- resetcomplete();
- if(commandpos>0) commandpos--;
- else if(!commandpos && len<=1) commandpos = -1;
- break;
- }
-
- case SDLK_LEFT:
- if(commandpos>0) commandpos--;
- else if(commandpos<0) commandpos = (int)strlen(commandbuf)-1;
- break;
-
- case SDLK_RIGHT:
- if(commandpos>=0 && ++commandpos>=(int)strlen(commandbuf)) commandpos = -1;
- break;
-
- case SDLK_UP:
- if(histpos > history.length()) histpos = history.length();
- if(histpos > 0) history[--histpos]->restore();
- break;
-
- case SDLK_DOWN:
- if(histpos + 1 < history.length()) history[++histpos]->restore();
- break;
-
- case SDLK_TAB:
- if(commandflags&CF_COMPLETE)
- {
- complete(commandbuf, sizeof(commandbuf), commandflags&CF_EXECUTE ? "/" : NULL);
- if(commandpos>=0 && commandpos>=(int)strlen(commandbuf)) commandpos = -1;
- }
- break;
-
- case SDLK_v:
- if(SDL_GetModState()&MOD_KEYS) pasteconsole();
- break;
- }
- }
- else
- {
- if(code==SDLK_RETURN || code==SDLK_KP_ENTER)
- {
- hline *h = NULL;
- if(commandbuf[0])
- {
- if(history.empty() || history.last()->shouldsave())
- {
- if(maxhistory && history.length() >= maxhistory)
- {
- loopi(history.length()-maxhistory+1) delete history[i];
- history.remove(0, history.length()-maxhistory+1);
- }
- history.add(h = new hline)->save();
- }
- else h = history.last();
- }
- histpos = history.length();
- inputcommand(NULL);
- if(h) h->run();
- }
- else if(code==SDLK_ESCAPE)
- {
- histpos = history.length();
- inputcommand(NULL);
- }
- }
-
- return true;
+ if(commandmillis < 0) return false;
+
+#define MOD_KEYS (KMOD_LCTRL|KMOD_RCTRL)
+
+ if(isdown)
+ {
+ switch(code)
+ {
+ case SDLK_RETURN:
+ case SDLK_KP_ENTER:
+ break;
+
+ case SDLK_HOME:
+ if(strlen(commandbuf)) commandpos = 0;
+ break;
+
+ case SDLK_END:
+ commandpos = -1;
+ break;
+
+ case SDLK_DELETE:
+ {
+ int len = (int)strlen(commandbuf);
+ if(commandpos<0) break;
+ memmove(&commandbuf[commandpos], &commandbuf[commandpos+1], len - commandpos);
+ resetcomplete();
+ if(commandpos>=len-1) commandpos = -1;
+ break;
+ }
+
+ case SDLK_BACKSPACE:
+ {
+ int len = (int)strlen(commandbuf), i = commandpos>=0 ? commandpos : len;
+ if(i<1) break;
+ memmove(&commandbuf[i-1], &commandbuf[i], len - i + 1);
+ resetcomplete();
+ if(commandpos>0) commandpos--;
+ else if(!commandpos && len<=1) commandpos = -1;
+ break;
+ }
+
+ case SDLK_LEFT:
+ if(commandpos>0) commandpos--;
+ else if(commandpos<0) commandpos = (int)strlen(commandbuf)-1;
+ break;
+
+ case SDLK_RIGHT:
+ if(commandpos>=0 && ++commandpos>=(int)strlen(commandbuf)) commandpos = -1;
+ break;
+
+ case SDLK_UP:
+ if(histpos > history.length()) histpos = history.length();
+ if(histpos > 0) history[--histpos]->restore();
+ break;
+
+ case SDLK_DOWN:
+ if(histpos + 1 < history.length()) history[++histpos]->restore();
+ break;
+
+ case SDLK_TAB:
+ if(commandflags&CF_COMPLETE)
+ {
+ complete(commandbuf, sizeof(commandbuf), commandflags&CF_EXECUTE ? "/" : NULL);
+ if(commandpos>=0 && commandpos>=(int)strlen(commandbuf)) commandpos = -1;
+ }
+ break;
+
+ case SDLK_v:
+ if(SDL_GetModState()&MOD_KEYS) pasteconsole();
+ break;
+ }
+ }
+ else
+ {
+ if(code==SDLK_RETURN || code==SDLK_KP_ENTER)
+ {
+ hline *h = NULL;
+ if(commandbuf[0])
+ {
+ if(history.empty() || history.last()->shouldsave())
+ {
+ if(maxhistory && history.length() >= maxhistory)
+ {
+ loopi(history.length()-maxhistory+1) delete history[i];
+ history.remove(0, history.length()-maxhistory+1);
+ }
+ history.add(h = new hline)->save();
+ }
+ else h = history.last();
+ }
+ histpos = history.length();
+ inputcommand(NULL);
+ if(h) h->run();
+ }
+ else if(code==SDLK_ESCAPE)
+ {
+ histpos = history.length();
+ inputcommand(NULL);
+ }
+ }
+
+ return true;
}
void processtextinput(const char *str, int len)
{
- if(!g3d_input(str, len))
- consoleinput(str, len);
+ if(!g3d_input(str, len))
+ consoleinput(str, len);
}
void processkey(int code, bool isdown, int modstate)
{
- switch(code)
- {
- case SDLK_LGUI: case SDLK_RGUI:
- return;
- }
- keym *haskey = keyms.access(code);
- if(haskey && haskey->pressed) execbind(*haskey, isdown); // allow pressed keys to release
- else if(!g3d_key(code, isdown)) // 3D GUI mouse button intercept
- {
- if(!consolekey(code, isdown))
- {
- if(modstate&KMOD_GUI) return;
- if(haskey) execbind(*haskey, isdown);
- }
- }
+ switch(code)
+ {
+ case SDLK_LGUI: case SDLK_RGUI:
+ return;
+ }
+ keym *haskey = keyms.access(code);
+ if(haskey && haskey->pressed) execbind(*haskey, isdown); // allow pressed keys to release
+ else if(!g3d_key(code, isdown)) // 3D GUI mouse button intercept
+ {
+ if(!consolekey(code, isdown))
+ {
+ if(modstate&KMOD_GUI) return;
+ if(haskey) execbind(*haskey, isdown);
+ }
+ }
}
void clear_console()
{
- keyms.clear();
+ keyms.clear();
}
void writebinds(stream *f)
{
- static const char * const cmds[3] = { "bind", "specbind", "editbind" };
- vector<keym *> binds;
- enumerate(keyms, keym, km, binds.add(&km));
- binds.sortname();
- loopj(3)
- {
- loopv(binds)
- {
- keym &km = *binds[i];
- if(*km.actions[j])
- {
- if(validateblock(km.actions[j])) f->printf("%s %s [%s]\n", cmds[j], escapestring(km.name), km.actions[j]);
- else f->printf("%s %s %s\n", cmds[j], escapestring(km.name), escapestring(km.actions[j]));
- }
- }
- }
+ static const char * const cmds[3] = { "bind", "specbind", "editbind" };
+ vector<keym *> binds;
+ enumerate(keyms, keym, km, binds.add(&km));
+ binds.sortname();
+ loopj(3)
+ {
+ loopv(binds)
+ {
+ keym &km = *binds[i];
+ if(*km.actions[j])
+ {
+ if(validateblock(km.actions[j])) f->printf("%s %s [%s]\n", cmds[j], escapestring(km.name), km.actions[j]);
+ else f->printf("%s %s %s\n", cmds[j], escapestring(km.name), escapestring(km.actions[j]));
+ }
+ }
+ }
}
// tab-completion of all idents and base maps
@@ -597,62 +593,62 @@ enum { FILES_DIR = 0, FILES_VAR, FILES_LIST };
struct fileskey
{
- int type;
- const char *dir, *ext;
+ int type;
+ const char *dir, *ext;
- fileskey() {}
- fileskey(int type, const char *dir, const char *ext) : type(type), dir(dir), ext(ext) {}
+ fileskey() {}
+ fileskey(int type, const char *dir, const char *ext) : type(type), dir(dir), ext(ext) {}
};
static void cleanfilesdir(char *dir)
{
- int dirlen = (int)strlen(dir);
- while(dirlen > 0 && (dir[dirlen-1] == '/' || dir[dirlen-1] == '\\'))
- dir[--dirlen] = '\0';
+ int dirlen = (int)strlen(dir);
+ while(dirlen > 0 && (dir[dirlen-1] == '/' || dir[dirlen-1] == '\\'))
+ dir[--dirlen] = '\0';
}
struct filesval
{
- int type;
- char *dir, *ext;
- vector<char *> files;
- int millis;
-
- filesval(int type, const char *dir, const char *ext) : type(type), dir(newstring(dir)), ext(ext && ext[0] ? newstring(ext) : NULL), millis(-1) {}
- ~filesval() { DELETEA(dir); DELETEA(ext); files.deletearrays(); }
-
- void update()
- {
- if((type!=FILES_DIR && type!=FILES_VAR) || millis >= commandmillis) return;
- files.deletearrays();
- if(type==FILES_VAR)
- {
- string buf;
- buf[0] = '\0';
- if(ident *id = readident(dir)) switch(id->type)
- {
- case ID_SVAR: copystring(buf, *id->storage.s); break;
- case ID_ALIAS: copystring(buf, id->getstr()); break;
- }
- if(!buf[0]) copystring(buf, ".");
- cleanfilesdir(buf);
- listfiles(buf, ext, files);
- }
- else listfiles(dir, ext, files);
- files.sort();
- loopv(files) if(i && !strcmp(files[i], files[i-1])) delete[] files.remove(i--);
- millis = totalmillis;
- }
+ int type;
+ char *dir, *ext;
+ vector<char *> files;
+ int millis;
+
+ filesval(int type, const char *dir, const char *ext) : type(type), dir(newstring(dir)), ext(ext && ext[0] ? newstring(ext) : NULL), millis(-1) {}
+ ~filesval() { DELETEA(dir); DELETEA(ext); files.deletearrays(); }
+
+ void update()
+ {
+ if((type!=FILES_DIR && type!=FILES_VAR) || millis >= commandmillis) return;
+ files.deletearrays();
+ if(type==FILES_VAR)
+ {
+ string buf;
+ buf[0] = '\0';
+ if(ident *id = readident(dir)) switch(id->type)
+ {
+ case ID_SVAR: copystring(buf, *id->storage.s); break;
+ case ID_ALIAS: copystring(buf, id->getstr()); break;
+ }
+ if(!buf[0]) copystring(buf, ".");
+ cleanfilesdir(buf);
+ listfiles(buf, ext, files);
+ }
+ else listfiles(dir, ext, files);
+ files.sort();
+ loopv(files) if(i && !strcmp(files[i], files[i-1])) delete[] files.remove(i--);
+ millis = totalmillis;
+ }
};
static inline bool htcmp(const fileskey &x, const fileskey &y)
{
- return x.type==y.type && !strcmp(x.dir, y.dir) && (x.ext == y.ext || (x.ext && y.ext && !strcmp(x.ext, y.ext)));
+ return x.type==y.type && !strcmp(x.dir, y.dir) && (x.ext == y.ext || (x.ext && y.ext && !strcmp(x.ext, y.ext)));
}
static inline uint hthash(const fileskey &k)
{
- return hthash(k.dir);
+ return hthash(k.dir);
}
static hashtable<fileskey, filesval *> completefiles;
@@ -665,50 +661,50 @@ void resetcomplete() { completesize = 0; }
void addcomplete(char *command, int type, char *dir, char *ext)
{
- if(identflags&IDF_OVERRIDDEN)
- {
- conoutf(CON_ERROR, "cannot override complete %s", command);
- return;
- }
- if(!dir[0])
- {
- filesval **hasfiles = completions.access(command);
- if(hasfiles) *hasfiles = NULL;
- return;
- }
- if(type==FILES_DIR) cleanfilesdir(dir);
- if(ext)
- {
- if(strchr(ext, '*')) ext[0] = '\0';
- if(!ext[0]) ext = NULL;
- }
- fileskey key(type, dir, ext);
- filesval **val = completefiles.access(key);
- if(!val)
- {
- filesval *f = new filesval(type, dir, ext);
- if(type==FILES_LIST) explodelist(dir, f->files);
- val = &completefiles[fileskey(type, f->dir, f->ext)];
- *val = f;
- }
- filesval **hasfiles = completions.access(command);
- if(hasfiles) *hasfiles = *val;
- else completions[newstring(command)] = *val;
+ if(identflags&IDF_OVERRIDDEN)
+ {
+ conoutf(CON_ERROR, "cannot override complete %s", command);
+ return;
+ }
+ if(!dir[0])
+ {
+ filesval **hasfiles = completions.access(command);
+ if(hasfiles) *hasfiles = NULL;
+ return;
+ }
+ if(type==FILES_DIR) cleanfilesdir(dir);
+ if(ext)
+ {
+ if(strchr(ext, '*')) ext[0] = '\0';
+ if(!ext[0]) ext = NULL;
+ }
+ fileskey key(type, dir, ext);
+ filesval **val = completefiles.access(key);
+ if(!val)
+ {
+ filesval *f = new filesval(type, dir, ext);
+ if(type==FILES_LIST) explodelist(dir, f->files);
+ val = &completefiles[fileskey(type, f->dir, f->ext)];
+ *val = f;
+ }
+ filesval **hasfiles = completions.access(command);
+ if(hasfiles) *hasfiles = *val;
+ else completions[newstring(command)] = *val;
}
void addfilecomplete(char *command, char *dir, char *ext)
{
- addcomplete(command, FILES_DIR, dir, ext);
+ addcomplete(command, FILES_DIR, dir, ext);
}
void addvarcomplete(char *command, char *var, char *ext)
{
- addcomplete(command, FILES_VAR, var, ext);
+ addcomplete(command, FILES_VAR, var, ext);
}
void addlistcomplete(char *command, char *list)
{
- addcomplete(command, FILES_LIST, list, NULL);
+ addcomplete(command, FILES_LIST, list, NULL);
}
COMMANDN(complete, addfilecomplete, "sss");
@@ -717,69 +713,69 @@ COMMANDN(listcomplete, addlistcomplete, "ss");
void complete(char *s, int maxlen, const char *cmdprefix)
{
- int cmdlen = 0;
- if(cmdprefix)
- {
- cmdlen = strlen(cmdprefix);
- if(strncmp(s, cmdprefix, cmdlen)) prependstring(s, cmdprefix, maxlen);
- }
- if(!s[cmdlen]) return;
- if(!completesize) { completesize = (int)strlen(&s[cmdlen]); DELETEA(lastcomplete); }
-
- filesval *f = NULL;
- if(completesize)
- {
- char *end = strchr(&s[cmdlen], ' ');
- if(end) f = completions.find(stringslice(&s[cmdlen], end), NULL);
- }
-
- const char *nextcomplete = NULL;
- if(f) // complete using filenames
- {
- int commandsize = strchr(&s[cmdlen], ' ')+1-s;
- f->update();
- loopv(f->files)
- {
- if(strncmp(f->files[i], &s[commandsize], completesize+cmdlen-commandsize)==0 &&
- (!lastcomplete || strcmp(f->files[i], lastcomplete) > 0) && (!nextcomplete || strcmp(f->files[i], nextcomplete) < 0))
- nextcomplete = f->files[i];
- }
- cmdprefix = s;
- cmdlen = commandsize;
- }
- else // complete using command names
- {
- enumerate(idents, ident, id,
- if(strncmp(id.name, &s[cmdlen], completesize)==0 &&
- (!lastcomplete || strcmp(id.name, lastcomplete) > 0) && (!nextcomplete || strcmp(id.name, nextcomplete) < 0))
- nextcomplete = id.name;
- );
- }
- DELETEA(lastcomplete);
- if(nextcomplete)
- {
- cmdlen = min(cmdlen, maxlen-1);
- if(cmdlen) memmove(s, cmdprefix, cmdlen);
- copystring(&s[cmdlen], nextcomplete, maxlen-cmdlen);
- lastcomplete = newstring(nextcomplete);
- }
+ int cmdlen = 0;
+ if(cmdprefix)
+ {
+ cmdlen = strlen(cmdprefix);
+ if(strncmp(s, cmdprefix, cmdlen)) prependstring(s, cmdprefix, maxlen);
+ }
+ if(!s[cmdlen]) return;
+ if(!completesize) { completesize = (int)strlen(&s[cmdlen]); DELETEA(lastcomplete); }
+
+ filesval *f = NULL;
+ if(completesize)
+ {
+ char *end = strchr(&s[cmdlen], ' ');
+ if(end) f = completions.find(stringslice(&s[cmdlen], end), NULL);
+ }
+
+ const char *nextcomplete = NULL;
+ if(f) // complete using filenames
+ {
+ int commandsize = strchr(&s[cmdlen], ' ')+1-s;
+ f->update();
+ loopv(f->files)
+ {
+ if(strncmp(f->files[i], &s[commandsize], completesize+cmdlen-commandsize)==0 &&
+ (!lastcomplete || strcmp(f->files[i], lastcomplete) > 0) && (!nextcomplete || strcmp(f->files[i], nextcomplete) < 0))
+ nextcomplete = f->files[i];
+ }
+ cmdprefix = s;
+ cmdlen = commandsize;
+ }
+ else // complete using command names
+ {
+ enumerate(idents, ident, id,
+ if(strncmp(id.name, &s[cmdlen], completesize)==0 &&
+ (!lastcomplete || strcmp(id.name, lastcomplete) > 0) && (!nextcomplete || strcmp(id.name, nextcomplete) < 0))
+ nextcomplete = id.name;
+ );
+ }
+ DELETEA(lastcomplete);
+ if(nextcomplete)
+ {
+ cmdlen = min(cmdlen, maxlen-1);
+ if(cmdlen) memmove(s, cmdprefix, cmdlen);
+ copystring(&s[cmdlen], nextcomplete, maxlen-cmdlen);
+ lastcomplete = newstring(nextcomplete);
+ }
}
void writecompletions(stream *f)
{
- vector<char *> cmds;
- enumeratekt(completions, char *, k, filesval *, v, { if(v) cmds.add(k); });
- cmds.sort();
- loopv(cmds)
- {
- char *k = cmds[i];
- filesval *v = completions[k];
- if(v->type==FILES_LIST)
- {
- if(validateblock(v->dir)) f->printf("listcomplete %s [%s]\n", escapeid(k), v->dir);
- else f->printf("listcomplete %s %s\n", escapeid(k), escapestring(v->dir));
- }
- else f->printf("%s %s %s %s\n", v->type==FILES_VAR ? "varcomplete" : "complete", escapeid(k), escapestring(v->dir), escapestring(v->ext ? v->ext : "*"));
- }
+ vector<char *> cmds;
+ enumeratekt(completions, char *, k, filesval *, v, { if(v) cmds.add(k); });
+ cmds.sort();
+ loopv(cmds)
+ {
+ char *k = cmds[i];
+ filesval *v = completions[k];
+ if(v->type==FILES_LIST)
+ {
+ if(validateblock(v->dir)) f->printf("listcomplete %s [%s]\n", escapeid(k), v->dir);
+ else f->printf("listcomplete %s %s\n", escapeid(k), escapestring(v->dir));
+ }
+ else f->printf("%s %s %s %s\n", v->type==FILES_VAR ? "varcomplete" : "complete", escapeid(k), escapestring(v->dir), escapestring(v->ext ? v->ext : "*"));
+ }
}
diff --git a/src/engine/decal.cpp b/src/engine/decal.cpp
index fb70ebf..ccee329 100644
--- a/src/engine/decal.cpp
+++ b/src/engine/decal.cpp
@@ -2,641 +2,635 @@
struct decalvert
{
- vec pos;
- bvec4 color;
- vec2 tc;
+ vec pos;
+ bvec4 color;
+ vec2 tc;
};
struct decalinfo
{
- int millis;
- bvec color;
- ushort startvert, endvert;
+ int millis;
+ bvec color;
+ ushort startvert, endvert;
};
enum
{
- DF_RND4 = 1<<0,
- DF_ROTATE = 1<<1,
- DF_INVMOD = 1<<2,
- DF_OVERBRIGHT = 1<<3,
- DF_ADD = 1<<4,
- DF_SATURATE = 1<<5
+ DF_RND4 = 1<<0,
+ DF_ROTATE = 1<<1,
+ DF_INVMOD = 1<<2,
+ DF_OVERBRIGHT = 1<<3,
+ DF_ADD = 1<<4,
+ DF_SATURATE = 1<<5
};
VARFP(maxdecaltris, 1, 1024, 16384, initdecals());
VARP(decalfade, 1000, 10000, 60000);
-VAR(dbgdec, 0, 0, 1);
struct decalrenderer
{
- const char *texname;
- int flags, fadeintime, fadeouttime, timetolive;
- Texture *tex;
- decalinfo *decals;
- int maxdecals, startdecal, enddecal;
- decalvert *verts;
- int maxverts, startvert, endvert, lastvert, availverts;
- GLuint vbo;
- bool dirty;
-
- decalrenderer(const char *texname, int flags = 0, int fadeintime = 0, int fadeouttime = 1000, int timetolive = -1)
- : texname(texname), flags(flags),
- fadeintime(fadeintime), fadeouttime(fadeouttime), timetolive(timetolive),
- tex(NULL),
- decals(NULL), maxdecals(0), startdecal(0), enddecal(0),
- verts(NULL), maxverts(0), startvert(0), endvert(0), lastvert(0), availverts(0),
- vbo(0), dirty(false),
- decalu(0), decalv(0)
- {
- }
-
- ~decalrenderer()
- {
- DELETEA(decals);
- DELETEA(verts);
- }
-
- void init(int tris)
- {
- if(decals)
- {
- DELETEA(decals);
- maxdecals = startdecal = enddecal = 0;
- }
- if(verts)
- {
- DELETEA(verts);
- maxverts = startvert = endvert = lastvert = availverts = 0;
- }
- decals = new decalinfo[tris];
- maxdecals = tris;
- tex = textureload(texname, 3);
- maxverts = tris*3 + 3;
- availverts = maxverts - 3;
- verts = new decalvert[maxverts];
- }
-
- int hasdecals()
- {
- return enddecal < startdecal ? maxdecals - (startdecal - enddecal) : enddecal - startdecal;
- }
-
- void cleanup()
- {
- if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; }
- }
-
- void cleardecals()
- {
- startdecal = enddecal = 0;
- startvert = endvert = lastvert = 0;
- availverts = maxverts - 3;
- dirty = true;
- }
-
- int freedecal()
- {
- if(startdecal==enddecal) return 0;
-
- decalinfo &d = decals[startdecal];
- startdecal++;
- if(startdecal >= maxdecals) startdecal = 0;
-
- int removed = d.endvert < d.startvert ? maxverts - (d.startvert - d.endvert) : d.endvert - d.startvert;
- startvert = d.endvert;
- if(startvert==endvert) startvert = endvert = lastvert = 0;
- availverts += removed;
-
- return removed;
- }
-
- void fadedecal(decalinfo &d, uchar alpha)
- {
- bvec rgb;
- if(flags&DF_OVERBRIGHT) rgb = bvec(128, 128, 128);
- else
- {
- rgb = d.color;
- if(flags&(DF_ADD|DF_INVMOD)) rgb.scale(alpha, 255);
- }
- bvec4 color(rgb, alpha);
-
- decalvert *vert = &verts[d.startvert],
- *end = &verts[d.endvert < d.startvert ? maxverts : d.endvert];
- while(vert < end)
- {
- vert->color = color;
- vert++;
- }
- if(d.endvert < d.startvert)
- {
- vert = verts;
- end = &verts[d.endvert];
- while(vert < end)
- {
- vert->color = color;
- vert++;
- }
- }
- dirty = true;
- }
-
- void clearfadeddecals()
- {
- int threshold = lastmillis - (timetolive>=0 ? timetolive : decalfade) - fadeouttime;
- decalinfo *d = &decals[startdecal],
- *end = &decals[enddecal < startdecal ? maxdecals : enddecal];
- while(d < end && d->millis <= threshold) d++;
- if(d >= end && enddecal < startdecal)
- {
- d = decals;
- end = &decals[enddecal];
- while(d < end && d->millis <= threshold) d++;
- }
- int prevstart = startdecal;
- startdecal = d - decals;
- if(prevstart == startdecal) return;
- if(startdecal!=enddecal) startvert = decals[startdecal].startvert;
- else startvert = endvert = lastvert = 0;
- availverts = endvert < startvert ? startvert - endvert - 3 : maxverts - 3 - (endvert - startvert);
- dirty = true;
- }
-
- void fadeindecals()
- {
- if(!fadeintime) return;
- decalinfo *d = &decals[enddecal],
- *end = &decals[enddecal < startdecal ? 0 : startdecal];
- while(d > end)
- {
- d--;
- int fade = lastmillis - d->millis;
- if(fade >= fadeintime) return;
- fadedecal(*d, (fade<<8)/fadeintime);
- }
- if(enddecal < startdecal)
- {
- d = &decals[maxdecals];
- end = &decals[startdecal];
- while(d > end)
- {
- d--;
- int fade = lastmillis - d->millis;
- if(fade >= fadeintime) return;
- fadedecal(*d, (fade<<8)/fadeintime);
- }
- }
- }
-
- void fadeoutdecals()
- {
- decalinfo *d = &decals[startdecal],
- *end = &decals[enddecal < startdecal ? maxdecals : enddecal];
- int offset = (timetolive>=0 ? timetolive : decalfade) + fadeouttime - lastmillis;
- while(d < end)
- {
- int fade = d->millis + offset;
- if(fade >= fadeouttime) return;
- fadedecal(*d, (fade<<8)/fadeouttime);
- d++;
- }
- if(enddecal < startdecal)
- {
- d = decals;
- end = &decals[enddecal];
- while(d < end)
- {
- int fade = d->millis + offset;
- if(fade >= fadeouttime) return;
- fadedecal(*d, (fade<<8)/fadeouttime);
- d++;
- }
- }
- }
-
- static void setuprenderstate()
- {
- enablepolygonoffset(GL_POLYGON_OFFSET_FILL);
-
- glDepthMask(GL_FALSE);
- glEnable(GL_BLEND);
-
- gle::enablevertex();
- gle::enabletexcoord0();
- gle::enablecolor();
- }
-
- static void cleanuprenderstate()
- {
- gle::clearvbo();
-
- gle::disablevertex();
- gle::disabletexcoord0();
- gle::disablecolor();
-
- glDepthMask(GL_TRUE);
- glDisable(GL_BLEND);
-
- disablepolygonoffset(GL_POLYGON_OFFSET_FILL);
- }
-
- void render()
- {
- if(startvert==endvert) return;
-
- if(flags&DF_OVERBRIGHT)
- {
- glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
- SETSHADER(overbrightdecal);
- }
- else
- {
- if(flags&DF_INVMOD) { glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); zerofogcolor(); }
- else if(flags&DF_ADD) { glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); zerofogcolor(); }
- else glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- if(flags&DF_SATURATE) SETSHADER(saturatedecal);
- else foggedshader->set();
- }
-
- glBindTexture(GL_TEXTURE_2D, tex->id);
-
- if(!vbo) { glGenBuffers_(1, &vbo); dirty = true; }
- gle::bindvbo(vbo);
-
- int count = endvert < startvert ? maxverts - startvert : endvert - startvert;
- if(dirty)
- {
- glBufferData_(GL_ARRAY_BUFFER, maxverts*sizeof(decalvert), NULL, GL_STREAM_DRAW);
- glBufferSubData_(GL_ARRAY_BUFFER, 0, count*sizeof(decalvert), &verts[startvert]);
- if(endvert < startvert)
- {
- glBufferSubData_(GL_ARRAY_BUFFER, count*sizeof(decalvert), endvert*sizeof(decalvert), verts);
- count += endvert;
- }
- dirty = false;
- }
- else if(endvert < startvert) count += endvert;
-
- const decalvert *ptr = 0;
- gle::vertexpointer(sizeof(decalvert), ptr->pos.v);
- gle::texcoord0pointer(sizeof(decalvert), ptr->tc.v);
- gle::colorpointer(sizeof(decalvert), ptr->color.v);
-
- glDrawArrays(GL_TRIANGLES, 0, count);
- xtravertsva += count;
-
- if(flags&(DF_ADD|DF_INVMOD)) resetfogcolor();
-
- extern int intel_vertexarray_bug;
- if(intel_vertexarray_bug) glFlush();
- }
-
- decalinfo &newdecal()
- {
- decalinfo &d = decals[enddecal];
- int next = enddecal + 1;
- if(next>=maxdecals) next = 0;
- if(next==startdecal) freedecal();
- enddecal = next;
- dirty = true;
- return d;
- }
-
- ivec bbmin, bbmax;
- vec decalcenter, decalnormal, decaltangent, decalbitangent;
- float decalradius, decalu, decalv;
- bvec4 decalcolor;
-
- void adddecal(const vec &center, const vec &dir, float radius, const bvec &color, int info)
- {
- if(dir.iszero()) return;
-
- int bbradius = int(ceil(radius));
- bbmin = ivec(center).sub(bbradius);
- bbmax = ivec(center).add(bbradius);
-
- decalcolor = bvec4(color, 255);
- decalcenter = center;
- decalradius = radius;
- decalnormal = dir;
+ const char *texname;
+ int flags, fadeintime, fadeouttime, timetolive;
+ Texture *tex;
+ decalinfo *decals;
+ int maxdecals, startdecal, enddecal;
+ decalvert *verts;
+ int maxverts, startvert, endvert, lastvert, availverts;
+ GLuint vbo;
+ bool dirty;
+
+ decalrenderer(const char *texname, int flags = 0, int fadeintime = 0, int fadeouttime = 1000, int timetolive = -1)
+ : texname(texname), flags(flags),
+ fadeintime(fadeintime), fadeouttime(fadeouttime), timetolive(timetolive),
+ tex(NULL),
+ decals(NULL), maxdecals(0), startdecal(0), enddecal(0),
+ verts(NULL), maxverts(0), startvert(0), endvert(0), lastvert(0), availverts(0),
+ vbo(0), dirty(false),
+ decalu(0), decalv(0)
+ {
+ }
+
+ ~decalrenderer()
+ {
+ DELETEA(decals);
+ DELETEA(verts);
+ }
+
+ void init(int tris)
+ {
+ if(decals)
+ {
+ DELETEA(decals);
+ maxdecals = startdecal = enddecal = 0;
+ }
+ if(verts)
+ {
+ DELETEA(verts);
+ maxverts = startvert = endvert = lastvert = availverts = 0;
+ }
+ decals = new decalinfo[tris];
+ maxdecals = tris;
+ tex = textureload(texname, 3);
+ maxverts = tris*3 + 3;
+ availverts = maxverts - 3;
+ verts = new decalvert[maxverts];
+ }
+
+ int hasdecals()
+ {
+ return enddecal < startdecal ? maxdecals - (startdecal - enddecal) : enddecal - startdecal;
+ }
+
+ void cleanup()
+ {
+ if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; }
+ }
+
+ void cleardecals()
+ {
+ startdecal = enddecal = 0;
+ startvert = endvert = lastvert = 0;
+ availverts = maxverts - 3;
+ dirty = true;
+ }
+
+ int freedecal()
+ {
+ if(startdecal==enddecal) return 0;
+
+ decalinfo &d = decals[startdecal];
+ startdecal++;
+ if(startdecal >= maxdecals) startdecal = 0;
+
+ int removed = d.endvert < d.startvert ? maxverts - (d.startvert - d.endvert) : d.endvert - d.startvert;
+ startvert = d.endvert;
+ if(startvert==endvert) startvert = endvert = lastvert = 0;
+ availverts += removed;
+
+ return removed;
+ }
+
+ void fadedecal(decalinfo &d, uchar alpha)
+ {
+ bvec rgb;
+ if(flags&DF_OVERBRIGHT) rgb = bvec(128, 128, 128);
+ else
+ {
+ rgb = d.color;
+ if(flags&(DF_ADD|DF_INVMOD)) rgb.scale(alpha, 255);
+ }
+ bvec4 color(rgb, alpha);
+
+ decalvert *vert = &verts[d.startvert],
+ *end = &verts[d.endvert < d.startvert ? maxverts : d.endvert];
+ while(vert < end)
+ {
+ vert->color = color;
+ vert++;
+ }
+ if(d.endvert < d.startvert)
+ {
+ vert = verts;
+ end = &verts[d.endvert];
+ while(vert < end)
+ {
+ vert->color = color;
+ vert++;
+ }
+ }
+ dirty = true;
+ }
+
+ void clearfadeddecals()
+ {
+ int threshold = lastmillis - (timetolive>=0 ? timetolive : decalfade) - fadeouttime;
+ decalinfo *d = &decals[startdecal],
+ *end = &decals[enddecal < startdecal ? maxdecals : enddecal];
+ while(d < end && d->millis <= threshold) d++;
+ if(d >= end && enddecal < startdecal)
+ {
+ d = decals;
+ end = &decals[enddecal];
+ while(d < end && d->millis <= threshold) d++;
+ }
+ int prevstart = startdecal;
+ startdecal = d - decals;
+ if(prevstart == startdecal) return;
+ if(startdecal!=enddecal) startvert = decals[startdecal].startvert;
+ else startvert = endvert = lastvert = 0;
+ availverts = endvert < startvert ? startvert - endvert - 3 : maxverts - 3 - (endvert - startvert);
+ dirty = true;
+ }
+
+ void fadeindecals()
+ {
+ if(!fadeintime) return;
+ decalinfo *d = &decals[enddecal],
+ *end = &decals[enddecal < startdecal ? 0 : startdecal];
+ while(d > end)
+ {
+ d--;
+ int fade = lastmillis - d->millis;
+ if(fade >= fadeintime) return;
+ fadedecal(*d, (fade<<8)/fadeintime);
+ }
+ if(enddecal < startdecal)
+ {
+ d = &decals[maxdecals];
+ end = &decals[startdecal];
+ while(d > end)
+ {
+ d--;
+ int fade = lastmillis - d->millis;
+ if(fade >= fadeintime) return;
+ fadedecal(*d, (fade<<8)/fadeintime);
+ }
+ }
+ }
+
+ void fadeoutdecals()
+ {
+ decalinfo *d = &decals[startdecal],
+ *end = &decals[enddecal < startdecal ? maxdecals : enddecal];
+ int offset = (timetolive>=0 ? timetolive : decalfade) + fadeouttime - lastmillis;
+ while(d < end)
+ {
+ int fade = d->millis + offset;
+ if(fade >= fadeouttime) return;
+ fadedecal(*d, (fade<<8)/fadeouttime);
+ d++;
+ }
+ if(enddecal < startdecal)
+ {
+ d = decals;
+ end = &decals[enddecal];
+ while(d < end)
+ {
+ int fade = d->millis + offset;
+ if(fade >= fadeouttime) return;
+ fadedecal(*d, (fade<<8)/fadeouttime);
+ d++;
+ }
+ }
+ }
+
+ static void setuprenderstate()
+ {
+ enablepolygonoffset(GL_POLYGON_OFFSET_FILL);
+
+ glDepthMask(GL_FALSE);
+ glEnable(GL_BLEND);
+
+ gle::enablevertex();
+ gle::enabletexcoord0();
+ gle::enablecolor();
+ }
+
+ static void cleanuprenderstate()
+ {
+ gle::clearvbo();
+
+ gle::disablevertex();
+ gle::disabletexcoord0();
+ gle::disablecolor();
+
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+
+ disablepolygonoffset(GL_POLYGON_OFFSET_FILL);
+ }
+
+ void render()
+ {
+ if(startvert==endvert) return;
+
+ if(flags&DF_OVERBRIGHT)
+ {
+ glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
+ SETSHADER(overbrightdecal);
+ }
+ else
+ {
+ if(flags&DF_INVMOD) { glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); zerofogcolor(); }
+ else if(flags&DF_ADD) { glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); zerofogcolor(); }
+ else glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ if(flags&DF_SATURATE) SETSHADER(saturatedecal);
+ else foggedshader->set();
+ }
+
+ glBindTexture(GL_TEXTURE_2D, tex->id);
+
+ if(!vbo) { glGenBuffers_(1, &vbo); dirty = true; }
+ gle::bindvbo(vbo);
+
+ int count = endvert < startvert ? maxverts - startvert : endvert - startvert;
+ if(dirty)
+ {
+ glBufferData_(GL_ARRAY_BUFFER, maxverts*sizeof(decalvert), NULL, GL_STREAM_DRAW);
+ glBufferSubData_(GL_ARRAY_BUFFER, 0, count*sizeof(decalvert), &verts[startvert]);
+ if(endvert < startvert)
+ {
+ glBufferSubData_(GL_ARRAY_BUFFER, count*sizeof(decalvert), endvert*sizeof(decalvert), verts);
+ count += endvert;
+ }
+ dirty = false;
+ }
+ else if(endvert < startvert) count += endvert;
+
+ const decalvert *ptr = 0;
+ gle::vertexpointer(sizeof(decalvert), ptr->pos.v);
+ gle::texcoord0pointer(sizeof(decalvert), ptr->tc.v);
+ gle::colorpointer(sizeof(decalvert), ptr->color.v);
+
+ glDrawArrays(GL_TRIANGLES, 0, count);
+ xtravertsva += count;
+
+ if(flags&(DF_ADD|DF_INVMOD)) resetfogcolor();
+
+ extern int intel_vertexarray_bug;
+ if(intel_vertexarray_bug) glFlush();
+ }
+
+ decalinfo &newdecal()
+ {
+ decalinfo &d = decals[enddecal];
+ int next = enddecal + 1;
+ if(next>=maxdecals) next = 0;
+ if(next==startdecal) freedecal();
+ enddecal = next;
+ dirty = true;
+ return d;
+ }
+
+ ivec bbmin, bbmax;
+ vec decalcenter, decalnormal, decaltangent, decalbitangent;
+ float decalradius, decalu, decalv;
+ bvec4 decalcolor;
+
+ void adddecal(const vec &center, const vec &dir, float radius, const bvec &color, int info)
+ {
+ if(dir.iszero()) return;
+
+ int bbradius = int(ceil(radius));
+ bbmin = ivec(center).sub(bbradius);
+ bbmax = ivec(center).add(bbradius);
+
+ decalcolor = bvec4(color, 255);
+ decalcenter = center;
+ decalradius = radius;
+ decalnormal = dir;
#if 0
- decaltangent.orthogonal(dir);
+ decaltangent.orthogonal(dir);
#else
- decaltangent = vec(dir.z, -dir.x, dir.y);
- decaltangent.sub(vec(dir).mul(decaltangent.dot(dir)));
+ decaltangent = vec(dir.z, -dir.x, dir.y);
+ decaltangent.sub(vec(dir).mul(decaltangent.dot(dir)));
#endif
- if(flags&DF_ROTATE) decaltangent.rotate(rnd(360)*RAD, dir);
- decaltangent.normalize();
- decalbitangent.cross(decaltangent, dir);
- if(flags&DF_RND4)
- {
- decalu = 0.5f*(info&1);
- decalv = 0.5f*((info>>1)&1);
- }
-
- lastvert = endvert;
- gentris(worldroot, ivec(0, 0, 0), worldsize>>1);
- if(dbgdec)
- {
- int nverts = endvert < lastvert ? endvert + maxverts - lastvert : endvert - lastvert;
- conoutf(CON_DEBUG, "tris = %d, verts = %d, total tris = %d", nverts/3, nverts, (maxverts - 3 - availverts)/3);
- }
- if(endvert==lastvert) return;
-
- decalinfo &d = newdecal();
- d.color = color;
- d.millis = lastmillis;
- d.startvert = lastvert;
- d.endvert = endvert;
- }
-
- static int clip(const vec *in, int numin, const vec &dir, float below, float above, vec *out)
- {
- int numout = 0;
- const vec *p = &in[numin-1];
- float pc = dir.dot(*p);
- loopi(numin)
- {
- const vec &v = in[i];
- float c = dir.dot(v);
- if(c < below)
- {
- if(pc > above) out[numout++] = vec(*p).sub(v).mul((above - c)/(pc - c)).add(v);
- if(pc > below) out[numout++] = vec(*p).sub(v).mul((below - c)/(pc - c)).add(v);
- }
- else if(c > above)
- {
- if(pc < below) out[numout++] = vec(*p).sub(v).mul((below - c)/(pc - c)).add(v);
- if(pc < above) out[numout++] = vec(*p).sub(v).mul((above - c)/(pc - c)).add(v);
- }
- else
- {
- if(pc < below)
- {
- if(c > below) out[numout++] = vec(*p).sub(v).mul((below - c)/(pc - c)).add(v);
- }
- else if(pc > above && c < above) out[numout++] = vec(*p).sub(v).mul((above - c)/(pc - c)).add(v);
- out[numout++] = v;
- }
- p = &v;
- pc = c;
- }
- return numout;
- }
-
- void gentris(cube &cu, int orient, const ivec &o, int size, materialsurface *mat = NULL, int vismask = 0)
- {
- vec pos[MAXFACEVERTS+4] = { vec(0, 0, 0) };
- int numverts = 0, numplanes = 1;
- vec planes[2] = { vec(0, 0, 0) };
- if(mat)
- {
- planes[0] = vec(0, 0, 0);
- switch(orient)
- {
- #define GENFACEORIENT(orient, v0, v1, v2, v3) \
- case orient: \
- planes[0][dimension(orient)] = dimcoord(orient) ? 1 : -1; \
- v0 v1 v2 v3 \
- break;
- #define GENFACEVERT(orient, vert, x,y,z, xv,yv,zv) \
- pos[numverts++] = vec(x xv, y yv, z zv);
- GENFACEVERTS(o.x, o.x, o.y, o.y, o.z, o.z, , + mat->csize, , + mat->rsize, + 0.1f, - 0.1f);
- #undef GENFACEORIENT
- #undef GENFACEVERT
- }
- }
- else if(cu.texture[orient] == DEFAULT_SKY) return;
- else if(cu.ext && (numverts = cu.ext->surfaces[orient].numverts&MAXFACEVERTS))
- {
- vertinfo *verts = cu.ext->verts() + cu.ext->surfaces[orient].verts;
- ivec vo = ivec(o).mask(~0xFFF).shl(3);
- loopj(numverts) pos[j] = vec(verts[j].getxyz().add(vo)).mul(1/8.0f);
- planes[0].cross(pos[0], pos[1], pos[2]).normalize();
- if(numverts >= 4 && !(cu.merged&(1<<orient)) && !flataxisface(cu, orient) && faceconvexity(verts, numverts, size))
- {
- planes[1].cross(pos[0], pos[2], pos[3]).normalize();
- numplanes++;
- }
- }
- else if(cu.merged&(1<<orient)) return;
- else if(!vismask || (vismask&0x40 && visibleface(cu, orient, o, size, MAT_AIR, (cu.material&MAT_ALPHA)^MAT_ALPHA, MAT_ALPHA)))
- {
- ivec v[4];
- genfaceverts(cu, orient, v);
- int vis = 3, convex = faceconvexity(v, vis), order = convex < 0 ? 1 : 0;
- vec vo(o);
- pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
- if(vis&1) pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
- pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
- if(vis&2) pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
- planes[0].cross(pos[0], pos[1], pos[2]).normalize();
- if(convex) { planes[1].cross(pos[0], pos[2], pos[3]).normalize(); numplanes++; }
- }
- else return;
-
- loopl(numplanes)
- {
- const vec &n = planes[l];
- float facing = n.dot(decalnormal);
- if(facing <= 0) continue;
- vec p = vec(pos[0]).sub(decalcenter);
+ if(flags&DF_ROTATE) decaltangent.rotate(rnd(360)*RAD, dir);
+ decaltangent.normalize();
+ decalbitangent.cross(decaltangent, dir);
+ if(flags&DF_RND4)
+ {
+ decalu = 0.5f*(info&1);
+ decalv = 0.5f*((info>>1)&1);
+ }
+
+ lastvert = endvert;
+ gentris(worldroot, ivec(0, 0, 0), worldsize>>1);
+ if(endvert==lastvert) return;
+
+ decalinfo &d = newdecal();
+ d.color = color;
+ d.millis = lastmillis;
+ d.startvert = lastvert;
+ d.endvert = endvert;
+ }
+
+ static int clip(const vec *in, int numin, const vec &dir, float below, float above, vec *out)
+ {
+ int numout = 0;
+ const vec *p = &in[numin-1];
+ float pc = dir.dot(*p);
+ loopi(numin)
+ {
+ const vec &v = in[i];
+ float c = dir.dot(v);
+ if(c < below)
+ {
+ if(pc > above) out[numout++] = vec(*p).sub(v).mul((above - c)/(pc - c)).add(v);
+ if(pc > below) out[numout++] = vec(*p).sub(v).mul((below - c)/(pc - c)).add(v);
+ }
+ else if(c > above)
+ {
+ if(pc < below) out[numout++] = vec(*p).sub(v).mul((below - c)/(pc - c)).add(v);
+ if(pc < above) out[numout++] = vec(*p).sub(v).mul((above - c)/(pc - c)).add(v);
+ }
+ else
+ {
+ if(pc < below)
+ {
+ if(c > below) out[numout++] = vec(*p).sub(v).mul((below - c)/(pc - c)).add(v);
+ }
+ else if(pc > above && c < above) out[numout++] = vec(*p).sub(v).mul((above - c)/(pc - c)).add(v);
+ out[numout++] = v;
+ }
+ p = &v;
+ pc = c;
+ }
+ return numout;
+ }
+
+ void gentris(cube &cu, int orient, const ivec &o, int size, materialsurface *mat = NULL, int vismask = 0)
+ {
+ vec pos[MAXFACEVERTS+4] = { vec(0, 0, 0) };
+ int numverts = 0, numplanes = 1;
+ vec planes[2] = { vec(0, 0, 0) };
+ if(mat)
+ {
+ planes[0] = vec(0, 0, 0);
+ switch(orient)
+ {
+ #define GENFACEORIENT(orient, v0, v1, v2, v3) \
+ case orient: \
+ planes[0][dimension(orient)] = dimcoord(orient) ? 1 : -1; \
+ v0 v1 v2 v3 \
+ break;
+ #define GENFACEVERT(orient, vert, x,y,z, xv,yv,zv) \
+ pos[numverts++] = vec(x xv, y yv, z zv);
+ GENFACEVERTS(o.x, o.x, o.y, o.y, o.z, o.z, , + mat->csize, , + mat->rsize, + 0.1f, - 0.1f);
+ #undef GENFACEORIENT
+ #undef GENFACEVERT
+ }
+ }
+ else if(cu.texture[orient] == DEFAULT_SKY) return;
+ else if(cu.ext && (numverts = cu.ext->surfaces[orient].numverts&MAXFACEVERTS))
+ {
+ vertinfo *verts = cu.ext->verts() + cu.ext->surfaces[orient].verts;
+ ivec vo = ivec(o).mask(~0xFFF).shl(3);
+ loopj(numverts) pos[j] = vec(verts[j].getxyz().add(vo)).mul(1/8.0f);
+ planes[0].cross(pos[0], pos[1], pos[2]).normalize();
+ if(numverts >= 4 && !(cu.merged&(1<<orient)) && !flataxisface(cu, orient) && faceconvexity(verts, numverts, size))
+ {
+ planes[1].cross(pos[0], pos[2], pos[3]).normalize();
+ numplanes++;
+ }
+ }
+ else if(cu.merged&(1<<orient)) return;
+ else if(!vismask || (vismask&0x40 && visibleface(cu, orient, o, size, MAT_AIR, (cu.material&MAT_ALPHA)^MAT_ALPHA, MAT_ALPHA)))
+ {
+ ivec v[4];
+ genfaceverts(cu, orient, v);
+ int vis = 3, convex = faceconvexity(v, vis), order = convex < 0 ? 1 : 0;
+ vec vo(o);
+ pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
+ if(vis&1) pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
+ pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
+ if(vis&2) pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
+ planes[0].cross(pos[0], pos[1], pos[2]).normalize();
+ if(convex) { planes[1].cross(pos[0], pos[2], pos[3]).normalize(); numplanes++; }
+ }
+ else return;
+
+ loopl(numplanes)
+ {
+ const vec &n = planes[l];
+ float facing = n.dot(decalnormal);
+ if(facing <= 0) continue;
+ vec p = vec(pos[0]).sub(decalcenter);
#if 0
- // intersect ray along decal normal with plane
- float dist = n.dot(p) / facing;
- if(fabs(dist) > decalradius) continue;
- vec pcenter = vec(decalnormal).mul(dist).add(decalcenter);
+ // intersect ray along decal normal with plane
+ float dist = n.dot(p) / facing;
+ if(fabs(dist) > decalradius) continue;
+ vec pcenter = vec(decalnormal).mul(dist).add(decalcenter);
#else
- // travel back along plane normal from the decal center
- float dist = n.dot(p);
- if(fabs(dist) > decalradius) continue;
- vec pcenter = vec(n).mul(dist).add(decalcenter);
+ // travel back along plane normal from the decal center
+ float dist = n.dot(p);
+ if(fabs(dist) > decalradius) continue;
+ vec pcenter = vec(n).mul(dist).add(decalcenter);
#endif
- vec ft, fb;
- ft.orthogonal(n);
- ft.normalize();
- fb.cross(ft, n);
- vec pt = vec(ft).mul(ft.dot(decaltangent)).add(vec(fb).mul(fb.dot(decaltangent))).normalize(),
- pb = vec(ft).mul(ft.dot(decalbitangent)).add(vec(fb).mul(fb.dot(decalbitangent))).normalize();
- // orthonormalize projected bitangent to prevent streaking
- pb.sub(vec(pt).mul(pt.dot(pb))).normalize();
- vec v1[MAXFACEVERTS+4], v2[MAXFACEVERTS+4];
- float ptc = pt.dot(pcenter), pbc = pb.dot(pcenter);
- int numv;
- if(numplanes >= 2)
- {
- if(l) { pos[1] = pos[2]; pos[2] = pos[3]; }
- numv = clip(pos, 3, pt, ptc - decalradius, ptc + decalradius, v1);
- if(numv<3) continue;
- }
- else
- {
- numv = clip(pos, numverts, pt, ptc - decalradius, ptc + decalradius, v1);
- if(numv<3) continue;
- }
- numv = clip(v1, numv, pb, pbc - decalradius, pbc + decalradius, v2);
- if(numv<3) continue;
- float tsz = flags&DF_RND4 ? 0.5f : 1.0f, scale = tsz*0.5f/decalradius,
- tu = decalu + tsz*0.5f - ptc*scale, tv = decalv + tsz*0.5f - pbc*scale;
- pt.mul(scale); pb.mul(scale);
- decalvert dv1 = { v2[0], decalcolor, vec2(pt.dot(v2[0]) + tu, pb.dot(v2[0]) + tv) },
- dv2 = { v2[1], decalcolor, vec2(pt.dot(v2[1]) + tu, pb.dot(v2[1]) + tv) };
- int totalverts = 3*(numv-2);
- if(totalverts > maxverts-3) return;
- while(availverts < totalverts)
- {
- if(!freedecal()) return;
- }
- availverts -= totalverts;
- loopk(numv-2)
- {
- verts[endvert++] = dv1;
- verts[endvert++] = dv2;
- dv2.pos = v2[k+2];
- dv2.tc = vec2(pt.dot(v2[k+2]) + tu, pb.dot(v2[k+2]) + tv);
- verts[endvert++] = dv2;
- if(endvert>=maxverts) endvert = 0;
- }
- }
- }
-
- void findmaterials(vtxarray *va)
- {
- materialsurface *matbuf = va->matbuf;
- int matsurfs = va->matsurfs;
- loopi(matsurfs)
- {
- materialsurface &m = matbuf[i];
- if(!isclipped(m.material&MATF_VOLUME)) { i += m.skip; continue; }
- int dim = dimension(m.orient), dc = dimcoord(m.orient);
- if(dc ? decalnormal[dim] <= 0 : decalnormal[dim] >= 0) { i += m.skip; continue; }
- int c = C[dim], r = R[dim];
- for(;;)
- {
- materialsurface &m = matbuf[i];
- if(m.o[dim] >= bbmin[dim] && m.o[dim] <= bbmax[dim] &&
- m.o[c] + m.csize >= bbmin[c] && m.o[c] <= bbmax[c] &&
- m.o[r] + m.rsize >= bbmin[r] && m.o[r] <= bbmax[r])
- {
- static cube dummy;
- gentris(dummy, m.orient, m.o, max(m.csize, m.rsize), &m);
- }
- if(i+1 >= matsurfs) break;
- materialsurface &n = matbuf[i+1];
- if(n.material != m.material || n.orient != m.orient) break;
- i++;
- }
- }
- }
-
- void findescaped(cube *cu, const ivec &o, int size, int escaped)
- {
- loopi(8)
- {
- if(escaped&(1<<i))
- {
- ivec co(i, o, size);
- if(cu[i].children) findescaped(cu[i].children, co, size>>1, cu[i].escaped);
- else
- {
- int vismask = cu[i].merged;
- if(vismask) loopj(6) if(vismask&(1<<j)) gentris(cu[i], j, co, size);
- }
- }
- }
- }
-
- void gentris(cube *cu, const ivec &o, int size, int escaped = 0)
- {
- int overlap = octaboxoverlap(o, size, bbmin, bbmax);
- loopi(8)
- {
- if(overlap&(1<<i))
- {
- ivec co(i, o, size);
- if(cu[i].ext && cu[i].ext->va && cu[i].ext->va->matsurfs)
- findmaterials(cu[i].ext->va);
- if(cu[i].children) gentris(cu[i].children, co, size>>1, cu[i].escaped);
- else
- {
- int vismask = cu[i].visible;
- if(vismask&0xC0)
- {
- if(vismask&0x80) loopj(6) gentris(cu[i], j, co, size, NULL, vismask);
- else loopj(6) if(vismask&(1<<j)) gentris(cu[i], j, co, size);
- }
- }
- }
- else if(escaped&(1<<i))
- {
- ivec co(i, o, size);
- if(cu[i].children) findescaped(cu[i].children, co, size>>1, cu[i].escaped);
- else
- {
- int vismask = cu[i].merged;
- if(vismask) loopj(6) if(vismask&(1<<j)) gentris(cu[i], j, co, size);
- }
- }
- }
- }
+ vec ft, fb;
+ ft.orthogonal(n);
+ ft.normalize();
+ fb.cross(ft, n);
+ vec pt = vec(ft).mul(ft.dot(decaltangent)).add(vec(fb).mul(fb.dot(decaltangent))).normalize(),
+ pb = vec(ft).mul(ft.dot(decalbitangent)).add(vec(fb).mul(fb.dot(decalbitangent))).normalize();
+ // orthonormalize projected bitangent to prevent streaking
+ pb.sub(vec(pt).mul(pt.dot(pb))).normalize();
+ vec v1[MAXFACEVERTS+4], v2[MAXFACEVERTS+4];
+ float ptc = pt.dot(pcenter), pbc = pb.dot(pcenter);
+ int numv;
+ if(numplanes >= 2)
+ {
+ if(l) { pos[1] = pos[2]; pos[2] = pos[3]; }
+ numv = clip(pos, 3, pt, ptc - decalradius, ptc + decalradius, v1);
+ if(numv<3) continue;
+ }
+ else
+ {
+ numv = clip(pos, numverts, pt, ptc - decalradius, ptc + decalradius, v1);
+ if(numv<3) continue;
+ }
+ numv = clip(v1, numv, pb, pbc - decalradius, pbc + decalradius, v2);
+ if(numv<3) continue;
+ float tsz = flags&DF_RND4 ? 0.5f : 1.0f, scale = tsz*0.5f/decalradius,
+ tu = decalu + tsz*0.5f - ptc*scale, tv = decalv + tsz*0.5f - pbc*scale;
+ pt.mul(scale); pb.mul(scale);
+ decalvert dv1 = { v2[0], decalcolor, vec2(pt.dot(v2[0]) + tu, pb.dot(v2[0]) + tv) },
+ dv2 = { v2[1], decalcolor, vec2(pt.dot(v2[1]) + tu, pb.dot(v2[1]) + tv) };
+ int totalverts = 3*(numv-2);
+ if(totalverts > maxverts-3) return;
+ while(availverts < totalverts)
+ {
+ if(!freedecal()) return;
+ }
+ availverts -= totalverts;
+ loopk(numv-2)
+ {
+ verts[endvert++] = dv1;
+ verts[endvert++] = dv2;
+ dv2.pos = v2[k+2];
+ dv2.tc = vec2(pt.dot(v2[k+2]) + tu, pb.dot(v2[k+2]) + tv);
+ verts[endvert++] = dv2;
+ if(endvert>=maxverts) endvert = 0;
+ }
+ }
+ }
+
+ void findmaterials(vtxarray *va)
+ {
+ materialsurface *matbuf = va->matbuf;
+ int matsurfs = va->matsurfs;
+ loopi(matsurfs)
+ {
+ materialsurface &m = matbuf[i];
+ if(!isclipped(m.material&MATF_VOLUME)) { i += m.skip; continue; }
+ int dim = dimension(m.orient), dc = dimcoord(m.orient);
+ if(dc ? decalnormal[dim] <= 0 : decalnormal[dim] >= 0) { i += m.skip; continue; }
+ int c = C[dim], r = R[dim];
+ for(;;)
+ {
+ materialsurface &m = matbuf[i];
+ if(m.o[dim] >= bbmin[dim] && m.o[dim] <= bbmax[dim] &&
+ m.o[c] + m.csize >= bbmin[c] && m.o[c] <= bbmax[c] &&
+ m.o[r] + m.rsize >= bbmin[r] && m.o[r] <= bbmax[r])
+ {
+ static cube dummy;
+ gentris(dummy, m.orient, m.o, max(m.csize, m.rsize), &m);
+ }
+ if(i+1 >= matsurfs) break;
+ materialsurface &n = matbuf[i+1];
+ if(n.material != m.material || n.orient != m.orient) break;
+ i++;
+ }
+ }
+ }
+
+ void findescaped(cube *cu, const ivec &o, int size, int escaped)
+ {
+ loopi(8)
+ {
+ if(escaped&(1<<i))
+ {
+ ivec co(i, o, size);
+ if(cu[i].children) findescaped(cu[i].children, co, size>>1, cu[i].escaped);
+ else
+ {
+ int vismask = cu[i].merged;
+ if(vismask) loopj(6) if(vismask&(1<<j)) gentris(cu[i], j, co, size);
+ }
+ }
+ }
+ }
+
+ void gentris(cube *cu, const ivec &o, int size, int escaped = 0)
+ {
+ int overlap = octaboxoverlap(o, size, bbmin, bbmax);
+ loopi(8)
+ {
+ if(overlap&(1<<i))
+ {
+ ivec co(i, o, size);
+ if(cu[i].ext && cu[i].ext->va && cu[i].ext->va->matsurfs)
+ findmaterials(cu[i].ext->va);
+ if(cu[i].children) gentris(cu[i].children, co, size>>1, cu[i].escaped);
+ else
+ {
+ int vismask = cu[i].visible;
+ if(vismask&0xC0)
+ {
+ if(vismask&0x80) loopj(6) gentris(cu[i], j, co, size, NULL, vismask);
+ else loopj(6) if(vismask&(1<<j)) gentris(cu[i], j, co, size);
+ }
+ }
+ }
+ else if(escaped&(1<<i))
+ {
+ ivec co(i, o, size);
+ if(cu[i].children) findescaped(cu[i].children, co, size>>1, cu[i].escaped);
+ else
+ {
+ int vismask = cu[i].merged;
+ if(vismask) loopj(6) if(vismask&(1<<j)) gentris(cu[i], j, co, size);
+ }
+ }
+ }
+ }
};
decalrenderer decals[] =
{
- decalrenderer("<grey>packages/particles/scorch.png", DF_ROTATE, 500),
- decalrenderer("<grey>packages/particles/blood.png", DF_RND4|DF_ROTATE|DF_INVMOD),
- decalrenderer("<grey>packages/particles/bullet.png", DF_OVERBRIGHT)
+ decalrenderer("<grey>packages/particles/scorch.png", DF_ROTATE, 500),
+ decalrenderer("<grey>packages/particles/blood.png", DF_RND4|DF_ROTATE|DF_INVMOD),
+ decalrenderer("<grey>packages/particles/bullet.png", DF_OVERBRIGHT)
};
void initdecals()
{
- loopi(sizeof(decals)/sizeof(decals[0])) decals[i].init(maxdecaltris);
+ loopi(sizeof(decals)/sizeof(decals[0])) decals[i].init(maxdecaltris);
}
void cleardecals()
{
- loopi(sizeof(decals)/sizeof(decals[0])) decals[i].cleardecals();
+ loopi(sizeof(decals)/sizeof(decals[0])) decals[i].cleardecals();
}
void cleanupdecals()
{
- loopi(sizeof(decals)/sizeof(decals[0])) decals[i].cleanup();
+ loopi(sizeof(decals)/sizeof(decals[0])) decals[i].cleanup();
}
VARNP(decals, showdecals, 0, 1, 1);
void renderdecals(bool mainpass)
{
- bool rendered = false;
- loopi(sizeof(decals)/sizeof(decals[0]))
- {
- decalrenderer &d = decals[i];
- if(mainpass)
- {
- d.clearfadeddecals();
- d.fadeindecals();
- d.fadeoutdecals();
- }
- if(!showdecals || !d.hasdecals()) continue;
- if(!rendered)
- {
- rendered = true;
- decalrenderer::setuprenderstate();
- }
- d.render();
- }
- if(!rendered) return;
- decalrenderer::cleanuprenderstate();
+ bool rendered = false;
+ loopi(sizeof(decals)/sizeof(decals[0]))
+ {
+ decalrenderer &d = decals[i];
+ if(mainpass)
+ {
+ d.clearfadeddecals();
+ d.fadeindecals();
+ d.fadeoutdecals();
+ }
+ if(!showdecals || !d.hasdecals()) continue;
+ if(!rendered)
+ {
+ rendered = true;
+ decalrenderer::setuprenderstate();
+ }
+ d.render();
+ }
+ if(!rendered) return;
+ decalrenderer::cleanuprenderstate();
}
VARP(maxdecaldistance, 1, 512, 10000);
void adddecal(int type, const vec &center, const vec &surface, float radius, const bvec &color, int info)
{
- if(!showdecals || type<0 || (size_t)type>=sizeof(decals)/sizeof(decals[0]) || center.dist(camera1->o) - radius > maxdecaldistance) return;
- decalrenderer &d = decals[type];
- d.adddecal(center, surface, radius, color, info);
+ if(!showdecals || type<0 || (size_t)type>=sizeof(decals)/sizeof(decals[0]) || center.dist(camera1->o) - radius > maxdecaldistance) return;
+ decalrenderer &d = decals[type];
+ d.adddecal(center, surface, radius, color, info);
}
diff --git a/src/engine/depthfx.h b/src/engine/depthfx.h
index 8332ab5..d313b27 100644
--- a/src/engine/depthfx.h
+++ b/src/engine/depthfx.h
@@ -25,150 +25,150 @@ vec depthfxmin(1e16f, 1e16f, 1e16f), depthfxmax(1e16f, 1e16f, 1e16f);
static struct depthfxtexture : rendertarget
{
- const GLenum *colorformats() const
- {
- static const GLenum colorfmts[] = { GL_RG16F, GL_RGB16F, GL_RGBA, GL_RGBA8, GL_RGB, GL_RGB8, GL_FALSE };
- return &colorfmts[fpdepthfx && hasTF ? (hasTRG ? 0 : 1) : 2];
- }
-
- float eyedepth(const vec &p) const
- {
- return max(-cammatrix.transform<vec>(p).z, 0.0f);
- }
-
- void addscissorvert(const vec &v, float &sx1, float &sy1, float &sx2, float &sy2)
- {
- vec p = camprojmatrix.perspectivetransform(v);
- sx1 = min(sx1, p.x);
- sy1 = min(sy1, p.y);
- sx2 = max(sx2, p.x);
- sy2 = max(sy2, p.y);
- }
-
- bool addscissorbox(const vec &center, float size)
- {
- float sx1, sy1, sx2, sy2;
- calcspherescissor(center, size, sx1, sy1, sx2, sy2);
- return addblurtiles(sx1, sy1, sx2, sy2);
- }
-
- bool addscissorbox(const vec &bbmin, const vec &bbmax)
- {
- float sx1 = 1, sy1 = 1, sx2 = -1, sy2 = -1;
- loopi(8)
- {
- vec v(i&1 ? bbmax.x : bbmin.x, i&2 ? bbmax.y : bbmin.y, i&4 ? bbmax.z : bbmin.z);
- addscissorvert(v, sx1, sy1, sx2, sy2);
- }
- return addblurtiles(sx1, sy1, sx2, sy2);
- }
-
- bool screenrect() const { return true; }
- bool filter() const { return blurdepthfx!=0; }
- bool highprecision() const { return colorfmt==GL_RG16F || colorfmt==GL_RGB16F; }
- bool emulatehighprecision() const { return depthfxemuprecision && !blurdepthfx; }
-
- bool shouldrender()
- {
- extern void finddepthfxranges();
- finddepthfxranges();
- return (numdepthfxranges && scissorx1 < scissorx2 && scissory1 < scissory2);
- }
-
- bool dorender()
- {
- glClearColor(1, 1, 1, 1);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- depthfxing = true;
- refracting = -1;
-
- extern void renderdepthobstacles(const vec &bbmin, const vec &bbmax, float scale, float *ranges, int numranges);
- float scale = depthfxscale;
- float *ranges = depthfxranges;
- int numranges = numdepthfxranges;
- if(highprecision())
- {
- scale = depthfxfpscale;
- ranges = NULL;
- numranges = 0;
- }
- else if(emulatehighprecision())
- {
- scale = depthfxfpscale;
- ranges = NULL;
- numranges = -3;
- }
- renderdepthobstacles(depthfxmin, depthfxmax, scale, ranges, numranges);
-
- refracting = 0;
- depthfxing = false;
-
- return numdepthfxranges > 0;
- }
+ const GLenum *colorformats() const
+ {
+ static const GLenum colorfmts[] = { GL_RG16F, GL_RGB16F, GL_RGBA, GL_RGBA8, GL_RGB, GL_RGB8, GL_FALSE };
+ return &colorfmts[fpdepthfx && hasTF ? (hasTRG ? 0 : 1) : 2];
+ }
+
+ float eyedepth(const vec &p) const
+ {
+ return max(-cammatrix.transform<vec>(p).z, 0.0f);
+ }
+
+ void addscissorvert(const vec &v, float &sx1, float &sy1, float &sx2, float &sy2)
+ {
+ vec p = camprojmatrix.perspectivetransform(v);
+ sx1 = min(sx1, p.x);
+ sy1 = min(sy1, p.y);
+ sx2 = max(sx2, p.x);
+ sy2 = max(sy2, p.y);
+ }
+
+ bool addscissorbox(const vec &center, float size)
+ {
+ float sx1, sy1, sx2, sy2;
+ calcspherescissor(center, size, sx1, sy1, sx2, sy2);
+ return addblurtiles(sx1, sy1, sx2, sy2);
+ }
+
+ bool addscissorbox(const vec &bbmin, const vec &bbmax)
+ {
+ float sx1 = 1, sy1 = 1, sx2 = -1, sy2 = -1;
+ loopi(8)
+ {
+ vec v(i&1 ? bbmax.x : bbmin.x, i&2 ? bbmax.y : bbmin.y, i&4 ? bbmax.z : bbmin.z);
+ addscissorvert(v, sx1, sy1, sx2, sy2);
+ }
+ return addblurtiles(sx1, sy1, sx2, sy2);
+ }
+
+ bool screenrect() const { return true; }
+ bool filter() const { return blurdepthfx!=0; }
+ bool highprecision() const { return colorfmt==GL_RG16F || colorfmt==GL_RGB16F; }
+ bool emulatehighprecision() const { return depthfxemuprecision && !blurdepthfx; }
+
+ bool shouldrender()
+ {
+ extern void finddepthfxranges();
+ finddepthfxranges();
+ return (numdepthfxranges && scissorx1 < scissorx2 && scissory1 < scissory2);
+ }
+
+ bool dorender()
+ {
+ glClearColor(1, 1, 1, 1);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ depthfxing = true;
+ refracting = -1;
+
+ extern void renderdepthobstacles(const vec &bbmin, const vec &bbmax, float scale, float *ranges, int numranges);
+ float scale = depthfxscale;
+ float *ranges = depthfxranges;
+ int numranges = numdepthfxranges;
+ if(highprecision())
+ {
+ scale = depthfxfpscale;
+ ranges = NULL;
+ numranges = 0;
+ }
+ else if(emulatehighprecision())
+ {
+ scale = depthfxfpscale;
+ ranges = NULL;
+ numranges = -3;
+ }
+ renderdepthobstacles(depthfxmin, depthfxmax, scale, ranges, numranges);
+
+ refracting = 0;
+ depthfxing = false;
+
+ return numdepthfxranges > 0;
+ }
} depthfxtex;
void cleanupdepthfx()
{
- depthfxtex.cleanup(true);
+ depthfxtex.cleanup(true);
}
bool depthfxing = false;
bool binddepthfxtex()
{
- if(!reflecting && !refracting && depthfx && depthfxtex.rendertex && numdepthfxranges>0)
- {
- glActiveTexture_(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, depthfxtex.rendertex);
- glActiveTexture_(GL_TEXTURE0);
- return true;
- }
- return false;
+ if(!reflecting && !refracting && depthfx && depthfxtex.rendertex && numdepthfxranges>0)
+ {
+ glActiveTexture_(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, depthfxtex.rendertex);
+ glActiveTexture_(GL_TEXTURE0);
+ return true;
+ }
+ return false;
}
void binddepthfxparams(float blend, float minblend = 0, bool allow = true, void *owner = NULL)
{
- if(!reflecting && !refracting && depthfx && depthfxtex.rendertex && numdepthfxranges>0)
- {
- float scale = 0, offset = -1, texscale = 0;
- if(!depthfxtex.highprecision())
- {
- float select[4] = { 0, 0, 0, 0 };
- if(!depthfxtex.emulatehighprecision())
- {
- loopi(numdepthfxranges) if(depthfxowners[i]==owner)
- {
- select[i] = float(depthfxscale)/blend;
- scale = 1.0f/blend;
- offset = -float(depthfxranges[i] - depthfxbias)/blend;
- break;
- }
- }
- else if(allow)
- {
- select[0] = float(depthfxfpscale)/blend;
- select[1] = select[0]/256;
- select[2] = select[1]/256;
- scale = 1.0f/blend;
- offset = 0;
- }
- LOCALPARAMF(depthfxselect, select[0], select[1], select[2], select[3]);
- }
- else if(allow)
- {
- scale = 1.0f/blend;
- offset = 0;
- texscale = float(depthfxfpscale)/blend;
- }
- LOCALPARAMF(depthfxparams, scale, offset, texscale, minblend);
- }
+ if(!reflecting && !refracting && depthfx && depthfxtex.rendertex && numdepthfxranges>0)
+ {
+ float scale = 0, offset = -1, texscale = 0;
+ if(!depthfxtex.highprecision())
+ {
+ float select[4] = { 0, 0, 0, 0 };
+ if(!depthfxtex.emulatehighprecision())
+ {
+ loopi(numdepthfxranges) if(depthfxowners[i]==owner)
+ {
+ select[i] = float(depthfxscale)/blend;
+ scale = 1.0f/blend;
+ offset = -float(depthfxranges[i] - depthfxbias)/blend;
+ break;
+ }
+ }
+ else if(allow)
+ {
+ select[0] = float(depthfxfpscale)/blend;
+ select[1] = select[0]/256;
+ select[2] = select[1]/256;
+ scale = 1.0f/blend;
+ offset = 0;
+ }
+ LOCALPARAMF(depthfxselect, select[0], select[1], select[2], select[3]);
+ }
+ else if(allow)
+ {
+ scale = 1.0f/blend;
+ offset = 0;
+ texscale = float(depthfxfpscale)/blend;
+ }
+ LOCALPARAMF(depthfxparams, scale, offset, texscale, minblend);
+ }
}
void drawdepthfxtex()
{
- if(!depthfx) return;
+ if(!depthfx) return;
- depthfxtex.render(1<<depthfxsize, 1<<depthfxsize, blurdepthfx, blurdepthfxsigma/100.0f);
+ depthfxtex.render(1<<depthfxsize, 1<<depthfxsize, blurdepthfx, blurdepthfxsigma/100.0f);
}
diff --git a/src/engine/dynlight.cpp b/src/engine/dynlight.cpp
index 646353b..66d8088 100644
--- a/src/engine/dynlight.cpp
+++ b/src/engine/dynlight.cpp
@@ -5,48 +5,48 @@ VARP(dynlightdist, 0, 1024, 10000);
struct dynlight
{
- vec o, hud;
- float radius, initradius, curradius, dist;
- vec color, initcolor, curcolor;
- int fade, peak, expire, flags;
- physent *owner;
-
- void calcradius()
- {
- if(fade + peak > 0)
- {
- int remaining = expire - lastmillis;
- if(flags&DL_EXPAND)
- curradius = initradius + (radius - initradius) * (1.0f - remaining/float(fade + peak));
- else if(!(flags&DL_FLASH) && remaining > fade)
- curradius = initradius + (radius - initradius) * (1.0f - float(remaining - fade)/peak);
- else if(flags&DL_SHRINK)
- curradius = (radius*remaining)/fade;
- else curradius = radius;
- }
- else curradius = radius;
- }
-
- void calccolor()
- {
- if(flags&DL_FLASH || peak <= 0) curcolor = color;
- else
- {
- int peaking = expire - lastmillis - fade;
- if(peaking <= 0) curcolor = color;
- else curcolor.lerp(initcolor, color, 1.0f - float(peaking)/peak);
- }
-
- float intensity = 1.0f;
- if(fade > 0)
- {
- int fading = expire - lastmillis;
- if(fading < fade) intensity = float(fading)/fade;
- }
- curcolor.mul(intensity);
- // KLUGE: this prevents nvidia drivers from trying to recompile dynlight fragment programs
- loopk(3) if(fmod(curcolor[k], 1.0f/256) < 0.001f) curcolor[k] += 0.001f;
- }
+ vec o, hud;
+ float radius, initradius, curradius, dist;
+ vec color, initcolor, curcolor;
+ int fade, peak, expire, flags;
+ physent *owner;
+
+ void calcradius()
+ {
+ if(fade + peak > 0)
+ {
+ int remaining = expire - lastmillis;
+ if(flags&DL_EXPAND)
+ curradius = initradius + (radius - initradius) * (1.0f - remaining/float(fade + peak));
+ else if(!(flags&DL_FLASH) && remaining > fade)
+ curradius = initradius + (radius - initradius) * (1.0f - float(remaining - fade)/peak);
+ else if(flags&DL_SHRINK)
+ curradius = (radius*remaining)/fade;
+ else curradius = radius;
+ }
+ else curradius = radius;
+ }
+
+ void calccolor()
+ {
+ if(flags&DL_FLASH || peak <= 0) curcolor = color;
+ else
+ {
+ int peaking = expire - lastmillis - fade;
+ if(peaking <= 0) curcolor = color;
+ else curcolor.lerp(initcolor, color, 1.0f - float(peaking)/peak);
+ }
+
+ float intensity = 1.0f;
+ if(fade > 0)
+ {
+ int fading = expire - lastmillis;
+ if(fading < fade) intensity = float(fading)/fade;
+ }
+ curcolor.mul(intensity);
+ // KLUGE: this prevents nvidia drivers from trying to recompile dynlight fragment programs
+ loopk(3) if(fmod(curcolor[k], 1.0f/256) < 0.001f) curcolor[k] += 0.001f;
+ }
};
vector<dynlight> dynlights;
@@ -54,174 +54,174 @@ vector<dynlight *> closedynlights;
void adddynlight(const vec &o, float radius, const vec &color, int fade, int peak, int flags, float initradius, const vec &initcolor, physent *owner)
{
- if(!maxdynlights) return;
- if(o.dist(camera1->o) > dynlightdist || radius <= 0) return;
-
- int insert = 0, expire = fade + peak + lastmillis;
- loopvrev(dynlights) if(expire>=dynlights[i].expire) { insert = i+1; break; }
- dynlight d;
- d.o = d.hud = o;
- d.radius = radius;
- d.initradius = initradius;
- d.color = color;
- d.initcolor = initcolor;
- d.fade = fade;
- d.peak = peak;
- d.expire = expire;
- d.flags = flags;
- d.owner = owner;
- dynlights.insert(insert, d);
+ if(!maxdynlights) return;
+ if(o.dist(camera1->o) > dynlightdist || radius <= 0) return;
+
+ int insert = 0, expire = fade + peak + lastmillis;
+ loopvrev(dynlights) if(expire>=dynlights[i].expire) { insert = i+1; break; }
+ dynlight d;
+ d.o = d.hud = o;
+ d.radius = radius;
+ d.initradius = initradius;
+ d.color = color;
+ d.initcolor = initcolor;
+ d.fade = fade;
+ d.peak = peak;
+ d.expire = expire;
+ d.flags = flags;
+ d.owner = owner;
+ dynlights.insert(insert, d);
}
void cleardynlights()
{
- int faded = -1;
- loopv(dynlights) if(lastmillis<dynlights[i].expire) { faded = i; break; }
- if(faded<0) dynlights.setsize(0);
- else if(faded>0) dynlights.remove(0, faded);
+ int faded = -1;
+ loopv(dynlights) if(lastmillis<dynlights[i].expire) { faded = i; break; }
+ if(faded<0) dynlights.setsize(0);
+ else if(faded>0) dynlights.remove(0, faded);
}
void removetrackeddynlights(physent *owner)
{
- loopvrev(dynlights) if(owner ? dynlights[i].owner == owner : dynlights[i].owner != NULL) dynlights.remove(i);
+ loopvrev(dynlights) if(owner ? dynlights[i].owner == owner : dynlights[i].owner != NULL) dynlights.remove(i);
}
void updatedynlights()
{
- cleardynlights();
- game::adddynlights();
-
- loopv(dynlights)
- {
- dynlight &d = dynlights[i];
- if(d.owner) game::dynlighttrack(d.owner, d.o, d.hud);
- d.calcradius();
- d.calccolor();
- }
+ cleardynlights();
+ game::adddynlights();
+
+ loopv(dynlights)
+ {
+ dynlight &d = dynlights[i];
+ if(d.owner) game::dynlighttrack(d.owner, d.o, d.hud);
+ d.calcradius();
+ d.calccolor();
+ }
}
int finddynlights()
{
- closedynlights.setsize(0);
- if(!maxdynlights) return 0;
- physent e;
- e.type = ENT_CAMERA;
- loopvj(dynlights)
- {
- dynlight &d = dynlights[j];
- if(d.curradius <= 0) continue;
- d.dist = camera1->o.dist(d.o) - d.curradius;
- if(d.dist > dynlightdist || isfoggedsphere(d.curradius, d.o))
- continue;
- if(reflecting || refracting > 0)
- {
- if(d.o.z + d.curradius < reflectz) continue;
- }
- else if(refracting < 0 && d.o.z - d.curradius > reflectz) continue;
- e.o = d.o;
- e.radius = e.xradius = e.yradius = e.eyeheight = e.aboveeye = d.curradius;
- if(!collide(&e, vec(0, 0, 0), 0, false)) continue;
-
- int insert = 0;
- loopvrev(closedynlights) if(d.dist >= closedynlights[i]->dist) { insert = i+1; break; }
- closedynlights.insert(insert, &d);
- if(closedynlights.length() >= DYNLIGHTMASK) break;
- }
- return closedynlights.length();
+ closedynlights.setsize(0);
+ if(!maxdynlights) return 0;
+ physent e;
+ e.type = ENT_CAMERA;
+ loopvj(dynlights)
+ {
+ dynlight &d = dynlights[j];
+ if(d.curradius <= 0) continue;
+ d.dist = camera1->o.dist(d.o) - d.curradius;
+ if(d.dist > dynlightdist || isfoggedsphere(d.curradius, d.o))
+ continue;
+ if(reflecting || refracting > 0)
+ {
+ if(d.o.z + d.curradius < reflectz) continue;
+ }
+ else if(refracting < 0 && d.o.z - d.curradius > reflectz) continue;
+ e.o = d.o;
+ e.radius = e.xradius = e.yradius = e.eyeheight = e.aboveeye = d.curradius;
+ if(!collide(&e, vec(0, 0, 0), 0, false)) continue;
+
+ int insert = 0;
+ loopvrev(closedynlights) if(d.dist >= closedynlights[i]->dist) { insert = i+1; break; }
+ closedynlights.insert(insert, &d);
+ if(closedynlights.length() >= DYNLIGHTMASK) break;
+ }
+ return closedynlights.length();
}
bool getdynlight(int n, vec &o, float &radius, vec &color)
{
- if(!closedynlights.inrange(n)) return false;
- dynlight &d = *closedynlights[n];
- o = d.o;
- radius = d.curradius;
- color = d.curcolor;
- return true;
+ if(!closedynlights.inrange(n)) return false;
+ dynlight &d = *closedynlights[n];
+ o = d.o;
+ radius = d.curradius;
+ color = d.curcolor;
+ return true;
}
void dynlightreaching(const vec &target, vec &color, vec &dir, bool hud)
{
- vec dyncolor(0, 0, 0);//, dyndir(0, 0, 0);
- loopv(dynlights)
- {
- dynlight &d = dynlights[i];
- if(d.curradius<=0) continue;
-
- vec ray(hud ? d.hud : d.o);
- ray.sub(target);
- float mag = ray.squaredlen();
- if(mag >= d.curradius*d.curradius) continue;
-
- vec color = d.curcolor;
- color.mul(1 - sqrtf(mag)/d.curradius);
- dyncolor.add(color);
- //dyndir.add(ray.mul(intensity/mag));
- }
+ vec dyncolor(0, 0, 0);//, dyndir(0, 0, 0);
+ loopv(dynlights)
+ {
+ dynlight &d = dynlights[i];
+ if(d.curradius<=0) continue;
+
+ vec ray(hud ? d.hud : d.o);
+ ray.sub(target);
+ float mag = ray.squaredlen();
+ if(mag >= d.curradius*d.curradius) continue;
+
+ vec color = d.curcolor;
+ color.mul(1 - sqrtf(mag)/d.curradius);
+ dyncolor.add(color);
+ //dyndir.add(ray.mul(intensity/mag));
+ }
#if 0
- if(!dyndir.iszero())
- {
- dyndir.normalize();
- float x = dyncolor.magnitude(), y = color.magnitude();
- if(x+y>0)
- {
- dir.mul(x);
- dyndir.mul(y);
- dir.add(dyndir).div(x+y);
- if(dir.iszero()) dir = vec(0, 0, 1);
- else dir.normalize();
- }
- }
+ if(!dyndir.iszero())
+ {
+ dyndir.normalize();
+ float x = dyncolor.magnitude(), y = color.magnitude();
+ if(x+y>0)
+ {
+ dir.mul(x);
+ dyndir.mul(y);
+ dir.add(dyndir).div(x+y);
+ if(dir.iszero()) dir = vec(0, 0, 1);
+ else dir.normalize();
+ }
+ }
#endif
- color.add(dyncolor);
+ color.add(dyncolor);
}
void calcdynlightmask(vtxarray *va)
{
- uint mask = 0;
- int offset = 0;
- loopv(closedynlights)
- {
- dynlight &d = *closedynlights[i];
- if(d.o.dist_to_bb(va->geommin, va->geommax) >= d.curradius) continue;
-
- mask |= (i+1)<<offset;
- offset += DYNLIGHTBITS;
- if(offset >= maxdynlights*DYNLIGHTBITS) break;
- }
- va->dynlightmask = mask;
+ uint mask = 0;
+ int offset = 0;
+ loopv(closedynlights)
+ {
+ dynlight &d = *closedynlights[i];
+ if(d.o.dist_to_bb(va->geommin, va->geommax) >= d.curradius) continue;
+
+ mask |= (i+1)<<offset;
+ offset += DYNLIGHTBITS;
+ if(offset >= maxdynlights*DYNLIGHTBITS) break;
+ }
+ va->dynlightmask = mask;
}
int setdynlights(vtxarray *va)
{
- if(closedynlights.empty() || !va->dynlightmask) return 0;
+ if(closedynlights.empty() || !va->dynlightmask) return 0;
- extern bool minimizedynlighttcusage();
+ extern bool minimizedynlighttcusage();
- static vec4 posv[MAXDYNLIGHTS];
- static vec colorv[MAXDYNLIGHTS];
+ static vec4 posv[MAXDYNLIGHTS];
+ static vec colorv[MAXDYNLIGHTS];
- int index = 0;
- for(uint mask = va->dynlightmask; mask; mask >>= DYNLIGHTBITS, index++)
- {
- dynlight &d = *closedynlights[(mask&DYNLIGHTMASK)-1];
+ int index = 0;
+ for(uint mask = va->dynlightmask; mask; mask >>= DYNLIGHTBITS, index++)
+ {
+ dynlight &d = *closedynlights[(mask&DYNLIGHTMASK)-1];
- float scale = 1.0f/d.curradius;
- vec origin = vec(d.o).mul(-scale);
+ float scale = 1.0f/d.curradius;
+ vec origin = vec(d.o).mul(-scale);
- if(index>0 && minimizedynlighttcusage())
- {
- scale /= posv[0].w;
- origin.sub(vec(posv[0]).mul(scale));
- }
+ if(index>0 && minimizedynlighttcusage())
+ {
+ scale /= posv[0].w;
+ origin.sub(vec(posv[0]).mul(scale));
+ }
- posv[index] = vec4(origin, scale);
- colorv[index] = d.curcolor;
- }
+ posv[index] = vec4(origin, scale);
+ colorv[index] = d.curcolor;
+ }
- GLOBALPARAMV(dynlightpos, posv, index);
- GLOBALPARAMV(dynlightcolor, colorv, index);
+ GLOBALPARAMV(dynlightpos, posv, index);
+ GLOBALPARAMV(dynlightcolor, colorv, index);
- return index;
+ return index;
}
diff --git a/src/engine/engine.h b/src/engine/engine.h
index a9d70ef..de7e525 100644
--- a/src/engine/engine.h
+++ b/src/engine/engine.h
@@ -13,7 +13,7 @@
#include "model.h"
extern dynent *player;
-extern physent *camera1; // special ent that acts as camera, same object as player1 in FPS mode
+extern physent *camera1; // special ent that acts as camera, same object as player1 in FPS mode
extern int worldscale, worldsize;
extern int mapversion;
@@ -36,18 +36,18 @@ extern vector<int> entgroup;
// rendertext
struct font
{
- struct charinfo
- {
- short x, y, w, h, offsetx, offsety, advance, tex;
- };
-
- char *name;
- vector<Texture *> texs;
- vector<charinfo> chars;
- int charoffset, defaultw, defaulth, scale;
-
- font() : name(NULL) {}
- ~font() { DELETEA(name); }
+ struct charinfo
+ {
+ short x, y, w, h, offsetx, offsety, advance, tex;
+ };
+
+ char *name;
+ vector<Texture *> texs;
+ vector<charinfo> chars;
+ int charoffset, defaultw, defaulth, scale;
+
+ font() : name(NULL) {}
+ ~font() { DELETEA(name); }
};
#define FONTH (curfont->scale)
@@ -131,7 +131,6 @@ extern void gl_init();
extern void gl_resize();
extern void cleanupgl();
extern void rendergame(bool mainpass = false);
-extern void invalidatepostfx();
extern void gl_drawhud();
extern void gl_drawframe();
extern void gl_drawmainmenu();
@@ -159,8 +158,8 @@ extern void writecrosshairs(stream *f);
namespace modelpreview
{
- extern void start(int x, int y, int w, int h, bool background = true);
- extern void end();
+ extern void start(int x, int y, int w, int h, bool background = true);
+ extern void end();
}
// renderextras
@@ -217,7 +216,7 @@ extern void mincubeface(const cube &cu, int orient, const ivec &o, int size, con
static inline cubeext &ext(cube &c)
{
- return *(c.ext ? c.ext : newcubeext(c));
+ return *(c.ext ? c.ext : newcubeext(c));
}
// ents
@@ -316,16 +315,16 @@ extern float reflectz;
extern int reflectdist, vertwater, waterrefract, waterreflect, waterfade, caustics, waterfallrefract;
#define GETMATIDXVAR(name, var, type) \
- type get##name##var(int mat) \
- { \
- switch(mat&MATF_INDEX) \
- { \
- default: case 0: return name##var; \
- case 1: return name##2##var; \
- case 2: return name##3##var; \
- case 3: return name##4##var; \
- } \
- }
+ type get##name##var(int mat) \
+ { \
+ switch(mat&MATF_INDEX) \
+ { \
+ default: case 0: return name##var; \
+ case 1: return name##2##var; \
+ case 2: return name##3##var; \
+ case 3: return name##4##var; \
+ } \
+ }
extern const bvec &getwatercolor(int mat);
extern const bvec &getwaterfallcolor(int mat);
@@ -408,17 +407,17 @@ extern void writecompletions(stream *f);
// main
enum
{
- NOT_INITING = 0,
- INIT_GAME,
- INIT_LOAD,
- INIT_RESET
+ NOT_INITING = 0,
+ INIT_GAME,
+ INIT_LOAD,
+ INIT_RESET
};
extern int initing, numcpus;
enum
{
- CHANGE_GFX = 1<<0,
- CHANGE_SOUND = 1<<1
+ CHANGE_GFX = 1<<0,
+ CHANGE_SOUND = 1<<1
};
extern bool initwarning(const char *desc, int level = INIT_RESET, int type = CHANGE_GFX);
@@ -485,13 +484,13 @@ extern void preloadusedmapmodels(bool msg = false, bool bih = false);
static inline model *loadmapmodel(int n)
{
- extern vector<mapmodelinfo> mapmodels;
- if(mapmodels.inrange(n))
- {
- model *m = mapmodels[n].m;
- return m ? m : loadmodel(NULL, n);
- }
- return NULL;
+ extern vector<mapmodelinfo> mapmodels;
+ if(mapmodels.inrange(n))
+ {
+ model *m = mapmodels[n].m;
+ return m ? m : loadmodel(NULL, n);
+ }
+ return NULL;
}
// renderparticles
diff --git a/src/engine/explosion.h b/src/engine/explosion.h
index 626d5bf..cda02f4 100644
--- a/src/engine/explosion.h
+++ b/src/engine/explosion.h
@@ -1,249 +1,249 @@
namespace sphere
{
- struct vert
- {
- vec pos;
- ushort s, t;
- } *verts = NULL;
- GLushort *indices = NULL;
- int numverts = 0, numindices = 0;
- GLuint vbuf = 0, ebuf = 0;
-
- void init(int slices, int stacks)
- {
- numverts = (stacks+1)*(slices+1);
- verts = new vert[numverts];
- float ds = 1.0f/slices, dt = 1.0f/stacks, t = 1.0f;
- loopi(stacks+1)
- {
- float rho = M_PI*(1-t), s = 0.0f, sinrho = i && i < stacks ? sin(rho) : 0, cosrho = !i ? 1 : (i < stacks ? cos(rho) : -1);
- loopj(slices+1)
- {
- float theta = j==slices ? 0 : 2*M_PI*s;
- vert &v = verts[i*(slices+1) + j];
- v.pos = vec(-sin(theta)*sinrho, cos(theta)*sinrho, cosrho);
- v.s = ushort(s*0xFFFF);
- v.t = ushort(t*0xFFFF);
- s += ds;
- }
- t -= dt;
- }
-
- numindices = (stacks-1)*slices*3*2;
- indices = new ushort[numindices];
- GLushort *curindex = indices;
- loopi(stacks)
- {
- loopk(slices)
- {
- int j = i%2 ? slices-k-1 : k;
- if(i)
- {
- *curindex++ = i*(slices+1)+j;
- *curindex++ = (i+1)*(slices+1)+j;
- *curindex++ = i*(slices+1)+j+1;
- }
- if(i+1 < stacks)
- {
- *curindex++ = i*(slices+1)+j+1;
- *curindex++ = (i+1)*(slices+1)+j;
- *curindex++ = (i+1)*(slices+1)+j+1;
- }
- }
- }
-
- if(!vbuf) glGenBuffers_(1, &vbuf);
- gle::bindvbo(vbuf);
- glBufferData_(GL_ARRAY_BUFFER, numverts*sizeof(vert), verts, GL_STATIC_DRAW);
- DELETEA(verts);
-
- if(!ebuf) glGenBuffers_(1, &ebuf);
- gle::bindebo(ebuf);
- glBufferData_(GL_ELEMENT_ARRAY_BUFFER, numindices*sizeof(GLushort), indices, GL_STATIC_DRAW);
- DELETEA(indices);
- }
-
- void enable()
- {
- if(!vbuf) init(12, 6);
-
- gle::bindvbo(vbuf);
- gle::bindebo(ebuf);
-
- gle::vertexpointer(sizeof(vert), &verts->pos);
- gle::texcoord0pointer(sizeof(vert), &verts->s, GL_UNSIGNED_SHORT, 2, GL_TRUE);
- gle::enablevertex();
- gle::enabletexcoord0();
- }
-
- void draw()
- {
- glDrawRangeElements_(GL_TRIANGLES, 0, numverts-1, numindices, GL_UNSIGNED_SHORT, indices);
- xtraverts += numindices;
- glde++;
- }
-
- void disable()
- {
- gle::disablevertex();
- gle::disabletexcoord0();
-
- gle::clearvbo();
- gle::clearebo();
- }
-
- void cleanup()
- {
- if(vbuf) { glDeleteBuffers_(1, &vbuf); vbuf = 0; }
- if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; }
- }
+ struct vert
+ {
+ vec pos;
+ ushort s, t;
+ } *verts = NULL;
+ GLushort *indices = NULL;
+ int numverts = 0, numindices = 0;
+ GLuint vbuf = 0, ebuf = 0;
+
+ void init(int slices, int stacks)
+ {
+ numverts = (stacks+1)*(slices+1);
+ verts = new vert[numverts];
+ float ds = 1.0f/slices, dt = 1.0f/stacks, t = 1.0f;
+ loopi(stacks+1)
+ {
+ float rho = M_PI*(1-t), s = 0.0f, sinrho = i && i < stacks ? sin(rho) : 0, cosrho = !i ? 1 : (i < stacks ? cos(rho) : -1);
+ loopj(slices+1)
+ {
+ float theta = j==slices ? 0 : 2*M_PI*s;
+ vert &v = verts[i*(slices+1) + j];
+ v.pos = vec(-sin(theta)*sinrho, cos(theta)*sinrho, cosrho);
+ v.s = ushort(s*0xFFFF);
+ v.t = ushort(t*0xFFFF);
+ s += ds;
+ }
+ t -= dt;
+ }
+
+ numindices = (stacks-1)*slices*3*2;
+ indices = new ushort[numindices];
+ GLushort *curindex = indices;
+ loopi(stacks)
+ {
+ loopk(slices)
+ {
+ int j = i%2 ? slices-k-1 : k;
+ if(i)
+ {
+ *curindex++ = i*(slices+1)+j;
+ *curindex++ = (i+1)*(slices+1)+j;
+ *curindex++ = i*(slices+1)+j+1;
+ }
+ if(i+1 < stacks)
+ {
+ *curindex++ = i*(slices+1)+j+1;
+ *curindex++ = (i+1)*(slices+1)+j;
+ *curindex++ = (i+1)*(slices+1)+j+1;
+ }
+ }
+ }
+
+ if(!vbuf) glGenBuffers_(1, &vbuf);
+ gle::bindvbo(vbuf);
+ glBufferData_(GL_ARRAY_BUFFER, numverts*sizeof(vert), verts, GL_STATIC_DRAW);
+ DELETEA(verts);
+
+ if(!ebuf) glGenBuffers_(1, &ebuf);
+ gle::bindebo(ebuf);
+ glBufferData_(GL_ELEMENT_ARRAY_BUFFER, numindices*sizeof(GLushort), indices, GL_STATIC_DRAW);
+ DELETEA(indices);
+ }
+
+ void enable()
+ {
+ if(!vbuf) init(12, 6);
+
+ gle::bindvbo(vbuf);
+ gle::bindebo(ebuf);
+
+ gle::vertexpointer(sizeof(vert), &verts->pos);
+ gle::texcoord0pointer(sizeof(vert), &verts->s, GL_UNSIGNED_SHORT, 2, GL_TRUE);
+ gle::enablevertex();
+ gle::enabletexcoord0();
+ }
+
+ void draw()
+ {
+ glDrawRangeElements_(GL_TRIANGLES, 0, numverts-1, numindices, GL_UNSIGNED_SHORT, indices);
+ xtraverts += numindices;
+ glde++;
+ }
+
+ void disable()
+ {
+ gle::disablevertex();
+ gle::disabletexcoord0();
+
+ gle::clearvbo();
+ gle::clearebo();
+ }
+
+ void cleanup()
+ {
+ if(vbuf) { glDeleteBuffers_(1, &vbuf); vbuf = 0; }
+ if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; }
+ }
}
static const float WOBBLE = 1.25f;
struct fireballrenderer : listrenderer
{
- fireballrenderer(const char *texname)
- : listrenderer(texname, 0, PT_FIREBALL|PT_GLARE|PT_SHADER)
- {}
-
- void startrender()
- {
- if(glaring) SETSHADER(explosionglare);
- else if(!reflecting && !refracting && depthfx && depthfxtex.rendertex && numdepthfxranges>0)
- {
- if(!depthfxtex.highprecision()) SETSHADER(explosionsoft8);
- else SETSHADER(explosionsoft);
- }
- else SETSHADER(explosion);
-
- sphere::enable();
- }
-
- void endrender()
- {
- sphere::disable();
- }
-
- void cleanup()
- {
- sphere::cleanup();
- }
-
- int finddepthfxranges(void **owners, float *ranges, int numranges, int maxranges, vec &bbmin, vec &bbmax)
- {
- static struct fireballent : physent
- {
- fireballent()
- {
- type = ENT_CAMERA;
- }
- } e;
-
- for(listparticle *p = list; p; p = p->next)
- {
- int ts = p->fade <= 5 ? 1 : lastmillis-p->millis;
- float pmax = p->val,
- size = p->fade ? float(ts)/p->fade : 1,
- psize = (p->size + pmax * size)*WOBBLE;
- if(2*(p->size + pmax)*WOBBLE < depthfxblend ||
- (!depthfxtex.highprecision() && !depthfxtex.emulatehighprecision() && psize > depthfxscale - depthfxbias) ||
- isfoggedsphere(psize, p->o)) continue;
-
- e.o = p->o;
- e.radius = e.xradius = e.yradius = e.eyeheight = e.aboveeye = psize;
- if(!::collide(&e, vec(0, 0, 0), 0, false)) continue;
-
- if(depthfxscissor==2 && !depthfxtex.addscissorbox(p->o, psize)) continue;
-
- vec dir = camera1->o;
- dir.sub(p->o);
- float dist = dir.magnitude();
- dir.mul(psize/dist).add(p->o);
- float depth = depthfxtex.eyedepth(dir);
-
- loopk(3)
- {
- bbmin[k] = min(bbmin[k], p->o[k] - psize);
- bbmax[k] = max(bbmax[k], p->o[k] + psize);
- }
-
- int pos = numranges;
- loopi(numranges) if(depth < ranges[i]) { pos = i; break; }
- if(pos >= maxranges) continue;
-
- if(numranges > pos)
- {
- int moved = min(numranges-pos, maxranges-(pos+1));
- memmove(&ranges[pos+1], &ranges[pos], moved*sizeof(float));
- memmove(&owners[pos+1], &owners[pos], moved*sizeof(void *));
- }
- if(numranges < maxranges) numranges++;
-
- ranges[pos] = depth;
- owners[pos] = p;
- }
-
- return numranges;
- }
-
- void seedemitter(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int gravity)
- {
- pe.maxfade = max(pe.maxfade, fade);
- pe.extendbb(o, (size+1+pe.ent->attr2)*WOBBLE);
- }
-
- void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts)
- {
- float pmax = p->val,
- size = p->fade ? float(ts)/p->fade : 1,
- psize = p->size + pmax * size;
-
- if(isfoggedsphere(psize*WOBBLE, p->o)) return;
-
- vec dir = vec(o).sub(camera1->o), s, t;
- float dist = dir.magnitude();
- bool inside = dist <= psize*WOBBLE;
- if(inside)
- {
- s = camright;
- t = camup;
- }
- else
- {
- if(reflecting) { dir.z = o.z - reflectz; dist = dir.magnitude(); }
- float mag2 = dir.magnitude2();
- dir.x /= mag2;
- dir.y /= mag2;
- dir.z /= dist;
- s = vec(dir.y, -dir.x, 0);
- t = vec(dir.x*dir.z, dir.y*dir.z, -mag2/dist);
- }
-
- matrix3 rot(lastmillis/1000.0f*143*RAD, vec(1/SQRT3, 1/SQRT3, 1/SQRT3));
- LOCALPARAM(texgenS, rot.transposedtransform(s));
- LOCALPARAM(texgenT, rot.transposedtransform(t));
-
- matrix4 m(rot, o);
- m.scale(psize, psize, inside ? -psize : psize);
- m.mul(camprojmatrix, m);
- LOCALPARAM(explosionmatrix, m);
-
- LOCALPARAM(center, o);
- LOCALPARAMF(millis, lastmillis/1000.0f);
- LOCALPARAMF(blendparams, inside ? 0.5f : 4, inside ? 0.25f : 0);
- binddepthfxparams(depthfxblend, inside ? blend/(2*255.0f) : 0, 2*(p->size + pmax)*WOBBLE >= depthfxblend, p);
-
- int passes = !reflecting && !refracting && inside ? 2 : 1;
- loopi(passes)
- {
- gle::color(p->color, i ? blend/2 : blend);
- if(i) glDepthFunc(GL_GEQUAL);
- sphere::draw();
- if(i) glDepthFunc(GL_LESS);
- }
- }
+ fireballrenderer(const char *texname)
+ : listrenderer(texname, 0, PT_FIREBALL|PT_GLARE|PT_SHADER)
+ {}
+
+ void startrender()
+ {
+ if(glaring) SETSHADER(explosionglare);
+ else if(!reflecting && !refracting && depthfx && depthfxtex.rendertex && numdepthfxranges>0)
+ {
+ if(!depthfxtex.highprecision()) SETSHADER(explosionsoft8);
+ else SETSHADER(explosionsoft);
+ }
+ else SETSHADER(explosion);
+
+ sphere::enable();
+ }
+
+ void endrender()
+ {
+ sphere::disable();
+ }
+
+ void cleanup()
+ {
+ sphere::cleanup();
+ }
+
+ int finddepthfxranges(void **owners, float *ranges, int numranges, int maxranges, vec &bbmin, vec &bbmax)
+ {
+ static struct fireballent : physent
+ {
+ fireballent()
+ {
+ type = ENT_CAMERA;
+ }
+ } e;
+
+ for(listparticle *p = list; p; p = p->next)
+ {
+ int ts = p->fade <= 5 ? 1 : lastmillis-p->millis;
+ float pmax = p->val,
+ size = p->fade ? float(ts)/p->fade : 1,
+ psize = (p->size + pmax * size)*WOBBLE;
+ if(2*(p->size + pmax)*WOBBLE < depthfxblend ||
+ (!depthfxtex.highprecision() && !depthfxtex.emulatehighprecision() && psize > depthfxscale - depthfxbias) ||
+ isfoggedsphere(psize, p->o)) continue;
+
+ e.o = p->o;
+ e.radius = e.xradius = e.yradius = e.eyeheight = e.aboveeye = psize;
+ if(!::collide(&e, vec(0, 0, 0), 0, false)) continue;
+
+ if(depthfxscissor==2 && !depthfxtex.addscissorbox(p->o, psize)) continue;
+
+ vec dir = camera1->o;
+ dir.sub(p->o);
+ float dist = dir.magnitude();
+ dir.mul(psize/dist).add(p->o);
+ float depth = depthfxtex.eyedepth(dir);
+
+ loopk(3)
+ {
+ bbmin[k] = min(bbmin[k], p->o[k] - psize);
+ bbmax[k] = max(bbmax[k], p->o[k] + psize);
+ }
+
+ int pos = numranges;
+ loopi(numranges) if(depth < ranges[i]) { pos = i; break; }
+ if(pos >= maxranges) continue;
+
+ if(numranges > pos)
+ {
+ int moved = min(numranges-pos, maxranges-(pos+1));
+ memmove(&ranges[pos+1], &ranges[pos], moved*sizeof(float));
+ memmove(&owners[pos+1], &owners[pos], moved*sizeof(void *));
+ }
+ if(numranges < maxranges) numranges++;
+
+ ranges[pos] = depth;
+ owners[pos] = p;
+ }
+
+ return numranges;
+ }
+
+ void seedemitter(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int gravity)
+ {
+ pe.maxfade = max(pe.maxfade, fade);
+ pe.extendbb(o, (size+1+pe.ent->attr2)*WOBBLE);
+ }
+
+ void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts)
+ {
+ float pmax = p->val,
+ size = p->fade ? float(ts)/p->fade : 1,
+ psize = p->size + pmax * size;
+
+ if(isfoggedsphere(psize*WOBBLE, p->o)) return;
+
+ vec dir = vec(o).sub(camera1->o), s, t;
+ float dist = dir.magnitude();
+ bool inside = dist <= psize*WOBBLE;
+ if(inside)
+ {
+ s = camright;
+ t = camup;
+ }
+ else
+ {
+ if(reflecting) { dir.z = o.z - reflectz; dist = dir.magnitude(); }
+ float mag2 = dir.magnitude2();
+ dir.x /= mag2;
+ dir.y /= mag2;
+ dir.z /= dist;
+ s = vec(dir.y, -dir.x, 0);
+ t = vec(dir.x*dir.z, dir.y*dir.z, -mag2/dist);
+ }
+
+ matrix3 rot(lastmillis/1000.0f*143*RAD, vec(1/SQRT3, 1/SQRT3, 1/SQRT3));
+ LOCALPARAM(texgenS, rot.transposedtransform(s));
+ LOCALPARAM(texgenT, rot.transposedtransform(t));
+
+ matrix4 m(rot, o);
+ m.scale(psize, psize, inside ? -psize : psize);
+ m.mul(camprojmatrix, m);
+ LOCALPARAM(explosionmatrix, m);
+
+ LOCALPARAM(center, o);
+ LOCALPARAMF(millis, lastmillis/1000.0f);
+ LOCALPARAMF(blendparams, inside ? 0.5f : 4, inside ? 0.25f : 0);
+ binddepthfxparams(depthfxblend, inside ? blend/(2*255.0f) : 0, 2*(p->size + pmax)*WOBBLE >= depthfxblend, p);
+
+ int passes = !reflecting && !refracting && inside ? 2 : 1;
+ loopi(passes)
+ {
+ gle::color(p->color, i ? blend/2 : blend);
+ if(i) glDepthFunc(GL_GEQUAL);
+ sphere::draw();
+ if(i) glDepthFunc(GL_LESS);
+ }
+ }
};
static fireballrenderer fireballs("packages/particles/explosion.png"), bluefireballs("packages/particles/plasma.png");
diff --git a/src/engine/glare.cpp b/src/engine/glare.cpp
index c674e35..7701430 100644
--- a/src/engine/glare.cpp
+++ b/src/engine/glare.cpp
@@ -3,17 +3,17 @@
static struct glaretexture : rendertarget
{
- bool dorender()
- {
- extern void drawglare();
- drawglare();
- return true;
- }
+ bool dorender()
+ {
+ extern void drawglare();
+ drawglare();
+ return true;
+ }
} glaretex;
void cleanupglare()
{
- glaretex.cleanup(true);
+ glaretex.cleanup(true);
}
VARFP(glaresize, 6, 8, 10, cleanupglare());
@@ -26,17 +26,17 @@ bool glaring = false;
void drawglaretex()
{
- if(!glare) return;
+ if(!glare) return;
- int w = 1<<glaresize, h = 1<<glaresize, blury = blurglare;
- if(blurglare && blurglareaspect)
- {
- while(h > (1<<5) && (screenw*h)/w >= (screenh*4)/3) h /= 2;
- blury = ((1 + 4*blurglare)*(screenw*h)/w + screenh*2)/(screenh*4);
- blury = clamp(blury, 1, MAXBLURRADIUS);
- }
+ int w = 1<<glaresize, h = 1<<glaresize, blury = blurglare;
+ if(blurglare && blurglareaspect)
+ {
+ while(h > (1<<5) && (screenw*h)/w >= (screenh*4)/3) h /= 2;
+ blury = ((1 + 4*blurglare)*(screenw*h)/w + screenh*2)/(screenh*4);
+ blury = clamp(blury, 1, MAXBLURRADIUS);
+ }
- glaretex.render(w, h, blurglare, blurglaresigma/100.0f, blury);
+ glaretex.render(w, h, blurglare, blurglaresigma/100.0f, blury);
}
FVAR(glaremod, 0.5f, 0.75f, 1);
@@ -44,20 +44,20 @@ FVARP(glarescale, 0, 1, 8);
void addglare()
{
- if(!glare) return;
+ if(!glare) return;
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE);
- SETSHADER(screenrect);
+ SETSHADER(screenrect);
- glBindTexture(GL_TEXTURE_2D, glaretex.rendertex);
+ glBindTexture(GL_TEXTURE_2D, glaretex.rendertex);
- float g = glarescale*glaremod;
- gle::colorf(g, g, g);
+ float g = glarescale*glaremod;
+ gle::colorf(g, g, g);
- screenquad(1, 1);
+ screenquad(1, 1);
- glDisable(GL_BLEND);
+ glDisable(GL_BLEND);
}
diff --git a/src/engine/iqm.h b/src/engine/iqm.h
index f426a6b..e36a628 100644
--- a/src/engine/iqm.h
+++ b/src/engine/iqm.h
@@ -2,394 +2,394 @@ struct iqm;
struct iqmheader
{
- char magic[16];
- uint version;
- uint filesize;
- uint flags;
- uint num_text, ofs_text;
- uint num_meshes, ofs_meshes;
- uint num_vertexarrays, num_vertexes, ofs_vertexarrays;
- uint num_triangles, ofs_triangles, ofs_adjacency;
- uint num_joints, ofs_joints;
- uint num_poses, ofs_poses;
- uint num_anims, ofs_anims;
- uint num_frames, num_framechannels, ofs_frames, ofs_bounds;
- uint num_comment, ofs_comment;
- uint num_extensions, ofs_extensions;
+ char magic[16];
+ uint version;
+ uint filesize;
+ uint flags;
+ uint num_text, ofs_text;
+ uint num_meshes, ofs_meshes;
+ uint num_vertexarrays, num_vertexes, ofs_vertexarrays;
+ uint num_triangles, ofs_triangles, ofs_adjacency;
+ uint num_joints, ofs_joints;
+ uint num_poses, ofs_poses;
+ uint num_anims, ofs_anims;
+ uint num_frames, num_framechannels, ofs_frames, ofs_bounds;
+ uint num_comment, ofs_comment;
+ uint num_extensions, ofs_extensions;
};
struct iqmmesh
{
- uint name;
- uint material;
- uint first_vertex, num_vertexes;
- uint first_triangle, num_triangles;
+ uint name;
+ uint material;
+ uint first_vertex, num_vertexes;
+ uint first_triangle, num_triangles;
};
enum
-{
- IQM_POSITION = 0,
- IQM_TEXCOORD = 1,
- IQM_NORMAL = 2,
- IQM_TANGENT = 3,
- IQM_BLENDINDEXES = 4,
- IQM_BLENDWEIGHTS = 5,
- IQM_COLOR = 6,
- IQM_CUSTOM = 0x10
-};
+{
+ IQM_POSITION = 0,
+ IQM_TEXCOORD = 1,
+ IQM_NORMAL = 2,
+ IQM_TANGENT = 3,
+ IQM_BLENDINDEXES = 4,
+ IQM_BLENDWEIGHTS = 5,
+ IQM_COLOR = 6,
+ IQM_CUSTOM = 0x10
+};
enum
{
- IQM_BYTE = 0,
- IQM_UBYTE = 1,
- IQM_SHORT = 2,
- IQM_USHORT = 3,
- IQM_INT = 4,
- IQM_UINT = 5,
- IQM_HALF = 6,
- IQM_FLOAT = 7,
- IQM_DOUBLE = 8,
+ IQM_BYTE = 0,
+ IQM_UBYTE = 1,
+ IQM_SHORT = 2,
+ IQM_USHORT = 3,
+ IQM_INT = 4,
+ IQM_UINT = 5,
+ IQM_HALF = 6,
+ IQM_FLOAT = 7,
+ IQM_DOUBLE = 8,
};
struct iqmtriangle
{
- uint vertex[3];
+ uint vertex[3];
};
struct iqmjoint
{
- uint name;
- int parent;
- vec pos;
- quat orient;
- vec size;
+ uint name;
+ int parent;
+ vec pos;
+ quat orient;
+ vec size;
};
struct iqmpose
{
- int parent;
- uint mask;
- vec offsetpos;
- vec4 offsetorient;
- vec offsetsize;
- vec scalepos;
- vec4 scaleorient;
- vec scalesize;
+ int parent;
+ uint mask;
+ vec offsetpos;
+ vec4 offsetorient;
+ vec offsetsize;
+ vec scalepos;
+ vec4 scaleorient;
+ vec scalesize;
};
struct iqmanim
{
- uint name;
- uint first_frame, num_frames;
- float framerate;
- uint flags;
+ uint name;
+ uint first_frame, num_frames;
+ float framerate;
+ uint flags;
};
struct iqmvertexarray
{
- uint type;
- uint flags;
- uint format;
- uint size;
- uint offset;
+ uint type;
+ uint flags;
+ uint format;
+ uint size;
+ uint offset;
};
struct iqm : skelloader<iqm>
{
- iqm(const char *name) : skelloader(name) {}
-
- static const char *formatname() { return "iqm"; }
- int type() const { return MDL_IQM; }
-
- struct iqmmeshgroup : skelmeshgroup
- {
- iqmmeshgroup()
- {
- }
-
- bool loadiqmmeshes(const char *filename, const iqmheader &hdr, uchar *buf)
- {
- lilswap((uint *)&buf[hdr.ofs_vertexarrays], hdr.num_vertexarrays*sizeof(iqmvertexarray)/sizeof(uint));
- lilswap((uint *)&buf[hdr.ofs_triangles], hdr.num_triangles*sizeof(iqmtriangle)/sizeof(uint));
- lilswap((uint *)&buf[hdr.ofs_meshes], hdr.num_meshes*sizeof(iqmmesh)/sizeof(uint));
- lilswap((uint *)&buf[hdr.ofs_joints], hdr.num_joints*sizeof(iqmjoint)/sizeof(uint));
-
- const char *str = hdr.ofs_text ? (char *)&buf[hdr.ofs_text] : "";
- float *vpos = NULL, *vnorm = NULL, *vtan = NULL, *vtc = NULL;
- uchar *vindex = NULL, *vweight = NULL;
- iqmvertexarray *vas = (iqmvertexarray *)&buf[hdr.ofs_vertexarrays];
- loopi(hdr.num_vertexarrays)
- {
- iqmvertexarray &va = vas[i];
- switch(va.type)
- {
- case IQM_POSITION: if(va.format != IQM_FLOAT || va.size != 3) return false; vpos = (float *)&buf[va.offset]; lilswap(vpos, 3*hdr.num_vertexes); break;
- case IQM_NORMAL: if(va.format != IQM_FLOAT || va.size != 3) return false; vnorm = (float *)&buf[va.offset]; lilswap(vnorm, 3*hdr.num_vertexes); break;
- case IQM_TANGENT: if(va.format != IQM_FLOAT || va.size != 4) return false; vtan = (float *)&buf[va.offset]; lilswap(vtan, 4*hdr.num_vertexes); break;
- case IQM_TEXCOORD: if(va.format != IQM_FLOAT || va.size != 2) return false; vtc = (float *)&buf[va.offset]; lilswap(vtc, 2*hdr.num_vertexes); break;
- case IQM_BLENDINDEXES: if(va.format != IQM_UBYTE || va.size != 4) return false; vindex = (uchar *)&buf[va.offset]; break;
- case IQM_BLENDWEIGHTS: if(va.format != IQM_UBYTE || va.size != 4) return false; vweight = (uchar *)&buf[va.offset]; break;
- }
- }
- if(!vpos) return false;
-
- iqmtriangle *tris = (iqmtriangle *)&buf[hdr.ofs_triangles];
- iqmmesh *imeshes = (iqmmesh *)&buf[hdr.ofs_meshes];
- iqmjoint *joints = (iqmjoint *)&buf[hdr.ofs_joints];
-
- if(hdr.num_joints)
- {
- if(skel->numbones <= 0)
- {
- skel->numbones = hdr.num_joints;
- skel->bones = new boneinfo[skel->numbones];
- loopi(hdr.num_joints)
- {
- iqmjoint &j = joints[i];
- boneinfo &b = skel->bones[i];
- if(!b.name) b.name = newstring(&str[j.name]);
- b.parent = j.parent;
- if(skel->shared <= 1)
- {
- j.pos.y = -j.pos.y;
- j.orient.x = -j.orient.x;
- j.orient.z = -j.orient.z;
- j.orient.normalize();
- b.base = dualquat(j.orient, j.pos);
- if(b.parent >= 0) b.base.mul(skel->bones[b.parent].base, dualquat(b.base));
- (b.invbase = b.base).invert();
- }
- }
- }
-
- if(skel->shared <= 1)
- skel->linkchildren();
- }
-
- loopi(hdr.num_meshes)
- {
- iqmmesh &im = imeshes[i];
- skelmesh *m = new skelmesh;
- m->group = this;
- meshes.add(m);
- m->name = newstring(&str[im.name]);
- m->numverts = im.num_vertexes;
- int noblend = -1;
- if(m->numverts)
- {
- m->verts = new vert[m->numverts];
- if(vtan) m->bumpverts = new bumpvert[m->numverts];
- if(!vindex || !vweight)
- {
- blendcombo c;
- c.finalize(0);
- noblend = m->addblendcombo(c);
- }
- }
- int fv = im.first_vertex;
- float *mpos = vpos + 3*fv,
- *mnorm = vnorm ? vnorm + 3*fv : NULL,
- *mtan = vtan ? vtan + 4*fv : NULL,
- *mtc = vtc ? vtc + 2*fv : NULL;
- uchar *mindex = vindex ? vindex + 4*fv : NULL, *mweight = vweight ? vweight + 4*fv : NULL;
- loopj(im.num_vertexes)
- {
- vert &v = m->verts[j];
- v.pos = vec(mpos[0], -mpos[1], mpos[2]);
- mpos += 3;
- if(mtc)
- {
- v.tc = vec2(mtc[0], mtc[1]);
- mtc += 2;
- }
- else v.tc = vec2(0, 0);
- if(mnorm)
- {
- v.norm = vec(mnorm[0], -mnorm[1], mnorm[2]);
- mnorm += 3;
- if(mtan)
- {
- m->calctangent(m->bumpverts[j], v.norm, vec(mtan[0], -mtan[1], mtan[2]), mtan[3]);
- mtan += 4;
- }
- }
- else v.norm = vec(0, 0, 0);
- if(noblend < 0)
- {
- blendcombo c;
- int sorted = 0;
- loopk(4) sorted = c.addweight(sorted, mweight[k], mindex[k]);
- mweight += 4;
- mindex += 4;
- c.finalize(sorted);
- v.blend = m->addblendcombo(c);
- }
- else v.blend = noblend;
- }
- m->numtris = im.num_triangles;
- if(m->numtris) m->tris = new tri[m->numtris];
- iqmtriangle *mtris = tris + im.first_triangle;
- loopj(im.num_triangles)
- {
- tri &t = m->tris[j];
- t.vert[0] = mtris->vertex[0] - fv;
- t.vert[1] = mtris->vertex[1] - fv;
- t.vert[2] = mtris->vertex[2] - fv;
- ++mtris;
- }
- if(!m->numtris || !m->numverts)
- {
- conoutf(CON_WARN, "empty mesh in %s", filename);
- meshes.removeobj(m);
- delete m;
- }
- }
-
- sortblendcombos();
-
- return true;
- }
-
- bool loadiqmanims(const char *filename, const iqmheader &hdr, uchar *buf)
- {
- lilswap((uint *)&buf[hdr.ofs_poses], hdr.num_poses*sizeof(iqmpose)/sizeof(uint));
- lilswap((uint *)&buf[hdr.ofs_anims], hdr.num_anims*sizeof(iqmanim)/sizeof(uint));
- lilswap((ushort *)&buf[hdr.ofs_frames], hdr.num_frames*hdr.num_framechannels);
-
- const char *str = hdr.ofs_text ? (char *)&buf[hdr.ofs_text] : "";
- iqmpose *poses = (iqmpose *)&buf[hdr.ofs_poses];
- iqmanim *anims = (iqmanim *)&buf[hdr.ofs_anims];
- ushort *frames = (ushort *)&buf[hdr.ofs_frames];
- loopi(hdr.num_anims)
- {
- iqmanim &a = anims[i];
- string name;
- copystring(name, filename);
- concatstring(name, ":");
- concatstring(name, &str[a.name]);
- skelanimspec *sa = skel->findskelanim(name);
- if(sa) continue;
- sa = &skel->addskelanim(name);
- sa->frame = skel->numframes;
- sa->range = a.num_frames;
- dualquat *animbones = new dualquat[(skel->numframes+a.num_frames)*skel->numbones];
- if(skel->bones)
- {
- memcpy(animbones, skel->framebones, skel->numframes*skel->numbones*sizeof(dualquat));
- delete[] skel->framebones;
- }
- skel->framebones = animbones;
- animbones += skel->numframes*skel->numbones;
- skel->numframes += a.num_frames;
- ushort *animdata = &frames[a.first_frame*hdr.num_framechannels];
- loopj(a.num_frames)
- {
- dualquat *frame = &animbones[j*skel->numbones];
- loopk(skel->numbones)
- {
- iqmpose &p = poses[k];
- vec pos;
- quat orient;
- pos.x = p.offsetpos.x; if(p.mask&0x01) pos.x += *animdata++ * p.scalepos.x;
- pos.y = -p.offsetpos.y; if(p.mask&0x02) pos.y -= *animdata++ * p.scalepos.y;
- pos.z = p.offsetpos.z; if(p.mask&0x04) pos.z += *animdata++ * p.scalepos.z;
- orient.x = -p.offsetorient.x; if(p.mask&0x08) orient.x -= *animdata++ * p.scaleorient.x;
- orient.y = p.offsetorient.y; if(p.mask&0x10) orient.y += *animdata++ * p.scaleorient.y;
- orient.z = -p.offsetorient.z; if(p.mask&0x20) orient.z -= *animdata++ * p.scaleorient.z;
- orient.w = p.offsetorient.w; if(p.mask&0x40) orient.w += *animdata++ * p.scaleorient.w;
- orient.normalize();
- if(p.mask&0x380)
- {
- if(p.mask&0x80) animdata++;
- if(p.mask&0x100) animdata++;
- if(p.mask&0x200) animdata++;
- }
- frame[k] = dualquat(orient, pos);
- if(adjustments.inrange(k)) adjustments[k].adjust(frame[k]);
- boneinfo &b = skel->bones[k];
- frame[k].mul(b.invbase);
- if(b.parent >= 0) frame[k].mul(skel->bones[b.parent].base, dualquat(frame[k]));
- frame[k].fixantipodal(skel->framebones[k]);
- }
- }
- }
-
- return true;
- }
-
- bool loadiqm(const char *filename, bool doloadmesh, bool doloadanim)
- {
- stream *f = openfile(filename, "rb");
- if(!f) return false;
-
- uchar *buf = NULL;
- iqmheader hdr;
- if(f->read(&hdr, sizeof(hdr)) != sizeof(hdr) || memcmp(hdr.magic, "INTERQUAKEMODEL", sizeof(hdr.magic))) goto error;
- lilswap(&hdr.version, (sizeof(hdr) - sizeof(hdr.magic))/sizeof(uint));
- if(hdr.version != 2) goto error;
- if(hdr.filesize > (16<<20)) goto error; // sanity check... don't load files bigger than 16 MB
- buf = new (false) uchar[hdr.filesize];
- if(!buf || f->read(buf + sizeof(hdr), hdr.filesize - sizeof(hdr)) != hdr.filesize - sizeof(hdr)) goto error;
-
- if(doloadmesh && !loadiqmmeshes(filename, hdr, buf)) goto error;
- if(doloadanim && !loadiqmanims(filename, hdr, buf)) goto error;
-
- delete[] buf;
- delete f;
- return true;
-
- error:
- if(buf) delete[] buf;
- delete f;
- return false;
- }
-
- bool loadmesh(const char *filename)
- {
- name = newstring(filename);
-
- return loadiqm(filename, true, false);
- }
-
- skelanimspec *loadanim(const char *animname)
- {
- const char *sep = strchr(animname, ':');
- skelanimspec *sa = skel->findskelanim(animname, sep ? '\0' : ':');
- if(!sa)
- {
- string filename;
- copystring(filename, animname);
- if(sep) filename[sep - animname] = '\0';
- if(loadiqm(filename, false, true))
- sa = skel->findskelanim(animname, sep ? '\0' : ':');
- }
- return sa;
- }
- };
-
- meshgroup *loadmeshes(const char *name, va_list args)
- {
- iqmmeshgroup *group = new iqmmeshgroup;
- group->shareskeleton(va_arg(args, char *));
- if(!group->loadmesh(name)) { delete group; return NULL; }
- return group;
- }
-
- bool loaddefaultparts()
- {
- skelpart &mdl = addpart();
- mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0;
- adjustments.setsize(0);
- const char *fname = name + strlen(name);
- do --fname; while(fname >= name && *fname!='/' && *fname!='\\');
- fname++;
- defformatstring(meshname, "packages/models/%s/%s.iqm", name, fname);
- mdl.meshes = sharemeshes(path(meshname), NULL);
- if(!mdl.meshes) return false;
- mdl.initanimparts();
- mdl.initskins();
- return true;
- }
+ iqm(const char *name) : skelloader(name) {}
+
+ static const char *formatname() { return "iqm"; }
+ int type() const { return MDL_IQM; }
+
+ struct iqmmeshgroup : skelmeshgroup
+ {
+ iqmmeshgroup()
+ {
+ }
+
+ bool loadiqmmeshes(const char *filename, const iqmheader &hdr, uchar *buf)
+ {
+ lilswap((uint *)&buf[hdr.ofs_vertexarrays], hdr.num_vertexarrays*sizeof(iqmvertexarray)/sizeof(uint));
+ lilswap((uint *)&buf[hdr.ofs_triangles], hdr.num_triangles*sizeof(iqmtriangle)/sizeof(uint));
+ lilswap((uint *)&buf[hdr.ofs_meshes], hdr.num_meshes*sizeof(iqmmesh)/sizeof(uint));
+ lilswap((uint *)&buf[hdr.ofs_joints], hdr.num_joints*sizeof(iqmjoint)/sizeof(uint));
+
+ const char *str = hdr.ofs_text ? (char *)&buf[hdr.ofs_text] : "";
+ float *vpos = NULL, *vnorm = NULL, *vtan = NULL, *vtc = NULL;
+ uchar *vindex = NULL, *vweight = NULL;
+ iqmvertexarray *vas = (iqmvertexarray *)&buf[hdr.ofs_vertexarrays];
+ loopi(hdr.num_vertexarrays)
+ {
+ iqmvertexarray &va = vas[i];
+ switch(va.type)
+ {
+ case IQM_POSITION: if(va.format != IQM_FLOAT || va.size != 3) return false; vpos = (float *)&buf[va.offset]; lilswap(vpos, 3*hdr.num_vertexes); break;
+ case IQM_NORMAL: if(va.format != IQM_FLOAT || va.size != 3) return false; vnorm = (float *)&buf[va.offset]; lilswap(vnorm, 3*hdr.num_vertexes); break;
+ case IQM_TANGENT: if(va.format != IQM_FLOAT || va.size != 4) return false; vtan = (float *)&buf[va.offset]; lilswap(vtan, 4*hdr.num_vertexes); break;
+ case IQM_TEXCOORD: if(va.format != IQM_FLOAT || va.size != 2) return false; vtc = (float *)&buf[va.offset]; lilswap(vtc, 2*hdr.num_vertexes); break;
+ case IQM_BLENDINDEXES: if(va.format != IQM_UBYTE || va.size != 4) return false; vindex = (uchar *)&buf[va.offset]; break;
+ case IQM_BLENDWEIGHTS: if(va.format != IQM_UBYTE || va.size != 4) return false; vweight = (uchar *)&buf[va.offset]; break;
+ }
+ }
+ if(!vpos) return false;
+
+ iqmtriangle *tris = (iqmtriangle *)&buf[hdr.ofs_triangles];
+ iqmmesh *imeshes = (iqmmesh *)&buf[hdr.ofs_meshes];
+ iqmjoint *joints = (iqmjoint *)&buf[hdr.ofs_joints];
+
+ if(hdr.num_joints)
+ {
+ if(skel->numbones <= 0)
+ {
+ skel->numbones = hdr.num_joints;
+ skel->bones = new boneinfo[skel->numbones];
+ loopi(hdr.num_joints)
+ {
+ iqmjoint &j = joints[i];
+ boneinfo &b = skel->bones[i];
+ if(!b.name) b.name = newstring(&str[j.name]);
+ b.parent = j.parent;
+ if(skel->shared <= 1)
+ {
+ j.pos.y = -j.pos.y;
+ j.orient.x = -j.orient.x;
+ j.orient.z = -j.orient.z;
+ j.orient.normalize();
+ b.base = dualquat(j.orient, j.pos);
+ if(b.parent >= 0) b.base.mul(skel->bones[b.parent].base, dualquat(b.base));
+ (b.invbase = b.base).invert();
+ }
+ }
+ }
+
+ if(skel->shared <= 1)
+ skel->linkchildren();
+ }
+
+ loopi(hdr.num_meshes)
+ {
+ iqmmesh &im = imeshes[i];
+ skelmesh *m = new skelmesh;
+ m->group = this;
+ meshes.add(m);
+ m->name = newstring(&str[im.name]);
+ m->numverts = im.num_vertexes;
+ int noblend = -1;
+ if(m->numverts)
+ {
+ m->verts = new vert[m->numverts];
+ if(vtan) m->bumpverts = new bumpvert[m->numverts];
+ if(!vindex || !vweight)
+ {
+ blendcombo c;
+ c.finalize(0);
+ noblend = m->addblendcombo(c);
+ }
+ }
+ int fv = im.first_vertex;
+ float *mpos = vpos + 3*fv,
+ *mnorm = vnorm ? vnorm + 3*fv : NULL,
+ *mtan = vtan ? vtan + 4*fv : NULL,
+ *mtc = vtc ? vtc + 2*fv : NULL;
+ uchar *mindex = vindex ? vindex + 4*fv : NULL, *mweight = vweight ? vweight + 4*fv : NULL;
+ loopj(im.num_vertexes)
+ {
+ vert &v = m->verts[j];
+ v.pos = vec(mpos[0], -mpos[1], mpos[2]);
+ mpos += 3;
+ if(mtc)
+ {
+ v.tc = vec2(mtc[0], mtc[1]);
+ mtc += 2;
+ }
+ else v.tc = vec2(0, 0);
+ if(mnorm)
+ {
+ v.norm = vec(mnorm[0], -mnorm[1], mnorm[2]);
+ mnorm += 3;
+ if(mtan)
+ {
+ m->calctangent(m->bumpverts[j], v.norm, vec(mtan[0], -mtan[1], mtan[2]), mtan[3]);
+ mtan += 4;
+ }
+ }
+ else v.norm = vec(0, 0, 0);
+ if(noblend < 0)
+ {
+ blendcombo c;
+ int sorted = 0;
+ loopk(4) sorted = c.addweight(sorted, mweight[k], mindex[k]);
+ mweight += 4;
+ mindex += 4;
+ c.finalize(sorted);
+ v.blend = m->addblendcombo(c);
+ }
+ else v.blend = noblend;
+ }
+ m->numtris = im.num_triangles;
+ if(m->numtris) m->tris = new tri[m->numtris];
+ iqmtriangle *mtris = tris + im.first_triangle;
+ loopj(im.num_triangles)
+ {
+ tri &t = m->tris[j];
+ t.vert[0] = mtris->vertex[0] - fv;
+ t.vert[1] = mtris->vertex[1] - fv;
+ t.vert[2] = mtris->vertex[2] - fv;
+ ++mtris;
+ }
+ if(!m->numtris || !m->numverts)
+ {
+ conoutf(CON_WARN, "empty mesh in %s", filename);
+ meshes.removeobj(m);
+ delete m;
+ }
+ }
+
+ sortblendcombos();
+
+ return true;
+ }
+
+ bool loadiqmanims(const char *filename, const iqmheader &hdr, uchar *buf)
+ {
+ lilswap((uint *)&buf[hdr.ofs_poses], hdr.num_poses*sizeof(iqmpose)/sizeof(uint));
+ lilswap((uint *)&buf[hdr.ofs_anims], hdr.num_anims*sizeof(iqmanim)/sizeof(uint));
+ lilswap((ushort *)&buf[hdr.ofs_frames], hdr.num_frames*hdr.num_framechannels);
+
+ const char *str = hdr.ofs_text ? (char *)&buf[hdr.ofs_text] : "";
+ iqmpose *poses = (iqmpose *)&buf[hdr.ofs_poses];
+ iqmanim *anims = (iqmanim *)&buf[hdr.ofs_anims];
+ ushort *frames = (ushort *)&buf[hdr.ofs_frames];
+ loopi(hdr.num_anims)
+ {
+ iqmanim &a = anims[i];
+ string name;
+ copystring(name, filename);
+ concatstring(name, ":");
+ concatstring(name, &str[a.name]);
+ skelanimspec *sa = skel->findskelanim(name);
+ if(sa) continue;
+ sa = &skel->addskelanim(name);
+ sa->frame = skel->numframes;
+ sa->range = a.num_frames;
+ dualquat *animbones = new dualquat[(skel->numframes+a.num_frames)*skel->numbones];
+ if(skel->bones)
+ {
+ memcpy(animbones, skel->framebones, skel->numframes*skel->numbones*sizeof(dualquat));
+ delete[] skel->framebones;
+ }
+ skel->framebones = animbones;
+ animbones += skel->numframes*skel->numbones;
+ skel->numframes += a.num_frames;
+ ushort *animdata = &frames[a.first_frame*hdr.num_framechannels];
+ loopj(a.num_frames)
+ {
+ dualquat *frame = &animbones[j*skel->numbones];
+ loopk(skel->numbones)
+ {
+ iqmpose &p = poses[k];
+ vec pos;
+ quat orient;
+ pos.x = p.offsetpos.x; if(p.mask&0x01) pos.x += *animdata++ * p.scalepos.x;
+ pos.y = -p.offsetpos.y; if(p.mask&0x02) pos.y -= *animdata++ * p.scalepos.y;
+ pos.z = p.offsetpos.z; if(p.mask&0x04) pos.z += *animdata++ * p.scalepos.z;
+ orient.x = -p.offsetorient.x; if(p.mask&0x08) orient.x -= *animdata++ * p.scaleorient.x;
+ orient.y = p.offsetorient.y; if(p.mask&0x10) orient.y += *animdata++ * p.scaleorient.y;
+ orient.z = -p.offsetorient.z; if(p.mask&0x20) orient.z -= *animdata++ * p.scaleorient.z;
+ orient.w = p.offsetorient.w; if(p.mask&0x40) orient.w += *animdata++ * p.scaleorient.w;
+ orient.normalize();
+ if(p.mask&0x380)
+ {
+ if(p.mask&0x80) animdata++;
+ if(p.mask&0x100) animdata++;
+ if(p.mask&0x200) animdata++;
+ }
+ frame[k] = dualquat(orient, pos);
+ if(adjustments.inrange(k)) adjustments[k].adjust(frame[k]);
+ boneinfo &b = skel->bones[k];
+ frame[k].mul(b.invbase);
+ if(b.parent >= 0) frame[k].mul(skel->bones[b.parent].base, dualquat(frame[k]));
+ frame[k].fixantipodal(skel->framebones[k]);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ bool loadiqm(const char *filename, bool doloadmesh, bool doloadanim)
+ {
+ stream *f = openfile(filename, "rb");
+ if(!f) return false;
+
+ uchar *buf = NULL;
+ iqmheader hdr;
+ if(f->read(&hdr, sizeof(hdr)) != sizeof(hdr) || memcmp(hdr.magic, "INTERQUAKEMODEL", sizeof(hdr.magic))) goto error;
+ lilswap(&hdr.version, (sizeof(hdr) - sizeof(hdr.magic))/sizeof(uint));
+ if(hdr.version != 2) goto error;
+ if(hdr.filesize > (16<<20)) goto error; // sanity check... don't load files bigger than 16 MB
+ buf = new (false) uchar[hdr.filesize];
+ if(!buf || f->read(buf + sizeof(hdr), hdr.filesize - sizeof(hdr)) != hdr.filesize - sizeof(hdr)) goto error;
+
+ if(doloadmesh && !loadiqmmeshes(filename, hdr, buf)) goto error;
+ if(doloadanim && !loadiqmanims(filename, hdr, buf)) goto error;
+
+ delete[] buf;
+ delete f;
+ return true;
+
+ error:
+ if(buf) delete[] buf;
+ delete f;
+ return false;
+ }
+
+ bool loadmesh(const char *filename)
+ {
+ name = newstring(filename);
+
+ return loadiqm(filename, true, false);
+ }
+
+ skelanimspec *loadanim(const char *animname)
+ {
+ const char *sep = strchr(animname, ':');
+ skelanimspec *sa = skel->findskelanim(animname, sep ? '\0' : ':');
+ if(!sa)
+ {
+ string filename;
+ copystring(filename, animname);
+ if(sep) filename[sep - animname] = '\0';
+ if(loadiqm(filename, false, true))
+ sa = skel->findskelanim(animname, sep ? '\0' : ':');
+ }
+ return sa;
+ }
+ };
+
+ meshgroup *loadmeshes(const char *name, va_list args)
+ {
+ iqmmeshgroup *group = new iqmmeshgroup;
+ group->shareskeleton(va_arg(args, char *));
+ if(!group->loadmesh(name)) { delete group; return NULL; }
+ return group;
+ }
+
+ bool loaddefaultparts()
+ {
+ skelpart &mdl = addpart();
+ mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0;
+ adjustments.setsize(0);
+ const char *fname = name + strlen(name);
+ do --fname; while(fname >= name && *fname!='/' && *fname!='\\');
+ fname++;
+ defformatstring(meshname, "packages/models/%s/%s.iqm", name, fname);
+ mdl.meshes = sharemeshes(path(meshname), NULL);
+ if(!mdl.meshes) return false;
+ mdl.initanimparts();
+ mdl.initskins();
+ return true;
+ }
};
skelcommands<iqm> iqmcommands;
diff --git a/src/engine/lightmap.cpp b/src/engine/lightmap.cpp
index b4090e2..959782e 100644
--- a/src/engine/lightmap.cpp
+++ b/src/engine/lightmap.cpp
@@ -8,59 +8,59 @@ struct lightmaptask;
struct lightmapworker
{
- uchar *buf;
- int bufstart, bufused;
- lightmapinfo *firstlightmap, *lastlightmap, *curlightmaps;
- cube *c;
- cubeext *ext;
- uchar *colorbuf;
- bvec *raybuf;
- uchar *ambient, *blur;
- vec *colordata, *raydata;
- int type, bpp, w, h, orient, rotate;
- VSlot *vslot;
- Slot *slot;
- vector<const extentity *> lights;
- ShadowRayCache *shadowraycache;
- BlendMapCache *blendmapcache;
- bool needspace, doneworking;
- SDL_cond *spacecond;
- SDL_Thread *thread;
-
- lightmapworker();
- ~lightmapworker();
-
- void reset();
- bool setupthread();
- void cleanupthread();
-
- static int work(void *data);
+ uchar *buf;
+ int bufstart, bufused;
+ lightmapinfo *firstlightmap, *lastlightmap, *curlightmaps;
+ cube *c;
+ cubeext *ext;
+ uchar *colorbuf;
+ bvec *raybuf;
+ uchar *ambient, *blur;
+ vec *colordata, *raydata;
+ int type, bpp, w, h, orient, rotate;
+ VSlot *vslot;
+ Slot *slot;
+ vector<const extentity *> lights;
+ ShadowRayCache *shadowraycache;
+ BlendMapCache *blendmapcache;
+ bool needspace, doneworking;
+ SDL_cond *spacecond;
+ SDL_Thread *thread;
+
+ lightmapworker();
+ ~lightmapworker();
+
+ void reset();
+ bool setupthread();
+ void cleanupthread();
+
+ static int work(void *data);
};
struct lightmapinfo
{
- lightmapinfo *next;
- cube *c;
- uchar *colorbuf;
- bvec *raybuf;
- bool packed;
- int type, w, h, bpp, bufsize, surface, layers;
+ lightmapinfo *next;
+ cube *c;
+ uchar *colorbuf;
+ bvec *raybuf;
+ bool packed;
+ int type, w, h, bpp, bufsize, surface, layers;
};
struct lightmaptask
{
- ivec o;
- int size, usefaces, progress;
- cube *c;
- cubeext *ext;
- lightmapinfo *lightmaps;
- lightmapworker *worker;
+ ivec o;
+ int size, usefaces, progress;
+ cube *c;
+ cubeext *ext;
+ lightmapinfo *lightmaps;
+ lightmapworker *worker;
};
struct lightmapext
{
- cube *c;
- cubeext *ext;
+ cube *c;
+ cubeext *ext;
};
static vector<lightmapworker *> lightmapworkers;
@@ -79,24 +79,24 @@ VARR(lighterror, 1, 8, 16);
VARR(bumperror, 1, 3, 16);
VARR(lightlod, 0, 0, 10);
bvec ambientcolor(0x19, 0x19, 0x19), skylightcolor(0, 0, 0);
-HVARFR(ambient, 1, 0x191919, 0xFFFFFF,
+HVARFR(ambient, 1, 0x191919, 0xFFFFFF,
{
- if(ambient <= 255) ambient |= (ambient<<8) | (ambient<<16);
- ambientcolor = bvec((ambient>>16)&0xFF, (ambient>>8)&0xFF, ambient&0xFF);
+ if(ambient <= 255) ambient |= (ambient<<8) | (ambient<<16);
+ ambientcolor = bvec((ambient>>16)&0xFF, (ambient>>8)&0xFF, ambient&0xFF);
});
-HVARFR(skylight, 0, 0, 0xFFFFFF,
+HVARFR(skylight, 0, 0, 0xFFFFFF,
{
- if(skylight <= 255) skylight |= (skylight<<8) | (skylight<<16);
- skylightcolor = bvec((skylight>>16)&0xFF, (skylight>>8)&0xFF, skylight&0xFF);
+ if(skylight <= 255) skylight |= (skylight<<8) | (skylight<<16);
+ skylightcolor = bvec((skylight>>16)&0xFF, (skylight>>8)&0xFF, skylight&0xFF);
});
extern void setupsunlight();
bvec sunlightcolor(0, 0, 0);
HVARFR(sunlight, 0, 0, 0xFFFFFF,
{
- if(sunlight <= 255) sunlight |= (sunlight<<8) | (sunlight<<16);
- sunlightcolor = bvec((sunlight>>16)&0xFF, (sunlight>>8)&0xFF, sunlight&0xFF);
- setupsunlight();
+ if(sunlight <= 255) sunlight |= (sunlight<<8) | (sunlight<<16);
+ sunlightcolor = bvec((sunlight>>16)&0xFF, (sunlight>>8)&0xFF, sunlight&0xFF);
+ setupsunlight();
});
FVARFR(sunlightscale, 0, 1, 16, setupsunlight());
vec sunlightdir(0, 0, 1);
@@ -104,25 +104,25 @@ extern void setsunlightdir();
VARFR(sunlightyaw, 0, 0, 360, setsunlightdir());
VARFR(sunlightpitch, -90, 90, 90, setsunlightdir());
-void setsunlightdir()
-{
- sunlightdir = vec(sunlightyaw*RAD, sunlightpitch*RAD);
- loopk(3) if(fabs(sunlightdir[k]) < 1e-5f) sunlightdir[k] = 0;
- sunlightdir.normalize();
- setupsunlight();
+void setsunlightdir()
+{
+ sunlightdir = vec(sunlightyaw*RAD, sunlightpitch*RAD);
+ loopk(3) if(fabs(sunlightdir[k]) < 1e-5f) sunlightdir[k] = 0;
+ sunlightdir.normalize();
+ setupsunlight();
}
entity sunlightent;
void setupsunlight()
{
- memclear(sunlightent);
- sunlightent.type = ET_LIGHT;
- sunlightent.attr1 = 0;
- sunlightent.attr2 = int(sunlightcolor.x*sunlightscale);
- sunlightent.attr3 = int(sunlightcolor.y*sunlightscale);
- sunlightent.attr4 = int(sunlightcolor.z*sunlightscale);
- float dist = min(min(sunlightdir.x ? 1/fabs(sunlightdir.x) : 1e16f, sunlightdir.y ? 1/fabs(sunlightdir.y) : 1e16f), sunlightdir.z ? 1/fabs(sunlightdir.z) : 1e16f);
- sunlightent.o = vec(sunlightdir).mul(dist*worldsize).add(vec(worldsize/2, worldsize/2, worldsize/2));
+ memclear(sunlightent);
+ sunlightent.type = ET_LIGHT;
+ sunlightent.attr1 = 0;
+ sunlightent.attr2 = int(sunlightcolor.x*sunlightscale);
+ sunlightent.attr3 = int(sunlightcolor.y*sunlightscale);
+ sunlightent.attr4 = int(sunlightcolor.z*sunlightscale);
+ float dist = min(min(sunlightdir.x ? 1/fabs(sunlightdir.x) : 1e16f, sunlightdir.y ? 1/fabs(sunlightdir.y) : 1e16f), sunlightdir.z ? 1/fabs(sunlightdir.z) : 1e16f);
+ sunlightent.o = vec(sunlightdir).mul(dist*worldsize).add(vec(worldsize/2, worldsize/2, worldsize/2));
}
VARR(skytexturelight, 0, 1, 1);
@@ -130,97 +130,97 @@ extern int useskytexture;
static const surfaceinfo brightsurfaces[6] =
{
- brightsurface,
- brightsurface,
- brightsurface,
- brightsurface,
- brightsurface,
- brightsurface
+ brightsurface,
+ brightsurface,
+ brightsurface,
+ brightsurface,
+ brightsurface,
+ brightsurface
};
void brightencube(cube &c)
{
- if(!c.ext) newcubeext(c, 0, false);
- memcpy(c.ext->surfaces, brightsurfaces, sizeof(brightsurfaces));
+ if(!c.ext) newcubeext(c, 0, false);
+ memcpy(c.ext->surfaces, brightsurfaces, sizeof(brightsurfaces));
}
void setsurfaces(cube &c, const surfaceinfo *surfs, const vertinfo *verts, int numverts)
{
- if(!c.ext || c.ext->maxverts < numverts) newcubeext(c, numverts, false);
- memcpy(c.ext->surfaces, surfs, sizeof(c.ext->surfaces));
- memcpy(c.ext->verts(), verts, numverts*sizeof(vertinfo));
+ if(!c.ext || c.ext->maxverts < numverts) newcubeext(c, numverts, false);
+ memcpy(c.ext->surfaces, surfs, sizeof(c.ext->surfaces));
+ memcpy(c.ext->verts(), verts, numverts*sizeof(vertinfo));
}
void setsurface(cube &c, int orient, const surfaceinfo &src, const vertinfo *srcverts, int numsrcverts)
{
- int dstoffset = 0;
- if(!c.ext) newcubeext(c, numsrcverts, true);
- else
- {
- int numbefore = 0, beforeoffset = 0;
- loopi(orient)
- {
- surfaceinfo &surf = c.ext->surfaces[i];
- int numverts = surf.totalverts();
- if(!numverts) continue;
- numbefore += numverts;
- beforeoffset = surf.verts + numverts;
- }
- int numafter = 0, afteroffset = c.ext->maxverts;
- for(int i = 5; i > orient; i--)
- {
- surfaceinfo &surf = c.ext->surfaces[i];
- int numverts = surf.totalverts();
- if(!numverts) continue;
- numafter += numverts;
- afteroffset = surf.verts;
- }
- if(afteroffset - beforeoffset >= numsrcverts) dstoffset = beforeoffset;
- else
- {
- cubeext *ext = c.ext;
- if(numbefore + numsrcverts + numafter > c.ext->maxverts)
- {
- ext = growcubeext(c.ext, numbefore + numsrcverts + numafter);
- memcpy(ext->surfaces, c.ext->surfaces, sizeof(ext->surfaces));
- }
- int offset = 0;
- if(numbefore == beforeoffset)
- {
- if(numbefore && c.ext != ext) memcpy(ext->verts(), c.ext->verts(), numbefore*sizeof(vertinfo));
- offset = numbefore;
- }
- else loopi(orient)
- {
- surfaceinfo &surf = ext->surfaces[i];
- int numverts = surf.totalverts();
- if(!numverts) continue;
- memmove(ext->verts() + offset, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
- surf.verts = offset;
- offset += numverts;
- }
- dstoffset = offset;
- offset += numsrcverts;
- if(numafter && offset > afteroffset)
- {
- offset += numafter;
- for(int i = 5; i > orient; i--)
- {
- surfaceinfo &surf = ext->surfaces[i];
- int numverts = surf.totalverts();
- if(!numverts) continue;
- offset -= numverts;
- memmove(ext->verts() + offset, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
- surf.verts = offset;
- }
- }
- if(c.ext != ext) setcubeext(c, ext);
- }
- }
- surfaceinfo &dst = c.ext->surfaces[orient];
- dst = src;
- dst.verts = dstoffset;
- if(srcverts) memcpy(c.ext->verts() + dstoffset, srcverts, numsrcverts*sizeof(vertinfo));
+ int dstoffset = 0;
+ if(!c.ext) newcubeext(c, numsrcverts, true);
+ else
+ {
+ int numbefore = 0, beforeoffset = 0;
+ loopi(orient)
+ {
+ surfaceinfo &surf = c.ext->surfaces[i];
+ int numverts = surf.totalverts();
+ if(!numverts) continue;
+ numbefore += numverts;
+ beforeoffset = surf.verts + numverts;
+ }
+ int numafter = 0, afteroffset = c.ext->maxverts;
+ for(int i = 5; i > orient; i--)
+ {
+ surfaceinfo &surf = c.ext->surfaces[i];
+ int numverts = surf.totalverts();
+ if(!numverts) continue;
+ numafter += numverts;
+ afteroffset = surf.verts;
+ }
+ if(afteroffset - beforeoffset >= numsrcverts) dstoffset = beforeoffset;
+ else
+ {
+ cubeext *ext = c.ext;
+ if(numbefore + numsrcverts + numafter > c.ext->maxverts)
+ {
+ ext = growcubeext(c.ext, numbefore + numsrcverts + numafter);
+ memcpy(ext->surfaces, c.ext->surfaces, sizeof(ext->surfaces));
+ }
+ int offset = 0;
+ if(numbefore == beforeoffset)
+ {
+ if(numbefore && c.ext != ext) memcpy(ext->verts(), c.ext->verts(), numbefore*sizeof(vertinfo));
+ offset = numbefore;
+ }
+ else loopi(orient)
+ {
+ surfaceinfo &surf = ext->surfaces[i];
+ int numverts = surf.totalverts();
+ if(!numverts) continue;
+ memmove(ext->verts() + offset, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
+ surf.verts = offset;
+ offset += numverts;
+ }
+ dstoffset = offset;
+ offset += numsrcverts;
+ if(numafter && offset > afteroffset)
+ {
+ offset += numafter;
+ for(int i = 5; i > orient; i--)
+ {
+ surfaceinfo &surf = ext->surfaces[i];
+ int numverts = surf.totalverts();
+ if(!numverts) continue;
+ offset -= numverts;
+ memmove(ext->verts() + offset, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
+ surf.verts = offset;
+ }
+ }
+ if(c.ext != ext) setcubeext(c, ext);
+ }
+ }
+ surfaceinfo &dst = c.ext->surfaces[orient];
+ dst = src;
+ dst.verts = dstoffset;
+ if(srcverts) memcpy(c.ext->verts() + dstoffset, srcverts, numsrcverts*sizeof(vertinfo));
}
// quality parameters, set by the calclight arg
@@ -238,38 +238,38 @@ volatile bool check_calclight_progress = false;
void check_calclight_canceled()
{
- if(interceptkey(SDLK_ESCAPE))
- {
- calclight_canceled = true;
- loopv(lightmapworkers) lightmapworkers[i]->doneworking = true;
- }
- if(!calclight_canceled) check_calclight_progress = false;
+ if(interceptkey(SDLK_ESCAPE))
+ {
+ calclight_canceled = true;
+ loopv(lightmapworkers) lightmapworkers[i]->doneworking = true;
+ }
+ if(!calclight_canceled) check_calclight_progress = false;
}
void show_calclight_progress()
{
- float bar1 = float(progress) / float(allocnodes);
- defformatstring(text1, "%d%% using %d textures", int(bar1 * 100), lightmaps.length());
-
- if(LM_PACKW <= hwtexsize && !progresstex)
- {
- glGenTextures(1, &progresstex);
- createtexture(progresstex, LM_PACKW, LM_PACKH, NULL, 3, 1, GL_RGB);
- }
-
- // only update once a sec (4 * 250 ms ticks) to not kill performance
- if(progresstex && !calclight_canceled && progresslightmap >= 0 && !(progresstexticks++ % 4))
- {
- if(tasklock) SDL_LockMutex(tasklock);
- LightMap &lm = lightmaps[progresslightmap];
- uchar *data = lm.data;
- int bpp = lm.bpp;
- if(tasklock) SDL_UnlockMutex(tasklock);
- glBindTexture(GL_TEXTURE_2D, progresstex);
- glPixelStorei(GL_UNPACK_ALIGNMENT, texalign(data, LM_PACKW, bpp));
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LM_PACKW, LM_PACKH, bpp > 3 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, data);
- }
- renderprogress(bar1, text1, progresstexticks ? progresstex : 0);
+ float bar1 = float(progress) / float(allocnodes);
+ defformatstring(text1, "%d%% using %d textures", int(bar1 * 100), lightmaps.length());
+
+ if(LM_PACKW <= hwtexsize && !progresstex)
+ {
+ glGenTextures(1, &progresstex);
+ createtexture(progresstex, LM_PACKW, LM_PACKH, NULL, 3, 1, GL_RGB);
+ }
+
+ // only update once a sec (4 * 250 ms ticks) to not kill performance
+ if(progresstex && !calclight_canceled && progresslightmap >= 0 && !(progresstexticks++ % 4))
+ {
+ if(tasklock) SDL_LockMutex(tasklock);
+ LightMap &lm = lightmaps[progresslightmap];
+ uchar *data = lm.data;
+ int bpp = lm.bpp;
+ if(tasklock) SDL_UnlockMutex(tasklock);
+ glBindTexture(GL_TEXTURE_2D, progresstex);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, texalign(data, LM_PACKW, bpp));
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LM_PACKW, LM_PACKH, bpp > 3 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, data);
+ }
+ renderprogress(bar1, text1, progresstexticks ? progresstex : 0);
}
#define CHECK_PROGRESS_LOCKED(exit, before, after) CHECK_CALCLIGHT_PROGRESS_LOCKED(exit, show_calclight_progress, before, after)
@@ -277,398 +277,398 @@ void show_calclight_progress()
bool PackNode::insert(ushort &tx, ushort &ty, ushort tw, ushort th)
{
- if((available < tw && available < th) || w < tw || h < th)
- return false;
- if(child1)
- {
- bool inserted = child1->insert(tx, ty, tw, th) ||
- child2->insert(tx, ty, tw, th);
- available = max(child1->available, child2->available);
- if(!available) clear();
- return inserted;
- }
- if(w == tw && h == th)
- {
- available = 0;
- tx = x;
- ty = y;
- return true;
- }
-
- if(w - tw > h - th)
- {
- child1 = new PackNode(x, y, tw, h);
- child2 = new PackNode(x + tw, y, w - tw, h);
- }
- else
- {
- child1 = new PackNode(x, y, w, th);
- child2 = new PackNode(x, y + th, w, h - th);
- }
-
- bool inserted = child1->insert(tx, ty, tw, th);
- available = max(child1->available, child2->available);
- return inserted;
+ if((available < tw && available < th) || w < tw || h < th)
+ return false;
+ if(child1)
+ {
+ bool inserted = child1->insert(tx, ty, tw, th) ||
+ child2->insert(tx, ty, tw, th);
+ available = max(child1->available, child2->available);
+ if(!available) clear();
+ return inserted;
+ }
+ if(w == tw && h == th)
+ {
+ available = 0;
+ tx = x;
+ ty = y;
+ return true;
+ }
+
+ if(w - tw > h - th)
+ {
+ child1 = new PackNode(x, y, tw, h);
+ child2 = new PackNode(x + tw, y, w - tw, h);
+ }
+ else
+ {
+ child1 = new PackNode(x, y, w, th);
+ child2 = new PackNode(x, y + th, w, h - th);
+ }
+
+ bool inserted = child1->insert(tx, ty, tw, th);
+ available = max(child1->available, child2->available);
+ return inserted;
}
bool LightMap::insert(ushort &tx, ushort &ty, uchar *src, ushort tw, ushort th)
{
- if((type&LM_TYPE) != LM_BUMPMAP1 && !packroot.insert(tx, ty, tw, th))
- return false;
+ if((type&LM_TYPE) != LM_BUMPMAP1 && !packroot.insert(tx, ty, tw, th))
+ return false;
- copy(tx, ty, src, tw, th);
- return true;
+ copy(tx, ty, src, tw, th);
+ return true;
}
void LightMap::copy(ushort tx, ushort ty, uchar *src, ushort tw, ushort th)
{
- uchar *dst = data + bpp * tx + ty * bpp * LM_PACKW;
- loopi(th)
- {
- memcpy(dst, src, bpp * tw);
- dst += bpp * LM_PACKW;
- src += bpp * tw;
- }
- ++lightmaps;
- lumels += tw * th;
+ uchar *dst = data + bpp * tx + ty * bpp * LM_PACKW;
+ loopi(th)
+ {
+ memcpy(dst, src, bpp * tw);
+ dst += bpp * LM_PACKW;
+ src += bpp * tw;
+ }
+ ++lightmaps;
+ lumels += tw * th;
}
static void insertunlit(int i)
{
- LightMap &l = lightmaps[i];
- if((l.type&LM_TYPE) == LM_BUMPMAP1)
- {
- l.unlitx = l.unlity = -1;
- return;
- }
- ushort x, y;
- uchar unlit[4] = { ambientcolor[0], ambientcolor[1], ambientcolor[2], 255 };
- if(l.insert(x, y, unlit, 1, 1))
- {
- if((l.type&LM_TYPE) == LM_BUMPMAP0)
- {
- bvec front(128, 128, 255);
- ASSERT(lightmaps[i+1].insert(x, y, front.v, 1, 1));
- }
- l.unlitx = x;
- l.unlity = y;
- }
+ LightMap &l = lightmaps[i];
+ if((l.type&LM_TYPE) == LM_BUMPMAP1)
+ {
+ l.unlitx = l.unlity = -1;
+ return;
+ }
+ ushort x, y;
+ uchar unlit[4] = { ambientcolor[0], ambientcolor[1], ambientcolor[2], 255 };
+ if(l.insert(x, y, unlit, 1, 1))
+ {
+ if((l.type&LM_TYPE) == LM_BUMPMAP0)
+ {
+ bvec front(128, 128, 255);
+ ASSERT(lightmaps[i+1].insert(x, y, front.v, 1, 1));
+ }
+ l.unlitx = x;
+ l.unlity = y;
+ }
}
struct layoutinfo
{
- ushort x, y, lmid;
- uchar w, h;
+ ushort x, y, lmid;
+ uchar w, h;
};
static void insertlightmap(lightmapinfo &li, layoutinfo &si)
{
- loopv(lightmaps)
- {
- if(lightmaps[i].type == li.type && lightmaps[i].insert(si.x, si.y, li.colorbuf, si.w, si.h))
- {
- si.lmid = i + LMID_RESERVED;
- if((li.type&LM_TYPE) == LM_BUMPMAP0) ASSERT(lightmaps[i+1].insert(si.x, si.y, (uchar *)li.raybuf, si.w, si.h));
- return;
- }
- }
-
- progresslightmap = lightmaps.length();
-
- si.lmid = lightmaps.length() + LMID_RESERVED;
- LightMap &l = lightmaps.add();
- l.type = li.type;
- l.bpp = li.bpp;
- l.data = new uchar[li.bpp*LM_PACKW*LM_PACKH];
- memset(l.data, 0, li.bpp*LM_PACKW*LM_PACKH);
- ASSERT(l.insert(si.x, si.y, li.colorbuf, si.w, si.h));
- if((li.type&LM_TYPE) == LM_BUMPMAP0)
- {
- LightMap &r = lightmaps.add();
- r.type = LM_BUMPMAP1 | (li.type&~LM_TYPE);
- r.bpp = 3;
- r.data = new uchar[3*LM_PACKW*LM_PACKH];
- memset(r.data, 0, 3*LM_PACKW*LM_PACKH);
- ASSERT(r.insert(si.x, si.y, (uchar *)li.raybuf, si.w, si.h));
- }
+ loopv(lightmaps)
+ {
+ if(lightmaps[i].type == li.type && lightmaps[i].insert(si.x, si.y, li.colorbuf, si.w, si.h))
+ {
+ si.lmid = i + LMID_RESERVED;
+ if((li.type&LM_TYPE) == LM_BUMPMAP0) ASSERT(lightmaps[i+1].insert(si.x, si.y, (uchar *)li.raybuf, si.w, si.h));
+ return;
+ }
+ }
+
+ progresslightmap = lightmaps.length();
+
+ si.lmid = lightmaps.length() + LMID_RESERVED;
+ LightMap &l = lightmaps.add();
+ l.type = li.type;
+ l.bpp = li.bpp;
+ l.data = new uchar[li.bpp*LM_PACKW*LM_PACKH];
+ memset(l.data, 0, li.bpp*LM_PACKW*LM_PACKH);
+ ASSERT(l.insert(si.x, si.y, li.colorbuf, si.w, si.h));
+ if((li.type&LM_TYPE) == LM_BUMPMAP0)
+ {
+ LightMap &r = lightmaps.add();
+ r.type = LM_BUMPMAP1 | (li.type&~LM_TYPE);
+ r.bpp = 3;
+ r.data = new uchar[3*LM_PACKW*LM_PACKH];
+ memset(r.data, 0, 3*LM_PACKW*LM_PACKH);
+ ASSERT(r.insert(si.x, si.y, (uchar *)li.raybuf, si.w, si.h));
+ }
}
static void copylightmap(lightmapinfo &li, layoutinfo &si)
{
- lightmaps[si.lmid-LMID_RESERVED].copy(si.x, si.y, li.colorbuf, si.w, si.h);
- if((li.type&LM_TYPE)==LM_BUMPMAP0 && lightmaps.inrange(si.lmid+1-LMID_RESERVED))
- lightmaps[si.lmid+1-LMID_RESERVED].copy(si.x, si.y, (uchar *)li.raybuf, si.w, si.h);
+ lightmaps[si.lmid-LMID_RESERVED].copy(si.x, si.y, li.colorbuf, si.w, si.h);
+ if((li.type&LM_TYPE)==LM_BUMPMAP0 && lightmaps.inrange(si.lmid+1-LMID_RESERVED))
+ lightmaps[si.lmid+1-LMID_RESERVED].copy(si.x, si.y, (uchar *)li.raybuf, si.w, si.h);
}
static inline bool htcmp(const lightmapinfo &k, const layoutinfo &v)
{
- int kw = k.w, kh = k.h;
- if(kw != v.w || kh != v.h) return false;
- LightMap &vlm = lightmaps[v.lmid - LMID_RESERVED];
- int ktype = k.type;
- if(ktype != vlm.type) return false;
- int kbpp = k.bpp;
- const uchar *kcolor = k.colorbuf, *vcolor = vlm.data + kbpp*(v.x + v.y*LM_PACKW);
- loopi(kh)
- {
- if(memcmp(kcolor, vcolor, kbpp*kw)) return false;
- kcolor += kbpp*kw;
- vcolor += kbpp*LM_PACKW;
- }
- if((ktype&LM_TYPE) != LM_BUMPMAP0) return true;
- const bvec *kdir = k.raybuf, *vdir = (const bvec *)lightmaps[v.lmid+1 - LMID_RESERVED].data + (v.x + v.y*LM_PACKW);
- loopi(kh)
- {
- if(memcmp(kdir, vdir, kw*sizeof(bvec))) return false;
- kdir += kw;
- vdir += LM_PACKW;
- }
- return true;
-}
-
+ int kw = k.w, kh = k.h;
+ if(kw != v.w || kh != v.h) return false;
+ LightMap &vlm = lightmaps[v.lmid - LMID_RESERVED];
+ int ktype = k.type;
+ if(ktype != vlm.type) return false;
+ int kbpp = k.bpp;
+ const uchar *kcolor = k.colorbuf, *vcolor = vlm.data + kbpp*(v.x + v.y*LM_PACKW);
+ loopi(kh)
+ {
+ if(memcmp(kcolor, vcolor, kbpp*kw)) return false;
+ kcolor += kbpp*kw;
+ vcolor += kbpp*LM_PACKW;
+ }
+ if((ktype&LM_TYPE) != LM_BUMPMAP0) return true;
+ const bvec *kdir = k.raybuf, *vdir = (const bvec *)lightmaps[v.lmid+1 - LMID_RESERVED].data + (v.x + v.y*LM_PACKW);
+ loopi(kh)
+ {
+ if(memcmp(kdir, vdir, kw*sizeof(bvec))) return false;
+ kdir += kw;
+ vdir += LM_PACKW;
+ }
+ return true;
+}
+
static inline uint hthash(const lightmapinfo &k)
{
- int kw = k.w, kh = k.h, kbpp = k.bpp;
- uint hash = kw + (kh<<8);
- const uchar *color = k.colorbuf;
- loopi(kw*kh)
- {
- hash ^= color[0] + (color[1] << 4) + (color[2] << 8);
- color += kbpp;
- }
- return hash;
+ int kw = k.w, kh = k.h, kbpp = k.bpp;
+ uint hash = kw + (kh<<8);
+ const uchar *color = k.colorbuf;
+ loopi(kw*kh)
+ {
+ hash ^= color[0] + (color[1] << 4) + (color[2] << 8);
+ color += kbpp;
+ }
+ return hash;
}
static hashset<layoutinfo> compressed;
VAR(lightcompress, 0, 3, 6);
-static bool packlightmap(lightmapinfo &l, layoutinfo &surface)
-{
- surface.w = l.w;
- surface.h = l.h;
- if((int)l.w <= lightcompress && (int)l.h <= lightcompress)
- {
- layoutinfo *val = compressed.access(l);
- if(!val)
- {
- insertlightmap(l, surface);
- compressed[l] = surface;
- }
- else
- {
- surface.x = val->x;
- surface.y = val->y;
- surface.lmid = val->lmid;
- return false;
- }
- }
- else insertlightmap(l, surface);
- return true;
+static bool packlightmap(lightmapinfo &l, layoutinfo &surface)
+{
+ surface.w = l.w;
+ surface.h = l.h;
+ if((int)l.w <= lightcompress && (int)l.h <= lightcompress)
+ {
+ layoutinfo *val = compressed.access(l);
+ if(!val)
+ {
+ insertlightmap(l, surface);
+ compressed[l] = surface;
+ }
+ else
+ {
+ surface.x = val->x;
+ surface.y = val->y;
+ surface.lmid = val->lmid;
+ return false;
+ }
+ }
+ else insertlightmap(l, surface);
+ return true;
}
static void updatelightmap(const layoutinfo &surface)
{
- if(max(LM_PACKW, LM_PACKH) > hwtexsize || !lightmaps.inrange(surface.lmid-LMID_RESERVED)) return;
-
- LightMap &lm = lightmaps[surface.lmid-LMID_RESERVED];
- if(lm.tex < 0)
- {
- lm.offsetx = lm.offsety = 0;
- lm.tex = lightmaptexs.length();
- LightMapTexture &tex = lightmaptexs.add();
- tex.type = lm.type;
- tex.w = LM_PACKW;
- tex.h = LM_PACKH;
- tex.unlitx = lm.unlitx;
- tex.unlity = lm.unlity;
- glGenTextures(1, &tex.id);
- createtexture(tex.id, tex.w, tex.h, NULL, 3, 1, tex.type&LM_ALPHA ? GL_RGBA : GL_RGB);
- if((lm.type&LM_TYPE)==LM_BUMPMAP0 && lightmaps.inrange(surface.lmid+1-LMID_RESERVED))
- {
- LightMap &lm2 = lightmaps[surface.lmid+1-LMID_RESERVED];
- lm2.offsetx = lm2.offsety = 0;
- lm2.tex = lightmaptexs.length();
- LightMapTexture &tex2 = lightmaptexs.add();
- tex2.type = (lm.type&~LM_TYPE) | LM_BUMPMAP0;
- tex2.w = LM_PACKW;
- tex2.h = LM_PACKH;
- tex2.unlitx = lm2.unlitx;
- tex2.unlity = lm2.unlity;
- glGenTextures(1, &tex2.id);
- createtexture(tex2.id, tex2.w, tex2.h, NULL, 3, 1, GL_RGB);
- }
- }
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glPixelStorei(GL_UNPACK_ROW_LENGTH, LM_PACKW);
-
- glBindTexture(GL_TEXTURE_2D, lightmaptexs[lm.tex].id);
- glTexSubImage2D(GL_TEXTURE_2D, 0, lm.offsetx + surface.x, lm.offsety + surface.y, surface.w, surface.h, lm.type&LM_ALPHA ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, &lm.data[(surface.y*LM_PACKW + surface.x)*lm.bpp]);
- if((lm.type&LM_TYPE)==LM_BUMPMAP0 && lightmaps.inrange(surface.lmid+1-LMID_RESERVED))
- {
- LightMap &lm2 = lightmaps[surface.lmid+1-LMID_RESERVED];
- glBindTexture(GL_TEXTURE_2D, lightmaptexs[lm2.tex].id);
- glTexSubImage2D(GL_TEXTURE_2D, 0, lm2.offsetx + surface.x, lm2.offsety + surface.y, surface.w, surface.h, GL_RGB, GL_UNSIGNED_BYTE, &lm2.data[(surface.y*LM_PACKW + surface.x)*3]);
- }
-
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
-}
-
-
+ if(max(LM_PACKW, LM_PACKH) > hwtexsize || !lightmaps.inrange(surface.lmid-LMID_RESERVED)) return;
+
+ LightMap &lm = lightmaps[surface.lmid-LMID_RESERVED];
+ if(lm.tex < 0)
+ {
+ lm.offsetx = lm.offsety = 0;
+ lm.tex = lightmaptexs.length();
+ LightMapTexture &tex = lightmaptexs.add();
+ tex.type = lm.type;
+ tex.w = LM_PACKW;
+ tex.h = LM_PACKH;
+ tex.unlitx = lm.unlitx;
+ tex.unlity = lm.unlity;
+ glGenTextures(1, &tex.id);
+ createtexture(tex.id, tex.w, tex.h, NULL, 3, 1, tex.type&LM_ALPHA ? GL_RGBA : GL_RGB);
+ if((lm.type&LM_TYPE)==LM_BUMPMAP0 && lightmaps.inrange(surface.lmid+1-LMID_RESERVED))
+ {
+ LightMap &lm2 = lightmaps[surface.lmid+1-LMID_RESERVED];
+ lm2.offsetx = lm2.offsety = 0;
+ lm2.tex = lightmaptexs.length();
+ LightMapTexture &tex2 = lightmaptexs.add();
+ tex2.type = (lm.type&~LM_TYPE) | LM_BUMPMAP0;
+ tex2.w = LM_PACKW;
+ tex2.h = LM_PACKH;
+ tex2.unlitx = lm2.unlitx;
+ tex2.unlity = lm2.unlity;
+ glGenTextures(1, &tex2.id);
+ createtexture(tex2.id, tex2.w, tex2.h, NULL, 3, 1, GL_RGB);
+ }
+ }
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, LM_PACKW);
+
+ glBindTexture(GL_TEXTURE_2D, lightmaptexs[lm.tex].id);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, lm.offsetx + surface.x, lm.offsety + surface.y, surface.w, surface.h, lm.type&LM_ALPHA ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, &lm.data[(surface.y*LM_PACKW + surface.x)*lm.bpp]);
+ if((lm.type&LM_TYPE)==LM_BUMPMAP0 && lightmaps.inrange(surface.lmid+1-LMID_RESERVED))
+ {
+ LightMap &lm2 = lightmaps[surface.lmid+1-LMID_RESERVED];
+ glBindTexture(GL_TEXTURE_2D, lightmaptexs[lm2.tex].id);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, lm2.offsetx + surface.x, lm2.offsety + surface.y, surface.w, surface.h, GL_RGB, GL_UNSIGNED_BYTE, &lm2.data[(surface.y*LM_PACKW + surface.x)*3]);
+ }
+
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+}
+
+
static uint generatelumel(lightmapworker *w, const float tolerance, uint lightmask, const vector<const extentity *> &lights, const vec &target, const vec &normal, vec &sample, int x, int y)
{
- vec avgray(0, 0, 0);
- float r = 0, g = 0, b = 0;
- uint lightused = 0;
- loopv(lights)
- {
- if(lightmask&(1<<i)) continue;
- const extentity &light = *lights[i];
- vec ray = target;
- ray.sub(light.o);
- float mag = ray.magnitude();
- if(!mag) continue;
- float attenuation = 1;
- if(light.attr1)
- {
- attenuation -= mag / float(light.attr1);
- if(attenuation <= 0) continue;
- }
- ray.mul(1.0f / mag);
- float angle = -ray.dot(normal);
- if(angle <= 0) continue;
- if(light.attached && light.attached->type==ET_SPOTLIGHT)
- {
- vec spot = vec(light.attached->o).sub(light.o).normalize();
- float maxatten = sincos360[clamp(int(light.attached->attr1), 1, 89)].x, spotatten = (ray.dot(spot) - maxatten) / (1 - maxatten);
- if(spotatten <= 0) continue;
- attenuation *= spotatten;
- }
- if(lmshadows && mag)
- {
- float dist = shadowray(w->shadowraycache, light.o, ray, mag - tolerance, RAY_SHADOW | (lmshadows > 1 ? RAY_ALPHAPOLY : 0));
- if(dist < mag - tolerance) continue;
- }
- lightused |= 1<<i;
- float intensity;
- switch(w->type&LM_TYPE)
- {
- case LM_BUMPMAP0:
- intensity = attenuation;
- avgray.add(ray.mul(-attenuation));
- break;
- default:
- intensity = angle * attenuation;
- break;
- }
- r += intensity * float(light.attr2);
- g += intensity * float(light.attr3);
- b += intensity * float(light.attr4);
- }
- if(sunlight)
- {
- float angle = sunlightdir.dot(normal);
- if(angle > 0 &&
- (!lmshadows ||
- shadowray(w->shadowraycache, vec(sunlightdir).mul(tolerance).add(target), sunlightdir, 1e16f, RAY_SHADOW | (lmshadows > 1 ? RAY_ALPHAPOLY : 0) | (skytexturelight ? RAY_SKIPSKY | (useskytexture ? RAY_SKYTEX : 0) : 0)) > 1e15f))
- {
- float intensity;
- switch(w->type&LM_TYPE)
- {
- case LM_BUMPMAP0:
- intensity = 1;
- avgray.add(sunlightdir);
- break;
- default:
- intensity = angle;
- break;
- }
- r += intensity * (sunlightcolor.x*sunlightscale);
- g += intensity * (sunlightcolor.y*sunlightscale);
- b += intensity * (sunlightcolor.z*sunlightscale);
- }
- }
- switch(w->type&LM_TYPE)
- {
- case LM_BUMPMAP0:
- if(avgray.iszero()) break;
- // transform to tangent space
- extern const vec orientation_tangent[8][3];
- extern const vec orientation_bitangent[8][3];
- vec S(orientation_tangent[w->rotate][dimension(w->orient)]),
- T(orientation_bitangent[w->rotate][dimension(w->orient)]);
- normal.orthonormalize(S, T);
- avgray.normalize();
- w->raydata[y*w->w+x].add(vec(S.dot(avgray)/S.magnitude(), T.dot(avgray)/T.magnitude(), normal.dot(avgray)));
- break;
- }
- sample.x = min(255.0f, max(r, float(ambientcolor[0])));
- sample.y = min(255.0f, max(g, float(ambientcolor[1])));
- sample.z = min(255.0f, max(b, float(ambientcolor[2])));
- return lightused;
+ vec avgray(0, 0, 0);
+ float r = 0, g = 0, b = 0;
+ uint lightused = 0;
+ loopv(lights)
+ {
+ if(lightmask&(1<<i)) continue;
+ const extentity &light = *lights[i];
+ vec ray = target;
+ ray.sub(light.o);
+ float mag = ray.magnitude();
+ if(!mag) continue;
+ float attenuation = 1;
+ if(light.attr1)
+ {
+ attenuation -= mag / float(light.attr1);
+ if(attenuation <= 0) continue;
+ }
+ ray.mul(1.0f / mag);
+ float angle = -ray.dot(normal);
+ if(angle <= 0) continue;
+ if(light.attached && light.attached->type==ET_SPOTLIGHT)
+ {
+ vec spot = vec(light.attached->o).sub(light.o).normalize();
+ float maxatten = sincos360[clamp(int(light.attached->attr1), 1, 89)].x, spotatten = (ray.dot(spot) - maxatten) / (1 - maxatten);
+ if(spotatten <= 0) continue;
+ attenuation *= spotatten;
+ }
+ if(lmshadows && mag)
+ {
+ float dist = shadowray(w->shadowraycache, light.o, ray, mag - tolerance, RAY_SHADOW | (lmshadows > 1 ? RAY_ALPHAPOLY : 0));
+ if(dist < mag - tolerance) continue;
+ }
+ lightused |= 1<<i;
+ float intensity;
+ switch(w->type&LM_TYPE)
+ {
+ case LM_BUMPMAP0:
+ intensity = attenuation;
+ avgray.add(ray.mul(-attenuation));
+ break;
+ default:
+ intensity = angle * attenuation;
+ break;
+ }
+ r += intensity * float(light.attr2);
+ g += intensity * float(light.attr3);
+ b += intensity * float(light.attr4);
+ }
+ if(sunlight)
+ {
+ float angle = sunlightdir.dot(normal);
+ if(angle > 0 &&
+ (!lmshadows ||
+ shadowray(w->shadowraycache, vec(sunlightdir).mul(tolerance).add(target), sunlightdir, 1e16f, RAY_SHADOW | (lmshadows > 1 ? RAY_ALPHAPOLY : 0) | (skytexturelight ? RAY_SKIPSKY | (useskytexture ? RAY_SKYTEX : 0) : 0)) > 1e15f))
+ {
+ float intensity;
+ switch(w->type&LM_TYPE)
+ {
+ case LM_BUMPMAP0:
+ intensity = 1;
+ avgray.add(sunlightdir);
+ break;
+ default:
+ intensity = angle;
+ break;
+ }
+ r += intensity * (sunlightcolor.x*sunlightscale);
+ g += intensity * (sunlightcolor.y*sunlightscale);
+ b += intensity * (sunlightcolor.z*sunlightscale);
+ }
+ }
+ switch(w->type&LM_TYPE)
+ {
+ case LM_BUMPMAP0:
+ if(avgray.iszero()) break;
+ // transform to tangent space
+ extern const vec orientation_tangent[8][3];
+ extern const vec orientation_bitangent[8][3];
+ vec S(orientation_tangent[w->rotate][dimension(w->orient)]),
+ T(orientation_bitangent[w->rotate][dimension(w->orient)]);
+ normal.orthonormalize(S, T);
+ avgray.normalize();
+ w->raydata[y*w->w+x].add(vec(S.dot(avgray)/S.magnitude(), T.dot(avgray)/T.magnitude(), normal.dot(avgray)));
+ break;
+ }
+ sample.x = min(255.0f, max(r, float(ambientcolor[0])));
+ sample.y = min(255.0f, max(g, float(ambientcolor[1])));
+ sample.z = min(255.0f, max(b, float(ambientcolor[2])));
+ return lightused;
}
static bool lumelsample(const vec &sample, int aasample, int stride)
{
- if(sample.x >= int(ambientcolor[0])+1 || sample.y >= int(ambientcolor[1])+1 || sample.z >= int(ambientcolor[2])+1) return true;
+ if(sample.x >= int(ambientcolor[0])+1 || sample.y >= int(ambientcolor[1])+1 || sample.z >= int(ambientcolor[2])+1) return true;
#define NCHECK(n) \
- if((n).x >= int(ambientcolor[0])+1 || (n).y >= int(ambientcolor[1])+1 || (n).z >= int(ambientcolor[2])+1) \
- return true;
- const vec *n = &sample - stride - aasample;
- NCHECK(n[0]); NCHECK(n[aasample]); NCHECK(n[2*aasample]);
- n += stride;
- NCHECK(n[0]); NCHECK(n[2*aasample]);
- n += stride;
- NCHECK(n[0]); NCHECK(n[aasample]); NCHECK(n[2*aasample]);
- return false;
+ if((n).x >= int(ambientcolor[0])+1 || (n).y >= int(ambientcolor[1])+1 || (n).z >= int(ambientcolor[2])+1) \
+ return true;
+ const vec *n = &sample - stride - aasample;
+ NCHECK(n[0]); NCHECK(n[aasample]); NCHECK(n[2*aasample]);
+ n += stride;
+ NCHECK(n[0]); NCHECK(n[2*aasample]);
+ n += stride;
+ NCHECK(n[0]); NCHECK(n[aasample]); NCHECK(n[2*aasample]);
+ return false;
}
static void calcskylight(lightmapworker *w, const vec &o, const vec &normal, float tolerance, uchar *skylight, int flags = RAY_ALPHAPOLY, extentity *t = NULL)
{
- static const vec rays[17] =
- {
- vec(cosf(21*RAD)*cosf(50*RAD), sinf(21*RAD)*cosf(50*RAD), sinf(50*RAD)),
- vec(cosf(111*RAD)*cosf(50*RAD), sinf(111*RAD)*cosf(50*RAD), sinf(50*RAD)),
- vec(cosf(201*RAD)*cosf(50*RAD), sinf(201*RAD)*cosf(50*RAD), sinf(50*RAD)),
- vec(cosf(291*RAD)*cosf(50*RAD), sinf(291*RAD)*cosf(50*RAD), sinf(50*RAD)),
-
- vec(cosf(66*RAD)*cosf(70*RAD), sinf(66*RAD)*cosf(70*RAD), sinf(70*RAD)),
- vec(cosf(156*RAD)*cosf(70*RAD), sinf(156*RAD)*cosf(70*RAD), sinf(70*RAD)),
- vec(cosf(246*RAD)*cosf(70*RAD), sinf(246*RAD)*cosf(70*RAD), sinf(70*RAD)),
- vec(cosf(336*RAD)*cosf(70*RAD), sinf(336*RAD)*cosf(70*RAD), sinf(70*RAD)),
-
- vec(0, 0, 1),
-
- vec(cosf(43*RAD)*cosf(60*RAD), sinf(43*RAD)*cosf(60*RAD), sinf(60*RAD)),
- vec(cosf(133*RAD)*cosf(60*RAD), sinf(133*RAD)*cosf(60*RAD), sinf(60*RAD)),
- vec(cosf(223*RAD)*cosf(60*RAD), sinf(223*RAD)*cosf(60*RAD), sinf(60*RAD)),
- vec(cosf(313*RAD)*cosf(60*RAD), sinf(313*RAD)*cosf(60*RAD), sinf(60*RAD)),
-
- vec(cosf(88*RAD)*cosf(80*RAD), sinf(88*RAD)*cosf(80*RAD), sinf(80*RAD)),
- vec(cosf(178*RAD)*cosf(80*RAD), sinf(178*RAD)*cosf(80*RAD), sinf(80*RAD)),
- vec(cosf(268*RAD)*cosf(80*RAD), sinf(268*RAD)*cosf(80*RAD), sinf(80*RAD)),
- vec(cosf(358*RAD)*cosf(80*RAD), sinf(358*RAD)*cosf(80*RAD), sinf(80*RAD)),
-
- };
- flags |= RAY_SHADOW;
- if(skytexturelight) flags |= RAY_SKIPSKY | (useskytexture ? RAY_SKYTEX : 0);
- int hit = 0;
- if(w) loopi(17)
- {
- if(normal.dot(rays[i])>=0 && shadowray(w->shadowraycache, vec(rays[i]).mul(tolerance).add(o), rays[i], 1e16f, flags, t)>1e15f) hit++;
- }
- else loopi(17)
- {
- if(normal.dot(rays[i])>=0 && shadowray(vec(rays[i]).mul(tolerance).add(o), rays[i], 1e16f, flags, t)>1e15f) hit++;
- }
-
- loopk(3) skylight[k] = uchar(ambientcolor[k] + (max(skylightcolor[k], ambientcolor[k]) - ambientcolor[k])*hit/17.0f);
+ static const vec rays[17] =
+ {
+ vec(cosf(21*RAD)*cosf(50*RAD), sinf(21*RAD)*cosf(50*RAD), sinf(50*RAD)),
+ vec(cosf(111*RAD)*cosf(50*RAD), sinf(111*RAD)*cosf(50*RAD), sinf(50*RAD)),
+ vec(cosf(201*RAD)*cosf(50*RAD), sinf(201*RAD)*cosf(50*RAD), sinf(50*RAD)),
+ vec(cosf(291*RAD)*cosf(50*RAD), sinf(291*RAD)*cosf(50*RAD), sinf(50*RAD)),
+
+ vec(cosf(66*RAD)*cosf(70*RAD), sinf(66*RAD)*cosf(70*RAD), sinf(70*RAD)),
+ vec(cosf(156*RAD)*cosf(70*RAD), sinf(156*RAD)*cosf(70*RAD), sinf(70*RAD)),
+ vec(cosf(246*RAD)*cosf(70*RAD), sinf(246*RAD)*cosf(70*RAD), sinf(70*RAD)),
+ vec(cosf(336*RAD)*cosf(70*RAD), sinf(336*RAD)*cosf(70*RAD), sinf(70*RAD)),
+
+ vec(0, 0, 1),
+
+ vec(cosf(43*RAD)*cosf(60*RAD), sinf(43*RAD)*cosf(60*RAD), sinf(60*RAD)),
+ vec(cosf(133*RAD)*cosf(60*RAD), sinf(133*RAD)*cosf(60*RAD), sinf(60*RAD)),
+ vec(cosf(223*RAD)*cosf(60*RAD), sinf(223*RAD)*cosf(60*RAD), sinf(60*RAD)),
+ vec(cosf(313*RAD)*cosf(60*RAD), sinf(313*RAD)*cosf(60*RAD), sinf(60*RAD)),
+
+ vec(cosf(88*RAD)*cosf(80*RAD), sinf(88*RAD)*cosf(80*RAD), sinf(80*RAD)),
+ vec(cosf(178*RAD)*cosf(80*RAD), sinf(178*RAD)*cosf(80*RAD), sinf(80*RAD)),
+ vec(cosf(268*RAD)*cosf(80*RAD), sinf(268*RAD)*cosf(80*RAD), sinf(80*RAD)),
+ vec(cosf(358*RAD)*cosf(80*RAD), sinf(358*RAD)*cosf(80*RAD), sinf(80*RAD)),
+
+ };
+ flags |= RAY_SHADOW;
+ if(skytexturelight) flags |= RAY_SKIPSKY | (useskytexture ? RAY_SKYTEX : 0);
+ int hit = 0;
+ if(w) loopi(17)
+ {
+ if(normal.dot(rays[i])>=0 && shadowray(w->shadowraycache, vec(rays[i]).mul(tolerance).add(o), rays[i], 1e16f, flags, t)>1e15f) hit++;
+ }
+ else loopi(17)
+ {
+ if(normal.dot(rays[i])>=0 && shadowray(vec(rays[i]).mul(tolerance).add(o), rays[i], 1e16f, flags, t)>1e15f) hit++;
+ }
+
+ loopk(3) skylight[k] = uchar(ambientcolor[k] + (max(skylightcolor[k], ambientcolor[k]) - ambientcolor[k])*hit/17.0f);
}
static inline bool hasskylight()
{
- return skylightcolor[0]>ambientcolor[0] || skylightcolor[1]>ambientcolor[1] || skylightcolor[2]>ambientcolor[2];
+ return skylightcolor[0]>ambientcolor[0] || skylightcolor[1]>ambientcolor[1] || skylightcolor[2]>ambientcolor[2];
}
VARR(blurlms, 0, 0, 2);
@@ -676,45 +676,45 @@ VARR(blurskylight, 0, 0, 2);
static inline void generatealpha(lightmapworker *w, float tolerance, const vec &pos, uchar &alpha)
{
- alpha = lookupblendmap(w->blendmapcache, pos);
- if(w->slot->layermask)
- {
- static const int sdim[] = { 1, 0, 0 }, tdim[] = { 2, 2, 1 };
- int dim = dimension(w->orient);
- float k = 8.0f/w->vslot->scale,
- s = (pos[sdim[dim]] * k - w->vslot->offset.y) / w->slot->layermaskscale,
- t = (pos[tdim[dim]] * (dim <= 1 ? -k : k) - w->vslot->offset.y) / w->slot->layermaskscale;
- const texrotation &r = texrotations[w->rotate];
- if(r.swapxy) swap(s, t);
- if(r.flipx) s = -s;
- if(r.flipy) t = -t;
- const ImageData &mask = *w->slot->layermask;
- int mx = int(floor(s))%mask.w, my = int(floor(t))%mask.h;
- if(mx < 0) mx += mask.w;
- if(my < 0) my += mask.h;
- uchar maskval = mask.data[mask.bpp*(mx + 1) - 1 + mask.pitch*my];
- switch(w->slot->layermaskmode)
- {
- case 2: alpha = min(alpha, maskval); break;
- case 3: alpha = max(alpha, maskval); break;
- case 4: alpha = min(alpha, uchar(0xFF - maskval)); break;
- case 5: alpha = max(alpha, uchar(0xFF - maskval)); break;
- default: alpha = maskval; break;
- }
- }
-}
-
+ alpha = lookupblendmap(w->blendmapcache, pos);
+ if(w->slot->layermask)
+ {
+ static const int sdim[] = { 1, 0, 0 }, tdim[] = { 2, 2, 1 };
+ int dim = dimension(w->orient);
+ float k = 8.0f/w->vslot->scale,
+ s = (pos[sdim[dim]] * k - w->vslot->offset.y) / w->slot->layermaskscale,
+ t = (pos[tdim[dim]] * (dim <= 1 ? -k : k) - w->vslot->offset.y) / w->slot->layermaskscale;
+ const texrotation &r = texrotations[w->rotate];
+ if(r.swapxy) swap(s, t);
+ if(r.flipx) s = -s;
+ if(r.flipy) t = -t;
+ const ImageData &mask = *w->slot->layermask;
+ int mx = int(floor(s))%mask.w, my = int(floor(t))%mask.h;
+ if(mx < 0) mx += mask.w;
+ if(my < 0) my += mask.h;
+ uchar maskval = mask.data[mask.bpp*(mx + 1) - 1 + mask.pitch*my];
+ switch(w->slot->layermaskmode)
+ {
+ case 2: alpha = min(alpha, maskval); break;
+ case 3: alpha = max(alpha, maskval); break;
+ case 4: alpha = min(alpha, uchar(0xFF - maskval)); break;
+ case 5: alpha = max(alpha, uchar(0xFF - maskval)); break;
+ default: alpha = maskval; break;
+ }
+ }
+}
+
VAR(edgetolerance, 1, 4, 64);
VAR(adaptivesample, 0, 2, 2);
enum
{
- NO_SURFACE = 0,
- SURFACE_AMBIENT_BOTTOM,
- SURFACE_AMBIENT_TOP,
- SURFACE_LIGHTMAP_BOTTOM,
- SURFACE_LIGHTMAP_TOP,
- SURFACE_LIGHTMAP_BLEND
+ NO_SURFACE = 0,
+ SURFACE_AMBIENT_BOTTOM,
+ SURFACE_AMBIENT_TOP,
+ SURFACE_LIGHTMAP_BOTTOM,
+ SURFACE_LIGHTMAP_TOP,
+ SURFACE_LIGHTMAP_BLEND
};
#define SURFACE_AMBIENT SURFACE_AMBIENT_BOTTOM
@@ -722,316 +722,316 @@ enum
static bool generatelightmap(lightmapworker *w, float lpu, const lerpvert *lv, int numv, vec origin1, const vec &xstep1, const vec &ystep1, vec origin2, const vec &xstep2, const vec &ystep2, float side0, float sidestep)
{
- static const float aacoords[8][2] =
- {
- {0.0f, 0.0f},
- {-0.5f, -0.5f},
- {0.0f, -0.5f},
- {-0.5f, 0.0f},
-
- {0.3f, -0.6f},
- {0.6f, 0.3f},
- {-0.3f, 0.6f},
- {-0.6f, -0.3f},
- };
- float tolerance = 0.5 / lpu;
- uint lightmask = 0, lightused = 0;
- vec offsets1[8], offsets2[8];
- loopi(8)
- {
- offsets1[i] = vec(xstep1).mul(aacoords[i][0]).add(vec(ystep1).mul(aacoords[i][1]));
- offsets2[i] = vec(xstep2).mul(aacoords[i][0]).add(vec(ystep2).mul(aacoords[i][1]));
- }
- if((w->type&LM_TYPE) == LM_BUMPMAP0) memclear(w->raydata, (LM_MAXW + 4)*(LM_MAXH + 4));
-
- origin1.sub(vec(ystep1).add(xstep1).mul(blurlms));
- origin2.sub(vec(ystep2).add(xstep2).mul(blurlms));
-
- int aasample = min(1 << lmaa, 4);
- int stride = aasample*(w->w+1);
- vec *sample = w->colordata;
- uchar *skylight = w->ambient;
- lerpbounds start, end;
- initlerpbounds(-blurlms, -blurlms, lv, numv, start, end);
- float sidex = side0 + blurlms*sidestep;
- for(int y = 0; y < w->h; ++y, sidex += sidestep)
- {
- vec normal, nstep;
- lerpnormal(-blurlms, y - blurlms, lv, numv, start, end, normal, nstep);
-
- for(int x = 0; x < w->w; ++x, normal.add(nstep), skylight += w->bpp)
- {
+ static const float aacoords[8][2] =
+ {
+ {0.0f, 0.0f},
+ {-0.5f, -0.5f},
+ {0.0f, -0.5f},
+ {-0.5f, 0.0f},
+
+ {0.3f, -0.6f},
+ {0.6f, 0.3f},
+ {-0.3f, 0.6f},
+ {-0.6f, -0.3f},
+ };
+ float tolerance = 0.5 / lpu;
+ uint lightmask = 0, lightused = 0;
+ vec offsets1[8], offsets2[8];
+ loopi(8)
+ {
+ offsets1[i] = vec(xstep1).mul(aacoords[i][0]).add(vec(ystep1).mul(aacoords[i][1]));
+ offsets2[i] = vec(xstep2).mul(aacoords[i][0]).add(vec(ystep2).mul(aacoords[i][1]));
+ }
+ if((w->type&LM_TYPE) == LM_BUMPMAP0) memclear(w->raydata, (LM_MAXW + 4)*(LM_MAXH + 4));
+
+ origin1.sub(vec(ystep1).add(xstep1).mul(blurlms));
+ origin2.sub(vec(ystep2).add(xstep2).mul(blurlms));
+
+ int aasample = min(1 << lmaa, 4);
+ int stride = aasample*(w->w+1);
+ vec *sample = w->colordata;
+ uchar *skylight = w->ambient;
+ lerpbounds start, end;
+ initlerpbounds(-blurlms, -blurlms, lv, numv, start, end);
+ float sidex = side0 + blurlms*sidestep;
+ for(int y = 0; y < w->h; ++y, sidex += sidestep)
+ {
+ vec normal, nstep;
+ lerpnormal(-blurlms, y - blurlms, lv, numv, start, end, normal, nstep);
+
+ for(int x = 0; x < w->w; ++x, normal.add(nstep), skylight += w->bpp)
+ {
#define EDGE_TOLERANCE(x, y) \
- (x < blurlms \
- || x+1 > w->w - blurlms \
- || y < blurlms \
- || y+1 > w->h - blurlms \
- ? edgetolerance : 1)
- float t = EDGE_TOLERANCE(x, y) * tolerance;
- vec u = x < sidex ? vec(xstep1).mul(x).add(vec(ystep1).mul(y)).add(origin1) : vec(xstep2).mul(x).add(vec(ystep2).mul(y)).add(origin2);
- lightused |= generatelumel(w, t, 0, w->lights, u, vec(normal).normalize(), *sample, x, y);
- if(hasskylight())
- {
- if((w->type&LM_TYPE)==LM_BUMPMAP0 || !adaptivesample || sample->x<skylightcolor[0] || sample->y<skylightcolor[1] || sample->z<skylightcolor[2])
- calcskylight(w, u, normal, t, skylight, lmshadows > 1 ? RAY_ALPHAPOLY : 0);
- else loopk(3) skylight[k] = max(skylightcolor[k], ambientcolor[k]);
- }
- else loopk(3) skylight[k] = ambientcolor[k];
- if(w->type&LM_ALPHA) generatealpha(w, t, u, skylight[3]);
- sample += aasample;
- }
- sample += aasample;
- }
- if(adaptivesample > 1 && min(w->w, w->h) >= 2) lightmask = ~lightused;
- sample = w->colordata;
- initlerpbounds(-blurlms, -blurlms, lv, numv, start, end);
- sidex = side0 + blurlms*sidestep;
- for(int y = 0; y < w->h; ++y, sidex += sidestep)
- {
- vec normal, nstep;
- lerpnormal(-blurlms, y - blurlms, lv, numv, start, end, normal, nstep);
-
- for(int x = 0; x < w->w; ++x, normal.add(nstep))
- {
- vec &center = *sample++;
- if(adaptivesample && x > 0 && x+1 < w->w && y > 0 && y+1 < w->h && !lumelsample(center, aasample, stride))
- loopi(aasample-1) *sample++ = center;
- else
- {
+ (x < blurlms \
+ || x+1 > w->w - blurlms \
+ || y < blurlms \
+ || y+1 > w->h - blurlms \
+ ? edgetolerance : 1)
+ float t = EDGE_TOLERANCE(x, y) * tolerance;
+ vec u = x < sidex ? vec(xstep1).mul(x).add(vec(ystep1).mul(y)).add(origin1) : vec(xstep2).mul(x).add(vec(ystep2).mul(y)).add(origin2);
+ lightused |= generatelumel(w, t, 0, w->lights, u, vec(normal).normalize(), *sample, x, y);
+ if(hasskylight())
+ {
+ if((w->type&LM_TYPE)==LM_BUMPMAP0 || !adaptivesample || sample->x<skylightcolor[0] || sample->y<skylightcolor[1] || sample->z<skylightcolor[2])
+ calcskylight(w, u, normal, t, skylight, lmshadows > 1 ? RAY_ALPHAPOLY : 0);
+ else loopk(3) skylight[k] = max(skylightcolor[k], ambientcolor[k]);
+ }
+ else loopk(3) skylight[k] = ambientcolor[k];
+ if(w->type&LM_ALPHA) generatealpha(w, t, u, skylight[3]);
+ sample += aasample;
+ }
+ sample += aasample;
+ }
+ if(adaptivesample > 1 && min(w->w, w->h) >= 2) lightmask = ~lightused;
+ sample = w->colordata;
+ initlerpbounds(-blurlms, -blurlms, lv, numv, start, end);
+ sidex = side0 + blurlms*sidestep;
+ for(int y = 0; y < w->h; ++y, sidex += sidestep)
+ {
+ vec normal, nstep;
+ lerpnormal(-blurlms, y - blurlms, lv, numv, start, end, normal, nstep);
+
+ for(int x = 0; x < w->w; ++x, normal.add(nstep))
+ {
+ vec &center = *sample++;
+ if(adaptivesample && x > 0 && x+1 < w->w && y > 0 && y+1 < w->h && !lumelsample(center, aasample, stride))
+ loopi(aasample-1) *sample++ = center;
+ else
+ {
#define AA_EDGE_TOLERANCE(x, y, i) EDGE_TOLERANCE(x + aacoords[i][0], y + aacoords[i][1])
- vec u = x < sidex ? vec(xstep1).mul(x).add(vec(ystep1).mul(y)).add(origin1) : vec(xstep2).mul(x).add(vec(ystep2).mul(y)).add(origin2);
- const vec *offsets = x < sidex ? offsets1 : offsets2;
- vec n = vec(normal).normalize();
- loopi(aasample-1)
- generatelumel(w, AA_EDGE_TOLERANCE(x, y, i+1) * tolerance, lightmask, w->lights, vec(u).add(offsets[i+1]), n, *sample++, x, y);
- if(lmaa == 3)
- {
- loopi(4)
- {
- vec s;
- generatelumel(w, AA_EDGE_TOLERANCE(x, y, i+4) * tolerance, lightmask, w->lights, vec(u).add(offsets[i+4]), n, s, x, y);
- center.add(s);
- }
- center.div(5);
- }
- }
- }
- if(aasample > 1)
- {
- vec u = w->w < sidex ? vec(xstep1).mul(w->w).add(vec(ystep1).mul(y)).add(origin1) : vec(xstep2).mul(w->w).add(vec(ystep2).mul(y)).add(origin2);
- const vec *offsets = w->w < sidex ? offsets1 : offsets2;
- vec n = vec(normal).normalize();
- generatelumel(w, edgetolerance * tolerance, lightmask, w->lights, vec(u).add(offsets[1]), n, sample[1], w->w-1, y);
- if(aasample > 2)
- generatelumel(w, edgetolerance * tolerance, lightmask, w->lights, vec(u).add(offsets[3]), n, sample[3], w->w-1, y);
- }
- sample += aasample;
- }
-
- if(aasample > 1)
- {
- vec normal, nstep;
- lerpnormal(-blurlms, w->h - blurlms, lv, numv, start, end, normal, nstep);
-
- for(int x = 0; x <= w->w; ++x, normal.add(nstep))
- {
- vec u = x < sidex ? vec(xstep1).mul(x).add(vec(ystep1).mul(w->h)).add(origin1) : vec(xstep2).mul(x).add(vec(ystep2).mul(w->h)).add(origin2);
- const vec *offsets = x < sidex ? offsets1 : offsets2;
- vec n = vec(normal).normalize();
- generatelumel(w, edgetolerance * tolerance, lightmask, w->lights, vec(u).add(offsets[1]), n, sample[1], min(x, w->w-1), w->h-1);
- if(aasample > 2)
- generatelumel(w, edgetolerance * tolerance, lightmask, w->lights, vec(u).add(offsets[2]), n, sample[2], min(x, w->w-1), w->h-1);
- sample += aasample;
- }
- }
- return true;
-}
-
+ vec u = x < sidex ? vec(xstep1).mul(x).add(vec(ystep1).mul(y)).add(origin1) : vec(xstep2).mul(x).add(vec(ystep2).mul(y)).add(origin2);
+ const vec *offsets = x < sidex ? offsets1 : offsets2;
+ vec n = vec(normal).normalize();
+ loopi(aasample-1)
+ generatelumel(w, AA_EDGE_TOLERANCE(x, y, i+1) * tolerance, lightmask, w->lights, vec(u).add(offsets[i+1]), n, *sample++, x, y);
+ if(lmaa == 3)
+ {
+ loopi(4)
+ {
+ vec s;
+ generatelumel(w, AA_EDGE_TOLERANCE(x, y, i+4) * tolerance, lightmask, w->lights, vec(u).add(offsets[i+4]), n, s, x, y);
+ center.add(s);
+ }
+ center.div(5);
+ }
+ }
+ }
+ if(aasample > 1)
+ {
+ vec u = w->w < sidex ? vec(xstep1).mul(w->w).add(vec(ystep1).mul(y)).add(origin1) : vec(xstep2).mul(w->w).add(vec(ystep2).mul(y)).add(origin2);
+ const vec *offsets = w->w < sidex ? offsets1 : offsets2;
+ vec n = vec(normal).normalize();
+ generatelumel(w, edgetolerance * tolerance, lightmask, w->lights, vec(u).add(offsets[1]), n, sample[1], w->w-1, y);
+ if(aasample > 2)
+ generatelumel(w, edgetolerance * tolerance, lightmask, w->lights, vec(u).add(offsets[3]), n, sample[3], w->w-1, y);
+ }
+ sample += aasample;
+ }
+
+ if(aasample > 1)
+ {
+ vec normal, nstep;
+ lerpnormal(-blurlms, w->h - blurlms, lv, numv, start, end, normal, nstep);
+
+ for(int x = 0; x <= w->w; ++x, normal.add(nstep))
+ {
+ vec u = x < sidex ? vec(xstep1).mul(x).add(vec(ystep1).mul(w->h)).add(origin1) : vec(xstep2).mul(x).add(vec(ystep2).mul(w->h)).add(origin2);
+ const vec *offsets = x < sidex ? offsets1 : offsets2;
+ vec n = vec(normal).normalize();
+ generatelumel(w, edgetolerance * tolerance, lightmask, w->lights, vec(u).add(offsets[1]), n, sample[1], min(x, w->w-1), w->h-1);
+ if(aasample > 2)
+ generatelumel(w, edgetolerance * tolerance, lightmask, w->lights, vec(u).add(offsets[2]), n, sample[2], min(x, w->w-1), w->h-1);
+ sample += aasample;
+ }
+ }
+ return true;
+}
+
static int finishlightmap(lightmapworker *w)
-{
- if(hasskylight() && blurskylight && (w->w>1 || w->h>1))
- {
- blurtexture(blurskylight, w->bpp, w->w, w->h, w->blur, w->ambient);
- swap(w->blur, w->ambient);
- }
- vec *sample = w->colordata;
- int aasample = min(1 << lmaa, 4), stride = aasample*(w->w+1);
- float weight = 1.0f / (1.0f + 4.0f*lmaa),
- cweight = weight * (lmaa == 3 ? 5.0f : 1.0f);
- uchar *skylight = w->ambient;
- vec *ray = w->raydata;
- uchar *dstcolor = blurlms && (w->w > 1 || w->h > 1) ? w->blur : w->colorbuf;
- uchar mincolor[4] = { 255, 255, 255, 255 }, maxcolor[4] = { 0, 0, 0, 0 };
- bvec *dstray = blurlms && (w->w > 1 || w->h > 1) ? (bvec *)w->raydata : w->raybuf;
- bvec minray(255, 255, 255), maxray(0, 0, 0);
- loop(y, w->h)
- {
- loop(x, w->w)
- {
- vec l(0, 0, 0);
- const vec &center = *sample++;
- loopi(aasample-1) l.add(*sample++);
- if(aasample > 1)
- {
- l.add(sample[1]);
- if(aasample > 2) l.add(sample[3]);
- }
- vec *next = sample + stride - aasample;
- if(aasample > 1)
- {
- l.add(next[1]);
- if(aasample > 2) l.add(next[2]);
- l.add(next[aasample+1]);
- }
-
- int r = int(center.x*cweight + l.x*weight),
- g = int(center.y*cweight + l.y*weight),
- b = int(center.z*cweight + l.z*weight),
- ar = skylight[0], ag = skylight[1], ab = skylight[2];
- dstcolor[0] = max(ar, r);
- dstcolor[1] = max(ag, g);
- dstcolor[2] = max(ab, b);
- loopk(3)
- {
- mincolor[k] = min(mincolor[k], dstcolor[k]);
- maxcolor[k] = max(maxcolor[k], dstcolor[k]);
- }
- if(w->type&LM_ALPHA)
- {
- dstcolor[3] = skylight[3];
- mincolor[3] = min(mincolor[3], dstcolor[3]);
- maxcolor[3] = max(maxcolor[3], dstcolor[3]);
- }
- if((w->type&LM_TYPE) == LM_BUMPMAP0)
- {
- if(ray->iszero()) dstray[0] = bvec(128, 128, 255);
- else
- {
- // bias the normals towards the amount of ambient/skylight in the lumel
- // this is necessary to prevent the light values in shaders from dropping too far below the skylight (to the ambient) if N.L is small
- ray->normalize();
- int l = max(r, max(g, b)), a = max(ar, max(ag, ab));
- ray->mul(max(l-a, 0));
- ray->z += a;
- dstray[0] = bvec(ray->normalize());
- }
- loopk(3)
- {
- minray[k] = min(minray[k], dstray[0][k]);
- maxray[k] = max(maxray[k], dstray[0][k]);
- }
- ray++;
- dstray++;
- }
- dstcolor += w->bpp;
- skylight += w->bpp;
- }
- sample += aasample;
- }
- if(int(maxcolor[0]) - int(mincolor[0]) <= lighterror &&
- int(maxcolor[1]) - int(mincolor[1]) <= lighterror &&
- int(maxcolor[2]) - int(mincolor[2]) <= lighterror &&
- mincolor[3] >= maxcolor[3])
- {
- uchar color[3];
- loopk(3) color[k] = (int(maxcolor[k]) + int(mincolor[k])) / 2;
- if(color[0] <= int(ambientcolor[0]) + lighterror &&
- color[1] <= int(ambientcolor[1]) + lighterror &&
- color[2] <= int(ambientcolor[2]) + lighterror &&
- (maxcolor[3]==0 || mincolor[3]==255))
- return mincolor[3]==255 ? SURFACE_AMBIENT_TOP : SURFACE_AMBIENT_BOTTOM;
- if((w->type&LM_TYPE) != LM_BUMPMAP0 ||
- (int(maxray.x) - int(minray.x) <= bumperror &&
- int(maxray.y) - int(minray.z) <= bumperror &&
- int(maxray.z) - int(minray.z) <= bumperror))
-
- {
- memcpy(w->colorbuf, color, 3);
- if(w->type&LM_ALPHA) w->colorbuf[3] = mincolor[3];
- if((w->type&LM_TYPE) == LM_BUMPMAP0)
- {
- loopk(3) w->raybuf[0][k] = uchar((int(maxray[k])+int(minray[k]))/2);
- }
- w->lastlightmap->w = w->w = 1;
- w->lastlightmap->h = w->h = 1;
- }
- }
- if(blurlms && (w->w>1 || w->h>1))
- {
- blurtexture(blurlms, w->bpp, w->w, w->h, w->colorbuf, w->blur, blurlms);
- if((w->type&LM_TYPE) == LM_BUMPMAP0) blurnormals(blurlms, w->w, w->h, w->raybuf, (const bvec *)w->raydata, blurlms);
- w->lastlightmap->w = (w->w -= 2*blurlms);
- w->lastlightmap->h = (w->h -= 2*blurlms);
- }
- if(mincolor[3]==255) return SURFACE_LIGHTMAP_TOP;
- else if(maxcolor[3]==0) return SURFACE_LIGHTMAP_BOTTOM;
- else return SURFACE_LIGHTMAP_BLEND;
+{
+ if(hasskylight() && blurskylight && (w->w>1 || w->h>1))
+ {
+ blurtexture(blurskylight, w->bpp, w->w, w->h, w->blur, w->ambient);
+ swap(w->blur, w->ambient);
+ }
+ vec *sample = w->colordata;
+ int aasample = min(1 << lmaa, 4), stride = aasample*(w->w+1);
+ float weight = 1.0f / (1.0f + 4.0f*lmaa),
+ cweight = weight * (lmaa == 3 ? 5.0f : 1.0f);
+ uchar *skylight = w->ambient;
+ vec *ray = w->raydata;
+ uchar *dstcolor = blurlms && (w->w > 1 || w->h > 1) ? w->blur : w->colorbuf;
+ uchar mincolor[4] = { 255, 255, 255, 255 }, maxcolor[4] = { 0, 0, 0, 0 };
+ bvec *dstray = blurlms && (w->w > 1 || w->h > 1) ? (bvec *)w->raydata : w->raybuf;
+ bvec minray(255, 255, 255), maxray(0, 0, 0);
+ loop(y, w->h)
+ {
+ loop(x, w->w)
+ {
+ vec l(0, 0, 0);
+ const vec &center = *sample++;
+ loopi(aasample-1) l.add(*sample++);
+ if(aasample > 1)
+ {
+ l.add(sample[1]);
+ if(aasample > 2) l.add(sample[3]);
+ }
+ vec *next = sample + stride - aasample;
+ if(aasample > 1)
+ {
+ l.add(next[1]);
+ if(aasample > 2) l.add(next[2]);
+ l.add(next[aasample+1]);
+ }
+
+ int r = int(center.x*cweight + l.x*weight),
+ g = int(center.y*cweight + l.y*weight),
+ b = int(center.z*cweight + l.z*weight),
+ ar = skylight[0], ag = skylight[1], ab = skylight[2];
+ dstcolor[0] = max(ar, r);
+ dstcolor[1] = max(ag, g);
+ dstcolor[2] = max(ab, b);
+ loopk(3)
+ {
+ mincolor[k] = min(mincolor[k], dstcolor[k]);
+ maxcolor[k] = max(maxcolor[k], dstcolor[k]);
+ }
+ if(w->type&LM_ALPHA)
+ {
+ dstcolor[3] = skylight[3];
+ mincolor[3] = min(mincolor[3], dstcolor[3]);
+ maxcolor[3] = max(maxcolor[3], dstcolor[3]);
+ }
+ if((w->type&LM_TYPE) == LM_BUMPMAP0)
+ {
+ if(ray->iszero()) dstray[0] = bvec(128, 128, 255);
+ else
+ {
+ // bias the normals towards the amount of ambient/skylight in the lumel
+ // this is necessary to prevent the light values in shaders from dropping too far below the skylight (to the ambient) if N.L is small
+ ray->normalize();
+ int l = max(r, max(g, b)), a = max(ar, max(ag, ab));
+ ray->mul(max(l-a, 0));
+ ray->z += a;
+ dstray[0] = bvec(ray->normalize());
+ }
+ loopk(3)
+ {
+ minray[k] = min(minray[k], dstray[0][k]);
+ maxray[k] = max(maxray[k], dstray[0][k]);
+ }
+ ray++;
+ dstray++;
+ }
+ dstcolor += w->bpp;
+ skylight += w->bpp;
+ }
+ sample += aasample;
+ }
+ if(int(maxcolor[0]) - int(mincolor[0]) <= lighterror &&
+ int(maxcolor[1]) - int(mincolor[1]) <= lighterror &&
+ int(maxcolor[2]) - int(mincolor[2]) <= lighterror &&
+ mincolor[3] >= maxcolor[3])
+ {
+ uchar color[3];
+ loopk(3) color[k] = (int(maxcolor[k]) + int(mincolor[k])) / 2;
+ if(color[0] <= int(ambientcolor[0]) + lighterror &&
+ color[1] <= int(ambientcolor[1]) + lighterror &&
+ color[2] <= int(ambientcolor[2]) + lighterror &&
+ (maxcolor[3]==0 || mincolor[3]==255))
+ return mincolor[3]==255 ? SURFACE_AMBIENT_TOP : SURFACE_AMBIENT_BOTTOM;
+ if((w->type&LM_TYPE) != LM_BUMPMAP0 ||
+ (int(maxray.x) - int(minray.x) <= bumperror &&
+ int(maxray.y) - int(minray.z) <= bumperror &&
+ int(maxray.z) - int(minray.z) <= bumperror))
+
+ {
+ memcpy(w->colorbuf, color, 3);
+ if(w->type&LM_ALPHA) w->colorbuf[3] = mincolor[3];
+ if((w->type&LM_TYPE) == LM_BUMPMAP0)
+ {
+ loopk(3) w->raybuf[0][k] = uchar((int(maxray[k])+int(minray[k]))/2);
+ }
+ w->lastlightmap->w = w->w = 1;
+ w->lastlightmap->h = w->h = 1;
+ }
+ }
+ if(blurlms && (w->w>1 || w->h>1))
+ {
+ blurtexture(blurlms, w->bpp, w->w, w->h, w->colorbuf, w->blur, blurlms);
+ if((w->type&LM_TYPE) == LM_BUMPMAP0) blurnormals(blurlms, w->w, w->h, w->raybuf, (const bvec *)w->raydata, blurlms);
+ w->lastlightmap->w = (w->w -= 2*blurlms);
+ w->lastlightmap->h = (w->h -= 2*blurlms);
+ }
+ if(mincolor[3]==255) return SURFACE_LIGHTMAP_TOP;
+ else if(maxcolor[3]==0) return SURFACE_LIGHTMAP_BOTTOM;
+ else return SURFACE_LIGHTMAP_BLEND;
}
static int previewlightmapalpha(lightmapworker *w, float lpu, const vec &origin1, const vec &xstep1, const vec &ystep1, const vec &origin2, const vec &xstep2, const vec &ystep2, float side0, float sidestep)
{
- extern int fullbrightlevel;
- float tolerance = 0.5 / lpu;
- uchar *dst = w->colorbuf;
- uchar minalpha = 255, maxalpha = 0;
- float sidex = side0;
- for(int y = 0; y < w->h; ++y, sidex += sidestep)
- {
- for(int x = 0; x < w->w; ++x, dst += 4)
- {
- vec u = x < sidex ?
- vec(xstep1).mul(x).add(vec(ystep1).mul(y)).add(origin1) :
- vec(xstep2).mul(x).add(vec(ystep2).mul(y)).add(origin2);
- loopk(3) dst[k] = fullbrightlevel;
- generatealpha(w, tolerance, u, dst[3]);
- minalpha = min(minalpha, dst[3]);
- maxalpha = max(maxalpha, dst[3]);
- }
- }
- if(minalpha==255) return SURFACE_AMBIENT_TOP;
- if(maxalpha==0) return SURFACE_AMBIENT_BOTTOM;
- if(minalpha==maxalpha) w->w = w->h = 1;
- if((w->type&LM_TYPE) == LM_BUMPMAP0) loopi(w->w*w->h) w->raybuf[i] = bvec(128, 128, 255);
- return SURFACE_LIGHTMAP_BLEND;
-}
+ extern int fullbrightlevel;
+ float tolerance = 0.5 / lpu;
+ uchar *dst = w->colorbuf;
+ uchar minalpha = 255, maxalpha = 0;
+ float sidex = side0;
+ for(int y = 0; y < w->h; ++y, sidex += sidestep)
+ {
+ for(int x = 0; x < w->w; ++x, dst += 4)
+ {
+ vec u = x < sidex ?
+ vec(xstep1).mul(x).add(vec(ystep1).mul(y)).add(origin1) :
+ vec(xstep2).mul(x).add(vec(ystep2).mul(y)).add(origin2);
+ loopk(3) dst[k] = fullbrightlevel;
+ generatealpha(w, tolerance, u, dst[3]);
+ minalpha = min(minalpha, dst[3]);
+ maxalpha = max(maxalpha, dst[3]);
+ }
+ }
+ if(minalpha==255) return SURFACE_AMBIENT_TOP;
+ if(maxalpha==0) return SURFACE_AMBIENT_BOTTOM;
+ if(minalpha==maxalpha) w->w = w->h = 1;
+ if((w->type&LM_TYPE) == LM_BUMPMAP0) loopi(w->w*w->h) w->raybuf[i] = bvec(128, 128, 255);
+ return SURFACE_LIGHTMAP_BLEND;
+}
static void clearsurfaces(cube *c)
{
- loopi(8)
- {
- if(c[i].ext)
- {
- loopj(6)
- {
- surfaceinfo &surf = c[i].ext->surfaces[j];
- if(!surf.used()) continue;
- surf.clear();
- int numverts = surf.numverts&MAXFACEVERTS;
- if(numverts)
- {
- if(!(c[i].merged&(1<<j))) { surf.numverts &= ~MAXFACEVERTS; continue; }
-
- vertinfo *verts = c[i].ext->verts() + surf.verts;
- loopk(numverts)
- {
- vertinfo &v = verts[k];
- v.u = 0;
- v.v = 0;
- v.norm = 0;
- }
- }
- }
- }
- if(c[i].children) clearsurfaces(c[i].children);
- }
+ loopi(8)
+ {
+ if(c[i].ext)
+ {
+ loopj(6)
+ {
+ surfaceinfo &surf = c[i].ext->surfaces[j];
+ if(!surf.used()) continue;
+ surf.clear();
+ int numverts = surf.numverts&MAXFACEVERTS;
+ if(numverts)
+ {
+ if(!(c[i].merged&(1<<j))) { surf.numverts &= ~MAXFACEVERTS; continue; }
+
+ vertinfo *verts = c[i].ext->verts() + surf.verts;
+ loopk(numverts)
+ {
+ vertinfo &v = verts[k];
+ v.u = 0;
+ v.v = 0;
+ v.norm = 0;
+ }
+ }
+ }
+ }
+ if(c[i].children) clearsurfaces(c[i].children);
+ }
}
#define LIGHTCACHESIZE 1024
static struct lightcacheentry
{
- int x, y;
- vector<int> lights;
+ int x, y;
+ vector<int> lights;
} lightcache[LIGHTCACHESIZE];
#define LIGHTCACHEHASH(x, y) (((((x)^(y))<<5) + (((x)^(y))>>5)) & (LIGHTCACHESIZE - 1))
@@ -1040,1015 +1040,1015 @@ VARF(lightcachesize, 4, 6, 12, clearlightcache());
void clearlightcache(int id)
{
- if(id >= 0)
- {
- const extentity &light = *entities::getents()[id];
- int radius = light.attr1;
- if(radius)
- {
- for(int x = int(max(light.o.x-radius, 0.0f))>>lightcachesize, ex = int(min(light.o.x+radius, worldsize-1.0f))>>lightcachesize; x <= ex; x++)
- for(int y = int(max(light.o.y-radius, 0.0f))>>lightcachesize, ey = int(min(light.o.y+radius, worldsize-1.0f))>>lightcachesize; y <= ey; y++)
- {
- lightcacheentry &lce = lightcache[LIGHTCACHEHASH(x, y)];
- if(lce.x != x || lce.y != y) continue;
- lce.x = -1;
- lce.lights.setsize(0);
- }
- return;
- }
- }
-
- for(lightcacheentry *lce = lightcache; lce < &lightcache[LIGHTCACHESIZE]; lce++)
- {
- lce->x = -1;
- lce->lights.setsize(0);
- }
+ if(id >= 0)
+ {
+ const extentity &light = *entities::getents()[id];
+ int radius = light.attr1;
+ if(radius)
+ {
+ for(int x = int(max(light.o.x-radius, 0.0f))>>lightcachesize, ex = int(min(light.o.x+radius, worldsize-1.0f))>>lightcachesize; x <= ex; x++)
+ for(int y = int(max(light.o.y-radius, 0.0f))>>lightcachesize, ey = int(min(light.o.y+radius, worldsize-1.0f))>>lightcachesize; y <= ey; y++)
+ {
+ lightcacheentry &lce = lightcache[LIGHTCACHEHASH(x, y)];
+ if(lce.x != x || lce.y != y) continue;
+ lce.x = -1;
+ lce.lights.setsize(0);
+ }
+ return;
+ }
+ }
+
+ for(lightcacheentry *lce = lightcache; lce < &lightcache[LIGHTCACHESIZE]; lce++)
+ {
+ lce->x = -1;
+ lce->lights.setsize(0);
+ }
}
const vector<int> &checklightcache(int x, int y)
{
- x >>= lightcachesize;
- y >>= lightcachesize;
- lightcacheentry &lce = lightcache[LIGHTCACHEHASH(x, y)];
- if(lce.x == x && lce.y == y) return lce.lights;
-
- lce.lights.setsize(0);
- int csize = 1<<lightcachesize, cx = x<<lightcachesize, cy = y<<lightcachesize;
- const vector<extentity *> &ents = entities::getents();
- loopv(ents)
- {
- const extentity &light = *ents[i];
- switch(light.type)
- {
- case ET_LIGHT:
- {
- int radius = light.attr1;
- if(radius > 0)
- {
- if(light.o.x + radius < cx || light.o.x - radius > cx + csize ||
- light.o.y + radius < cy || light.o.y - radius > cy + csize)
- continue;
- }
- break;
- }
- default: continue;
- }
- lce.lights.add(i);
- }
-
- lce.x = x;
- lce.y = y;
- return lce.lights;
+ x >>= lightcachesize;
+ y >>= lightcachesize;
+ lightcacheentry &lce = lightcache[LIGHTCACHEHASH(x, y)];
+ if(lce.x == x && lce.y == y) return lce.lights;
+
+ lce.lights.setsize(0);
+ int csize = 1<<lightcachesize, cx = x<<lightcachesize, cy = y<<lightcachesize;
+ const vector<extentity *> &ents = entities::getents();
+ loopv(ents)
+ {
+ const extentity &light = *ents[i];
+ switch(light.type)
+ {
+ case ET_LIGHT:
+ {
+ int radius = light.attr1;
+ if(radius > 0)
+ {
+ if(light.o.x + radius < cx || light.o.x - radius > cx + csize ||
+ light.o.y + radius < cy || light.o.y - radius > cy + csize)
+ continue;
+ }
+ break;
+ }
+ default: continue;
+ }
+ lce.lights.add(i);
+ }
+
+ lce.x = x;
+ lce.y = y;
+ return lce.lights;
}
static inline void addlight(lightmapworker *w, const extentity &light, int cx, int cy, int cz, int size, const vec *v, const vec *n, int numv)
{
- int radius = light.attr1;
- if(radius > 0)
- {
- if(light.o.x + radius < cx || light.o.x - radius > cx + size ||
- light.o.y + radius < cy || light.o.y - radius > cy + size ||
- light.o.z + radius < cz || light.o.z - radius > cz + size)
- return;
- }
-
- loopi(4)
- {
- vec p(light.o);
- p.sub(v[i]);
- float dist = p.dot(n[i]);
- if(dist >= 0 && (!radius || dist < radius))
- {
- w->lights.add(&light);
- break;
- }
- }
-}
+ int radius = light.attr1;
+ if(radius > 0)
+ {
+ if(light.o.x + radius < cx || light.o.x - radius > cx + size ||
+ light.o.y + radius < cy || light.o.y - radius > cy + size ||
+ light.o.z + radius < cz || light.o.z - radius > cz + size)
+ return;
+ }
+
+ loopi(4)
+ {
+ vec p(light.o);
+ p.sub(v[i]);
+ float dist = p.dot(n[i]);
+ if(dist >= 0 && (!radius || dist < radius))
+ {
+ w->lights.add(&light);
+ break;
+ }
+ }
+}
static bool findlights(lightmapworker *w, int cx, int cy, int cz, int size, const vec *v, const vec *n, int numv, const Slot &slot, const VSlot &vslot)
{
- w->lights.setsize(0);
- const vector<extentity *> &ents = entities::getents();
- static volatile bool usinglightcache = false;
- if(size <= 1<<lightcachesize && (!lightlock || !usinglightcache))
- {
- if(lightlock) { SDL_LockMutex(lightlock); usinglightcache = true; }
- const vector<int> &lights = checklightcache(cx, cy);
- loopv(lights)
- {
- const extentity &light = *ents[lights[i]];
- switch(light.type)
- {
- case ET_LIGHT: addlight(w, light, cx, cy, cz, size, v, n, numv); break;
- }
- }
- if(lightlock) { usinglightcache = false; SDL_UnlockMutex(lightlock); }
- }
- else loopv(ents)
- {
- const extentity &light = *ents[i];
- switch(light.type)
- {
- case ET_LIGHT: addlight(w, light, cx, cy, cz, size, v, n, numv); break;
- }
- }
- if(vslot.layer && (setblendmaporigin(w->blendmapcache, ivec(cx, cy, cz), size) || slot.layermask)) return true;
- return w->lights.length() || hasskylight() || sunlight;
+ w->lights.setsize(0);
+ const vector<extentity *> &ents = entities::getents();
+ static volatile bool usinglightcache = false;
+ if(size <= 1<<lightcachesize && (!lightlock || !usinglightcache))
+ {
+ if(lightlock) { SDL_LockMutex(lightlock); usinglightcache = true; }
+ const vector<int> &lights = checklightcache(cx, cy);
+ loopv(lights)
+ {
+ const extentity &light = *ents[lights[i]];
+ switch(light.type)
+ {
+ case ET_LIGHT: addlight(w, light, cx, cy, cz, size, v, n, numv); break;
+ }
+ }
+ if(lightlock) { usinglightcache = false; SDL_UnlockMutex(lightlock); }
+ }
+ else loopv(ents)
+ {
+ const extentity &light = *ents[i];
+ switch(light.type)
+ {
+ case ET_LIGHT: addlight(w, light, cx, cy, cz, size, v, n, numv); break;
+ }
+ }
+ if(vslot.layer && (setblendmaporigin(w->blendmapcache, ivec(cx, cy, cz), size) || slot.layermask)) return true;
+ return w->lights.length() || hasskylight() || sunlight;
}
static int packlightmaps(lightmapworker *w = NULL)
{
- int numpacked = 0;
- for(; packidx < lightmaptasks[0].length(); packidx++, numpacked++)
- {
- lightmaptask &t = lightmaptasks[0][packidx];
- if(!t.lightmaps) break;
- if(t.ext && t.c->ext != t.ext)
- {
- lightmapext &e = lightmapexts.add();
- e.c = t.c;
- e.ext = t.ext;
- }
- progress = t.progress;
- lightmapinfo *l = t.lightmaps;
- if(l == (lightmapinfo *)-1) continue;
- int space = 0;
- for(; l && l->c == t.c; l = l->next)
- {
- l->packed = true;
- space += l->bufsize;
- if(l->surface < 0 || !t.ext) continue;
- surfaceinfo &surf = t.ext->surfaces[l->surface];
- layoutinfo layout;
- packlightmap(*l, layout);
- int numverts = surf.numverts&MAXFACEVERTS;
- vertinfo *verts = t.ext->verts() + surf.verts;
- if(l->layers&LAYER_DUP)
- {
- if(l->type&LM_ALPHA) surf.lmid[0] = layout.lmid;
- else { surf.lmid[1] = layout.lmid; verts += numverts; }
- }
- else
- {
- if(l->layers&LAYER_TOP) surf.lmid[0] = layout.lmid;
- if(l->layers&LAYER_BOTTOM) surf.lmid[1] = layout.lmid;
- }
- ushort offsetx = layout.x*((USHRT_MAX+1)/LM_PACKW), offsety = layout.y*((USHRT_MAX+1)/LM_PACKH);
- loopk(numverts)
- {
- vertinfo &v = verts[k];
- v.u += offsetx;
- v.v += offsety;
- }
- }
- if(t.worker == w)
- {
- w->bufused -= space;
- w->bufstart = (w->bufstart + space)%LIGHTMAPBUFSIZE;
- w->firstlightmap = l;
- if(!l)
- {
- w->lastlightmap = NULL;
- w->bufstart = w->bufused = 0;
- }
- }
- if(t.worker->needspace) SDL_CondSignal(t.worker->spacecond);
- }
- return numpacked;
+ int numpacked = 0;
+ for(; packidx < lightmaptasks[0].length(); packidx++, numpacked++)
+ {
+ lightmaptask &t = lightmaptasks[0][packidx];
+ if(!t.lightmaps) break;
+ if(t.ext && t.c->ext != t.ext)
+ {
+ lightmapext &e = lightmapexts.add();
+ e.c = t.c;
+ e.ext = t.ext;
+ }
+ progress = t.progress;
+ lightmapinfo *l = t.lightmaps;
+ if(l == (lightmapinfo *)-1) continue;
+ int space = 0;
+ for(; l && l->c == t.c; l = l->next)
+ {
+ l->packed = true;
+ space += l->bufsize;
+ if(l->surface < 0 || !t.ext) continue;
+ surfaceinfo &surf = t.ext->surfaces[l->surface];
+ layoutinfo layout;
+ packlightmap(*l, layout);
+ int numverts = surf.numverts&MAXFACEVERTS;
+ vertinfo *verts = t.ext->verts() + surf.verts;
+ if(l->layers&LAYER_DUP)
+ {
+ if(l->type&LM_ALPHA) surf.lmid[0] = layout.lmid;
+ else { surf.lmid[1] = layout.lmid; verts += numverts; }
+ }
+ else
+ {
+ if(l->layers&LAYER_TOP) surf.lmid[0] = layout.lmid;
+ if(l->layers&LAYER_BOTTOM) surf.lmid[1] = layout.lmid;
+ }
+ ushort offsetx = layout.x*((USHRT_MAX+1)/LM_PACKW), offsety = layout.y*((USHRT_MAX+1)/LM_PACKH);
+ loopk(numverts)
+ {
+ vertinfo &v = verts[k];
+ v.u += offsetx;
+ v.v += offsety;
+ }
+ }
+ if(t.worker == w)
+ {
+ w->bufused -= space;
+ w->bufstart = (w->bufstart + space)%LIGHTMAPBUFSIZE;
+ w->firstlightmap = l;
+ if(!l)
+ {
+ w->lastlightmap = NULL;
+ w->bufstart = w->bufused = 0;
+ }
+ }
+ if(t.worker->needspace) SDL_CondSignal(t.worker->spacecond);
+ }
+ return numpacked;
}
static lightmapinfo *alloclightmap(lightmapworker *w)
{
- int needspace1 = sizeof(lightmapinfo) + w->w*w->h*w->bpp,
- needspace2 = (w->type&LM_TYPE) == LM_BUMPMAP0 ? w->w*w->h*3 : 0,
- needspace = needspace1 + needspace2,
- bufend = (w->bufstart + w->bufused)%LIGHTMAPBUFSIZE,
- availspace = LIGHTMAPBUFSIZE - w->bufused,
- availspace1 = min(availspace, LIGHTMAPBUFSIZE - bufend),
- availspace2 = min(availspace, w->bufstart);
- if(availspace < needspace || (max(availspace1, availspace2) < needspace && (availspace1 < needspace1 || availspace2 < needspace2)))
- {
- if(tasklock) SDL_LockMutex(tasklock);
- while(!w->doneworking)
- {
- lightmapinfo *l = w->firstlightmap;
- for(; l && l->packed; l = l->next)
- {
- w->bufused -= l->bufsize;
- w->bufstart = (w->bufstart + l->bufsize)%LIGHTMAPBUFSIZE;
- }
- w->firstlightmap = l;
- if(!l)
- {
- w->lastlightmap = NULL;
- w->bufstart = w->bufused = 0;
- }
- bufend = (w->bufstart + w->bufused)%LIGHTMAPBUFSIZE;
- availspace = LIGHTMAPBUFSIZE - w->bufused;
- availspace1 = min(availspace, LIGHTMAPBUFSIZE - bufend);
- availspace2 = min(availspace, w->bufstart);
- if(availspace >= needspace && (max(availspace1, availspace2) >= needspace || (availspace1 >= needspace1 && availspace2 >= needspace2))) break;
- if(packlightmaps(w)) continue;
- if(!w->spacecond || !tasklock) break;
- w->needspace = true;
- SDL_CondWait(w->spacecond, tasklock);
- w->needspace = false;
- }
- if(tasklock) SDL_UnlockMutex(tasklock);
- }
- int usedspace = needspace;
- lightmapinfo *l = NULL;
- if(availspace1 >= needspace1)
- {
- l = (lightmapinfo *)&w->buf[bufend];
- w->colorbuf = (uchar *)(l + 1);
- if((w->type&LM_TYPE) != LM_BUMPMAP0) w->raybuf = NULL;
- else if(availspace1 >= needspace) w->raybuf = (bvec *)&w->buf[bufend + needspace1];
- else
- {
- w->raybuf = (bvec *)w->buf;
- usedspace += availspace1 - needspace1;
- }
- }
- else if(availspace2 >= needspace)
- {
- usedspace += availspace1;
- l = (lightmapinfo *)w->buf;
- w->colorbuf = (uchar *)(l + 1);
- w->raybuf = (w->type&LM_TYPE) == LM_BUMPMAP0 ? (bvec *)&w->buf[needspace1] : NULL;
- }
- else return NULL;
- w->bufused += usedspace;
- l->next = NULL;
- l->c = w->c;
- l->type = w->type;
- l->w = w->w;
- l->h = w->h;
- l->bpp = w->bpp;
- l->colorbuf = w->colorbuf;
- l->raybuf = w->raybuf;
- l->packed = false;
- l->bufsize = usedspace;
- l->surface = -1;
- l->layers = 0;
- if(!w->firstlightmap) w->firstlightmap = l;
- if(w->lastlightmap) w->lastlightmap->next = l;
- w->lastlightmap = l;
- if(!w->curlightmaps) w->curlightmaps = l;
- return l;
+ int needspace1 = sizeof(lightmapinfo) + w->w*w->h*w->bpp,
+ needspace2 = (w->type&LM_TYPE) == LM_BUMPMAP0 ? w->w*w->h*3 : 0,
+ needspace = needspace1 + needspace2,
+ bufend = (w->bufstart + w->bufused)%LIGHTMAPBUFSIZE,
+ availspace = LIGHTMAPBUFSIZE - w->bufused,
+ availspace1 = min(availspace, LIGHTMAPBUFSIZE - bufend),
+ availspace2 = min(availspace, w->bufstart);
+ if(availspace < needspace || (max(availspace1, availspace2) < needspace && (availspace1 < needspace1 || availspace2 < needspace2)))
+ {
+ if(tasklock) SDL_LockMutex(tasklock);
+ while(!w->doneworking)
+ {
+ lightmapinfo *l = w->firstlightmap;
+ for(; l && l->packed; l = l->next)
+ {
+ w->bufused -= l->bufsize;
+ w->bufstart = (w->bufstart + l->bufsize)%LIGHTMAPBUFSIZE;
+ }
+ w->firstlightmap = l;
+ if(!l)
+ {
+ w->lastlightmap = NULL;
+ w->bufstart = w->bufused = 0;
+ }
+ bufend = (w->bufstart + w->bufused)%LIGHTMAPBUFSIZE;
+ availspace = LIGHTMAPBUFSIZE - w->bufused;
+ availspace1 = min(availspace, LIGHTMAPBUFSIZE - bufend);
+ availspace2 = min(availspace, w->bufstart);
+ if(availspace >= needspace && (max(availspace1, availspace2) >= needspace || (availspace1 >= needspace1 && availspace2 >= needspace2))) break;
+ if(packlightmaps(w)) continue;
+ if(!w->spacecond || !tasklock) break;
+ w->needspace = true;
+ SDL_CondWait(w->spacecond, tasklock);
+ w->needspace = false;
+ }
+ if(tasklock) SDL_UnlockMutex(tasklock);
+ }
+ int usedspace = needspace;
+ lightmapinfo *l = NULL;
+ if(availspace1 >= needspace1)
+ {
+ l = (lightmapinfo *)&w->buf[bufend];
+ w->colorbuf = (uchar *)(l + 1);
+ if((w->type&LM_TYPE) != LM_BUMPMAP0) w->raybuf = NULL;
+ else if(availspace1 >= needspace) w->raybuf = (bvec *)&w->buf[bufend + needspace1];
+ else
+ {
+ w->raybuf = (bvec *)w->buf;
+ usedspace += availspace1 - needspace1;
+ }
+ }
+ else if(availspace2 >= needspace)
+ {
+ usedspace += availspace1;
+ l = (lightmapinfo *)w->buf;
+ w->colorbuf = (uchar *)(l + 1);
+ w->raybuf = (w->type&LM_TYPE) == LM_BUMPMAP0 ? (bvec *)&w->buf[needspace1] : NULL;
+ }
+ else return NULL;
+ w->bufused += usedspace;
+ l->next = NULL;
+ l->c = w->c;
+ l->type = w->type;
+ l->w = w->w;
+ l->h = w->h;
+ l->bpp = w->bpp;
+ l->colorbuf = w->colorbuf;
+ l->raybuf = w->raybuf;
+ l->packed = false;
+ l->bufsize = usedspace;
+ l->surface = -1;
+ l->layers = 0;
+ if(!w->firstlightmap) w->firstlightmap = l;
+ if(w->lastlightmap) w->lastlightmap->next = l;
+ w->lastlightmap = l;
+ if(!w->curlightmaps) w->curlightmaps = l;
+ return l;
}
static void freelightmap(lightmapworker *w)
{
- lightmapinfo *l = w->lastlightmap;
- if(!l || l->surface >= 0) return;
- if(w->firstlightmap == w->lastlightmap)
- {
- w->firstlightmap = w->lastlightmap = w->curlightmaps = NULL;
- w->bufstart = w->bufused = 0;
- }
- else
- {
- w->bufused -= l->bufsize - sizeof(lightmapinfo);
- l->bufsize = sizeof(lightmapinfo);
- l->packed = true;
- }
- if(w->curlightmaps == l) w->curlightmaps = NULL;
+ lightmapinfo *l = w->lastlightmap;
+ if(!l || l->surface >= 0) return;
+ if(w->firstlightmap == w->lastlightmap)
+ {
+ w->firstlightmap = w->lastlightmap = w->curlightmaps = NULL;
+ w->bufstart = w->bufused = 0;
+ }
+ else
+ {
+ w->bufused -= l->bufsize - sizeof(lightmapinfo);
+ l->bufsize = sizeof(lightmapinfo);
+ l->packed = true;
+ }
+ if(w->curlightmaps == l) w->curlightmaps = NULL;
}
static int setupsurface(lightmapworker *w, plane planes[2], int numplanes, const vec *p, const vec *n, int numverts, vertinfo *litverts, bool preview = false)
{
- vec u, v, t;
- vec2 c[MAXFACEVERTS];
-
- u = vec(p[2]).sub(p[0]).normalize();
- v.cross(planes[0], u);
- c[0] = vec2(0, 0);
- if(numplanes >= 2) t.cross(planes[1], u); else t = v;
- vec r1 = vec(p[1]).sub(p[0]);
- c[1] = vec2(r1.dot(u), min(r1.dot(v), 0.0f));
- c[2] = vec2(vec(p[2]).sub(p[0]).dot(u), 0);
- for(int i = 3; i < numverts; i++)
- {
- vec r = vec(p[i]).sub(p[0]);
- c[i] = vec2(r.dot(u), max(r.dot(t), 0.0f));
- }
-
- float carea = 1e16f;
- vec2 cx(0, 0), cy(0, 0), co(0, 0), cmin(0, 0), cmax(0, 0);
- loopi(numverts)
- {
- vec2 px = vec2(c[i+1 < numverts ? i+1 : 0]).sub(c[i]);
- float len = px.squaredlen();
- if(!len) continue;
- px.mul(1/sqrtf(len));
- vec2 py(-px.y, px.x), pmin(0, 0), pmax(0, 0);
- if(numplanes >= 2 && (i == 0 || i >= 3)) px.neg();
- loopj(numverts)
- {
- vec2 rj = vec2(c[j]).sub(c[i]), pj(rj.dot(px), rj.dot(py));
- pmin.x = min(pmin.x, pj.x);
- pmin.y = min(pmin.y, pj.y);
- pmax.x = max(pmax.x, pj.x);
- pmax.y = max(pmax.y, pj.y);
- }
- float area = (pmax.x-pmin.x)*(pmax.y-pmin.y);
- if(area < carea) { carea = area; cx = px; cy = py; co = c[i]; cmin = pmin; cmax = pmax; }
- }
-
- int scale = int(min(cmax.x - cmin.x, cmax.y - cmin.y));
- float lpu = 16.0f / float(lightlod && scale < (1 << lightlod) ? max(lightprecision / 2, 1) : lightprecision);
- int lw = clamp(int(ceil((cmax.x - cmin.x + 1)*lpu)), LM_MINW, LM_MAXW), lh = clamp(int(ceil((cmax.y - cmin.y + 1)*lpu)), LM_MINH, LM_MAXH);
- w->w = lw;
- w->h = lh;
- if(!preview)
- {
- w->w += 2*blurlms;
- w->h += 2*blurlms;
- }
- if(!alloclightmap(w)) return NO_SURFACE;
-
- vec2 cscale = vec2(cmax).sub(cmin).div(vec2(lw-1, lh-1)),
- comin = vec2(cx).mul(cmin.x).add(vec2(cy).mul(cmin.y)).add(co);
- loopi(numverts)
- {
- vec2 ri = vec2(c[i]).sub(comin);
- c[i] = vec2(ri.dot(cx)/cscale.x, ri.dot(cy)/cscale.y);
- }
-
- vec xstep1 = vec(v).mul(cx.y).add(vec(u).mul(cx.x)).mul(cscale.x),
- ystep1 = vec(v).mul(cy.y).add(vec(u).mul(cy.x)).mul(cscale.y),
- origin1 = vec(v).mul(comin.y).add(vec(u).mul(comin.x)).add(p[0]),
- xstep2 = xstep1, ystep2 = ystep1, origin2 = origin1;
- float side0 = LM_MAXW + 1, sidestep = 0;
- if(numplanes >= 2)
- {
- xstep2 = vec(t).mul(cx.y).add(vec(u).mul(cx.x)).mul(cscale.x);
- ystep2 = vec(t).mul(cy.y).add(vec(u).mul(cy.x)).mul(cscale.y);
- origin2 = vec(t).mul(comin.y).add(vec(u).mul(comin.x)).add(p[0]);
- if(cx.y) { side0 = comin.y/-(cx.y*cscale.x); sidestep = cy.y*cscale.y/-(cx.y*cscale.x); }
- else if(cy.y) { side0 = ceil(comin.y/-(cy.y*cscale.y))*(LM_MAXW + 1); sidestep = -(LM_MAXW + 1); if(cy.y < 0) { side0 = (LM_MAXW + 1) - side0; sidestep = -sidestep; } }
- else side0 = comin.y <= 0 ? LM_MAXW + 1 : -1;
- }
-
- int surftype = NO_SURFACE;
- if(preview)
- {
- surftype = previewlightmapalpha(w, lpu, origin1, xstep1, ystep1, origin2, xstep2, ystep2, side0, sidestep);
- }
- else
- {
- lerpvert lv[MAXFACEVERTS];
- int numv = numverts;
- calclerpverts(c, n, lv, numv);
-
- if(!generatelightmap(w, lpu, lv, numv, origin1, xstep1, ystep1, origin2, xstep2, ystep2, side0, sidestep)) return NO_SURFACE;
- surftype = finishlightmap(w);
- }
- if(surftype<SURFACE_LIGHTMAP) return surftype;
-
- vec2 texscale(float(USHRT_MAX+1)/LM_PACKW, float(USHRT_MAX+1)/LM_PACKH);
- if(lw != w->w) texscale.x *= float(w->w - 1) / (lw - 1);
- if(lh != w->h) texscale.y *= float(w->h - 1) / (lh - 1);
- loopk(numverts)
- {
- litverts[k].u = ushort(floor(clamp(c[k].x*texscale.x, 0.0f, float(USHRT_MAX))));
- litverts[k].v = ushort(floor(clamp(c[k].y*texscale.y, 0.0f, float(USHRT_MAX))));
- }
- return surftype;
+ vec u, v, t;
+ vec2 c[MAXFACEVERTS];
+
+ u = vec(p[2]).sub(p[0]).normalize();
+ v.cross(planes[0], u);
+ c[0] = vec2(0, 0);
+ if(numplanes >= 2) t.cross(planes[1], u); else t = v;
+ vec r1 = vec(p[1]).sub(p[0]);
+ c[1] = vec2(r1.dot(u), min(r1.dot(v), 0.0f));
+ c[2] = vec2(vec(p[2]).sub(p[0]).dot(u), 0);
+ for(int i = 3; i < numverts; i++)
+ {
+ vec r = vec(p[i]).sub(p[0]);
+ c[i] = vec2(r.dot(u), max(r.dot(t), 0.0f));
+ }
+
+ float carea = 1e16f;
+ vec2 cx(0, 0), cy(0, 0), co(0, 0), cmin(0, 0), cmax(0, 0);
+ loopi(numverts)
+ {
+ vec2 px = vec2(c[i+1 < numverts ? i+1 : 0]).sub(c[i]);
+ float len = px.squaredlen();
+ if(!len) continue;
+ px.mul(1/sqrtf(len));
+ vec2 py(-px.y, px.x), pmin(0, 0), pmax(0, 0);
+ if(numplanes >= 2 && (i == 0 || i >= 3)) px.neg();
+ loopj(numverts)
+ {
+ vec2 rj = vec2(c[j]).sub(c[i]), pj(rj.dot(px), rj.dot(py));
+ pmin.x = min(pmin.x, pj.x);
+ pmin.y = min(pmin.y, pj.y);
+ pmax.x = max(pmax.x, pj.x);
+ pmax.y = max(pmax.y, pj.y);
+ }
+ float area = (pmax.x-pmin.x)*(pmax.y-pmin.y);
+ if(area < carea) { carea = area; cx = px; cy = py; co = c[i]; cmin = pmin; cmax = pmax; }
+ }
+
+ int scale = int(min(cmax.x - cmin.x, cmax.y - cmin.y));
+ float lpu = 16.0f / float(lightlod && scale < (1 << lightlod) ? max(lightprecision / 2, 1) : lightprecision);
+ int lw = clamp(int(ceil((cmax.x - cmin.x + 1)*lpu)), LM_MINW, LM_MAXW), lh = clamp(int(ceil((cmax.y - cmin.y + 1)*lpu)), LM_MINH, LM_MAXH);
+ w->w = lw;
+ w->h = lh;
+ if(!preview)
+ {
+ w->w += 2*blurlms;
+ w->h += 2*blurlms;
+ }
+ if(!alloclightmap(w)) return NO_SURFACE;
+
+ vec2 cscale = vec2(cmax).sub(cmin).div(vec2(lw-1, lh-1)),
+ comin = vec2(cx).mul(cmin.x).add(vec2(cy).mul(cmin.y)).add(co);
+ loopi(numverts)
+ {
+ vec2 ri = vec2(c[i]).sub(comin);
+ c[i] = vec2(ri.dot(cx)/cscale.x, ri.dot(cy)/cscale.y);
+ }
+
+ vec xstep1 = vec(v).mul(cx.y).add(vec(u).mul(cx.x)).mul(cscale.x),
+ ystep1 = vec(v).mul(cy.y).add(vec(u).mul(cy.x)).mul(cscale.y),
+ origin1 = vec(v).mul(comin.y).add(vec(u).mul(comin.x)).add(p[0]),
+ xstep2 = xstep1, ystep2 = ystep1, origin2 = origin1;
+ float side0 = LM_MAXW + 1, sidestep = 0;
+ if(numplanes >= 2)
+ {
+ xstep2 = vec(t).mul(cx.y).add(vec(u).mul(cx.x)).mul(cscale.x);
+ ystep2 = vec(t).mul(cy.y).add(vec(u).mul(cy.x)).mul(cscale.y);
+ origin2 = vec(t).mul(comin.y).add(vec(u).mul(comin.x)).add(p[0]);
+ if(cx.y) { side0 = comin.y/-(cx.y*cscale.x); sidestep = cy.y*cscale.y/-(cx.y*cscale.x); }
+ else if(cy.y) { side0 = ceil(comin.y/-(cy.y*cscale.y))*(LM_MAXW + 1); sidestep = -(LM_MAXW + 1); if(cy.y < 0) { side0 = (LM_MAXW + 1) - side0; sidestep = -sidestep; } }
+ else side0 = comin.y <= 0 ? LM_MAXW + 1 : -1;
+ }
+
+ int surftype = NO_SURFACE;
+ if(preview)
+ {
+ surftype = previewlightmapalpha(w, lpu, origin1, xstep1, ystep1, origin2, xstep2, ystep2, side0, sidestep);
+ }
+ else
+ {
+ lerpvert lv[MAXFACEVERTS];
+ int numv = numverts;
+ calclerpverts(c, n, lv, numv);
+
+ if(!generatelightmap(w, lpu, lv, numv, origin1, xstep1, ystep1, origin2, xstep2, ystep2, side0, sidestep)) return NO_SURFACE;
+ surftype = finishlightmap(w);
+ }
+ if(surftype<SURFACE_LIGHTMAP) return surftype;
+
+ vec2 texscale(float(USHRT_MAX+1)/LM_PACKW, float(USHRT_MAX+1)/LM_PACKH);
+ if(lw != w->w) texscale.x *= float(w->w - 1) / (lw - 1);
+ if(lh != w->h) texscale.y *= float(w->h - 1) / (lh - 1);
+ loopk(numverts)
+ {
+ litverts[k].u = ushort(floor(clamp(c[k].x*texscale.x, 0.0f, float(USHRT_MAX))));
+ litverts[k].v = ushort(floor(clamp(c[k].y*texscale.y, 0.0f, float(USHRT_MAX))));
+ }
+ return surftype;
}
static void removelmalpha(lightmapworker *w)
{
- if(!(w->type&LM_ALPHA)) return;
- for(uchar *dst = w->colorbuf, *src = w->colorbuf, *end = &src[w->w*w->h*4];
- src < end;
- dst += 3, src += 4)
- {
- dst[0] = src[0];
- dst[1] = src[1];
- dst[2] = src[2];
- }
- w->type &= ~LM_ALPHA;
- w->bpp = 3;
- w->lastlightmap->type = w->type;
- w->lastlightmap->bpp = w->bpp;
+ if(!(w->type&LM_ALPHA)) return;
+ for(uchar *dst = w->colorbuf, *src = w->colorbuf, *end = &src[w->w*w->h*4];
+ src < end;
+ dst += 3, src += 4)
+ {
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ }
+ w->type &= ~LM_ALPHA;
+ w->bpp = 3;
+ w->lastlightmap->type = w->type;
+ w->lastlightmap->bpp = w->bpp;
}
static lightmapinfo *setupsurfaces(lightmapworker *w, lightmaptask &task)
{
- cube &c = *task.c;
- const ivec &co = task.o;
- int size = task.size, usefacemask = task.usefaces;
-
- w->curlightmaps = NULL;
- w->c = &c;
-
- surfaceinfo surfaces[6];
- vertinfo litverts[6*2*MAXFACEVERTS];
- int numlitverts = 0;
- memclear(surfaces);
- loopi(6)
- {
- int usefaces = usefacemask&0xF;
- usefacemask >>= 4;
- if(!usefaces)
- {
- if(!c.ext) continue;
- surfaceinfo &surf = surfaces[i];
- surf = c.ext->surfaces[i];
- int numverts = surf.totalverts();
- if(numverts)
- {
- memcpy(&litverts[numlitverts], c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
- surf.verts = numlitverts;
- numlitverts += numverts;
- }
- continue;
- }
-
- VSlot &vslot = lookupvslot(c.texture[i], false),
- *layer = vslot.layer && !(c.material&MAT_ALPHA) ? &lookupvslot(vslot.layer, false) : NULL;
- Shader *shader = vslot.slot->shader;
- int shadertype = shader->type;
- if(layer) shadertype |= layer->slot->shader->type;
-
- surfaceinfo &surf = surfaces[i];
- vertinfo *curlitverts = &litverts[numlitverts];
- int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0;
- ivec mo(co);
- int msz = size, convex = 0;
- if(numverts)
- {
- vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
- loopj(numverts) curlitverts[j].set(verts[j].getxyz());
- if(c.merged&(1<<i))
- {
- msz = 1<<calcmergedsize(i, mo, size, verts, numverts);
- mo.mask(~(msz-1));
-
- if(!(surf.numverts&MAXFACEVERTS))
- {
- surf.verts = numlitverts;
- surf.numverts |= numverts;
- numlitverts += numverts;
- }
- }
- else if(!flataxisface(c, i)) convex = faceconvexity(verts, numverts, size);
- }
- else
- {
- ivec v[4];
- genfaceverts(c, i, v);
- if(!flataxisface(c, i)) convex = faceconvexity(v);
- int order = usefaces&4 || convex < 0 ? 1 : 0;
- ivec vo = ivec(co).mask(0xFFF).shl(3);
- curlitverts[numverts++].set(v[order].mul(size).add(vo));
- if(usefaces&1) curlitverts[numverts++].set(v[order+1].mul(size).add(vo));
- curlitverts[numverts++].set(v[order+2].mul(size).add(vo));
- if(usefaces&2) curlitverts[numverts++].set(v[(order+3)&3].mul(size).add(vo));
- }
-
- vec pos[MAXFACEVERTS], n[MAXFACEVERTS], po(ivec(co).mask(~0xFFF));
- loopj(numverts) pos[j] = vec(curlitverts[j].getxyz()).mul(1.0f/8).add(po);
-
- plane planes[2];
- int numplanes = 0;
- planes[numplanes++].toplane(pos[0], pos[1], pos[2]);
- if(numverts < 4 || !convex) loopk(numverts) findnormal(pos[k], planes[0], n[k]);
- else
- {
- planes[numplanes++].toplane(pos[0], pos[2], pos[3]);
- vec avg = vec(planes[0]).add(planes[1]).normalize();
- findnormal(pos[0], avg, n[0]);
- findnormal(pos[1], planes[0], n[1]);
- findnormal(pos[2], avg, n[2]);
- for(int k = 3; k < numverts; k++) findnormal(pos[k], planes[1], n[k]);
- }
-
- if(shadertype&(SHADER_NORMALSLMS | SHADER_ENVMAP))
- {
- loopk(numverts) curlitverts[k].norm = encodenormal(n[k]);
- if(!(surf.numverts&MAXFACEVERTS))
- {
- surf.verts = numlitverts;
- surf.numverts |= numverts;
- numlitverts += numverts;
- }
- }
-
- if(!findlights(w, mo.x, mo.y, mo.z, msz, pos, n, numverts, *vslot.slot, vslot))
- {
- if(surf.numverts&MAXFACEVERTS) surf.numverts |= LAYER_TOP;
- continue;
- }
-
- w->slot = vslot.slot;
- w->vslot = &vslot;
- w->type = shader->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE;
- if(layer) w->type |= LM_ALPHA;
- w->bpp = w->type&LM_ALPHA ? 4 : 3;
- w->orient = i;
- w->rotate = vslot.rotation;
- int surftype = setupsurface(w, planes, numplanes, pos, n, numverts, curlitverts);
- switch(surftype)
- {
- case SURFACE_LIGHTMAP_BOTTOM:
- if((shader->type^layer->slot->shader->type)&SHADER_NORMALSLMS ||
- (shader->type&SHADER_NORMALSLMS && vslot.rotation!=layer->rotation))
- {
- freelightmap(w);
- break;
- }
- // fall through
- case SURFACE_LIGHTMAP_BLEND:
- case SURFACE_LIGHTMAP_TOP:
- {
- if(!(surf.numverts&MAXFACEVERTS))
- {
- surf.verts = numlitverts;
- surf.numverts |= numverts;
- numlitverts += numverts;
- }
-
- w->lastlightmap->surface = i;
- w->lastlightmap->layers = (surftype==SURFACE_LIGHTMAP_BOTTOM ? LAYER_BOTTOM : LAYER_TOP);
- if(surftype==SURFACE_LIGHTMAP_BLEND)
- {
- surf.numverts |= LAYER_BLEND;
- w->lastlightmap->layers = LAYER_TOP;
- if((shader->type^layer->slot->shader->type)&SHADER_NORMALSLMS ||
- (shader->type&SHADER_NORMALSLMS && vslot.rotation!=layer->rotation))
- break;
- w->lastlightmap->layers |= LAYER_BOTTOM;
- }
- else
- {
- if(surftype==SURFACE_LIGHTMAP_BOTTOM)
- {
- surf.numverts |= LAYER_BOTTOM;
- w->lastlightmap->layers = LAYER_BOTTOM;
- }
- else
- {
- surf.numverts |= LAYER_TOP;
- w->lastlightmap->layers = LAYER_TOP;
- }
- if(w->type&LM_ALPHA) removelmalpha(w);
- }
- continue;
- }
-
- case SURFACE_AMBIENT_BOTTOM:
- freelightmap(w);
- surf.numverts |= layer ? LAYER_BOTTOM : LAYER_TOP;
- continue;
-
- case SURFACE_AMBIENT_TOP:
- freelightmap(w);
- surf.numverts |= LAYER_TOP;
- continue;
-
- default:
- freelightmap(w);
- continue;
- }
-
- w->slot = layer->slot;
- w->vslot = layer;
- w->type = layer->slot->shader->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE;
- w->bpp = 3;
- w->rotate = layer->rotation;
- vertinfo *blendverts = surf.numverts&MAXFACEVERTS ? &curlitverts[numverts] : curlitverts;
- switch(setupsurface(w, planes, numplanes, pos, n, numverts, blendverts))
- {
- case SURFACE_LIGHTMAP_TOP:
- {
- if(!(surf.numverts&MAXFACEVERTS))
- {
- surf.verts = numlitverts;
- surf.numverts |= numverts;
- numlitverts += numverts;
- }
- else if(!(surf.numverts&LAYER_DUP))
- {
- surf.numverts |= LAYER_DUP;
- w->lastlightmap->layers |= LAYER_DUP;
- loopk(numverts)
- {
- vertinfo &src = curlitverts[k];
- vertinfo &dst = blendverts[k];
- dst.setxyz(src.getxyz());
- dst.norm = src.norm;
- }
- numlitverts += numverts;
- }
- surf.numverts |= LAYER_BOTTOM;
- w->lastlightmap->layers |= LAYER_BOTTOM;
-
- w->lastlightmap->surface = i;
- break;
- }
-
- case SURFACE_AMBIENT_TOP:
- {
- freelightmap(w);
- surf.numverts |= LAYER_BOTTOM;
- break;
- }
-
- default: freelightmap(w); break;
- }
- }
- loopk(6)
- {
- surfaceinfo &surf = surfaces[k];
- if(surf.used())
- {
- cubeext *ext = c.ext && c.ext->maxverts >= numlitverts ? c.ext : growcubeext(c.ext, numlitverts);
- memcpy(ext->surfaces, surfaces, sizeof(ext->surfaces));
- memcpy(ext->verts(), litverts, numlitverts*sizeof(vertinfo));
- task.ext = ext;
- break;
- }
- }
- return w->curlightmaps ? w->curlightmaps : (lightmapinfo *)-1;
+ cube &c = *task.c;
+ const ivec &co = task.o;
+ int size = task.size, usefacemask = task.usefaces;
+
+ w->curlightmaps = NULL;
+ w->c = &c;
+
+ surfaceinfo surfaces[6];
+ vertinfo litverts[6*2*MAXFACEVERTS];
+ int numlitverts = 0;
+ memclear(surfaces);
+ loopi(6)
+ {
+ int usefaces = usefacemask&0xF;
+ usefacemask >>= 4;
+ if(!usefaces)
+ {
+ if(!c.ext) continue;
+ surfaceinfo &surf = surfaces[i];
+ surf = c.ext->surfaces[i];
+ int numverts = surf.totalverts();
+ if(numverts)
+ {
+ memcpy(&litverts[numlitverts], c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
+ surf.verts = numlitverts;
+ numlitverts += numverts;
+ }
+ continue;
+ }
+
+ VSlot &vslot = lookupvslot(c.texture[i], false),
+ *layer = vslot.layer && !(c.material&MAT_ALPHA) ? &lookupvslot(vslot.layer, false) : NULL;
+ Shader *shader = vslot.slot->shader;
+ int shadertype = shader->type;
+ if(layer) shadertype |= layer->slot->shader->type;
+
+ surfaceinfo &surf = surfaces[i];
+ vertinfo *curlitverts = &litverts[numlitverts];
+ int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0;
+ ivec mo(co);
+ int msz = size, convex = 0;
+ if(numverts)
+ {
+ vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
+ loopj(numverts) curlitverts[j].set(verts[j].getxyz());
+ if(c.merged&(1<<i))
+ {
+ msz = 1<<calcmergedsize(i, mo, size, verts, numverts);
+ mo.mask(~(msz-1));
+
+ if(!(surf.numverts&MAXFACEVERTS))
+ {
+ surf.verts = numlitverts;
+ surf.numverts |= numverts;
+ numlitverts += numverts;
+ }
+ }
+ else if(!flataxisface(c, i)) convex = faceconvexity(verts, numverts, size);
+ }
+ else
+ {
+ ivec v[4];
+ genfaceverts(c, i, v);
+ if(!flataxisface(c, i)) convex = faceconvexity(v);
+ int order = usefaces&4 || convex < 0 ? 1 : 0;
+ ivec vo = ivec(co).mask(0xFFF).shl(3);
+ curlitverts[numverts++].set(v[order].mul(size).add(vo));
+ if(usefaces&1) curlitverts[numverts++].set(v[order+1].mul(size).add(vo));
+ curlitverts[numverts++].set(v[order+2].mul(size).add(vo));
+ if(usefaces&2) curlitverts[numverts++].set(v[(order+3)&3].mul(size).add(vo));
+ }
+
+ vec pos[MAXFACEVERTS], n[MAXFACEVERTS], po(ivec(co).mask(~0xFFF));
+ loopj(numverts) pos[j] = vec(curlitverts[j].getxyz()).mul(1.0f/8).add(po);
+
+ plane planes[2];
+ int numplanes = 0;
+ planes[numplanes++].toplane(pos[0], pos[1], pos[2]);
+ if(numverts < 4 || !convex) loopk(numverts) findnormal(pos[k], planes[0], n[k]);
+ else
+ {
+ planes[numplanes++].toplane(pos[0], pos[2], pos[3]);
+ vec avg = vec(planes[0]).add(planes[1]).normalize();
+ findnormal(pos[0], avg, n[0]);
+ findnormal(pos[1], planes[0], n[1]);
+ findnormal(pos[2], avg, n[2]);
+ for(int k = 3; k < numverts; k++) findnormal(pos[k], planes[1], n[k]);
+ }
+
+ if(shadertype&(SHADER_NORMALSLMS | SHADER_ENVMAP))
+ {
+ loopk(numverts) curlitverts[k].norm = encodenormal(n[k]);
+ if(!(surf.numverts&MAXFACEVERTS))
+ {
+ surf.verts = numlitverts;
+ surf.numverts |= numverts;
+ numlitverts += numverts;
+ }
+ }
+
+ if(!findlights(w, mo.x, mo.y, mo.z, msz, pos, n, numverts, *vslot.slot, vslot))
+ {
+ if(surf.numverts&MAXFACEVERTS) surf.numverts |= LAYER_TOP;
+ continue;
+ }
+
+ w->slot = vslot.slot;
+ w->vslot = &vslot;
+ w->type = shader->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE;
+ if(layer) w->type |= LM_ALPHA;
+ w->bpp = w->type&LM_ALPHA ? 4 : 3;
+ w->orient = i;
+ w->rotate = vslot.rotation;
+ int surftype = setupsurface(w, planes, numplanes, pos, n, numverts, curlitverts);
+ switch(surftype)
+ {
+ case SURFACE_LIGHTMAP_BOTTOM:
+ if((shader->type^layer->slot->shader->type)&SHADER_NORMALSLMS ||
+ (shader->type&SHADER_NORMALSLMS && vslot.rotation!=layer->rotation))
+ {
+ freelightmap(w);
+ break;
+ }
+ // fall through
+ case SURFACE_LIGHTMAP_BLEND:
+ case SURFACE_LIGHTMAP_TOP:
+ {
+ if(!(surf.numverts&MAXFACEVERTS))
+ {
+ surf.verts = numlitverts;
+ surf.numverts |= numverts;
+ numlitverts += numverts;
+ }
+
+ w->lastlightmap->surface = i;
+ w->lastlightmap->layers = (surftype==SURFACE_LIGHTMAP_BOTTOM ? LAYER_BOTTOM : LAYER_TOP);
+ if(surftype==SURFACE_LIGHTMAP_BLEND)
+ {
+ surf.numverts |= LAYER_BLEND;
+ w->lastlightmap->layers = LAYER_TOP;
+ if((shader->type^layer->slot->shader->type)&SHADER_NORMALSLMS ||
+ (shader->type&SHADER_NORMALSLMS && vslot.rotation!=layer->rotation))
+ break;
+ w->lastlightmap->layers |= LAYER_BOTTOM;
+ }
+ else
+ {
+ if(surftype==SURFACE_LIGHTMAP_BOTTOM)
+ {
+ surf.numverts |= LAYER_BOTTOM;
+ w->lastlightmap->layers = LAYER_BOTTOM;
+ }
+ else
+ {
+ surf.numverts |= LAYER_TOP;
+ w->lastlightmap->layers = LAYER_TOP;
+ }
+ if(w->type&LM_ALPHA) removelmalpha(w);
+ }
+ continue;
+ }
+
+ case SURFACE_AMBIENT_BOTTOM:
+ freelightmap(w);
+ surf.numverts |= layer ? LAYER_BOTTOM : LAYER_TOP;
+ continue;
+
+ case SURFACE_AMBIENT_TOP:
+ freelightmap(w);
+ surf.numverts |= LAYER_TOP;
+ continue;
+
+ default:
+ freelightmap(w);
+ continue;
+ }
+
+ w->slot = layer->slot;
+ w->vslot = layer;
+ w->type = layer->slot->shader->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE;
+ w->bpp = 3;
+ w->rotate = layer->rotation;
+ vertinfo *blendverts = surf.numverts&MAXFACEVERTS ? &curlitverts[numverts] : curlitverts;
+ switch(setupsurface(w, planes, numplanes, pos, n, numverts, blendverts))
+ {
+ case SURFACE_LIGHTMAP_TOP:
+ {
+ if(!(surf.numverts&MAXFACEVERTS))
+ {
+ surf.verts = numlitverts;
+ surf.numverts |= numverts;
+ numlitverts += numverts;
+ }
+ else if(!(surf.numverts&LAYER_DUP))
+ {
+ surf.numverts |= LAYER_DUP;
+ w->lastlightmap->layers |= LAYER_DUP;
+ loopk(numverts)
+ {
+ vertinfo &src = curlitverts[k];
+ vertinfo &dst = blendverts[k];
+ dst.setxyz(src.getxyz());
+ dst.norm = src.norm;
+ }
+ numlitverts += numverts;
+ }
+ surf.numverts |= LAYER_BOTTOM;
+ w->lastlightmap->layers |= LAYER_BOTTOM;
+
+ w->lastlightmap->surface = i;
+ break;
+ }
+
+ case SURFACE_AMBIENT_TOP:
+ {
+ freelightmap(w);
+ surf.numverts |= LAYER_BOTTOM;
+ break;
+ }
+
+ default: freelightmap(w); break;
+ }
+ }
+ loopk(6)
+ {
+ surfaceinfo &surf = surfaces[k];
+ if(surf.used())
+ {
+ cubeext *ext = c.ext && c.ext->maxverts >= numlitverts ? c.ext : growcubeext(c.ext, numlitverts);
+ memcpy(ext->surfaces, surfaces, sizeof(ext->surfaces));
+ memcpy(ext->verts(), litverts, numlitverts*sizeof(vertinfo));
+ task.ext = ext;
+ break;
+ }
+ }
+ return w->curlightmaps ? w->curlightmaps : (lightmapinfo *)-1;
}
int lightmapworker::work(void *data)
{
- lightmapworker *w = (lightmapworker *)data;
- SDL_LockMutex(tasklock);
- while(!w->doneworking)
- {
- if(allocidx < lightmaptasks[0].length())
- {
- lightmaptask &t = lightmaptasks[0][allocidx++];
- t.worker = w;
- SDL_UnlockMutex(tasklock);
- lightmapinfo *l = setupsurfaces(w, t);
- SDL_LockMutex(tasklock);
- t.lightmaps = l;
- packlightmaps(w);
- }
- else
- {
- if(packidx >= lightmaptasks[0].length()) SDL_CondSignal(emptycond);
- SDL_CondWait(fullcond, tasklock);
- }
- }
- SDL_UnlockMutex(tasklock);
- return 0;
+ lightmapworker *w = (lightmapworker *)data;
+ SDL_LockMutex(tasklock);
+ while(!w->doneworking)
+ {
+ if(allocidx < lightmaptasks[0].length())
+ {
+ lightmaptask &t = lightmaptasks[0][allocidx++];
+ t.worker = w;
+ SDL_UnlockMutex(tasklock);
+ lightmapinfo *l = setupsurfaces(w, t);
+ SDL_LockMutex(tasklock);
+ t.lightmaps = l;
+ packlightmaps(w);
+ }
+ else
+ {
+ if(packidx >= lightmaptasks[0].length()) SDL_CondSignal(emptycond);
+ SDL_CondWait(fullcond, tasklock);
+ }
+ }
+ SDL_UnlockMutex(tasklock);
+ return 0;
}
static bool processtasks(bool finish = false)
{
- if(tasklock) SDL_LockMutex(tasklock);
- while(finish || lightmaptasks[1].length())
- {
- if(packidx >= lightmaptasks[0].length())
- {
- if(lightmaptasks[1].empty()) break;
- lightmaptasks[0].setsize(0);
- lightmaptasks[0].move(lightmaptasks[1]);
- packidx = allocidx = 0;
- if(fullcond) SDL_CondBroadcast(fullcond);
- }
- else if(lightmapping > 1)
- {
- SDL_CondWaitTimeout(emptycond, tasklock, 250);
- CHECK_PROGRESS_LOCKED({ SDL_UnlockMutex(tasklock); return false; }, SDL_UnlockMutex(tasklock), SDL_LockMutex(tasklock));
- }
- else
- {
- while(allocidx < lightmaptasks[0].length())
- {
- lightmaptask &t = lightmaptasks[0][allocidx++];
- t.worker = lightmapworkers[0];
- t.lightmaps = setupsurfaces(lightmapworkers[0], t);
- packlightmaps(lightmapworkers[0]);
- CHECK_PROGRESS(return false);
- }
- }
- }
- if(tasklock) SDL_UnlockMutex(tasklock);
- return true;
+ if(tasklock) SDL_LockMutex(tasklock);
+ while(finish || lightmaptasks[1].length())
+ {
+ if(packidx >= lightmaptasks[0].length())
+ {
+ if(lightmaptasks[1].empty()) break;
+ lightmaptasks[0].setsize(0);
+ lightmaptasks[0].move(lightmaptasks[1]);
+ packidx = allocidx = 0;
+ if(fullcond) SDL_CondBroadcast(fullcond);
+ }
+ else if(lightmapping > 1)
+ {
+ SDL_CondWaitTimeout(emptycond, tasklock, 250);
+ CHECK_PROGRESS_LOCKED({ SDL_UnlockMutex(tasklock); return false; }, SDL_UnlockMutex(tasklock), SDL_LockMutex(tasklock));
+ }
+ else
+ {
+ while(allocidx < lightmaptasks[0].length())
+ {
+ lightmaptask &t = lightmaptasks[0][allocidx++];
+ t.worker = lightmapworkers[0];
+ t.lightmaps = setupsurfaces(lightmapworkers[0], t);
+ packlightmaps(lightmapworkers[0]);
+ CHECK_PROGRESS(return false);
+ }
+ }
+ }
+ if(tasklock) SDL_UnlockMutex(tasklock);
+ return true;
}
static void generatelightmaps(cube *c, const ivec &co, int size)
{
- CHECK_PROGRESS(return);
-
- taskprogress++;
-
- loopi(8)
- {
- ivec o(i, co, size);
- if(c[i].children)
- generatelightmaps(c[i].children, o, size >> 1);
- else if(!isempty(c[i]))
- {
- if(c[i].ext)
- {
- loopj(6)
- {
- surfaceinfo &surf = c[i].ext->surfaces[j];
- if(surf.lmid[0] >= LMID_RESERVED || surf.lmid[1] >= LMID_RESERVED) goto nextcube;
- surf.clear();
- }
- }
- int usefacemask = 0;
- loopj(6) if(c[i].texture[j] != DEFAULT_SKY && (!(c[i].merged&(1<<j)) || (c[i].ext && c[i].ext->surfaces[j].numverts&MAXFACEVERTS)))
- {
- usefacemask |= visibletris(c[i], j, o, size)<<(4*j);
- }
- if(usefacemask)
- {
- lightmaptask &t = lightmaptasks[1].add();
- t.o = o;
- t.size = size;
- t.usefaces = usefacemask;
- t.c = &c[i];
- t.ext = NULL;
- t.lightmaps = NULL;
- t.progress = taskprogress;
- if(lightmaptasks[1].length() >= MAXLIGHTMAPTASKS && !processtasks()) return;
- }
- }
- nextcube:;
- }
+ CHECK_PROGRESS(return);
+
+ taskprogress++;
+
+ loopi(8)
+ {
+ ivec o(i, co, size);
+ if(c[i].children)
+ generatelightmaps(c[i].children, o, size >> 1);
+ else if(!isempty(c[i]))
+ {
+ if(c[i].ext)
+ {
+ loopj(6)
+ {
+ surfaceinfo &surf = c[i].ext->surfaces[j];
+ if(surf.lmid[0] >= LMID_RESERVED || surf.lmid[1] >= LMID_RESERVED) goto nextcube;
+ surf.clear();
+ }
+ }
+ int usefacemask = 0;
+ loopj(6) if(c[i].texture[j] != DEFAULT_SKY && (!(c[i].merged&(1<<j)) || (c[i].ext && c[i].ext->surfaces[j].numverts&MAXFACEVERTS)))
+ {
+ usefacemask |= visibletris(c[i], j, o, size)<<(4*j);
+ }
+ if(usefacemask)
+ {
+ lightmaptask &t = lightmaptasks[1].add();
+ t.o = o;
+ t.size = size;
+ t.usefaces = usefacemask;
+ t.c = &c[i];
+ t.ext = NULL;
+ t.lightmaps = NULL;
+ t.progress = taskprogress;
+ if(lightmaptasks[1].length() >= MAXLIGHTMAPTASKS && !processtasks()) return;
+ }
+ }
+ nextcube:;
+ }
}
static bool previewblends(lightmapworker *w, cube &c, const ivec &co, int size)
{
- if(isempty(c) || c.material&MAT_ALPHA) return false;
-
- int usefacemask = 0;
- loopi(6) if(c.texture[i] != DEFAULT_SKY && lookupvslot(c.texture[i], false).layer)
- usefacemask |= visibletris(c, i, co, size)<<(4*i);
- if(!usefacemask) return false;
-
- if(!setblendmaporigin(w->blendmapcache, co, size))
- {
- if(!c.ext) return false;
- bool blends = false;
- loopi(6) if(c.ext->surfaces[i].numverts&LAYER_BOTTOM)
- {
- c.ext->surfaces[i].brighten();
- blends = true;
- }
- return blends;
- }
-
- w->firstlightmap = w->lastlightmap = w->curlightmaps = NULL;
- w->bufstart = w->bufused = 0;
- w->c = &c;
-
- surfaceinfo surfaces[6];
- vertinfo litverts[6*2*MAXFACEVERTS];
- int numlitverts = 0;
- memcpy(surfaces, c.ext ? c.ext->surfaces : brightsurfaces, sizeof(surfaces));
- loopi(6)
- {
- int usefaces = usefacemask&0xF;
- usefacemask >>= 4;
- if(!usefaces)
- {
- surfaceinfo &surf = surfaces[i];
- int numverts = surf.totalverts();
- if(numverts)
- {
- memcpy(&litverts[numlitverts], c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
- surf.verts = numlitverts;
- numlitverts += numverts;
- }
- continue;
- }
-
- VSlot &vslot = lookupvslot(c.texture[i], false),
- &layer = lookupvslot(vslot.layer, false);
- Shader *shader = vslot.slot->shader;
- int shadertype = shader->type | layer.slot->shader->type;
-
- vertinfo *curlitverts = &litverts[numlitverts];
- int numverts = 0;
- ivec v[4];
- genfaceverts(c, i, v);
- int convex = flataxisface(c, i) ? 0 : faceconvexity(v),
- order = usefaces&4 || convex < 0 ? 1 : 0;
- ivec vo = ivec(co).mask(0xFFF).shl(3);
- curlitverts[numverts++].set(v[order].mul(size).add(vo));
- if(usefaces&1) curlitverts[numverts++].set(v[order+1].mul(size).add(vo));
- curlitverts[numverts++].set(v[order+2].mul(size).add(vo));
- if(usefaces&2) curlitverts[numverts++].set(v[(order+3)&3].mul(size).add(vo));
-
- vec pos[4], n[4], po(ivec(co).mask(~0xFFF));
- loopj(numverts) pos[j] = vec(curlitverts[j].getxyz()).mul(1.0f/8).add(po);
-
- plane planes[2];
- int numplanes = 0;
- planes[numplanes++].toplane(pos[0], pos[1], pos[2]);
- if(numverts < 4 || !convex) loopk(numverts) n[k] = planes[0];
- else
- {
- planes[numplanes++].toplane(pos[0], pos[2], pos[3]);
- vec avg = vec(planes[0]).add(planes[1]).normalize();
- n[0] = avg;
- n[1] = planes[0];
- n[2] = avg;
- for(int k = 3; k < numverts; k++) n[k] = planes[1];
- }
-
- surfaceinfo &surf = surfaces[i];
- w->slot = vslot.slot;
- w->vslot = &vslot;
- w->type = shadertype&SHADER_NORMALSLMS ? LM_BUMPMAP0|LM_ALPHA : LM_DIFFUSE|LM_ALPHA;
- w->bpp = 4;
- w->orient = i;
- w->rotate = vslot.rotation;
- int surftype = setupsurface(w, planes, numplanes, pos, n, numverts, curlitverts, true);
- switch(surftype)
- {
- case SURFACE_AMBIENT_TOP:
- surf = brightsurface;
- continue;
-
- case SURFACE_AMBIENT_BOTTOM:
- surf = brightbottomsurface;
- continue;
-
- case SURFACE_LIGHTMAP_BLEND:
- {
- if(surf.numverts == (LAYER_BLEND|numverts) &&
- surf.lmid[0] == surf.lmid[1] &&
- (surf.numverts&MAXFACEVERTS) == numverts &&
- !memcmp(curlitverts, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo)) &&
- lightmaps.inrange(surf.lmid[0]-LMID_RESERVED) &&
- lightmaps[surf.lmid[0]-LMID_RESERVED].type==w->type)
- {
- vertinfo *oldverts = c.ext->verts() + surf.verts;
- layoutinfo layout;
- layout.w = w->w;
- layout.h = w->h;
- layout.x = (oldverts[0].x - curlitverts[0].x)/((USHRT_MAX+1)/LM_PACKW);
- layout.y = (oldverts[0].y - curlitverts[0].y)/((USHRT_MAX+1)/LM_PACKH);
- if(LM_PACKW - layout.x >= w->w && LM_PACKH - layout.y >= w->h)
- {
- layout.lmid = surf.lmid[0];
- copylightmap(*w->lastlightmap, layout);
- updatelightmap(layout);
- surf.verts = numlitverts;
- numlitverts += numverts;
- continue;
- }
- }
-
- surf.verts = numlitverts;
- surf.numverts = LAYER_BLEND|numverts;
- numlitverts += numverts;
- layoutinfo layout;
- if(packlightmap(*w->lastlightmap, layout)) updatelightmap(layout);
- surf.lmid[0] = surf.lmid[1] = layout.lmid;
- ushort offsetx = layout.x*((USHRT_MAX+1)/LM_PACKW), offsety = layout.y*((USHRT_MAX+1)/LM_PACKH);
- loopk(numverts)
- {
- vertinfo &v = curlitverts[k];
- v.u += offsetx;
- v.v += offsety;
- }
- continue;
- }
- }
- }
-
- setsurfaces(c, surfaces, litverts, numlitverts);
- return true;
+ if(isempty(c) || c.material&MAT_ALPHA) return false;
+
+ int usefacemask = 0;
+ loopi(6) if(c.texture[i] != DEFAULT_SKY && lookupvslot(c.texture[i], false).layer)
+ usefacemask |= visibletris(c, i, co, size)<<(4*i);
+ if(!usefacemask) return false;
+
+ if(!setblendmaporigin(w->blendmapcache, co, size))
+ {
+ if(!c.ext) return false;
+ bool blends = false;
+ loopi(6) if(c.ext->surfaces[i].numverts&LAYER_BOTTOM)
+ {
+ c.ext->surfaces[i].brighten();
+ blends = true;
+ }
+ return blends;
+ }
+
+ w->firstlightmap = w->lastlightmap = w->curlightmaps = NULL;
+ w->bufstart = w->bufused = 0;
+ w->c = &c;
+
+ surfaceinfo surfaces[6];
+ vertinfo litverts[6*2*MAXFACEVERTS];
+ int numlitverts = 0;
+ memcpy(surfaces, c.ext ? c.ext->surfaces : brightsurfaces, sizeof(surfaces));
+ loopi(6)
+ {
+ int usefaces = usefacemask&0xF;
+ usefacemask >>= 4;
+ if(!usefaces)
+ {
+ surfaceinfo &surf = surfaces[i];
+ int numverts = surf.totalverts();
+ if(numverts)
+ {
+ memcpy(&litverts[numlitverts], c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
+ surf.verts = numlitverts;
+ numlitverts += numverts;
+ }
+ continue;
+ }
+
+ VSlot &vslot = lookupvslot(c.texture[i], false),
+ &layer = lookupvslot(vslot.layer, false);
+ Shader *shader = vslot.slot->shader;
+ int shadertype = shader->type | layer.slot->shader->type;
+
+ vertinfo *curlitverts = &litverts[numlitverts];
+ int numverts = 0;
+ ivec v[4];
+ genfaceverts(c, i, v);
+ int convex = flataxisface(c, i) ? 0 : faceconvexity(v),
+ order = usefaces&4 || convex < 0 ? 1 : 0;
+ ivec vo = ivec(co).mask(0xFFF).shl(3);
+ curlitverts[numverts++].set(v[order].mul(size).add(vo));
+ if(usefaces&1) curlitverts[numverts++].set(v[order+1].mul(size).add(vo));
+ curlitverts[numverts++].set(v[order+2].mul(size).add(vo));
+ if(usefaces&2) curlitverts[numverts++].set(v[(order+3)&3].mul(size).add(vo));
+
+ vec pos[4], n[4], po(ivec(co).mask(~0xFFF));
+ loopj(numverts) pos[j] = vec(curlitverts[j].getxyz()).mul(1.0f/8).add(po);
+
+ plane planes[2];
+ int numplanes = 0;
+ planes[numplanes++].toplane(pos[0], pos[1], pos[2]);
+ if(numverts < 4 || !convex) loopk(numverts) n[k] = planes[0];
+ else
+ {
+ planes[numplanes++].toplane(pos[0], pos[2], pos[3]);
+ vec avg = vec(planes[0]).add(planes[1]).normalize();
+ n[0] = avg;
+ n[1] = planes[0];
+ n[2] = avg;
+ for(int k = 3; k < numverts; k++) n[k] = planes[1];
+ }
+
+ surfaceinfo &surf = surfaces[i];
+ w->slot = vslot.slot;
+ w->vslot = &vslot;
+ w->type = shadertype&SHADER_NORMALSLMS ? LM_BUMPMAP0|LM_ALPHA : LM_DIFFUSE|LM_ALPHA;
+ w->bpp = 4;
+ w->orient = i;
+ w->rotate = vslot.rotation;
+ int surftype = setupsurface(w, planes, numplanes, pos, n, numverts, curlitverts, true);
+ switch(surftype)
+ {
+ case SURFACE_AMBIENT_TOP:
+ surf = brightsurface;
+ continue;
+
+ case SURFACE_AMBIENT_BOTTOM:
+ surf = brightbottomsurface;
+ continue;
+
+ case SURFACE_LIGHTMAP_BLEND:
+ {
+ if(surf.numverts == (LAYER_BLEND|numverts) &&
+ surf.lmid[0] == surf.lmid[1] &&
+ (surf.numverts&MAXFACEVERTS) == numverts &&
+ !memcmp(curlitverts, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo)) &&
+ lightmaps.inrange(surf.lmid[0]-LMID_RESERVED) &&
+ lightmaps[surf.lmid[0]-LMID_RESERVED].type==w->type)
+ {
+ vertinfo *oldverts = c.ext->verts() + surf.verts;
+ layoutinfo layout;
+ layout.w = w->w;
+ layout.h = w->h;
+ layout.x = (oldverts[0].x - curlitverts[0].x)/((USHRT_MAX+1)/LM_PACKW);
+ layout.y = (oldverts[0].y - curlitverts[0].y)/((USHRT_MAX+1)/LM_PACKH);
+ if(LM_PACKW - layout.x >= w->w && LM_PACKH - layout.y >= w->h)
+ {
+ layout.lmid = surf.lmid[0];
+ copylightmap(*w->lastlightmap, layout);
+ updatelightmap(layout);
+ surf.verts = numlitverts;
+ numlitverts += numverts;
+ continue;
+ }
+ }
+
+ surf.verts = numlitverts;
+ surf.numverts = LAYER_BLEND|numverts;
+ numlitverts += numverts;
+ layoutinfo layout;
+ if(packlightmap(*w->lastlightmap, layout)) updatelightmap(layout);
+ surf.lmid[0] = surf.lmid[1] = layout.lmid;
+ ushort offsetx = layout.x*((USHRT_MAX+1)/LM_PACKW), offsety = layout.y*((USHRT_MAX+1)/LM_PACKH);
+ loopk(numverts)
+ {
+ vertinfo &v = curlitverts[k];
+ v.u += offsetx;
+ v.v += offsety;
+ }
+ continue;
+ }
+ }
+ }
+
+ setsurfaces(c, surfaces, litverts, numlitverts);
+ return true;
}
static bool previewblends(lightmapworker *w, cube *c, const ivec &co, int size, const ivec &bo, const ivec &bs)
{
- bool changed = false;
- loopoctabox(co, size, bo, bs)
- {
- ivec o(i, co, size);
- cubeext *ext = c[i].ext;
- if(ext && ext->va && ext->va->hasmerges)
- {
- changed = true;
- destroyva(ext->va);
- ext->va = NULL;
- invalidatemerges(c[i], co, size, true);
- }
- if(c[i].children ? previewblends(w, c[i].children, o, size/2, bo, bs) : previewblends(w, c[i], o, size))
- {
- changed = true;
- ext = c[i].ext;
- if(ext && ext->va)
- {
- destroyva(ext->va);
- ext->va = NULL;
- }
- }
- }
- return changed;
+ bool changed = false;
+ loopoctabox(co, size, bo, bs)
+ {
+ ivec o(i, co, size);
+ cubeext *ext = c[i].ext;
+ if(ext && ext->va && ext->va->hasmerges)
+ {
+ changed = true;
+ destroyva(ext->va);
+ ext->va = NULL;
+ invalidatemerges(c[i], co, size, true);
+ }
+ if(c[i].children ? previewblends(w, c[i].children, o, size/2, bo, bs) : previewblends(w, c[i], o, size))
+ {
+ changed = true;
+ ext = c[i].ext;
+ if(ext && ext->va)
+ {
+ destroyva(ext->va);
+ ext->va = NULL;
+ }
+ }
+ }
+ return changed;
}
void previewblends(const ivec &bo, const ivec &bs)
{
- loadlayermasks();
- if(lightmapworkers.empty()) lightmapworkers.add(new lightmapworker);
- lightmapworkers[0]->reset();
- if(previewblends(lightmapworkers[0], worldroot, ivec(0, 0, 0), worldsize/2, bo, bs))
- commitchanges(true);
+ loadlayermasks();
+ if(lightmapworkers.empty()) lightmapworkers.add(new lightmapworker);
+ lightmapworkers[0]->reset();
+ if(previewblends(lightmapworkers[0], worldroot, ivec(0, 0, 0), worldsize/2, bo, bs))
+ commitchanges(true);
}
-
+
void cleanuplightmaps()
{
- loopv(lightmaps)
- {
- LightMap &lm = lightmaps[i];
- lm.tex = lm.offsetx = lm.offsety = -1;
- }
- loopv(lightmaptexs) glDeleteTextures(1, &lightmaptexs[i].id);
- lightmaptexs.shrink(0);
- if(progresstex) { glDeleteTextures(1, &progresstex); progresstex = 0; }
+ loopv(lightmaps)
+ {
+ LightMap &lm = lightmaps[i];
+ lm.tex = lm.offsetx = lm.offsety = -1;
+ }
+ loopv(lightmaptexs) glDeleteTextures(1, &lightmaptexs[i].id);
+ lightmaptexs.shrink(0);
+ if(progresstex) { glDeleteTextures(1, &progresstex); progresstex = 0; }
}
void resetlightmaps(bool fullclean)
{
- cleanuplightmaps();
- lightmaps.shrink(0);
- compressed.clear();
- clearlightcache();
- if(fullclean) while(lightmapworkers.length()) delete lightmapworkers.pop();
+ cleanuplightmaps();
+ lightmaps.shrink(0);
+ compressed.clear();
+ clearlightcache();
+ if(fullclean) while(lightmapworkers.length()) delete lightmapworkers.pop();
}
lightmapworker::lightmapworker()
{
- buf = new uchar[LIGHTMAPBUFSIZE];
- bufstart = bufused = 0;
- firstlightmap = lastlightmap = curlightmaps = NULL;
- ambient = new uchar[4*(LM_MAXW + 4)*(LM_MAXH + 4)];
- blur = new uchar[4*(LM_MAXW + 4)*(LM_MAXH + 4)];
- colordata = new vec[4*(LM_MAXW+1 + 4)*(LM_MAXH+1 + 4)];
- raydata = new vec[(LM_MAXW + 4)*(LM_MAXH + 4)];
- shadowraycache = newshadowraycache();
- blendmapcache = newblendmapcache();
- needspace = doneworking = false;
- spacecond = NULL;
- thread = NULL;
+ buf = new uchar[LIGHTMAPBUFSIZE];
+ bufstart = bufused = 0;
+ firstlightmap = lastlightmap = curlightmaps = NULL;
+ ambient = new uchar[4*(LM_MAXW + 4)*(LM_MAXH + 4)];
+ blur = new uchar[4*(LM_MAXW + 4)*(LM_MAXH + 4)];
+ colordata = new vec[4*(LM_MAXW+1 + 4)*(LM_MAXH+1 + 4)];
+ raydata = new vec[(LM_MAXW + 4)*(LM_MAXH + 4)];
+ shadowraycache = newshadowraycache();
+ blendmapcache = newblendmapcache();
+ needspace = doneworking = false;
+ spacecond = NULL;
+ thread = NULL;
}
lightmapworker::~lightmapworker()
{
- cleanupthread();
- delete[] buf;
- delete[] ambient;
- delete[] blur;
- delete[] colordata;
- delete[] raydata;
- freeshadowraycache(shadowraycache);
- freeblendmapcache(blendmapcache);
+ cleanupthread();
+ delete[] buf;
+ delete[] ambient;
+ delete[] blur;
+ delete[] colordata;
+ delete[] raydata;
+ freeshadowraycache(shadowraycache);
+ freeblendmapcache(blendmapcache);
}
void lightmapworker::cleanupthread()
{
- if(spacecond) { SDL_DestroyCond(spacecond); spacecond = NULL; }
- thread = NULL;
+ if(spacecond) { SDL_DestroyCond(spacecond); spacecond = NULL; }
+ thread = NULL;
}
void lightmapworker::reset()
{
- bufstart = bufused = 0;
- firstlightmap = lastlightmap = curlightmaps = NULL;
- needspace = doneworking = false;
- resetshadowraycache(shadowraycache);
+ bufstart = bufused = 0;
+ firstlightmap = lastlightmap = curlightmaps = NULL;
+ needspace = doneworking = false;
+ resetshadowraycache(shadowraycache);
}
bool lightmapworker::setupthread()
{
- if(!spacecond) spacecond = SDL_CreateCond();
- if(!spacecond) return false;
- thread = SDL_CreateThread(work, "lightmap worker", this);
- return thread!=NULL;
+ if(!spacecond) spacecond = SDL_CreateCond();
+ if(!spacecond) return false;
+ thread = SDL_CreateThread(work, "lightmap worker", this);
+ return thread!=NULL;
}
static Uint32 calclighttimer(Uint32 interval, void *param)
{
- check_calclight_progress = true;
- return interval;
+ check_calclight_progress = true;
+ return interval;
}
bool setlightmapquality(int quality)
{
- switch(quality)
- {
- case 1: lmshadows = 2; lmaa = 3; lerptjoints = 1; break;
- case 0: lmshadows = lmshadows_; lmaa = lmaa_; lerptjoints = lerptjoints_; break;
- case -1: lmshadows = 1; lmaa = 0; lerptjoints = 0; break;
- default: return false;
- }
- return true;
+ switch(quality)
+ {
+ case 1: lmshadows = 2; lmaa = 3; lerptjoints = 1; break;
+ case 0: lmshadows = lmshadows_; lmaa = lmaa_; lerptjoints = lerptjoints_; break;
+ case -1: lmshadows = 1; lmaa = 0; lerptjoints = 0; break;
+ default: return false;
+ }
+ return true;
}
VARP(lightthreads, 0, 0, 16);
@@ -2058,200 +2058,142 @@ VARP(lightthreads, 0, 0, 16);
static void cleanuplocks()
{
- FREELOCK(lightlock, SDL_DestroyMutex);
- FREELOCK(tasklock, SDL_DestroyMutex);
- FREELOCK(fullcond, SDL_DestroyCond);
- FREELOCK(emptycond, SDL_DestroyCond);
+ FREELOCK(lightlock, SDL_DestroyMutex);
+ FREELOCK(tasklock, SDL_DestroyMutex);
+ FREELOCK(fullcond, SDL_DestroyCond);
+ FREELOCK(emptycond, SDL_DestroyCond);
}
static void setupthreads(int numthreads)
{
- loopi(2) lightmaptasks[i].setsize(0);
- lightmapexts.setsize(0);
- packidx = allocidx = 0;
- lightmapping = numthreads;
- if(lightmapping > 1)
- {
- ALLOCLOCK(lightlock, SDL_CreateMutex);
- ALLOCLOCK(tasklock, SDL_CreateMutex);
- ALLOCLOCK(fullcond, SDL_CreateCond);
- ALLOCLOCK(emptycond, SDL_CreateCond);
- }
- while(lightmapworkers.length() < lightmapping) lightmapworkers.add(new lightmapworker);
- loopi(lightmapping)
- {
- lightmapworker *w = lightmapworkers[i];
- w->reset();
- if(lightmapping <= 1 || w->setupthread()) continue;
- w->cleanupthread();
- lightmapping = i >= 1 ? max(i, 2) : 1;
- break;
- }
- if(lightmapping <= 1) cleanuplocks();
+ loopi(2) lightmaptasks[i].setsize(0);
+ lightmapexts.setsize(0);
+ packidx = allocidx = 0;
+ lightmapping = numthreads;
+ if(lightmapping > 1)
+ {
+ ALLOCLOCK(lightlock, SDL_CreateMutex);
+ ALLOCLOCK(tasklock, SDL_CreateMutex);
+ ALLOCLOCK(fullcond, SDL_CreateCond);
+ ALLOCLOCK(emptycond, SDL_CreateCond);
+ }
+ while(lightmapworkers.length() < lightmapping) lightmapworkers.add(new lightmapworker);
+ loopi(lightmapping)
+ {
+ lightmapworker *w = lightmapworkers[i];
+ w->reset();
+ if(lightmapping <= 1 || w->setupthread()) continue;
+ w->cleanupthread();
+ lightmapping = i >= 1 ? max(i, 2) : 1;
+ break;
+ }
+ if(lightmapping <= 1) cleanuplocks();
}
static void cleanupthreads()
{
- processtasks(true);
- if(lightmapping > 1)
- {
- SDL_LockMutex(tasklock);
- loopv(lightmapworkers) lightmapworkers[i]->doneworking = true;
- SDL_CondBroadcast(fullcond);
- loopv(lightmapworkers)
- {
- lightmapworker *w = lightmapworkers[i];
- if(w->needspace && w->spacecond) SDL_CondSignal(w->spacecond);
- }
- SDL_UnlockMutex(tasklock);
- loopv(lightmapworkers)
- {
- lightmapworker *w = lightmapworkers[i];
- if(w->thread) SDL_WaitThread(w->thread, NULL);
- }
- }
- loopv(lightmapexts)
- {
- lightmapext &e = lightmapexts[i];
- setcubeext(*e.c, e.ext);
- }
- loopv(lightmapworkers) lightmapworkers[i]->cleanupthread();
- cleanuplocks();
- lightmapping = 0;
+ processtasks(true);
+ if(lightmapping > 1)
+ {
+ SDL_LockMutex(tasklock);
+ loopv(lightmapworkers) lightmapworkers[i]->doneworking = true;
+ SDL_CondBroadcast(fullcond);
+ loopv(lightmapworkers)
+ {
+ lightmapworker *w = lightmapworkers[i];
+ if(w->needspace && w->spacecond) SDL_CondSignal(w->spacecond);
+ }
+ SDL_UnlockMutex(tasklock);
+ loopv(lightmapworkers)
+ {
+ lightmapworker *w = lightmapworkers[i];
+ if(w->thread) SDL_WaitThread(w->thread, NULL);
+ }
+ }
+ loopv(lightmapexts)
+ {
+ lightmapext &e = lightmapexts[i];
+ setcubeext(*e.c, e.ext);
+ }
+ loopv(lightmapworkers) lightmapworkers[i]->cleanupthread();
+ cleanuplocks();
+ lightmapping = 0;
}
void calclight(int *quality)
{
- if(!setlightmapquality(*quality))
- {
- conoutf(CON_ERROR, "valid range for calclight quality is -1..1");
- return;
- }
- renderbackground("computing lightmaps... (esc to abort)");
- mpremip(true);
- optimizeblendmap();
- loadlayermasks();
- int numthreads = lightthreads > 0 ? lightthreads : numcpus;
- if(numthreads > 1) preloadusedmapmodels(false, true);
- resetlightmaps(false);
- clearsurfaces(worldroot);
- taskprogress = progress = 0;
- progresstexticks = 0;
- progresslightmap = -1;
- calclight_canceled = false;
- check_calclight_progress = false;
- SDL_TimerID timer = SDL_AddTimer(250, calclighttimer, NULL);
- Uint32 start = SDL_GetTicks();
- calcnormals(lerptjoints > 0);
- show_calclight_progress();
- setupthreads(numthreads);
- generatelightmaps(worldroot, ivec(0, 0, 0), worldsize >> 1);
- cleanupthreads();
- clearnormals();
- Uint32 end = SDL_GetTicks();
- if(timer) SDL_RemoveTimer(timer);
- uint total = 0, lumels = 0;
- loopv(lightmaps)
- {
- insertunlit(i);
- if(!editmode) lightmaps[i].finalize();
- total += lightmaps[i].lightmaps;
- lumels += lightmaps[i].lumels;
- }
- if(!editmode) compressed.clear();
- initlights();
- renderbackground("lighting done...");
- allchanged();
- if(calclight_canceled)
- conoutf("calclight aborted");
- else
- conoutf("generated %d lightmaps using %d%% of %d textures (%.1f seconds)",
- total,
- lightmaps.length() ? lumels * 100 / (lightmaps.length() * LM_PACKW * LM_PACKH) : 0,
- lightmaps.length(),
- (end - start) / 1000.0f);
+ if(!setlightmapquality(*quality))
+ {
+ conoutf(CON_ERROR, "valid range for calclight quality is -1..1");
+ return;
+ }
+ renderbackground("computing lightmaps... (esc to abort)");
+ mpremip(true);
+ optimizeblendmap();
+ loadlayermasks();
+ int numthreads = lightthreads > 0 ? lightthreads : numcpus;
+ if(numthreads > 1) preloadusedmapmodels(false, true);
+ resetlightmaps(false);
+ clearsurfaces(worldroot);
+ taskprogress = progress = 0;
+ progresstexticks = 0;
+ progresslightmap = -1;
+ calclight_canceled = false;
+ check_calclight_progress = false;
+ SDL_TimerID timer = SDL_AddTimer(250, calclighttimer, NULL);
+ Uint32 start = SDL_GetTicks();
+ calcnormals(lerptjoints > 0);
+ show_calclight_progress();
+ setupthreads(numthreads);
+ generatelightmaps(worldroot, ivec(0, 0, 0), worldsize >> 1);
+ cleanupthreads();
+ clearnormals();
+ Uint32 end = SDL_GetTicks();
+ if(timer) SDL_RemoveTimer(timer);
+ uint total = 0, lumels = 0;
+ loopv(lightmaps)
+ {
+ insertunlit(i);
+ if(!editmode) lightmaps[i].finalize();
+ total += lightmaps[i].lightmaps;
+ lumels += lightmaps[i].lumels;
+ }
+ if(!editmode) compressed.clear();
+ initlights();
+ renderbackground("lighting done...");
+ allchanged();
+ if(calclight_canceled)
+ conoutf("calclight aborted");
+ else
+ conoutf("generated %d lightmaps using %d%% of %d textures (%.1f seconds)",
+ total,
+ lightmaps.length() ? lumels * 100 / (lightmaps.length() * LM_PACKW * LM_PACKH) : 0,
+ lightmaps.length(),
+ (end - start) / 1000.0f);
}
COMMAND(calclight, "i");
-VAR(patchnormals, 0, 0, 1);
-
-void patchlight(int *quality)
-{
- if(noedit(true)) return;
- if(!setlightmapquality(*quality))
- {
- conoutf(CON_ERROR, "valid range for patchlight quality is -1..1");
- return;
- }
- renderbackground("patching lightmaps... (esc to abort)");
- loadlayermasks();
- int numthreads = lightthreads > 0 ? lightthreads : numcpus;
- if(numthreads > 1) preloadusedmapmodels(false, true);
- cleanuplightmaps();
- taskprogress = progress = 0;
- progresstexticks = 0;
- progresslightmap = -1;
- int total = 0, lumels = 0;
- loopv(lightmaps)
- {
- if((lightmaps[i].type&LM_TYPE) != LM_BUMPMAP1) progresslightmap = i;
- total -= lightmaps[i].lightmaps;
- lumels -= lightmaps[i].lumels;
- }
- calclight_canceled = false;
- check_calclight_progress = false;
- SDL_TimerID timer = SDL_AddTimer(250, calclighttimer, NULL);
- if(patchnormals) renderprogress(0, "computing normals...");
- Uint32 start = SDL_GetTicks();
- if(patchnormals) calcnormals(lerptjoints > 0);
- show_calclight_progress();
- setupthreads(numthreads);
- generatelightmaps(worldroot, ivec(0, 0, 0), worldsize >> 1);
- cleanupthreads();
- if(patchnormals) clearnormals();
- Uint32 end = SDL_GetTicks();
- if(timer) SDL_RemoveTimer(timer);
- loopv(lightmaps)
- {
- total += lightmaps[i].lightmaps;
- lumels += lightmaps[i].lumels;
- }
- initlights();
- renderbackground("lighting done...");
- allchanged();
- if(calclight_canceled)
- conoutf("patchlight aborted");
- else
- conoutf("patched %d lightmaps using %d%% of %d textures (%.1f seconds)",
- total,
- lightmaps.length() ? lumels * 100 / (lightmaps.length() * LM_PACKW * LM_PACKH) : 0,
- lightmaps.length(),
- (end - start) / 1000.0f);
-}
-
-COMMAND(patchlight, "i");
-
void clearlightmaps()
{
- if(noedit(true)) return;
- renderprogress(0, "clearing lightmaps...");
- resetlightmaps(false);
- clearsurfaces(worldroot);
- initlights();
- allchanged();
+ if(noedit(true)) return;
+ renderprogress(0, "clearing lightmaps...");
+ resetlightmaps(false);
+ clearsurfaces(worldroot);
+ initlights();
+ allchanged();
}
COMMAND(clearlightmaps, "");
void setfullbrightlevel(int fullbrightlevel)
{
- if(lightmaptexs.length() > LMID_BRIGHT)
- {
- uchar bright[3] = { uchar(fullbrightlevel), uchar(fullbrightlevel), uchar(fullbrightlevel) };
- createtexture(lightmaptexs[LMID_BRIGHT].id, 1, 1, bright, 0, 1);
- }
- initlights();
+ if(lightmaptexs.length() > LMID_BRIGHT)
+ {
+ uchar bright[3] = { uchar(fullbrightlevel), uchar(fullbrightlevel), uchar(fullbrightlevel) };
+ createtexture(lightmaptexs[LMID_BRIGHT].id, 1, 1, bright, 0, 1);
+ }
+ initlights();
}
VARF(fullbright, 0, 0, 1, if(lightmaptexs.length()) { initlights(); lightents(); });
@@ -2259,183 +2201,68 @@ VARF(fullbrightlevel, 0, 128, 255, setfullbrightlevel(fullbrightlevel));
vector<LightMapTexture> lightmaptexs;
-static void rotatenormals(LightMap &lmlv, int x, int y, int w, int h, bool flipx, bool flipy, bool swapxy)
-{
- uchar *lv = lmlv.data + 3*(y*LM_PACKW + x);
- int stride = 3*(LM_PACKW-w);
- loopi(h)
- {
- loopj(w)
- {
- if(flipx) lv[0] = 255 - lv[0];
- if(flipy) lv[1] = 255 - lv[1];
- if(swapxy) swap(lv[0], lv[1]);
- lv += 3;
- }
- lv += stride;
- }
-}
-
-static void rotatenormals(cube *c)
-{
- loopi(8)
- {
- cube &ch = c[i];
- if(ch.children)
- {
- rotatenormals(ch.children);
- continue;
- }
- else if(!ch.ext) continue;
- loopj(6) if(lightmaps.inrange(ch.ext->surfaces[j].lmid[0]+1-LMID_RESERVED))
- {
- VSlot &vslot = lookupvslot(ch.texture[j], false);
- if(!vslot.rotation) continue;
- surfaceinfo &surface = ch.ext->surfaces[j];
- int numverts = surface.numverts&MAXFACEVERTS;
- if(!numverts) continue;
- LightMap &lmlv = lightmaps[surface.lmid[0]+1-LMID_RESERVED];
- if((lmlv.type&LM_TYPE)!=LM_BUMPMAP1) continue;
- ushort x1 = USHRT_MAX, y1 = USHRT_MAX, x2 = 0, y2 = 0;
- vertinfo *verts = ch.ext->verts() + surface.verts;
- loopk(numverts)
- {
- vertinfo &v = verts[k];
- x1 = min(x1, v.u);
- y1 = min(y1, v.u);
- x2 = max(x2, v.u);
- y2 = max(y2, v.v);
- }
- if(x1 > x2 || y1 > y2) continue;
- x1 /= (USHRT_MAX+1)/LM_PACKW;
- y1 /= (USHRT_MAX+1)/LM_PACKH;
- x2 /= (USHRT_MAX+1)/LM_PACKW;
- y2 /= (USHRT_MAX+1)/LM_PACKH;
- const texrotation &r = texrotations[vslot.rotation < 4 ? 4-vslot.rotation : vslot.rotation];
- rotatenormals(lmlv, x1, y1, x2-x1, y1-y1, r.flipx, r.flipy, r.swapxy);
- }
- }
-}
-
-void fixlightmapnormals()
-{
- rotatenormals(worldroot);
-}
-
-void fixrotatedlightmaps(cube &c, const ivec &co, int size)
-{
- if(c.children)
- {
- loopi(8) fixrotatedlightmaps(c.children[i], ivec(i, co, size>>1), size>>1);
- return;
- }
- if(!c.ext) return;
- loopi(6)
- {
- if(c.merged&(1<<i)) continue;
- surfaceinfo &surf = c.ext->surfaces[i];
- int numverts = surf.numverts&MAXFACEVERTS;
- if(numverts!=4 || (surf.lmid[0] < LMID_RESERVED && surf.lmid[1] < LMID_RESERVED)) continue;
- vertinfo *verts = c.ext->verts() + surf.verts;
- int vis = visibletris(c, i, co, size);
- if(!vis || vis==3) continue;
- if((verts[0].u != verts[1].u || verts[0].v != verts[1].v) &&
- (verts[0].u != verts[3].u || verts[0].v != verts[3].v) &&
- (verts[2].u != verts[1].u || verts[2].v != verts[1].v) &&
- (verts[2].u != verts[3].u || verts[2].v != verts[3].v))
- continue;
- if(vis&4)
- {
- vertinfo tmp = verts[0];
- verts[0].x = verts[1].x; verts[0].y = verts[1].y; verts[0].z = verts[1].z;
- verts[1].x = verts[2].x; verts[1].y = verts[2].y; verts[1].z = verts[2].z;
- verts[2].x = verts[3].x; verts[2].y = verts[3].y; verts[2].z = verts[3].z;
- verts[3].x = tmp.x; verts[3].y = tmp.y; verts[3].z = tmp.z;
- if(surf.numverts&LAYER_DUP) loopk(4)
- {
- vertinfo &v = verts[k], &b = verts[k+4];
- b.x = v.x;
- b.y = v.y;
- b.z = v.z;
- }
- }
- surf.numverts = (surf.numverts & ~MAXFACEVERTS) | 3;
- if(vis&2)
- {
- verts[1] = verts[2]; verts[2] = verts[3];
- if(surf.numverts&LAYER_DUP) { verts[3] = verts[4]; verts[4] = verts[6]; verts[5] = verts[7]; }
- }
- else if(surf.numverts&LAYER_DUP) { verts[3] = verts[4]; verts[4] = verts[5]; verts[5] = verts[6]; }
- }
-}
-
-void fixrotatedlightmaps()
-{
- loopi(8) fixrotatedlightmaps(worldroot[i], ivec(i, ivec(0, 0, 0), worldsize>>1), worldsize>>1);
-}
-
static void copylightmap(LightMap &lm, uchar *dst, size_t stride)
{
- const uchar *c = lm.data;
- loopi(LM_PACKH)
- {
- memcpy(dst, c, lm.bpp*LM_PACKW);
- c += lm.bpp*LM_PACKW;
- dst += stride;
- }
+ const uchar *c = lm.data;
+ loopi(LM_PACKH)
+ {
+ memcpy(dst, c, lm.bpp*LM_PACKW);
+ c += lm.bpp*LM_PACKW;
+ dst += stride;
+ }
}
void genreservedlightmaptexs()
{
- while(lightmaptexs.length() < LMID_RESERVED)
- {
- LightMapTexture &tex = lightmaptexs.add();
- tex.type = lightmaptexs.length()&1 ? LM_DIFFUSE : LM_BUMPMAP1;
- glGenTextures(1, &tex.id);
- }
- uchar unlit[3] = { ambientcolor[0], ambientcolor[1], ambientcolor[2] };
- createtexture(lightmaptexs[LMID_AMBIENT].id, 1, 1, unlit, 0, 1);
- bvec front(128, 128, 255);
- createtexture(lightmaptexs[LMID_AMBIENT1].id, 1, 1, &front, 0, 1);
- uchar bright[3] = { uchar(fullbrightlevel), uchar(fullbrightlevel), uchar(fullbrightlevel) };
- createtexture(lightmaptexs[LMID_BRIGHT].id, 1, 1, bright, 0, 1);
- createtexture(lightmaptexs[LMID_BRIGHT1].id, 1, 1, &front, 0, 1);
- uchar dark[3] = { 0, 0, 0 };
- createtexture(lightmaptexs[LMID_DARK].id, 1, 1, dark, 0, 1);
- createtexture(lightmaptexs[LMID_DARK1].id, 1, 1, &front, 0, 1);
+ while(lightmaptexs.length() < LMID_RESERVED)
+ {
+ LightMapTexture &tex = lightmaptexs.add();
+ tex.type = lightmaptexs.length()&1 ? LM_DIFFUSE : LM_BUMPMAP1;
+ glGenTextures(1, &tex.id);
+ }
+ uchar unlit[3] = { ambientcolor[0], ambientcolor[1], ambientcolor[2] };
+ createtexture(lightmaptexs[LMID_AMBIENT].id, 1, 1, unlit, 0, 1);
+ bvec front(128, 128, 255);
+ createtexture(lightmaptexs[LMID_AMBIENT1].id, 1, 1, &front, 0, 1);
+ uchar bright[3] = { uchar(fullbrightlevel), uchar(fullbrightlevel), uchar(fullbrightlevel) };
+ createtexture(lightmaptexs[LMID_BRIGHT].id, 1, 1, bright, 0, 1);
+ createtexture(lightmaptexs[LMID_BRIGHT1].id, 1, 1, &front, 0, 1);
+ uchar dark[3] = { 0, 0, 0 };
+ createtexture(lightmaptexs[LMID_DARK].id, 1, 1, dark, 0, 1);
+ createtexture(lightmaptexs[LMID_DARK1].id, 1, 1, &front, 0, 1);
}
static void findunlit(int i)
{
- LightMap &lm = lightmaps[i];
- if(lm.unlitx>=0) return;
- else if((lm.type&LM_TYPE)==LM_BUMPMAP0)
- {
- if(i+1>=lightmaps.length() || (lightmaps[i+1].type&LM_TYPE)!=LM_BUMPMAP1) return;
- }
- else if((lm.type&LM_TYPE)!=LM_DIFFUSE) return;
- uchar *data = lm.data;
- loop(y, 2) loop(x, LM_PACKW)
- {
- if(!data[0] && !data[1] && !data[2])
- {
- memcpy(data, ambientcolor.v, 3);
- if((lm.type&LM_TYPE)==LM_BUMPMAP0) ((bvec *)lightmaps[i+1].data)[y*LM_PACKW + x] = bvec(128, 128, 255);
- lm.unlitx = x;
- lm.unlity = y;
- return;
- }
- if(data[0]==ambientcolor[0] && data[1]==ambientcolor[1] && data[2]==ambientcolor[2])
- {
- if((lm.type&LM_TYPE)!=LM_BUMPMAP0 || ((bvec *)lightmaps[i+1].data)[y*LM_PACKW + x] == bvec(128, 128, 255))
- {
- lm.unlitx = x;
- lm.unlity = y;
- return;
- }
- }
- data += lm.bpp;
- }
+ LightMap &lm = lightmaps[i];
+ if(lm.unlitx>=0) return;
+ else if((lm.type&LM_TYPE)==LM_BUMPMAP0)
+ {
+ if(i+1>=lightmaps.length() || (lightmaps[i+1].type&LM_TYPE)!=LM_BUMPMAP1) return;
+ }
+ else if((lm.type&LM_TYPE)!=LM_DIFFUSE) return;
+ uchar *data = lm.data;
+ loop(y, 2) loop(x, LM_PACKW)
+ {
+ if(!data[0] && !data[1] && !data[2])
+ {
+ memcpy(data, ambientcolor.v, 3);
+ if((lm.type&LM_TYPE)==LM_BUMPMAP0) ((bvec *)lightmaps[i+1].data)[y*LM_PACKW + x] = bvec(128, 128, 255);
+ lm.unlitx = x;
+ lm.unlity = y;
+ return;
+ }
+ if(data[0]==ambientcolor[0] && data[1]==ambientcolor[1] && data[2]==ambientcolor[2])
+ {
+ if((lm.type&LM_TYPE)!=LM_BUMPMAP0 || ((bvec *)lightmaps[i+1].data)[y*LM_PACKW + x] == bvec(128, 128, 255))
+ {
+ lm.unlitx = x;
+ lm.unlity = y;
+ return;
+ }
+ }
+ data += lm.bpp;
+ }
}
VARF(roundlightmaptex, 0, 4, 16, { cleanuplightmaps(); initlights(); allchanged(); });
@@ -2443,286 +2270,286 @@ VARF(batchlightmaps, 0, 4, 256, { cleanuplightmaps(); initlights(); allchanged()
void genlightmaptexs(int flagmask, int flagval)
{
- if(lightmaptexs.length() < LMID_RESERVED) genreservedlightmaptexs();
-
- int remaining[LM_TYPE+1] = { 0 }, total = 0;
- loopv(lightmaps)
- {
- LightMap &lm = lightmaps[i];
- if(lm.tex >= 0 || (lm.type&flagmask)!=flagval) continue;
- int type = lm.type&LM_TYPE;
- remaining[type]++;
- total++;
- if(lm.unlitx < 0) findunlit(i);
- }
-
- int sizelimit = (maxtexsize ? min(maxtexsize, hwtexsize) : hwtexsize)/max(LM_PACKW, LM_PACKH);
- sizelimit = min(batchlightmaps, sizelimit*sizelimit);
- while(total)
- {
- int type = LM_DIFFUSE;
- LightMap *firstlm = NULL;
- loopv(lightmaps)
- {
- LightMap &lm = lightmaps[i];
- if(lm.tex >= 0 || (lm.type&flagmask) != flagval) continue;
- type = lm.type&LM_TYPE;
- firstlm = &lm;
- break;
- }
- if(!firstlm) break;
- int used = 0, uselimit = min(remaining[type], sizelimit);
- do used++; while((1<<used) <= uselimit);
- used--;
- int oldval = remaining[type];
- remaining[type] -= 1<<used;
- if(remaining[type] && (2<<used) <= min(roundlightmaptex, sizelimit))
- {
- remaining[type] -= min(remaining[type], 1<<used);
- used++;
- }
- total -= oldval - remaining[type];
- LightMapTexture &tex = lightmaptexs.add();
- tex.type = firstlm->type;
- tex.w = LM_PACKW<<((used+1)/2);
- tex.h = LM_PACKH<<(used/2);
- int bpp = firstlm->bpp;
- uchar *data = used ? new uchar[bpp*tex.w*tex.h] : NULL;
- int offsetx = 0, offsety = 0;
- loopv(lightmaps)
- {
- LightMap &lm = lightmaps[i];
- if(lm.tex >= 0 || (lm.type&flagmask) != flagval || (lm.type&LM_TYPE) != type) continue;
-
- lm.tex = lightmaptexs.length()-1;
- lm.offsetx = offsetx;
- lm.offsety = offsety;
- if(tex.unlitx < 0 && lm.unlitx >= 0)
- {
- tex.unlitx = offsetx + lm.unlitx;
- tex.unlity = offsety + lm.unlity;
- }
-
- if(data) copylightmap(lm, &data[bpp*(offsety*tex.w + offsetx)], bpp*tex.w);
-
- offsetx += LM_PACKW;
- if(offsetx >= tex.w) { offsetx = 0; offsety += LM_PACKH; }
- if(offsety >= tex.h) break;
- }
-
- glGenTextures(1, &tex.id);
- createtexture(tex.id, tex.w, tex.h, data ? data : firstlm->data, 3, 1, bpp==4 ? GL_RGBA : GL_RGB);
- if(data) delete[] data;
- }
+ if(lightmaptexs.length() < LMID_RESERVED) genreservedlightmaptexs();
+
+ int remaining[LM_TYPE+1] = { 0 }, total = 0;
+ loopv(lightmaps)
+ {
+ LightMap &lm = lightmaps[i];
+ if(lm.tex >= 0 || (lm.type&flagmask)!=flagval) continue;
+ int type = lm.type&LM_TYPE;
+ remaining[type]++;
+ total++;
+ if(lm.unlitx < 0) findunlit(i);
+ }
+
+ int sizelimit = (maxtexsize ? min(maxtexsize, hwtexsize) : hwtexsize)/max(LM_PACKW, LM_PACKH);
+ sizelimit = min(batchlightmaps, sizelimit*sizelimit);
+ while(total)
+ {
+ int type = LM_DIFFUSE;
+ LightMap *firstlm = NULL;
+ loopv(lightmaps)
+ {
+ LightMap &lm = lightmaps[i];
+ if(lm.tex >= 0 || (lm.type&flagmask) != flagval) continue;
+ type = lm.type&LM_TYPE;
+ firstlm = &lm;
+ break;
+ }
+ if(!firstlm) break;
+ int used = 0, uselimit = min(remaining[type], sizelimit);
+ do used++; while((1<<used) <= uselimit);
+ used--;
+ int oldval = remaining[type];
+ remaining[type] -= 1<<used;
+ if(remaining[type] && (2<<used) <= min(roundlightmaptex, sizelimit))
+ {
+ remaining[type] -= min(remaining[type], 1<<used);
+ used++;
+ }
+ total -= oldval - remaining[type];
+ LightMapTexture &tex = lightmaptexs.add();
+ tex.type = firstlm->type;
+ tex.w = LM_PACKW<<((used+1)/2);
+ tex.h = LM_PACKH<<(used/2);
+ int bpp = firstlm->bpp;
+ uchar *data = used ? new uchar[bpp*tex.w*tex.h] : NULL;
+ int offsetx = 0, offsety = 0;
+ loopv(lightmaps)
+ {
+ LightMap &lm = lightmaps[i];
+ if(lm.tex >= 0 || (lm.type&flagmask) != flagval || (lm.type&LM_TYPE) != type) continue;
+
+ lm.tex = lightmaptexs.length()-1;
+ lm.offsetx = offsetx;
+ lm.offsety = offsety;
+ if(tex.unlitx < 0 && lm.unlitx >= 0)
+ {
+ tex.unlitx = offsetx + lm.unlitx;
+ tex.unlity = offsety + lm.unlity;
+ }
+
+ if(data) copylightmap(lm, &data[bpp*(offsety*tex.w + offsetx)], bpp*tex.w);
+
+ offsetx += LM_PACKW;
+ if(offsetx >= tex.w) { offsetx = 0; offsety += LM_PACKH; }
+ if(offsety >= tex.h) break;
+ }
+
+ glGenTextures(1, &tex.id);
+ createtexture(tex.id, tex.w, tex.h, data ? data : firstlm->data, 3, 1, bpp==4 ? GL_RGBA : GL_RGB);
+ if(data) delete[] data;
+ }
}
bool brightengeom = false, shouldlightents = false;
void clearlights()
{
- clearlightcache();
- const vector<extentity *> &ents = entities::getents();
- loopv(ents)
- {
- extentity &e = *ents[i];
- e.light.color = vec(1, 1, 1);
- e.light.dir = vec(0, 0, 1);
- }
- shouldlightents = false;
+ clearlightcache();
+ const vector<extentity *> &ents = entities::getents();
+ loopv(ents)
+ {
+ extentity &e = *ents[i];
+ e.light.color = vec(1, 1, 1);
+ e.light.dir = vec(0, 0, 1);
+ }
+ shouldlightents = false;
- genlightmaptexs(LM_ALPHA, 0);
- genlightmaptexs(LM_ALPHA, LM_ALPHA);
- brightengeom = true;
+ genlightmaptexs(LM_ALPHA, 0);
+ genlightmaptexs(LM_ALPHA, LM_ALPHA);
+ brightengeom = true;
}
void lightent(extentity &e, float height)
{
- if(e.type==ET_LIGHT) return;
- float ambient = 0.0f;
- if(e.type==ET_MAPMODEL)
- {
- model *m = loadmodel(NULL, e.attr2);
- if(m) height = m->above()*0.75f;
- }
- else if(e.type>=ET_GAMESPECIFIC) ambient = 0.4f;
- vec target(e.o.x, e.o.y, e.o.z + height);
- lightreaching(target, e.light.color, e.light.dir, false, &e, ambient);
+ if(e.type==ET_LIGHT) return;
+ float ambient = 0.0f;
+ if(e.type==ET_MAPMODEL)
+ {
+ model *m = loadmodel(NULL, e.attr2);
+ if(m) height = m->above()*0.75f;
+ }
+ else if(e.type>=ET_GAMESPECIFIC) ambient = 0.4f;
+ vec target(e.o.x, e.o.y, e.o.z + height);
+ lightreaching(target, e.light.color, e.light.dir, false, &e, ambient);
}
void lightents(bool force)
{
- if(!force && !shouldlightents) return;
+ if(!force && !shouldlightents) return;
- const vector<extentity *> &ents = entities::getents();
- loopv(ents) lightent(*ents[i]);
+ const vector<extentity *> &ents = entities::getents();
+ loopv(ents) lightent(*ents[i]);
- shouldlightents = false;
+ shouldlightents = false;
}
void initlights()
{
- if((fullbright && editmode) || lightmaps.empty())
- {
- clearlights();
- return;
- }
+ if((fullbright && editmode) || lightmaps.empty())
+ {
+ clearlights();
+ return;
+ }
- clearlightcache();
- genlightmaptexs(LM_ALPHA, 0);
- genlightmaptexs(LM_ALPHA, LM_ALPHA);
- brightengeom = false;
- shouldlightents = true;
+ clearlightcache();
+ genlightmaptexs(LM_ALPHA, 0);
+ genlightmaptexs(LM_ALPHA, LM_ALPHA);
+ brightengeom = false;
+ shouldlightents = true;
}
static inline void fastskylight(const vec &o, float tolerance, uchar *skylight, int flags = RAY_ALPHAPOLY, extentity *t = NULL, bool fast = false)
{
- flags |= RAY_SHADOW;
- if(skytexturelight) flags |= RAY_SKIPSKY | (useskytexture ? RAY_SKYTEX : 0);
- if(fast)
- {
- static const vec ray(0, 0, 1);
- if(shadowray(vec(ray).mul(tolerance).add(o), ray, 1e16f, flags, t)>1e15f)
- memcpy(skylight, skylightcolor.v, 3);
- else memcpy(skylight, ambientcolor.v, 3);
- }
- else
- {
- static const vec rays[5] =
- {
- vec(cosf(66*RAD)*cosf(65*RAD), sinf(66*RAD)*cosf(65*RAD), sinf(65*RAD)),
- vec(cosf(156*RAD)*cosf(65*RAD), sinf(156*RAD)*cosf(65*RAD), sinf(65*RAD)),
- vec(cosf(246*RAD)*cosf(65*RAD), sinf(246*RAD)*cosf(65*RAD), sinf(65*RAD)),
- vec(cosf(336*RAD)*cosf(65*RAD), sinf(336*RAD)*cosf(65*RAD), sinf(65*RAD)),
- vec(0, 0, 1),
- };
- int hit = 0;
- loopi(5) if(shadowray(vec(rays[i]).mul(tolerance).add(o), rays[i], 1e16f, flags, t)>1e15f) hit++;
- loopk(3) skylight[k] = uchar(ambientcolor[k] + (max(skylightcolor[k], ambientcolor[k]) - ambientcolor[k])*hit/5.0f);
- }
+ flags |= RAY_SHADOW;
+ if(skytexturelight) flags |= RAY_SKIPSKY | (useskytexture ? RAY_SKYTEX : 0);
+ if(fast)
+ {
+ static const vec ray(0, 0, 1);
+ if(shadowray(vec(ray).mul(tolerance).add(o), ray, 1e16f, flags, t)>1e15f)
+ memcpy(skylight, skylightcolor.v, 3);
+ else memcpy(skylight, ambientcolor.v, 3);
+ }
+ else
+ {
+ static const vec rays[5] =
+ {
+ vec(cosf(66*RAD)*cosf(65*RAD), sinf(66*RAD)*cosf(65*RAD), sinf(65*RAD)),
+ vec(cosf(156*RAD)*cosf(65*RAD), sinf(156*RAD)*cosf(65*RAD), sinf(65*RAD)),
+ vec(cosf(246*RAD)*cosf(65*RAD), sinf(246*RAD)*cosf(65*RAD), sinf(65*RAD)),
+ vec(cosf(336*RAD)*cosf(65*RAD), sinf(336*RAD)*cosf(65*RAD), sinf(65*RAD)),
+ vec(0, 0, 1),
+ };
+ int hit = 0;
+ loopi(5) if(shadowray(vec(rays[i]).mul(tolerance).add(o), rays[i], 1e16f, flags, t)>1e15f) hit++;
+ loopk(3) skylight[k] = uchar(ambientcolor[k] + (max(skylightcolor[k], ambientcolor[k]) - ambientcolor[k])*hit/5.0f);
+ }
}
void lightreaching(const vec &target, vec &color, vec &dir, bool fast, extentity *t, float ambient)
{
- if((fullbright && editmode) || lightmaps.empty())
- {
- color = vec(1, 1, 1);
- dir = vec(0, 0, 1);
- return;
- }
-
- color = dir = vec(0, 0, 0);
- const vector<extentity *> &ents = entities::getents();
- const vector<int> &lights = checklightcache(int(target.x), int(target.y));
- loopv(lights)
- {
- extentity &e = *ents[lights[i]];
- if(e.type != ET_LIGHT)
- continue;
-
- vec ray(target);
- ray.sub(e.o);
- float mag = ray.magnitude();
- if(e.attr1 && mag >= float(e.attr1))
- continue;
-
- if(mag < 1e-4f) ray = vec(0, 0, -1);
- else
- {
- ray.div(mag);
- if(shadowray(e.o, ray, mag, RAY_SHADOW | RAY_POLY, t) < mag)
- continue;
- }
-
- float intensity = 1;
- if(e.attr1)
- intensity -= mag / float(e.attr1);
- if(e.attached && e.attached->type==ET_SPOTLIGHT)
- {
- vec spot = vec(e.attached->o).sub(e.o).normalize();
- float maxatten = sincos360[clamp(int(e.attached->attr1), 1, 89)].x, spotatten = (ray.dot(spot) - maxatten) / (1 - maxatten);
- if(spotatten <= 0) continue;
- intensity *= spotatten;
- }
-
- //if(target==player->o)
- //{
- // conoutf(CON_DEBUG, "%d - %f %f", i, intensity, mag);
- //}
-
- vec lightcol = vec(e.attr2, e.attr3, e.attr4).mul(1.0f/255);
- color.add(vec(lightcol).mul(intensity));
- dir.add(vec(ray).mul(-intensity*lightcol.x*lightcol.y*lightcol.z));
- }
- if(sunlight && shadowray(target, sunlightdir, 1e16f, RAY_SHADOW | RAY_POLY | (skytexturelight ? RAY_SKIPSKY | (useskytexture ? RAY_SKYTEX : 0) : 0), t) > 1e15f)
- {
- vec lightcol = vec(sunlightcolor.x, sunlightcolor.y, sunlightcolor.z).mul(sunlightscale/255);
- color.add(lightcol);
- dir.add(vec(sunlightdir).mul(lightcol.x*lightcol.y*lightcol.z));
- }
- if(hasskylight())
- {
- uchar skylight[3];
- if(t) calcskylight(NULL, target, vec(0, 0, 0), 0.5f, skylight, RAY_POLY, t);
- else fastskylight(target, 0.5f, skylight, RAY_POLY, t, fast);
- loopk(3) color[k] = min(1.5f, max(max(skylight[k]/255.0f, ambient), color[k]));
- }
- else loopk(3) color[k] = min(1.5f, max(max(ambientcolor[k]/255.0f, ambient), color[k]));
- if(dir.iszero()) dir = vec(0, 0, 1);
- else dir.normalize();
+ if((fullbright && editmode) || lightmaps.empty())
+ {
+ color = vec(1, 1, 1);
+ dir = vec(0, 0, 1);
+ return;
+ }
+
+ color = dir = vec(0, 0, 0);
+ const vector<extentity *> &ents = entities::getents();
+ const vector<int> &lights = checklightcache(int(target.x), int(target.y));
+ loopv(lights)
+ {
+ extentity &e = *ents[lights[i]];
+ if(e.type != ET_LIGHT)
+ continue;
+
+ vec ray(target);
+ ray.sub(e.o);
+ float mag = ray.magnitude();
+ if(e.attr1 && mag >= float(e.attr1))
+ continue;
+
+ if(mag < 1e-4f) ray = vec(0, 0, -1);
+ else
+ {
+ ray.div(mag);
+ if(shadowray(e.o, ray, mag, RAY_SHADOW | RAY_POLY, t) < mag)
+ continue;
+ }
+
+ float intensity = 1;
+ if(e.attr1)
+ intensity -= mag / float(e.attr1);
+ if(e.attached && e.attached->type==ET_SPOTLIGHT)
+ {
+ vec spot = vec(e.attached->o).sub(e.o).normalize();
+ float maxatten = sincos360[clamp(int(e.attached->attr1), 1, 89)].x, spotatten = (ray.dot(spot) - maxatten) / (1 - maxatten);
+ if(spotatten <= 0) continue;
+ intensity *= spotatten;
+ }
+
+ //if(target==player->o)
+ //{
+ // conoutf(CON_DEBUG, "%d - %f %f", i, intensity, mag);
+ //}
+
+ vec lightcol = vec(e.attr2, e.attr3, e.attr4).mul(1.0f/255);
+ color.add(vec(lightcol).mul(intensity));
+ dir.add(vec(ray).mul(-intensity*lightcol.x*lightcol.y*lightcol.z));
+ }
+ if(sunlight && shadowray(target, sunlightdir, 1e16f, RAY_SHADOW | RAY_POLY | (skytexturelight ? RAY_SKIPSKY | (useskytexture ? RAY_SKYTEX : 0) : 0), t) > 1e15f)
+ {
+ vec lightcol = vec(sunlightcolor.x, sunlightcolor.y, sunlightcolor.z).mul(sunlightscale/255);
+ color.add(lightcol);
+ dir.add(vec(sunlightdir).mul(lightcol.x*lightcol.y*lightcol.z));
+ }
+ if(hasskylight())
+ {
+ uchar skylight[3];
+ if(t) calcskylight(NULL, target, vec(0, 0, 0), 0.5f, skylight, RAY_POLY, t);
+ else fastskylight(target, 0.5f, skylight, RAY_POLY, t, fast);
+ loopk(3) color[k] = min(1.5f, max(max(skylight[k]/255.0f, ambient), color[k]));
+ }
+ else loopk(3) color[k] = min(1.5f, max(max(ambientcolor[k]/255.0f, ambient), color[k]));
+ if(dir.iszero()) dir = vec(0, 0, 1);
+ else dir.normalize();
}
entity *brightestlight(const vec &target, const vec &dir)
{
- if(sunlight && sunlightdir.dot(dir) > 0 && shadowray(target, sunlightdir, 1e16f, RAY_SHADOW | RAY_POLY | (skytexturelight ? RAY_SKIPSKY | (useskytexture ? RAY_SKYTEX : 0) : 0)) > 1e15f)
- return &sunlightent;
- const vector<extentity *> &ents = entities::getents();
- const vector<int> &lights = checklightcache(int(target.x), int(target.y));
- extentity *brightest = NULL;
- float bintensity = 0;
- loopv(lights)
- {
- extentity &e = *ents[lights[i]];
- if(e.type != ET_LIGHT || vec(e.o).sub(target).dot(dir)<0)
- continue;
-
- vec ray(target);
- ray.sub(e.o);
- float mag = ray.magnitude();
- if(e.attr1 && mag >= float(e.attr1))
- continue;
-
- ray.div(mag);
- if(shadowray(e.o, ray, mag, RAY_SHADOW | RAY_POLY) < mag)
- continue;
- float intensity = 1;
- if(e.attr1)
- intensity -= mag / float(e.attr1);
- if(e.attached && e.attached->type==ET_SPOTLIGHT)
- {
- vec spot = vec(e.attached->o).sub(e.o).normalize();
- float maxatten = sincos360[clamp(int(e.attached->attr1), 1, 89)].x, spotatten = (ray.dot(spot) - maxatten) / (1 - maxatten);
- if(spotatten <= 0) continue;
- intensity *= spotatten;
- }
-
- if(!brightest || intensity > bintensity)
- {
- brightest = &e;
- bintensity = intensity;
- }
- }
- return brightest;
+ if(sunlight && sunlightdir.dot(dir) > 0 && shadowray(target, sunlightdir, 1e16f, RAY_SHADOW | RAY_POLY | (skytexturelight ? RAY_SKIPSKY | (useskytexture ? RAY_SKYTEX : 0) : 0)) > 1e15f)
+ return &sunlightent;
+ const vector<extentity *> &ents = entities::getents();
+ const vector<int> &lights = checklightcache(int(target.x), int(target.y));
+ extentity *brightest = NULL;
+ float bintensity = 0;
+ loopv(lights)
+ {
+ extentity &e = *ents[lights[i]];
+ if(e.type != ET_LIGHT || vec(e.o).sub(target).dot(dir)<0)
+ continue;
+
+ vec ray(target);
+ ray.sub(e.o);
+ float mag = ray.magnitude();
+ if(e.attr1 && mag >= float(e.attr1))
+ continue;
+
+ ray.div(mag);
+ if(shadowray(e.o, ray, mag, RAY_SHADOW | RAY_POLY) < mag)
+ continue;
+ float intensity = 1;
+ if(e.attr1)
+ intensity -= mag / float(e.attr1);
+ if(e.attached && e.attached->type==ET_SPOTLIGHT)
+ {
+ vec spot = vec(e.attached->o).sub(e.o).normalize();
+ float maxatten = sincos360[clamp(int(e.attached->attr1), 1, 89)].x, spotatten = (ray.dot(spot) - maxatten) / (1 - maxatten);
+ if(spotatten <= 0) continue;
+ intensity *= spotatten;
+ }
+
+ if(!brightest || intensity > bintensity)
+ {
+ brightest = &e;
+ bintensity = intensity;
+ }
+ }
+ return brightest;
}
void dumplms()
{
- loopv(lightmaps)
- {
- ImageData temp(LM_PACKW, LM_PACKH, lightmaps[i].bpp, lightmaps[i].data);
- const char *map = game::getclientmap(), *name = strrchr(map, '/');
- defformatstring(buf, "lightmap_%s_%d.png", name ? name+1 : map, i);
- savepng(buf, temp, true);
- }
+ loopv(lightmaps)
+ {
+ ImageData temp(LM_PACKW, LM_PACKH, lightmaps[i].bpp, lightmaps[i].data);
+ const char *map = game::getclientmap(), *name = strrchr(map, '/');
+ defformatstring(buf, "lightmap_%s_%d.png", name ? name+1 : map, i);
+ savepng(buf, temp, true);
+ }
}
COMMAND(dumplms, "");
diff --git a/src/engine/lightmap.h b/src/engine/lightmap.h
index 005f1fe..2c09d0d 100644
--- a/src/engine/lightmap.h
+++ b/src/engine/lightmap.h
@@ -7,79 +7,79 @@
struct PackNode
{
- PackNode *child1, *child2;
- ushort x, y, w, h;
- int available;
+ PackNode *child1, *child2;
+ ushort x, y, w, h;
+ int available;
- PackNode() : child1(0), child2(0), x(0), y(0), w(LM_PACKW), h(LM_PACKH), available(min(LM_PACKW, LM_PACKH)) {}
- PackNode(ushort x, ushort y, ushort w, ushort h) : child1(0), child2(0), x(x), y(y), w(w), h(h), available(min(w, h)) {}
+ PackNode() : child1(0), child2(0), x(0), y(0), w(LM_PACKW), h(LM_PACKH), available(min(LM_PACKW, LM_PACKH)) {}
+ PackNode(ushort x, ushort y, ushort w, ushort h) : child1(0), child2(0), x(x), y(y), w(w), h(h), available(min(w, h)) {}
- void clear()
- {
- DELETEP(child1);
- DELETEP(child2);
- }
+ void clear()
+ {
+ DELETEP(child1);
+ DELETEP(child2);
+ }
- ~PackNode()
- {
- clear();
- }
+ ~PackNode()
+ {
+ clear();
+ }
- bool insert(ushort &tx, ushort &ty, ushort tw, ushort th);
+ bool insert(ushort &tx, ushort &ty, ushort tw, ushort th);
};
enum
{
- LM_DIFFUSE = 0,
- LM_BUMPMAP0,
- LM_BUMPMAP1,
- LM_TYPE = 0x0F,
+ LM_DIFFUSE = 0,
+ LM_BUMPMAP0,
+ LM_BUMPMAP1,
+ LM_TYPE = 0x0F,
- LM_ALPHA = 1<<4,
- LM_FLAGS = 0xF0
+ LM_ALPHA = 1<<4,
+ LM_FLAGS = 0xF0
};
struct LightMap
{
- int type, bpp, tex, offsetx, offsety;
- PackNode packroot;
- uint lightmaps, lumels;
- int unlitx, unlity;
- uchar *data;
-
- LightMap()
- : type(LM_DIFFUSE), bpp(3), tex(-1), offsetx(-1), offsety(-1),
- lightmaps(0), lumels(0), unlitx(-1), unlity(-1),
- data(NULL)
- {
- }
-
- ~LightMap()
- {
- if(data) delete[] data;
- }
-
- void finalize()
- {
- packroot.clear();
- packroot.available = 0;
- }
-
- void copy(ushort tx, ushort ty, uchar *src, ushort tw, ushort th);
- bool insert(ushort &tx, ushort &ty, uchar *src, ushort tw, ushort th);
+ int type, bpp, tex, offsetx, offsety;
+ PackNode packroot;
+ uint lightmaps, lumels;
+ int unlitx, unlity;
+ uchar *data;
+
+ LightMap()
+ : type(LM_DIFFUSE), bpp(3), tex(-1), offsetx(-1), offsety(-1),
+ lightmaps(0), lumels(0), unlitx(-1), unlity(-1),
+ data(NULL)
+ {
+ }
+
+ ~LightMap()
+ {
+ if(data) delete[] data;
+ }
+
+ void finalize()
+ {
+ packroot.clear();
+ packroot.available = 0;
+ }
+
+ void copy(ushort tx, ushort ty, uchar *src, ushort tw, ushort th);
+ bool insert(ushort &tx, ushort &ty, uchar *src, ushort tw, ushort th);
};
extern vector<LightMap> lightmaps;
struct LightMapTexture
{
- int w, h, type;
- GLuint id;
- int unlitx, unlity;
+ int w, h, type;
+ GLuint id;
+ int unlitx, unlity;
- LightMapTexture()
- : w(0), h(0), type(LM_DIFFUSE), id(0), unlitx(-1), unlity(-1)
- {}
+ LightMapTexture()
+ : w(0), h(0), type(LM_DIFFUSE), id(0), unlitx(-1), unlity(-1)
+ {}
};
extern vector<LightMapTexture> lightmaptexs;
@@ -100,20 +100,20 @@ extern void previewblends(const ivec &bo, const ivec &bs);
struct lerpvert
{
- vec normal;
- vec2 tc;
+ vec normal;
+ vec2 tc;
- bool operator==(const lerpvert &l) const { return tc == l.tc;; }
- bool operator!=(const lerpvert &l) const { return tc != l.tc; }
+ bool operator==(const lerpvert &l) const { return tc == l.tc;; }
+ bool operator!=(const lerpvert &l) const { return tc != l.tc; }
};
struct lerpbounds
{
- const lerpvert *min;
- const lerpvert *max;
- float u, ustep;
- vec normal, nstep;
- int winding;
+ const lerpvert *min;
+ const lerpvert *max;
+ float u, ustep;
+ vec normal, nstep;
+ int winding;
};
extern void calcnormals(bool lerptjoints = false);
@@ -124,17 +124,17 @@ extern void initlerpbounds(float u, float v, const lerpvert *lv, int numv, lerpb
extern void lerpnormal(float u, float v, const lerpvert *lv, int numv, lerpbounds &start, lerpbounds &end, vec &normal, vec &nstep);
#define CHECK_CALCLIGHT_PROGRESS_LOCKED(exit, show_calclight_progress, before, after) \
- if(check_calclight_progress) \
- { \
- if(!calclight_canceled) \
- { \
- before; \
- show_calclight_progress(); \
- check_calclight_canceled(); \
- after; \
- } \
- if(calclight_canceled) { exit; } \
- }
+ if(check_calclight_progress) \
+ { \
+ if(!calclight_canceled) \
+ { \
+ before; \
+ show_calclight_progress(); \
+ check_calclight_canceled(); \
+ after; \
+ } \
+ if(calclight_canceled) { exit; } \
+ }
#define CHECK_CALCLIGHT_PROGRESS(exit, show_calclight_progress) CHECK_CALCLIGHT_PROGRESS_LOCKED(exit, show_calclight_progress, , )
extern bool calclight_canceled;
diff --git a/src/engine/lightning.h b/src/engine/lightning.h
index 16b146d..cec993a 100644
--- a/src/engine/lightning.h
+++ b/src/engine/lightning.h
@@ -12,112 +12,112 @@ FVAR(lnblendpower, 0, 0.25f, 1000);
static void calclightningjitter(int frame)
{
- loopi(MAXLIGHTNINGSTEPS)
- {
- lnjitterx[lnjitterframe][i] = -lnjitterradius + rnd(2*lnjitterradius + 1);
- lnjittery[lnjitterframe][i] = -lnjitterradius + rnd(2*lnjitterradius + 1);
- }
+ loopi(MAXLIGHTNINGSTEPS)
+ {
+ lnjitterx[lnjitterframe][i] = -lnjitterradius + rnd(2*lnjitterradius + 1);
+ lnjittery[lnjitterframe][i] = -lnjitterradius + rnd(2*lnjitterradius + 1);
+ }
}
static void setuplightning()
{
- if(!lastlnjitter || lastmillis-lastlnjitter > lnjittermillis)
- {
- if(!lastlnjitter) calclightningjitter(lnjitterframe);
- lastlnjitter = lastmillis - (lastmillis%lnjittermillis);
- calclightningjitter(lnjitterframe ^= 1);
- }
+ if(!lastlnjitter || lastmillis-lastlnjitter > lnjittermillis)
+ {
+ if(!lastlnjitter) calclightningjitter(lnjitterframe);
+ lastlnjitter = lastmillis - (lastmillis%lnjittermillis);
+ calclightningjitter(lnjitterframe ^= 1);
+ }
}
static void renderlightning(Texture *tex, const vec &o, const vec &d, float sz)
{
- vec step(d);
- step.sub(o);
- float len = step.magnitude();
- int numsteps = clamp(int(ceil(len/LIGHTNINGSTEP)), 2, MAXLIGHTNINGSTEPS);
- step.div(numsteps+1);
- int jitteroffset = detrnd(int(d.x+d.y+d.z), MAXLIGHTNINGSTEPS);
- vec cur(o), up, right;
- up.orthogonal(step);
- up.normalize();
- right.cross(up, step);
- right.normalize();
- float scroll = -float(lastmillis%lnscrollmillis)/lnscrollmillis,
- scrollscale = lnscrollscale*(LIGHTNINGSTEP*tex->ys)/(sz*tex->xs),
- blend = pow(clamp(float(lastmillis - lastlnjitter)/lnjittermillis, 0.0f, 1.0f), lnblendpower),
- jitter0 = (1-blend)*lnjitterscale*sz/lnjitterradius, jitter1 = blend*lnjitterscale*sz/lnjitterradius;
- gle::begin(GL_TRIANGLE_STRIP);
- loopj(numsteps)
- {
- vec next(cur);
- next.add(step);
- if(j+1==numsteps) next = d;
- else
- {
- int lj = (j+jitteroffset)%MAXLIGHTNINGSTEPS;
- next.add(vec(right).mul((jitter1*lnjitterx[lnjitterframe][lj] + jitter0*lnjitterx[lnjitterframe^1][lj])));
- next.add(vec(up).mul((jitter1*lnjittery[lnjitterframe][lj] + jitter0*lnjittery[lnjitterframe^1][lj])));
- }
- vec dir1 = next, dir2 = next, across;
- dir1.sub(cur);
- dir2.sub(camera1->o);
- across.cross(dir2, dir1).normalize().mul(sz);
- gle::attribf(cur.x-across.x, cur.y-across.y, cur.z-across.z);
- gle::attribf(scroll, 1);
- gle::attribf(cur.x+across.x, cur.y+across.y, cur.z+across.z);
- gle::attribf(scroll, 0);
- scroll += scrollscale;
- if(j+1==numsteps)
- {
- gle::attribf(next.x-across.x, next.y-across.y, next.z-across.z);
- gle::attribf(scroll, 1);
- gle::attribf(next.x+across.x, next.y+across.y, next.z+across.z);
- gle::attribf(scroll, 0);
- }
- cur = next;
- }
- gle::end();
+ vec step(d);
+ step.sub(o);
+ float len = step.magnitude();
+ int numsteps = clamp(int(ceil(len/LIGHTNINGSTEP)), 2, MAXLIGHTNINGSTEPS);
+ step.div(numsteps+1);
+ int jitteroffset = detrnd(int(d.x+d.y+d.z), MAXLIGHTNINGSTEPS);
+ vec cur(o), up, right;
+ up.orthogonal(step);
+ up.normalize();
+ right.cross(up, step);
+ right.normalize();
+ float scroll = -float(lastmillis%lnscrollmillis)/lnscrollmillis,
+ scrollscale = lnscrollscale*(LIGHTNINGSTEP*tex->ys)/(sz*tex->xs),
+ blend = pow(clamp(float(lastmillis - lastlnjitter)/lnjittermillis, 0.0f, 1.0f), lnblendpower),
+ jitter0 = (1-blend)*lnjitterscale*sz/lnjitterradius, jitter1 = blend*lnjitterscale*sz/lnjitterradius;
+ gle::begin(GL_TRIANGLE_STRIP);
+ loopj(numsteps)
+ {
+ vec next(cur);
+ next.add(step);
+ if(j+1==numsteps) next = d;
+ else
+ {
+ int lj = (j+jitteroffset)%MAXLIGHTNINGSTEPS;
+ next.add(vec(right).mul((jitter1*lnjitterx[lnjitterframe][lj] + jitter0*lnjitterx[lnjitterframe^1][lj])));
+ next.add(vec(up).mul((jitter1*lnjittery[lnjitterframe][lj] + jitter0*lnjittery[lnjitterframe^1][lj])));
+ }
+ vec dir1 = next, dir2 = next, across;
+ dir1.sub(cur);
+ dir2.sub(camera1->o);
+ across.cross(dir2, dir1).normalize().mul(sz);
+ gle::attribf(cur.x-across.x, cur.y-across.y, cur.z-across.z);
+ gle::attribf(scroll, 1);
+ gle::attribf(cur.x+across.x, cur.y+across.y, cur.z+across.z);
+ gle::attribf(scroll, 0);
+ scroll += scrollscale;
+ if(j+1==numsteps)
+ {
+ gle::attribf(next.x-across.x, next.y-across.y, next.z-across.z);
+ gle::attribf(scroll, 1);
+ gle::attribf(next.x+across.x, next.y+across.y, next.z+across.z);
+ gle::attribf(scroll, 0);
+ }
+ cur = next;
+ }
+ gle::end();
}
struct lightningrenderer : listrenderer
{
- lightningrenderer()
- : listrenderer("packages/particles/lightning.png", 2, PT_LIGHTNING|PT_TRACK|PT_GLARE)
- {}
+ lightningrenderer()
+ : listrenderer("packages/particles/lightning.png", 2, PT_LIGHTNING|PT_TRACK|PT_GLARE)
+ {}
- void startrender()
- {
- glDisable(GL_CULL_FACE);
- gle::defattrib(gle::ATTRIB_VERTEX, 3, GL_FLOAT);
- gle::defattrib(gle::ATTRIB_TEXCOORD0, 2, GL_FLOAT);
- }
+ void startrender()
+ {
+ glDisable(GL_CULL_FACE);
+ gle::defattrib(gle::ATTRIB_VERTEX, 3, GL_FLOAT);
+ gle::defattrib(gle::ATTRIB_TEXCOORD0, 2, GL_FLOAT);
+ }
- void endrender()
- {
- glEnable(GL_CULL_FACE);
- }
+ void endrender()
+ {
+ glEnable(GL_CULL_FACE);
+ }
- void update()
- {
- setuplightning();
- }
+ void update()
+ {
+ setuplightning();
+ }
- void seedemitter(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int gravity)
- {
- pe.maxfade = max(pe.maxfade, fade);
- pe.extendbb(o, size);
- pe.extendbb(d, size);
- }
+ void seedemitter(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int gravity)
+ {
+ pe.maxfade = max(pe.maxfade, fade);
+ pe.extendbb(o, size);
+ pe.extendbb(d, size);
+ }
- void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts)
- {
- blend = min(blend<<2, 255);
- if(type&PT_MOD) //multiply alpha into color
- gle::colorub((p->color.r*blend)>>8, (p->color.g*blend)>>8, (p->color.b*blend)>>8);
- else
- gle::color(p->color, blend);
- renderlightning(tex, o, d, p->size);
- }
+ void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts)
+ {
+ blend = min(blend<<2, 255);
+ if(type&PT_MOD) //multiply alpha into color
+ gle::colorub((p->color.r*blend)>>8, (p->color.g*blend)>>8, (p->color.b*blend)>>8);
+ else
+ gle::color(p->color, blend);
+ renderlightning(tex, o, d, p->size);
+ }
};
static lightningrenderer lightnings;
diff --git a/src/engine/main.cpp b/src/engine/main.cpp
index 499002f..fb32dfd 100644
--- a/src/engine/main.cpp
+++ b/src/engine/main.cpp
@@ -12,65 +12,59 @@ extern void cleargamma();
void cleanup()
{
- cleanupserver();
- SDL_ShowCursor(SDL_TRUE);
- SDL_SetRelativeMouseMode(SDL_FALSE);
- if(screen) SDL_SetWindowGrab(screen, SDL_FALSE);
- cleargamma();
- freeocta(worldroot);
- extern void clear_command(); clear_command();
- extern void clear_console(); clear_console();
- extern void clear_mdls(); clear_mdls();
- extern void clear_sound(); clear_sound();
- closelogfile();
- #ifdef __APPLE__
- if(screen) SDL_SetWindowFullscreen(screen, 0);
- #endif
- SDL_Quit();
+ cleanupserver();
+ SDL_ShowCursor(SDL_TRUE);
+ SDL_SetRelativeMouseMode(SDL_FALSE);
+ if(screen) SDL_SetWindowGrab(screen, SDL_FALSE);
+ cleargamma();
+ freeocta(worldroot);
+ extern void clear_command(); clear_command();
+ extern void clear_console(); clear_console();
+ extern void clear_mdls(); clear_mdls();
+ extern void clear_sound(); clear_sound();
+ closelogfile();
+ SDL_Quit();
}
extern void writeinitcfg();
-void quit() // normal exit
+void quit() // normal exit
{
- writeinitcfg();
- writeservercfg();
- abortconnect();
- disconnect();
- localdisconnect();
- writecfg();
- cleanup();
- exit(EXIT_SUCCESS);
+ writeinitcfg();
+ writeservercfg();
+ abortconnect();
+ disconnect();
+ localdisconnect();
+ writecfg();
+ cleanup();
+ exit(EXIT_SUCCESS);
}
-void fatal(const char *s, ...) // failure exit
+void fatal(const char *s, ...) // failure exit
{
- static int errors = 0;
- errors++;
-
- if(errors <= 2) // print up to one extra recursive error
- {
- defvformatstring(msg,s,s);
- logoutf("%s", msg);
-
- if(errors <= 1) // avoid recursion
- {
- if(SDL_WasInit(SDL_INIT_VIDEO))
- {
- SDL_ShowCursor(SDL_TRUE);
- SDL_SetRelativeMouseMode(SDL_FALSE);
- if(screen) SDL_SetWindowGrab(screen, SDL_FALSE);
- cleargamma();
- #ifdef __APPLE__
- if(screen) SDL_SetWindowFullscreen(screen, 0);
- #endif
- }
- SDL_Quit();
- SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Cube 2: Sauerbraten fatal error", msg, NULL);
- }
- }
-
- exit(EXIT_FAILURE);
+ static int errors = 0;
+ errors++;
+
+ if(errors <= 2) // print up to one extra recursive error
+ {
+ defvformatstring(msg,s,s);
+ logoutf("%s", msg);
+
+ if(errors <= 1) // avoid recursion
+ {
+ if(SDL_WasInit(SDL_INIT_VIDEO))
+ {
+ SDL_ShowCursor(SDL_TRUE);
+ SDL_SetRelativeMouseMode(SDL_FALSE);
+ if(screen) SDL_SetWindowGrab(screen, SDL_FALSE);
+ cleargamma();
+ }
+ SDL_Quit();
+ SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Cube 2: Sauerbraten fatal error", msg, NULL);
+ }
+ }
+
+ exit(EXIT_FAILURE);
}
int curtime = 0, lastmillis = 1, elapsedtime = 0, totalmillis = 1;
@@ -81,12 +75,12 @@ int initing = NOT_INITING;
bool initwarning(const char *desc, int level, int type)
{
- if(initing < level)
- {
- addchange(desc, type);
- return true;
- }
- return false;
+ if(initing < level)
+ {
+ addchange(desc, type);
+ return true;
+ }
+ return false;
}
VAR(desktopw, 1, 0, 0);
@@ -108,36 +102,36 @@ VARF(fsaa, -1, -1, 16, initwarning("anti-aliasing"));
void writeinitcfg()
{
- stream *f = openutf8file("init.cfg", "w");
- if(!f) return;
- f->printf("// automatically written on exit, DO NOT MODIFY\n// modify settings in game\n");
- extern int fullscreen, fullscreendesktop;
- f->printf("fullscreen %d\n", fullscreen);
- f->printf("fullscreendesktop %d\n", fullscreendesktop);
- f->printf("scr_w %d\n", scr_w);
- f->printf("scr_h %d\n", scr_h);
- f->printf("depthbits %d\n", depthbits);
- f->printf("fsaa %d\n", fsaa);
- extern int usesound, soundchans, soundfreq, soundbufferlen;
- extern char *audiodriver;
- f->printf("usesound %d\n", usesound);
- f->printf("soundchans %d\n", soundchans);
- f->printf("soundfreq %d\n", soundfreq);
- f->printf("soundbufferlen %d\n", soundbufferlen);
- if(audiodriver[0]) f->printf("audiodriver %s\n", escapestring(audiodriver));
- delete f;
+ stream *f = openutf8file("init.cfg", "w");
+ if(!f) return;
+ f->printf("// automatically written on exit, DO NOT MODIFY\n// modify settings in game\n");
+ extern int fullscreen, fullscreendesktop;
+ f->printf("fullscreen %d\n", fullscreen);
+ f->printf("fullscreendesktop %d\n", fullscreendesktop);
+ f->printf("scr_w %d\n", scr_w);
+ f->printf("scr_h %d\n", scr_h);
+ f->printf("depthbits %d\n", depthbits);
+ f->printf("fsaa %d\n", fsaa);
+ extern int usesound, soundchans, soundfreq, soundbufferlen;
+ extern char *audiodriver;
+ f->printf("usesound %d\n", usesound);
+ f->printf("soundchans %d\n", soundchans);
+ f->printf("soundfreq %d\n", soundfreq);
+ f->printf("soundbufferlen %d\n", soundbufferlen);
+ if(audiodriver[0]) f->printf("audiodriver %s\n", escapestring(audiodriver));
+ delete f;
}
COMMAND(quit, "");
static void getbackgroundres(int &w, int &h)
{
- float wk = 1, hk = 1;
- if(w < 1024) wk = 1024.0f/w;
- if(h < 768) hk = 768.0f/h;
- wk = hk = max(wk, hk);
- w = int(ceil(w*wk));
- h = int(ceil(h*hk));
+ float wk = 1, hk = 1;
+ if(w < 1024) wk = 1024.0f/w;
+ if(h < 768) hk = 768.0f/h;
+ wk = hk = max(wk, hk);
+ w = int(ceil(w*wk));
+ h = int(ceil(h*hk));
}
string backgroundcaption = "";
@@ -147,156 +141,156 @@ char *backgroundmapinfo = NULL;
void setbackgroundinfo(const char *caption = NULL, Texture *mapshot = NULL, const char *mapname = NULL, const char *mapinfo = NULL)
{
- renderedframe = false;
- copystring(backgroundcaption, caption ? caption : "");
- backgroundmapshot = mapshot;
- copystring(backgroundmapname, mapname ? mapname : "");
- if(mapinfo != backgroundmapinfo)
- {
- DELETEA(backgroundmapinfo);
- if(mapinfo) backgroundmapinfo = newstring(mapinfo);
- }
+ renderedframe = false;
+ copystring(backgroundcaption, caption ? caption : "");
+ backgroundmapshot = mapshot;
+ copystring(backgroundmapname, mapname ? mapname : "");
+ if(mapinfo != backgroundmapinfo)
+ {
+ DELETEA(backgroundmapinfo);
+ if(mapinfo) backgroundmapinfo = newstring(mapinfo);
+ }
}
void restorebackground(bool force = false)
{
- if(renderedframe)
- {
- if(!force) return;
- setbackgroundinfo();
- }
- renderbackground(backgroundcaption[0] ? backgroundcaption : NULL, backgroundmapshot, backgroundmapname[0] ? backgroundmapname : NULL, backgroundmapinfo, true);
+ if(renderedframe)
+ {
+ if(!force) return;
+ setbackgroundinfo();
+ }
+ renderbackground(backgroundcaption[0] ? backgroundcaption : NULL, backgroundmapshot, backgroundmapname[0] ? backgroundmapname : NULL, backgroundmapinfo, true);
}
void bgquad(float x, float y, float w, float h, float tx = 0, float ty = 0, float tw = 1, float th = 1)
{
- gle::begin(GL_TRIANGLE_STRIP);
- gle::attribf(x, y); gle::attribf(tx, ty);
- gle::attribf(x+w, y); gle::attribf(tx + tw, ty);
- gle::attribf(x, y+h); gle::attribf(tx, ty + th);
- gle::attribf(x+w, y+h); gle::attribf(tx + tw, ty + th);
- gle::end();
+ gle::begin(GL_TRIANGLE_STRIP);
+ gle::attribf(x, y); gle::attribf(tx, ty);
+ gle::attribf(x+w, y); gle::attribf(tx + tw, ty);
+ gle::attribf(x, y+h); gle::attribf(tx, ty + th);
+ gle::attribf(x+w, y+h); gle::attribf(tx + tw, ty + th);
+ gle::end();
}
void renderbackground(const char *caption, Texture *mapshot, const char *mapname, const char *mapinfo, bool restore, bool force)
{
- if(!inbetweenframes && !force) return;
-
- if(!restore || force) stopsounds(); // stop sounds while loading
-
- int w = screenw, h = screenh;
- if(forceaspect) w = int(ceil(h*forceaspect));
- getbackgroundres(w, h);
- gettextres(w, h);
-
- static int lastupdate = -1, lastw = -1, lasth = -1;
- static int numdecals = 0;
- static struct decal { float x, y, size; int side; } decals[12];
- if((renderedframe && !mainmenu && lastupdate != lastmillis) || lastw != w || lasth != h)
- {
- lastupdate = lastmillis;
- lastw = w;
- lasth = h;
-
- numdecals = sizeof(decals)/sizeof(decals[0]);
- numdecals = numdecals/3 + rnd((numdecals*2)/3 + 1);
- float maxsize = min(w, h)/16.0f;
- loopi(numdecals)
- {
- decal d = { rndscale(w), rndscale(h), maxsize/2 + rndscale(maxsize/2), rnd(2) };
- decals[i] = d;
- }
- }
- else if(lastupdate != lastmillis) lastupdate = lastmillis;
-
- loopi(restore ? 1 : 3)
- {
- hudmatrix.ortho(0, w, h, 0, -1, 1);
- resethudmatrix();
-
- hudshader->set();
- gle::colorf(1, 1, 1);
-
- gle::defvertex(2);
- gle::deftexcoord0();
-
- settexture("background/daemex.png", 0);
- bgquad(0, 0, screenw, screenh, 0, 0, 1, 1);
- glEnable(GL_BLEND);
- float lh = 0.5f*min(w, h), lw = lh*2,
- lx = 0.5f*(w - lw), ly = 0.5f*(h*0.5f - lh);
- settexture((maxtexsize ? min(maxtexsize, hwtexsize) : hwtexsize) >= 1024 && (screenw > 1280 || screenh > 800) ? "data/logo_1024.png" : "data/logo.png", 3);
- bgquad(lx, ly, lw, lh);
- if(caption)
- {
- int tw = text_width(caption);
- float tsz = 0.04f*min(w, h)/FONTH,
- tx = 0.5f*(w - tw*tsz), ty = h - 0.075f*1.5f*min(w, h) - 1.25f*FONTH*tsz;
- pushhudmatrix();
- hudmatrix.translate(tx, ty, 0);
- hudmatrix.scale(tsz, tsz, 1);
- flushhudmatrix();
- draw_text(caption, 0, 0);
- pophudmatrix();
- }
- if(mapshot || mapname)
- {
- int infowidth = 12*FONTH;
- float sz = 0.35f*min(w, h), msz = (0.75f*min(w, h) - sz)/(infowidth + FONTH), x = 0.5f*(w-sz), y = ly+lh - sz/15;
- if(mapinfo)
- {
- int mw, mh;
- text_bounds(mapinfo, mw, mh, infowidth);
- x -= 0.5f*(mw*msz + FONTH*msz);
- }
- if(mapshot && mapshot!=notexture)
- {
- glBindTexture(GL_TEXTURE_2D, mapshot->id);
- bgquad(x, y, sz, sz);
- }
- else
- {
- int qw, qh;
- text_bounds("?", qw, qh);
- float qsz = sz*0.5f/max(qw, qh);
- pushhudmatrix();
- hudmatrix.translate(x + 0.5f*(sz - qw*qsz), y + 0.5f*(sz - qh*qsz), 0);
- hudmatrix.scale(qsz, qsz, 1);
- flushhudmatrix();
- draw_text("?", 0, 0);
- pophudmatrix();
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
- settexture("data/mapshot_frame.png", 3);
- bgquad(x, y, sz, sz);
- if(mapname)
- {
- int tw = text_width(mapname);
- float tsz = sz/(8*FONTH),
- tx = 0.9f*sz - tw*tsz, ty = 0.9f*sz - FONTH*tsz;
- if(tx < 0.1f*sz) { tsz = 0.1f*sz/tw; tx = 0.1f; }
- pushhudmatrix();
- hudmatrix.translate(x+tx, y+ty, 0);
- hudmatrix.scale(tsz, tsz, 1);
- flushhudmatrix();
- draw_text(mapname, 0, 0);
- pophudmatrix();
- }
- if(mapinfo)
- {
- pushhudmatrix();
- hudmatrix.translate(x+sz+FONTH*msz, y, 0);
- hudmatrix.scale(msz, msz, 1);
- flushhudmatrix();
- draw_text(mapinfo, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, -1, infowidth);
- pophudmatrix();
- }
- }
- glDisable(GL_BLEND);
- if(!restore) swapbuffers(false);
- }
-
- if(!restore) setbackgroundinfo(caption, mapshot, mapname, mapinfo);
+ if(!inbetweenframes && !force) return;
+
+ if(!restore || force) stopsounds(); // stop sounds while loading
+
+ int w = screenw, h = screenh;
+ if(forceaspect) w = int(ceil(h*forceaspect));
+ getbackgroundres(w, h);
+ gettextres(w, h);
+
+ static int lastupdate = -1, lastw = -1, lasth = -1;
+ static int numdecals = 0;
+ static struct decal { float x, y, size; int side; } decals[12];
+ if((renderedframe && !mainmenu && lastupdate != lastmillis) || lastw != w || lasth != h)
+ {
+ lastupdate = lastmillis;
+ lastw = w;
+ lasth = h;
+
+ numdecals = sizeof(decals)/sizeof(decals[0]);
+ numdecals = numdecals/3 + rnd((numdecals*2)/3 + 1);
+ float maxsize = min(w, h)/16.0f;
+ loopi(numdecals)
+ {
+ decal d = { rndscale(w), rndscale(h), maxsize/2 + rndscale(maxsize/2), rnd(2) };
+ decals[i] = d;
+ }
+ }
+ else if(lastupdate != lastmillis) lastupdate = lastmillis;
+
+ loopi(restore ? 1 : 3)
+ {
+ hudmatrix.ortho(0, w, h, 0, -1, 1);
+ resethudmatrix();
+
+ hudshader->set();
+ gle::colorf(1, 1, 1);
+
+ gle::defvertex(2);
+ gle::deftexcoord0();
+
+ settexture("background/daemex.png", 0);
+ bgquad(0, 0, screenw, screenh, 0, 0, 1, 1);
+ glEnable(GL_BLEND);
+ float lh = 0.5f*min(w, h), lw = lh*2,
+ lx = 0.5f*(w - lw), ly = 0.5f*(h*0.5f - lh);
+ settexture((maxtexsize ? min(maxtexsize, hwtexsize) : hwtexsize) >= 1024 && (screenw > 1280 || screenh > 800) ? "data/logo_1024.png" : "data/logo.png", 3);
+ bgquad(lx, ly, lw, lh);
+ if(caption)
+ {
+ int tw = text_width(caption);
+ float tsz = 0.04f*min(w, h)/FONTH,
+ tx = 0.5f*(w - tw*tsz), ty = h - 0.075f*1.5f*min(w, h) - 1.25f*FONTH*tsz;
+ pushhudmatrix();
+ hudmatrix.translate(tx, ty, 0);
+ hudmatrix.scale(tsz, tsz, 1);
+ flushhudmatrix();
+ draw_text(caption, 0, 0);
+ pophudmatrix();
+ }
+ if(mapshot || mapname)
+ {
+ int infowidth = 12*FONTH;
+ float sz = 0.35f*min(w, h), msz = (0.75f*min(w, h) - sz)/(infowidth + FONTH), x = 0.5f*(w-sz), y = ly+lh - sz/15;
+ if(mapinfo)
+ {
+ int mw, mh;
+ text_bounds(mapinfo, mw, mh, infowidth);
+ x -= 0.5f*(mw*msz + FONTH*msz);
+ }
+ if(mapshot && mapshot!=notexture)
+ {
+ glBindTexture(GL_TEXTURE_2D, mapshot->id);
+ bgquad(x, y, sz, sz);
+ }
+ else
+ {
+ int qw, qh;
+ text_bounds("?", qw, qh);
+ float qsz = sz*0.5f/max(qw, qh);
+ pushhudmatrix();
+ hudmatrix.translate(x + 0.5f*(sz - qw*qsz), y + 0.5f*(sz - qh*qsz), 0);
+ hudmatrix.scale(qsz, qsz, 1);
+ flushhudmatrix();
+ draw_text("?", 0, 0);
+ pophudmatrix();
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ settexture("data/mapshot_frame.png", 3);
+ bgquad(x, y, sz, sz);
+ if(mapname)
+ {
+ int tw = text_width(mapname);
+ float tsz = sz/(8*FONTH),
+ tx = 0.9f*sz - tw*tsz, ty = 0.9f*sz - FONTH*tsz;
+ if(tx < 0.1f*sz) { tsz = 0.1f*sz/tw; tx = 0.1f; }
+ pushhudmatrix();
+ hudmatrix.translate(x+tx, y+ty, 0);
+ hudmatrix.scale(tsz, tsz, 1);
+ flushhudmatrix();
+ draw_text(mapname, 0, 0);
+ pophudmatrix();
+ }
+ if(mapinfo)
+ {
+ pushhudmatrix();
+ hudmatrix.translate(x+sz+FONTH*msz, y, 0);
+ hudmatrix.scale(msz, msz, 1);
+ flushhudmatrix();
+ draw_text(mapinfo, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, -1, infowidth);
+ pophudmatrix();
+ }
+ }
+ glDisable(GL_BLEND);
+ if(!restore) swapbuffers(false);
+ }
+
+ if(!restore) setbackgroundinfo(caption, mapshot, mapname, mapinfo);
}
VAR(progressbackground, 0, 0, 1);
@@ -305,108 +299,108 @@ float loadprogress = 0;
void renderprogress(float bar, const char *text, GLuint tex, bool background) // also used during loading
{
- if(!inbetweenframes || drawtex) return;
-
- extern int menufps, maxfps;
- int fps = menufps ? (maxfps ? min(maxfps, menufps) : menufps) : maxfps;
- if(fps)
- {
- static int lastprogress = 0;
- int ticks = SDL_GetTicks(), diff = ticks - lastprogress;
- if(bar > 0 && diff >= 0 && diff < (1000 + fps-1)/fps) return;
- lastprogress = ticks;
- }
-
- clientkeepalive(); // make sure our connection doesn't time out while loading maps etc.
-
- SDL_PumpEvents(); // keep the event queue awake to avoid 'beachball' cursor
-
- extern int mesa_swap_bug, curvsync;
- bool forcebackground = progressbackground || (mesa_swap_bug && (curvsync || totalmillis==1));
- if(background || forcebackground) restorebackground(forcebackground);
-
- int w = screenw, h = screenh;
- if(forceaspect) w = int(ceil(h*forceaspect));
- getbackgroundres(w, h);
- gettextres(w, h);
-
- hudmatrix.ortho(0, w, h, 0, -1, 1);
- resethudmatrix();
-
- hudshader->set();
- gle::colorf(1, 1, 1);
-
- gle::defvertex(2);
- gle::deftexcoord0();
-
- float fh = 0.075f*min(w, h), fw = fh*10,
- fx = renderedframe ? w - fw - fh/4 : 0.5f*(w - fw),
- fy = renderedframe ? fh/4 : h - fh*1.5f,
- fu1 = 0/512.0f, fu2 = 511/512.0f,
- fv1 = 0/64.0f, fv2 = 52/64.0f;
- settexture("data/loading_frame.png", 3);
- bgquad(fx, fy, fw, fh, fu1, fv1, fu2-fu1, fv2-fv1);
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- float bw = fw*(511 - 2*17)/511.0f, bh = fh*20/52.0f,
- bx = fx + fw*17/511.0f, by = fy + fh*16/52.0f,
- bv1 = 0/32.0f, bv2 = 20/32.0f,
- su1 = 0/32.0f, su2 = 7/32.0f, sw = fw*7/511.0f,
- eu1 = 23/32.0f, eu2 = 30/32.0f, ew = fw*7/511.0f,
- mw = bw - sw - ew,
- ex = bx+sw + max(mw*bar, fw*7/511.0f);
- if(bar > 0)
- {
- settexture("data/loading_bar.png", 3);
- gle::begin(GL_QUADS);
- gle::attribf(bx, by); gle::attribf(su1, bv1);
- gle::attribf(bx+sw, by); gle::attribf(su2, bv1);
- gle::attribf(bx+sw, by+bh); gle::attribf(su2, bv2);
- gle::attribf(bx, by+bh); gle::attribf(su1, bv2);
-
- gle::attribf(bx+sw, by); gle::attribf(su2, bv1);
- gle::attribf(ex, by); gle::attribf(eu1, bv1);
- gle::attribf(ex, by+bh); gle::attribf(eu1, bv2);
- gle::attribf(bx+sw, by+bh); gle::attribf(su2, bv2);
-
- gle::attribf(ex, by); gle::attribf(eu1, bv1);
- gle::attribf(ex+ew, by); gle::attribf(eu2, bv1);
- gle::attribf(ex+ew, by+bh); gle::attribf(eu2, bv2);
- gle::attribf(ex, by+bh); gle::attribf(eu1, bv2);
- gle::end();
- }
-
- if(text)
- {
- int tw = text_width(text);
- float tsz = bh*0.8f/FONTH;
- if(tw*tsz > mw) tsz = mw/tw;
- pushhudmatrix();
- hudmatrix.translate(bx+sw, by + (bh - FONTH*tsz)/2, 0);
- hudmatrix.scale(tsz, tsz, 1);
- flushhudmatrix();
- draw_text(text, 0, 0);
- pophudmatrix();
- }
-
- glDisable(GL_BLEND);
-
- if(tex)
- {
- glBindTexture(GL_TEXTURE_2D, tex);
- float sz = 0.35f*min(w, h), x = 0.5f*(w-sz), y = 0.5f*min(w, h) - sz/15;
- bgquad(x, y, sz, sz);
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- settexture("data/mapshot_frame.png", 3);
- bgquad(x, y, sz, sz);
- glDisable(GL_BLEND);
- }
-
- swapbuffers(false);
+ if(!inbetweenframes || drawtex) return;
+
+ extern int menufps, maxfps;
+ int fps = menufps ? (maxfps ? min(maxfps, menufps) : menufps) : maxfps;
+ if(fps)
+ {
+ static int lastprogress = 0;
+ int ticks = SDL_GetTicks(), diff = ticks - lastprogress;
+ if(bar > 0 && diff >= 0 && diff < (1000 + fps-1)/fps) return;
+ lastprogress = ticks;
+ }
+
+ clientkeepalive(); // make sure our connection doesn't time out while loading maps etc.
+
+ SDL_PumpEvents(); // keep the event queue awake to avoid 'beachball' cursor
+
+ extern int mesa_swap_bug, curvsync;
+ bool forcebackground = progressbackground || (mesa_swap_bug && (curvsync || totalmillis==1));
+ if(background || forcebackground) restorebackground(forcebackground);
+
+ int w = screenw, h = screenh;
+ if(forceaspect) w = int(ceil(h*forceaspect));
+ getbackgroundres(w, h);
+ gettextres(w, h);
+
+ hudmatrix.ortho(0, w, h, 0, -1, 1);
+ resethudmatrix();
+
+ hudshader->set();
+ gle::colorf(1, 1, 1);
+
+ gle::defvertex(2);
+ gle::deftexcoord0();
+
+ float fh = 0.075f*min(w, h), fw = fh*10,
+ fx = renderedframe ? w - fw - fh/4 : 0.5f*(w - fw),
+ fy = renderedframe ? fh/4 : h - fh*1.5f,
+ fu1 = 0/512.0f, fu2 = 511/512.0f,
+ fv1 = 0/64.0f, fv2 = 52/64.0f;
+ settexture("data/loading_frame.png", 3);
+ bgquad(fx, fy, fw, fh, fu1, fv1, fu2-fu1, fv2-fv1);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ float bw = fw*(511 - 2*17)/511.0f, bh = fh*20/52.0f,
+ bx = fx + fw*17/511.0f, by = fy + fh*16/52.0f,
+ bv1 = 0/32.0f, bv2 = 20/32.0f,
+ su1 = 0/32.0f, su2 = 7/32.0f, sw = fw*7/511.0f,
+ eu1 = 23/32.0f, eu2 = 30/32.0f, ew = fw*7/511.0f,
+ mw = bw - sw - ew,
+ ex = bx+sw + max(mw*bar, fw*7/511.0f);
+ if(bar > 0)
+ {
+ settexture("data/loading_bar.png", 3);
+ gle::begin(GL_QUADS);
+ gle::attribf(bx, by); gle::attribf(su1, bv1);
+ gle::attribf(bx+sw, by); gle::attribf(su2, bv1);
+ gle::attribf(bx+sw, by+bh); gle::attribf(su2, bv2);
+ gle::attribf(bx, by+bh); gle::attribf(su1, bv2);
+
+ gle::attribf(bx+sw, by); gle::attribf(su2, bv1);
+ gle::attribf(ex, by); gle::attribf(eu1, bv1);
+ gle::attribf(ex, by+bh); gle::attribf(eu1, bv2);
+ gle::attribf(bx+sw, by+bh); gle::attribf(su2, bv2);
+
+ gle::attribf(ex, by); gle::attribf(eu1, bv1);
+ gle::attribf(ex+ew, by); gle::attribf(eu2, bv1);
+ gle::attribf(ex+ew, by+bh); gle::attribf(eu2, bv2);
+ gle::attribf(ex, by+bh); gle::attribf(eu1, bv2);
+ gle::end();
+ }
+
+ if(text)
+ {
+ int tw = text_width(text);
+ float tsz = bh*0.8f/FONTH;
+ if(tw*tsz > mw) tsz = mw/tw;
+ pushhudmatrix();
+ hudmatrix.translate(bx+sw, by + (bh - FONTH*tsz)/2, 0);
+ hudmatrix.scale(tsz, tsz, 1);
+ flushhudmatrix();
+ draw_text(text, 0, 0);
+ pophudmatrix();
+ }
+
+ glDisable(GL_BLEND);
+
+ if(tex)
+ {
+ glBindTexture(GL_TEXTURE_2D, tex);
+ float sz = 0.35f*min(w, h), x = 0.5f*(w-sz), y = 0.5f*min(w, h) - sz/15;
+ bgquad(x, y, sz, sz);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ settexture("data/mapshot_frame.png", 3);
+ bgquad(x, y, sz, sz);
+ glDisable(GL_BLEND);
+ }
+
+ swapbuffers(false);
}
int keyrepeatmask = 0, textinputmask = 0;
@@ -415,34 +409,29 @@ VAR(textinputfilter, 0, 5, 1000);
void keyrepeat(bool on, int mask)
{
- if(on) keyrepeatmask |= mask;
- else keyrepeatmask &= ~mask;
+ if(on) keyrepeatmask |= mask;
+ else keyrepeatmask &= ~mask;
}
void textinput(bool on, int mask)
{
- if(on)
- {
- if(!textinputmask)
- {
- SDL_StartTextInput();
- textinputtime = SDL_GetTicks();
- }
- textinputmask |= mask;
- }
- else if(textinputmask)
- {
- textinputmask &= ~mask;
- if(!textinputmask) SDL_StopTextInput();
- }
+ if(on)
+ {
+ if(!textinputmask)
+ {
+ SDL_StartTextInput();
+ textinputtime = SDL_GetTicks();
+ }
+ textinputmask |= mask;
+ }
+ else if(textinputmask)
+ {
+ textinputmask &= ~mask;
+ if(!textinputmask) SDL_StopTextInput();
+ }
}
-#ifdef WIN32
-// SDL_WarpMouseInWindow behaves erratically on Windows, so force relative mouse instead.
-VARN(relativemouse, userelativemouse, 1, 1, 0);
-#else
VARNP(relativemouse, userelativemouse, 0, 1, 1);
-#endif
bool shouldgrab = false, grabinput = false, minimized = false, canrelativemouse = true, relativemouse = false;
@@ -453,54 +442,54 @@ VAR(sdl_xgrab_bug, 0, 0, 1);
void inputgrab(bool on, bool delay = false)
{
#ifdef SDL_VIDEO_DRIVER_X11
- bool wasrelativemouse = relativemouse;
+ bool wasrelativemouse = relativemouse;
#endif
- if(on)
- {
- SDL_ShowCursor(SDL_FALSE);
- if(canrelativemouse && userelativemouse)
- {
- if(SDL_SetRelativeMouseMode(SDL_TRUE) >= 0)
- {
- SDL_SetWindowGrab(screen, SDL_TRUE);
- relativemouse = true;
- }
- else
- {
- SDL_SetWindowGrab(screen, SDL_FALSE);
- canrelativemouse = false;
- relativemouse = false;
- }
- }
- }
- else
- {
- SDL_ShowCursor(SDL_TRUE);
- if(relativemouse)
- {
- SDL_SetWindowGrab(screen, SDL_FALSE);
- SDL_SetRelativeMouseMode(SDL_FALSE);
- relativemouse = false;
- }
- }
- shouldgrab = delay;
+ if(on)
+ {
+ SDL_ShowCursor(SDL_FALSE);
+ if(canrelativemouse && userelativemouse)
+ {
+ if(SDL_SetRelativeMouseMode(SDL_TRUE) >= 0)
+ {
+ SDL_SetWindowGrab(screen, SDL_TRUE);
+ relativemouse = true;
+ }
+ else
+ {
+ SDL_SetWindowGrab(screen, SDL_FALSE);
+ canrelativemouse = false;
+ relativemouse = false;
+ }
+ }
+ }
+ else
+ {
+ SDL_ShowCursor(SDL_TRUE);
+ if(relativemouse)
+ {
+ SDL_SetWindowGrab(screen, SDL_FALSE);
+ SDL_SetRelativeMouseMode(SDL_FALSE);
+ relativemouse = false;
+ }
+ }
+ shouldgrab = delay;
#ifdef SDL_VIDEO_DRIVER_X11
- if((relativemouse || wasrelativemouse) && sdl_xgrab_bug)
- {
- // Workaround for buggy SDL X11 pointer grabbing
- union { SDL_SysWMinfo info; uchar buf[sizeof(SDL_SysWMinfo) + 128]; };
- SDL_GetVersion(&info.version);
- if(SDL_GetWindowWMInfo(screen, &info) && info.subsystem == SDL_SYSWM_X11)
- {
- if(relativemouse)
- {
- uint mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
- XGrabPointer(info.info.x11.display, info.info.x11.window, True, mask, GrabModeAsync, GrabModeAsync, info.info.x11.window, None, CurrentTime);
- }
- else XUngrabPointer(info.info.x11.display, CurrentTime);
- }
- }
+ if((relativemouse || wasrelativemouse) && sdl_xgrab_bug)
+ {
+ // Workaround for buggy SDL X11 pointer grabbing
+ union { SDL_SysWMinfo info; uchar buf[sizeof(SDL_SysWMinfo) + 128]; };
+ SDL_GetVersion(&info.version);
+ if(SDL_GetWindowWMInfo(screen, &info) && info.subsystem == SDL_SYSWM_X11)
+ {
+ if(relativemouse)
+ {
+ uint mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
+ XGrabPointer(info.info.x11.display, info.info.x11.window, True, mask, GrabModeAsync, GrabModeAsync, info.info.x11.window, None, CurrentTime);
+ }
+ else XUngrabPointer(info.info.x11.display, CurrentTime);
+ }
+ }
#endif
}
@@ -508,20 +497,20 @@ bool initwindowpos = false;
void setfullscreen(bool enable)
{
- if(!screen) return;
- //initwarning(enable ? "fullscreen" : "windowed");
- extern int fullscreendesktop;
- SDL_SetWindowFullscreen(screen, enable ? (fullscreendesktop ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN) : 0);
- if(!enable)
- {
- SDL_SetWindowSize(screen, scr_w, scr_h);
- if(initwindowpos)
- {
- int winx = SDL_WINDOWPOS_CENTERED, winy = SDL_WINDOWPOS_CENTERED;
- SDL_SetWindowPosition(screen, winx, winy);
- initwindowpos = false;
- }
- }
+ if(!screen) return;
+ //initwarning(enable ? "fullscreen" : "windowed");
+ extern int fullscreendesktop;
+ SDL_SetWindowFullscreen(screen, enable ? (fullscreendesktop ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN) : 0);
+ if(!enable)
+ {
+ SDL_SetWindowSize(screen, scr_w, scr_h);
+ if(initwindowpos)
+ {
+ int winx = SDL_WINDOWPOS_CENTERED, winy = SDL_WINDOWPOS_CENTERED;
+ SDL_SetWindowPosition(screen, winx, winy);
+ initwindowpos = false;
+ }
+ }
}
#ifdef _DEBUG
@@ -532,76 +521,76 @@ VARF(fullscreen, 0, 1, 1, setfullscreen(fullscreen!=0));
void resetfullscreen()
{
- setfullscreen(false);
- setfullscreen(true);
+ setfullscreen(false);
+ setfullscreen(true);
}
VARF(fullscreendesktop, 0, 0, 1, if(fullscreen) resetfullscreen());
void screenres(int w, int h)
{
- scr_w = clamp(w, SCR_MINW, SCR_MAXW);
- scr_h = clamp(h, SCR_MINH, SCR_MAXH);
- if(screen)
- {
- if(fullscreendesktop)
- {
- scr_w = min(scr_w, desktopw);
- scr_h = min(scr_h, desktoph);
- }
- if(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN)
- {
- if(fullscreendesktop) gl_resize();
- else resetfullscreen();
- initwindowpos = true;
- }
- else
- {
- SDL_SetWindowSize(screen, scr_w, scr_h);
- SDL_SetWindowPosition(screen, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
- initwindowpos = false;
- }
- }
- else
- {
- initwarning("screen resolution");
- }
+ scr_w = clamp(w, SCR_MINW, SCR_MAXW);
+ scr_h = clamp(h, SCR_MINH, SCR_MAXH);
+ if(screen)
+ {
+ if(fullscreendesktop)
+ {
+ scr_w = min(scr_w, desktopw);
+ scr_h = min(scr_h, desktoph);
+ }
+ if(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN)
+ {
+ if(fullscreendesktop) gl_resize();
+ else resetfullscreen();
+ initwindowpos = true;
+ }
+ else
+ {
+ SDL_SetWindowSize(screen, scr_w, scr_h);
+ SDL_SetWindowPosition(screen, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
+ initwindowpos = false;
+ }
+ }
+ else
+ {
+ initwarning("screen resolution");
+ }
}
ICOMMAND(screenres, "ii", (int *w, int *h), screenres(*w, *h));
static void setgamma(int val)
{
- if(screen && SDL_SetWindowBrightness(screen, val/100.0f) < 0) conoutf(CON_ERROR, "Could not set gamma: %s", SDL_GetError());
+ if(screen && SDL_SetWindowBrightness(screen, val/100.0f) < 0) conoutf(CON_ERROR, "Could not set gamma: %s", SDL_GetError());
}
static int curgamma = 100;
VARFNP(gamma, reqgamma, 30, 100, 300,
{
- if(initing || reqgamma == curgamma) return;
- curgamma = reqgamma;
- setgamma(curgamma);
+ if(initing || reqgamma == curgamma) return;
+ curgamma = reqgamma;
+ setgamma(curgamma);
});
void restoregamma()
{
- if(initing || reqgamma == 100) return;
- curgamma = reqgamma;
- setgamma(curgamma);
+ if(initing || reqgamma == 100) return;
+ curgamma = reqgamma;
+ setgamma(curgamma);
}
void cleargamma()
{
- if(curgamma != 100 && screen) SDL_SetWindowBrightness(screen, 1.0f);
+ if(curgamma != 100 && screen) SDL_SetWindowBrightness(screen, 1.0f);
}
int curvsync = -1;
void restorevsync()
{
- if(initing || !glcontext) return;
- extern int vsync, vsynctear;
- if(!SDL_GL_SetSwapInterval(vsync ? (vsynctear ? -1 : 1) : 0))
- curvsync = vsync;
+ if(initing || !glcontext) return;
+ extern int vsync, vsynctear;
+ if(!SDL_GL_SetSwapInterval(vsync ? (vsynctear ? -1 : 1) : 0))
+ curvsync = vsync;
}
VARFP(vsync, 0, 0, 1, restorevsync());
@@ -609,168 +598,162 @@ VARFP(vsynctear, 0, 0, 1, { if(vsync) restorevsync(); });
void setupscreen()
{
- if(glcontext)
- {
- SDL_GL_DeleteContext(glcontext);
- glcontext = NULL;
- }
- if(screen)
- {
- SDL_DestroyWindow(screen);
- screen = NULL;
- }
- curvsync = -1;
-
- SDL_Rect desktop;
- if(SDL_GetDisplayBounds(0, &desktop) < 0) fatal("failed querying desktop bounds: %s", SDL_GetError());
- desktopw = desktop.w;
- desktoph = desktop.h;
-
- if(scr_h < 0) scr_h = fullscreen ? desktoph : SCR_DEFAULTH;
- if(scr_w < 0) scr_w = (scr_h*desktopw)/desktoph;
- scr_w = clamp(scr_w, SCR_MINW, SCR_MAXW);
- scr_h = clamp(scr_h, SCR_MINH, SCR_MAXH);
- if(fullscreendesktop)
- {
- scr_w = min(scr_w, desktopw);
- scr_h = min(scr_h, desktoph);
- }
-
- int winx = SDL_WINDOWPOS_UNDEFINED, winy = SDL_WINDOWPOS_UNDEFINED, winw = scr_w, winh = scr_h, flags = SDL_WINDOW_RESIZABLE;
- if(fullscreen)
- {
- if(fullscreendesktop)
- {
- winw = desktopw;
- winh = desktoph;
- flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
- }
- else flags |= SDL_WINDOW_FULLSCREEN;
- initwindowpos = true;
- }
-
- SDL_GL_ResetAttributes();
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
- #if !defined(WIN32) && !defined(__APPLE__)
- SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
- #endif
- static const int configs[] =
- {
- 0x3, /* try everything */
- 0x2, 0x1, /* try disabling one at a time */
- 0 /* try disabling everything */
- };
- int config = 0;
- if(!depthbits) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
- if(!fsaa)
- {
- SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
- SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
- }
- loopi(sizeof(configs)/sizeof(configs[0]))
- {
- config = configs[i];
- if(!depthbits && config&1) continue;
- if(fsaa<=0 && config&2) continue;
- if(depthbits) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, config&1 ? depthbits : 24);
- if(fsaa>0)
- {
- SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, config&2 ? 1 : 0);
- SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config&2 ? fsaa : 0);
- }
- screen = SDL_CreateWindow("Cube 2: Sauerbraten", winx, winy, winw, winh, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS | flags);
- if(!screen) continue;
-
- #ifdef __APPLE__
- static const int glversions[] = { 32, 20 };
- #else
- static const int glversions[] = { 33, 32, 31, 30, 20 };
- #endif
- loopj(sizeof(glversions)/sizeof(glversions[0]))
- {
- glcompat = glversions[j] <= 30 ? 1 : 0;
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, glversions[j] / 10);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, glversions[j] % 10);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, glversions[j] >= 32 ? SDL_GL_CONTEXT_PROFILE_CORE : 0);
- glcontext = SDL_GL_CreateContext(screen);
- if(glcontext) break;
- }
- if(glcontext) break;
- }
- if(!screen) fatal("failed to create OpenGL window: %s", SDL_GetError());
- else if(!glcontext) fatal("failed to create OpenGL context: %s", SDL_GetError());
- else
- {
- if(depthbits && (config&1)==0) conoutf(CON_WARN, "%d bit z-buffer not supported - disabling", depthbits);
- if(fsaa>0 && (config&2)==0) conoutf(CON_WARN, "%dx anti-aliasing not supported - disabling", fsaa);
- }
-
- SDL_SetWindowMinimumSize(screen, SCR_MINW, SCR_MINH);
- SDL_SetWindowMaximumSize(screen, SCR_MAXW, SCR_MAXH);
-
- SDL_GetWindowSize(screen, &screenw, &screenh);
+ if(glcontext)
+ {
+ SDL_GL_DeleteContext(glcontext);
+ glcontext = NULL;
+ }
+ if(screen)
+ {
+ SDL_DestroyWindow(screen);
+ screen = NULL;
+ }
+ curvsync = -1;
+
+ SDL_Rect desktop;
+ if(SDL_GetDisplayBounds(0, &desktop) < 0) fatal("failed querying desktop bounds: %s", SDL_GetError());
+ desktopw = desktop.w;
+ desktoph = desktop.h;
+
+ if(scr_h < 0) scr_h = fullscreen ? desktoph : SCR_DEFAULTH;
+ if(scr_w < 0) scr_w = (scr_h*desktopw)/desktoph;
+ scr_w = clamp(scr_w, SCR_MINW, SCR_MAXW);
+ scr_h = clamp(scr_h, SCR_MINH, SCR_MAXH);
+ if(fullscreendesktop)
+ {
+ scr_w = min(scr_w, desktopw);
+ scr_h = min(scr_h, desktoph);
+ }
+
+ int winx = SDL_WINDOWPOS_UNDEFINED, winy = SDL_WINDOWPOS_UNDEFINED, winw = scr_w, winh = scr_h, flags = SDL_WINDOW_RESIZABLE;
+ if(fullscreen)
+ {
+ if(fullscreendesktop)
+ {
+ winw = desktopw;
+ winh = desktoph;
+ flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
+ }
+ else flags |= SDL_WINDOW_FULLSCREEN;
+ initwindowpos = true;
+ }
+
+ SDL_GL_ResetAttributes();
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ static const int configs[] =
+ {
+ 0x3, /* try everything */
+ 0x2, 0x1, /* try disabling one at a time */
+ 0 /* try disabling everything */
+ };
+ int config = 0;
+ if(!depthbits) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
+ if(!fsaa)
+ {
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
+ }
+ loopi(sizeof(configs)/sizeof(configs[0]))
+ {
+ config = configs[i];
+ if(!depthbits && config&1) continue;
+ if(fsaa<=0 && config&2) continue;
+ if(depthbits) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, config&1 ? depthbits : 24);
+ if(fsaa>0)
+ {
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, config&2 ? 1 : 0);
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config&2 ? fsaa : 0);
+ }
+ screen = SDL_CreateWindow("Cube 2: Sauerbraten", winx, winy, winw, winh, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS | flags);
+ if(!screen) continue;
+
+ static const int glversions[] = { 33, 32, 31, 30, 20 };
+ loopj(sizeof(glversions)/sizeof(glversions[0]))
+ {
+ glcompat = glversions[j] <= 30 ? 1 : 0;
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, glversions[j] / 10);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, glversions[j] % 10);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, glversions[j] >= 32 ? SDL_GL_CONTEXT_PROFILE_CORE : 0);
+ glcontext = SDL_GL_CreateContext(screen);
+ if(glcontext) break;
+ }
+ if(glcontext) break;
+ }
+ if(!screen) fatal("failed to create OpenGL window: %s", SDL_GetError());
+ else if(!glcontext) fatal("failed to create OpenGL context: %s", SDL_GetError());
+ else
+ {
+ if(depthbits && (config&1)==0) conoutf(CON_WARN, "%d bit z-buffer not supported - disabling", depthbits);
+ if(fsaa>0 && (config&2)==0) conoutf(CON_WARN, "%dx anti-aliasing not supported - disabling", fsaa);
+ }
+
+ SDL_SetWindowMinimumSize(screen, SCR_MINW, SCR_MINH);
+ SDL_SetWindowMaximumSize(screen, SCR_MAXW, SCR_MAXH);
+
+ SDL_GetWindowSize(screen, &screenw, &screenh);
}
void resetgl()
{
- clearchanges(CHANGE_GFX);
-
- renderbackground("resetting OpenGL");
-
- extern void cleanupva();
- extern void cleanupparticles();
- extern void cleanupdecals();
- extern void cleanupsky();
- extern void cleanupmodels();
- extern void cleanupprefabs();
- extern void cleanuplightmaps();
- extern void cleanupblendmap();
- extern void cleanshadowmap();
- extern void cleanreflections();
- extern void cleanupglare();
- extern void cleanupdepthfx();
- cleanupva();
- cleanupparticles();
- cleanupdecals();
- cleanupsky();
- cleanupmodels();
- cleanupprefabs();
- cleanuptextures();
- cleanuplightmaps();
- cleanupblendmap();
- cleanshadowmap();
- cleanreflections();
- cleanupglare();
- cleanupdepthfx();
- cleanupshaders();
- cleanupgl();
-
- setupscreen();
- inputgrab(grabinput);
- gl_init();
-
- inbetweenframes = false;
- if(!reloadtexture(*notexture) ||
- !reloadtexture("data/logo.png") ||
- !reloadtexture("data/logo_1024.png") ||
- !reloadtexture("data/background.png") ||
- !reloadtexture("data/background_detail.png") ||
- !reloadtexture("data/background_decal.png") ||
- !reloadtexture("data/mapshot_frame.png") ||
- !reloadtexture("data/loading_frame.png") ||
- !reloadtexture("data/loading_bar.png"))
- fatal("failed to reload core texture");
- reloadfonts();
- inbetweenframes = true;
- renderbackground("initializing...");
- restoregamma();
- restorevsync();
- reloadshaders();
- reloadtextures();
- initlights();
- allchanged(true);
+ clearchanges(CHANGE_GFX);
+
+ renderbackground("resetting OpenGL");
+
+ extern void cleanupva();
+ extern void cleanupparticles();
+ extern void cleanupdecals();
+ extern void cleanupsky();
+ extern void cleanupmodels();
+ extern void cleanupprefabs();
+ extern void cleanuplightmaps();
+ extern void cleanupblendmap();
+ extern void cleanshadowmap();
+ extern void cleanreflections();
+ extern void cleanupglare();
+ extern void cleanupdepthfx();
+ cleanupva();
+ cleanupparticles();
+ cleanupdecals();
+ cleanupsky();
+ cleanupmodels();
+ cleanupprefabs();
+ cleanuptextures();
+ cleanuplightmaps();
+ cleanupblendmap();
+ cleanshadowmap();
+ cleanreflections();
+ cleanupglare();
+ cleanupdepthfx();
+ cleanupshaders();
+ cleanupgl();
+
+ setupscreen();
+ inputgrab(grabinput);
+ gl_init();
+
+ inbetweenframes = false;
+ if(!reloadtexture(*notexture) ||
+ !reloadtexture("data/logo.png") ||
+ !reloadtexture("data/logo_1024.png") ||
+ !reloadtexture("data/background.png") ||
+ !reloadtexture("data/background_detail.png") ||
+ !reloadtexture("data/background_decal.png") ||
+ !reloadtexture("data/mapshot_frame.png") ||
+ !reloadtexture("data/loading_frame.png") ||
+ !reloadtexture("data/loading_bar.png"))
+ fatal("failed to reload core texture");
+ reloadfonts();
+ inbetweenframes = true;
+ renderbackground("initializing...");
+ restoregamma();
+ restorevsync();
+ reloadshaders();
+ reloadtextures();
+ initlights();
+ allchanged(true);
}
COMMAND(resetgl, "");
@@ -779,233 +762,229 @@ static queue<SDL_Event, 32> events;
static inline bool filterevent(const SDL_Event &event)
{
- switch(event.type)
- {
- case SDL_MOUSEMOTION:
- if(grabinput && !relativemouse && !(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
- {
- if(event.motion.x == screenw / 2 && event.motion.y == screenh / 2)
- return false; // ignore any motion events generated by SDL_WarpMouse
- #ifdef __APPLE__
- if(event.motion.y == 0)
- return false; // let mac users drag windows via the title bar
- #endif
- }
- break;
- }
- return true;
+ switch(event.type)
+ {
+ case SDL_MOUSEMOTION:
+ if(grabinput && !relativemouse && !(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
+ {
+ if(event.motion.x == screenw / 2 && event.motion.y == screenh / 2)
+ return false; // ignore any motion events generated by SDL_WarpMouse
+ }
+ break;
+ }
+ return true;
}
template <int SIZE> static inline bool pumpevents(queue<SDL_Event, SIZE> &events)
{
- while(events.empty())
- {
- SDL_PumpEvents();
- databuf<SDL_Event> buf = events.reserve(events.capacity());
- int n = SDL_PeepEvents(buf.getbuf(), buf.remaining(), SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
- if(n <= 0) return false;
- loopi(n) if(filterevent(buf.buf[i])) buf.put(buf.buf[i]);
- events.addbuf(buf);
- }
- return true;
+ while(events.empty())
+ {
+ SDL_PumpEvents();
+ databuf<SDL_Event> buf = events.reserve(events.capacity());
+ int n = SDL_PeepEvents(buf.getbuf(), buf.remaining(), SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
+ if(n <= 0) return false;
+ loopi(n) if(filterevent(buf.buf[i])) buf.put(buf.buf[i]);
+ events.addbuf(buf);
+ }
+ return true;
}
static int interceptkeysym = 0;
static int interceptevents(void *data, SDL_Event *event)
{
- switch(event->type)
- {
- case SDL_MOUSEMOTION: return 0;
- case SDL_KEYDOWN:
- if(event->key.keysym.sym == interceptkeysym)
- {
- interceptkeysym = -interceptkeysym;
- return 0;
- }
- break;
- }
- return 1;
+ switch(event->type)
+ {
+ case SDL_MOUSEMOTION: return 0;
+ case SDL_KEYDOWN:
+ if(event->key.keysym.sym == interceptkeysym)
+ {
+ interceptkeysym = -interceptkeysym;
+ return 0;
+ }
+ break;
+ }
+ return 1;
}
static void clearinterceptkey()
{
- SDL_DelEventWatch(interceptevents, NULL);
- interceptkeysym = 0;
+ SDL_DelEventWatch(interceptevents, NULL);
+ interceptkeysym = 0;
}
bool interceptkey(int sym)
{
- if(!interceptkeysym)
- {
- interceptkeysym = sym;
- SDL_FilterEvents(interceptevents, NULL);
- if(interceptkeysym < 0)
- {
- interceptkeysym = 0;
- return true;
- }
- SDL_AddEventWatch(interceptevents, NULL);
- }
- else if(abs(interceptkeysym) != sym) interceptkeysym = sym;
- SDL_PumpEvents();
- if(interceptkeysym < 0)
- {
- clearinterceptkey();
- interceptkeysym = sym;
- SDL_FilterEvents(interceptevents, NULL);
- interceptkeysym = 0;
- return true;
- }
- return false;
+ if(!interceptkeysym)
+ {
+ interceptkeysym = sym;
+ SDL_FilterEvents(interceptevents, NULL);
+ if(interceptkeysym < 0)
+ {
+ interceptkeysym = 0;
+ return true;
+ }
+ SDL_AddEventWatch(interceptevents, NULL);
+ }
+ else if(abs(interceptkeysym) != sym) interceptkeysym = sym;
+ SDL_PumpEvents();
+ if(interceptkeysym < 0)
+ {
+ clearinterceptkey();
+ interceptkeysym = sym;
+ SDL_FilterEvents(interceptevents, NULL);
+ interceptkeysym = 0;
+ return true;
+ }
+ return false;
}
static void ignoremousemotion()
{
- SDL_PumpEvents();
- SDL_FlushEvent(SDL_MOUSEMOTION);
+ SDL_PumpEvents();
+ SDL_FlushEvent(SDL_MOUSEMOTION);
}
static void resetmousemotion()
{
- if(grabinput && !relativemouse && !(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
- {
- SDL_WarpMouseInWindow(screen, screenw / 2, screenh / 2);
- }
+ if(grabinput && !relativemouse && !(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
+ {
+ SDL_WarpMouseInWindow(screen, screenw / 2, screenh / 2);
+ }
}
static void checkmousemotion(int &dx, int &dy)
{
- while(pumpevents(events))
- {
- SDL_Event &event = events.removing();
- if(event.type != SDL_MOUSEMOTION) return;
- dx += event.motion.xrel;
- dy += event.motion.yrel;
- events.remove();
- }
+ while(pumpevents(events))
+ {
+ SDL_Event &event = events.removing();
+ if(event.type != SDL_MOUSEMOTION) return;
+ dx += event.motion.xrel;
+ dy += event.motion.yrel;
+ events.remove();
+ }
}
void checkinput()
{
- if(interceptkeysym) clearinterceptkey();
- //int lasttype = 0, lastbut = 0;
- bool mousemoved = false;
- int focused = 0;
- while(pumpevents(events))
- {
- SDL_Event &event = events.remove();
-
- if(focused && event.type!=SDL_WINDOWEVENT) { if(grabinput != (focused>0)) inputgrab(grabinput = focused>0, shouldgrab); focused = 0; }
-
- switch(event.type)
- {
- case SDL_QUIT:
- quit();
- return;
-
- case SDL_TEXTINPUT:
- if(textinputmask && int(event.text.timestamp-textinputtime) >= textinputfilter)
- {
- uchar buf[SDL_TEXTINPUTEVENT_TEXT_SIZE+1];
- size_t len = decodeutf8(buf, sizeof(buf)-1, (const uchar *)event.text.text, strlen(event.text.text));
- if(len > 0) { buf[len] = '\0'; processtextinput((const char *)buf, len); }
- }
- break;
-
- case SDL_KEYDOWN:
- case SDL_KEYUP:
- if(keyrepeatmask || !event.key.repeat)
- processkey(event.key.keysym.sym, event.key.state==SDL_PRESSED, event.key.keysym.mod | SDL_GetModState());
- break;
-
- case SDL_WINDOWEVENT:
- switch(event.window.event)
- {
- case SDL_WINDOWEVENT_CLOSE:
- quit();
- break;
-
- case SDL_WINDOWEVENT_FOCUS_GAINED:
- shouldgrab = true;
- break;
- case SDL_WINDOWEVENT_ENTER:
- shouldgrab = false;
- focused = 1;
- break;
-
- case SDL_WINDOWEVENT_LEAVE:
- case SDL_WINDOWEVENT_FOCUS_LOST:
- shouldgrab = false;
- focused = -1;
- break;
-
- case SDL_WINDOWEVENT_MINIMIZED:
- minimized = true;
- break;
-
- case SDL_WINDOWEVENT_MAXIMIZED:
- case SDL_WINDOWEVENT_RESTORED:
- minimized = false;
- break;
-
- case SDL_WINDOWEVENT_RESIZED:
- break;
-
- case SDL_WINDOWEVENT_SIZE_CHANGED:
- {
- SDL_GetWindowSize(screen, &screenw, &screenh);
- if(!fullscreendesktop || !(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
- {
- scr_w = clamp(screenw, SCR_MINW, SCR_MAXW);
- scr_h = clamp(screenh, SCR_MINH, SCR_MAXH);
- }
- gl_resize();
- break;
- }
- }
- break;
-
- case SDL_MOUSEMOTION:
- if(grabinput)
- {
- int dx = event.motion.xrel, dy = event.motion.yrel;
- checkmousemotion(dx, dy);
- if(!g3d_movecursor(dx, dy)) mousemove(dx, dy);
- mousemoved = true;
- }
- else if(shouldgrab) inputgrab(grabinput = true);
- break;
-
- case SDL_MOUSEBUTTONDOWN:
- case SDL_MOUSEBUTTONUP:
- //if(lasttype==event.type && lastbut==event.button.button) break; // why?? get event twice without it
- switch(event.button.button)
- {
- case SDL_BUTTON_LEFT: processkey(-1, event.button.state==SDL_PRESSED); break;
- case SDL_BUTTON_MIDDLE: processkey(-2, event.button.state==SDL_PRESSED); break;
- case SDL_BUTTON_RIGHT: processkey(-3, event.button.state==SDL_PRESSED); break;
- case SDL_BUTTON_X1: processkey(-6, event.button.state==SDL_PRESSED); break;
- case SDL_BUTTON_X2: processkey(-7, event.button.state==SDL_PRESSED); break;
- }
- //lasttype = event.type;
- //lastbut = event.button.button;
- break;
-
- case SDL_MOUSEWHEEL:
- if(event.wheel.y > 0) { processkey(-4, true); processkey(-4, false); }
- else if(event.wheel.y < 0) { processkey(-5, true); processkey(-5, false); }
- break;
- }
- }
- if(focused) { if(grabinput != (focused>0)) inputgrab(grabinput = focused>0, shouldgrab); focused = 0; }
- if(mousemoved) resetmousemotion();
+ if(interceptkeysym) clearinterceptkey();
+ //int lasttype = 0, lastbut = 0;
+ bool mousemoved = false;
+ int focused = 0;
+ while(pumpevents(events))
+ {
+ SDL_Event &event = events.remove();
+
+ if(focused && event.type!=SDL_WINDOWEVENT) { if(grabinput != (focused>0)) inputgrab(grabinput = focused>0, shouldgrab); focused = 0; }
+
+ switch(event.type)
+ {
+ case SDL_QUIT:
+ quit();
+ return;
+
+ case SDL_TEXTINPUT:
+ if(textinputmask && int(event.text.timestamp-textinputtime) >= textinputfilter)
+ {
+ uchar buf[SDL_TEXTINPUTEVENT_TEXT_SIZE+1];
+ size_t len = decodeutf8(buf, sizeof(buf)-1, (const uchar *)event.text.text, strlen(event.text.text));
+ if(len > 0) { buf[len] = '\0'; processtextinput((const char *)buf, len); }
+ }
+ break;
+
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ if(keyrepeatmask || !event.key.repeat)
+ processkey(event.key.keysym.sym, event.key.state==SDL_PRESSED, event.key.keysym.mod | SDL_GetModState());
+ break;
+
+ case SDL_WINDOWEVENT:
+ switch(event.window.event)
+ {
+ case SDL_WINDOWEVENT_CLOSE:
+ quit();
+ break;
+
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ shouldgrab = true;
+ break;
+ case SDL_WINDOWEVENT_ENTER:
+ shouldgrab = false;
+ focused = 1;
+ break;
+
+ case SDL_WINDOWEVENT_LEAVE:
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ shouldgrab = false;
+ focused = -1;
+ break;
+
+ case SDL_WINDOWEVENT_MINIMIZED:
+ minimized = true;
+ break;
+
+ case SDL_WINDOWEVENT_MAXIMIZED:
+ case SDL_WINDOWEVENT_RESTORED:
+ minimized = false;
+ break;
+
+ case SDL_WINDOWEVENT_RESIZED:
+ break;
+
+ case SDL_WINDOWEVENT_SIZE_CHANGED:
+ {
+ SDL_GetWindowSize(screen, &screenw, &screenh);
+ if(!fullscreendesktop || !(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN))
+ {
+ scr_w = clamp(screenw, SCR_MINW, SCR_MAXW);
+ scr_h = clamp(screenh, SCR_MINH, SCR_MAXH);
+ }
+ gl_resize();
+ break;
+ }
+ }
+ break;
+
+ case SDL_MOUSEMOTION:
+ if(grabinput)
+ {
+ int dx = event.motion.xrel, dy = event.motion.yrel;
+ checkmousemotion(dx, dy);
+ if(!g3d_movecursor(dx, dy)) mousemove(dx, dy);
+ mousemoved = true;
+ }
+ else if(shouldgrab) inputgrab(grabinput = true);
+ break;
+
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ //if(lasttype==event.type && lastbut==event.button.button) break; // why?? get event twice without it
+ switch(event.button.button)
+ {
+ case SDL_BUTTON_LEFT: processkey(-1, event.button.state==SDL_PRESSED); break;
+ case SDL_BUTTON_MIDDLE: processkey(-2, event.button.state==SDL_PRESSED); break;
+ case SDL_BUTTON_RIGHT: processkey(-3, event.button.state==SDL_PRESSED); break;
+ case SDL_BUTTON_X1: processkey(-6, event.button.state==SDL_PRESSED); break;
+ case SDL_BUTTON_X2: processkey(-7, event.button.state==SDL_PRESSED); break;
+ }
+ //lasttype = event.type;
+ //lastbut = event.button.button;
+ break;
+
+ case SDL_MOUSEWHEEL:
+ if(event.wheel.y > 0) { processkey(-4, true); processkey(-4, false); }
+ else if(event.wheel.y < 0) { processkey(-5, true); processkey(-5, false); }
+ break;
+ }
+ }
+ if(focused) { if(grabinput != (focused>0)) inputgrab(grabinput = focused>0, shouldgrab); focused = 0; }
+ if(mousemoved) resetmousemotion();
}
void swapbuffers(bool overlay)
{
- gle::disable();
- SDL_GL_SwapWindow(screen);
+ gle::disable();
+ SDL_GL_SwapWindow(screen);
}
VAR(menufps, 0, 60, 1000);
@@ -1013,68 +992,26 @@ VARP(maxfps, 0, 200, 1000);
void limitfps(int &millis, int curmillis)
{
- int limit = (mainmenu || minimized) && menufps ? (maxfps ? min(maxfps, menufps) : menufps) : maxfps;
- if(!limit) return;
- static int fpserror = 0;
- int delay = 1000/limit - (millis-curmillis);
- if(delay < 0) fpserror = 0;
- else
- {
- fpserror += 1000%limit;
- if(fpserror >= limit)
- {
- ++delay;
- fpserror -= limit;
- }
- if(delay > 0)
- {
- SDL_Delay(delay);
- millis += delay;
- }
- }
-}
-
-#if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__)
-void stackdumper(unsigned int type, EXCEPTION_POINTERS *ep)
-{
- if(!ep) fatal("unknown type");
- EXCEPTION_RECORD *er = ep->ExceptionRecord;
- CONTEXT *context = ep->ContextRecord;
- char out[512];
- formatstring(out, "Cube 2: Sauerbraten Win32 Exception: 0x%x [0x%x]\n\n", er->ExceptionCode, er->ExceptionCode==EXCEPTION_ACCESS_VIOLATION ? er->ExceptionInformation[1] : -1);
- SymInitialize(GetCurrentProcess(), NULL, TRUE);
-#ifdef _AMD64_
- STACKFRAME64 sf = {{context->Rip, 0, AddrModeFlat}, {}, {context->Rbp, 0, AddrModeFlat}, {context->Rsp, 0, AddrModeFlat}, 0};
- while(::StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(), &sf, context, NULL, ::SymFunctionTableAccess, ::SymGetModuleBase, NULL))
+ int limit = (mainmenu || minimized) && menufps ? (maxfps ? min(maxfps, menufps) : menufps) : maxfps;
+ if(!limit) return;
+ static int fpserror = 0;
+ int delay = 1000/limit - (millis-curmillis);
+ if(delay < 0) fpserror = 0;
+ else
{
- union { IMAGEHLP_SYMBOL64 sym; char symext[sizeof(IMAGEHLP_SYMBOL64) + sizeof(string)]; };
- sym.SizeOfStruct = sizeof(sym);
- sym.MaxNameLength = sizeof(symext) - sizeof(sym);
- IMAGEHLP_LINE64 line;
- line.SizeOfStruct = sizeof(line);
- DWORD64 symoff;
- DWORD lineoff;
- if(SymGetSymFromAddr64(GetCurrentProcess(), sf.AddrPC.Offset, &symoff, &sym) && SymGetLineFromAddr64(GetCurrentProcess(), sf.AddrPC.Offset, &lineoff, &line))
-#else
- STACKFRAME sf = {{context->Eip, 0, AddrModeFlat}, {}, {context->Ebp, 0, AddrModeFlat}, {context->Esp, 0, AddrModeFlat}, 0};
- while(::StackWalk(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &sf, context, NULL, ::SymFunctionTableAccess, ::SymGetModuleBase, NULL))
- {
- union { IMAGEHLP_SYMBOL sym; char symext[sizeof(IMAGEHLP_SYMBOL) + sizeof(string)]; };
- sym.SizeOfStruct = sizeof(sym);
- sym.MaxNameLength = sizeof(symext) - sizeof(sym);
- IMAGEHLP_LINE line;
- line.SizeOfStruct = sizeof(line);
- DWORD symoff, lineoff;
- if(SymGetSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, &symoff, &sym) && SymGetLineFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, &lineoff, &line))
-#endif
- {
- char *del = strrchr(line.FileName, '\\');
- concformatstring(out, "%s - %s [%d]\n", sym.Name, del ? del + 1 : line.FileName, line.LineNumber);
- }
- }
- fatal(out);
+ fpserror += 1000%limit;
+ if(fpserror >= limit)
+ {
+ ++delay;
+ fpserror -= limit;
+ }
+ if(delay > 0)
+ {
+ SDL_Delay(delay);
+ millis += delay;
+ }
+ }
}
-#endif
#define MAXFPSHISTORY 60
@@ -1082,38 +1019,38 @@ int fpspos = 0, fpshistory[MAXFPSHISTORY];
void resetfpshistory()
{
- loopi(MAXFPSHISTORY) fpshistory[i] = 1;
- fpspos = 0;
+ loopi(MAXFPSHISTORY) fpshistory[i] = 1;
+ fpspos = 0;
}
void updatefpshistory(int millis)
{
- fpshistory[fpspos++] = max(1, min(1000, millis));
- if(fpspos>=MAXFPSHISTORY) fpspos = 0;
+ fpshistory[fpspos++] = max(1, min(1000, millis));
+ if(fpspos>=MAXFPSHISTORY) fpspos = 0;
}
void getfps(int &fps, int &bestdiff, int &worstdiff)
{
- int total = fpshistory[MAXFPSHISTORY-1], best = total, worst = total;
- loopi(MAXFPSHISTORY-1)
- {
- int millis = fpshistory[i];
- total += millis;
- if(millis < best) best = millis;
- if(millis > worst) worst = millis;
- }
-
- fps = (1000*MAXFPSHISTORY)/total;
- bestdiff = 1000/best-fps;
- worstdiff = fps-1000/worst;
+ int total = fpshistory[MAXFPSHISTORY-1], best = total, worst = total;
+ loopi(MAXFPSHISTORY-1)
+ {
+ int millis = fpshistory[i];
+ total += millis;
+ if(millis < best) best = millis;
+ if(millis > worst) worst = millis;
+ }
+
+ fps = (1000*MAXFPSHISTORY)/total;
+ bestdiff = 1000/best-fps;
+ worstdiff = fps-1000/worst;
}
void getfps_(int *raw)
{
- int fps, bestdiff, worstdiff;
- if(*raw) fps = 1000/fpshistory[(fpspos+MAXFPSHISTORY-1)%MAXFPSHISTORY];
- else getfps(fps, bestdiff, worstdiff);
- intret(fps);
+ int fps, bestdiff, worstdiff;
+ if(*raw) fps = 1000/fpshistory[(fpspos+MAXFPSHISTORY-1)%MAXFPSHISTORY];
+ else getfps(fps, bestdiff, worstdiff);
+ intret(fps);
}
COMMANDN(getfps, getfps_, "i");
@@ -1122,8 +1059,8 @@ bool inbetweenframes = false, renderedframe = true;
static bool findarg(int argc, char **argv, const char *str)
{
- for(int i = 1; i<argc; i++) if(strstr(argv[i], str)==argv[i]) return true;
- return false;
+ for(int i = 1; i<argc; i++) if(strstr(argv[i], str)==argv[i]) return true;
+ return false;
}
static int clockrealbase = 0, clockvirtbase = 0;
@@ -1133,239 +1070,224 @@ VARFP(clockfix, 0, 0, 1, clockreset());
int getclockmillis()
{
- int millis = SDL_GetTicks() - clockrealbase;
- if(clockfix) millis = int(millis*(double(clockerror)/1000000));
- millis += clockvirtbase;
- return max(millis, totalmillis);
+ int millis = SDL_GetTicks() - clockrealbase;
+ if(clockfix) millis = int(millis*(double(clockerror)/1000000));
+ millis += clockvirtbase;
+ return max(millis, totalmillis);
}
VAR(numcpus, 1, 1, 16);
int main(int argc, char **argv)
{
- #ifdef WIN32
- //atexit((void (__cdecl *)(void))_CrtDumpMemoryLeaks);
- #ifndef _DEBUG
- #ifndef __GNUC__
- __try {
- #endif
- #endif
- #endif
-
- setlogfile(NULL);
-
- int dedicated = 0;
- char *load = NULL, *initscript = NULL;
-
- initing = INIT_RESET;
- // set home dir first
- for(int i = 1; i<argc; i++) if(argv[i][0]=='-' && argv[i][1] == 'q') { sethomedir(&argv[i][2]); break; }
- // set log after home dir, but before anything else
- for(int i = 1; i<argc; i++) if(argv[i][0]=='-' && argv[i][1] == 'g')
- {
- const char *file = argv[i][2] ? &argv[i][2] : "log.txt";
- setlogfile(file);
- logoutf("Setting log file: %s", file);
- break;
- }
- execfile("init.cfg", false);
- for(int i = 1; i<argc; i++)
- {
- if(argv[i][0]=='-') switch(argv[i][1])
- {
- case 'q': if(homedir[0]) logoutf("Using home directory: %s", homedir); break;
- case 'r': /* compat, ignore */ break;
- case 'k':
- {
- const char *dir = addpackagedir(&argv[i][2]);
- if(dir) logoutf("Adding package directory: %s", dir);
- break;
- }
- case 'g': break;
- case 'd': dedicated = atoi(&argv[i][2]); if(dedicated<=0) dedicated = 2; break;
- case 'w': scr_w = clamp(atoi(&argv[i][2]), SCR_MINW, SCR_MAXW); if(!findarg(argc, argv, "-h")) scr_h = -1; break;
- case 'h': scr_h = clamp(atoi(&argv[i][2]), SCR_MINH, SCR_MAXH); if(!findarg(argc, argv, "-w")) scr_w = -1; break;
- case 'z': depthbits = atoi(&argv[i][2]); break;
- case 'b': /* compat, ignore */ break;
- case 'a': fsaa = atoi(&argv[i][2]); break;
- case 'v': /* compat, ignore */ break;
- case 't': fullscreen = atoi(&argv[i][2]); break;
- case 's': /* compat, ignore */ break;
- case 'f': /* compat, ignore */ break;
- case 'l':
- {
- char pkgdir[] = "packages/";
- load = strstr(path(&argv[i][2]), path(pkgdir));
- if(load) load += sizeof(pkgdir)-1;
- else load = &argv[i][2];
- break;
- }
- case 'x': initscript = &argv[i][2]; break;
- default: if(!serveroption(argv[i])) gameargs.add(argv[i]); break;
- }
- else gameargs.add(argv[i]);
- }
- initing = NOT_INITING;
-
- numcpus = clamp(SDL_GetCPUCount(), 1, 16);
-
- if(dedicated <= 1)
- {
- logoutf("init: sdl");
-
- if(SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO|SDL_INIT_AUDIO)<0) fatal("Unable to initialize SDL: %s", SDL_GetError());
+ setlogfile(NULL);
+
+ int dedicated = 0;
+ char *load = NULL, *initscript = NULL;
+
+ initing = INIT_RESET;
+ // set home dir first
+ for(int i = 1; i<argc; i++) if(argv[i][0]=='-' && argv[i][1] == 'q') { sethomedir(&argv[i][2]); break; }
+ // set log after home dir, but before anything else
+ for(int i = 1; i<argc; i++) if(argv[i][0]=='-' && argv[i][1] == 'g')
+ {
+ const char *file = argv[i][2] ? &argv[i][2] : "log.txt";
+ setlogfile(file);
+ logoutf("Setting log file: %s", file);
+ break;
+ }
+ execfile("init.cfg", false);
+ for(int i = 1; i<argc; i++)
+ {
+ if(argv[i][0]=='-') switch(argv[i][1])
+ {
+ case 'q': if(homedir[0]) logoutf("Using home directory: %s", homedir); break;
+ case 'r': /* compat, ignore */ break;
+ case 'k':
+ {
+ const char *dir = addpackagedir(&argv[i][2]);
+ if(dir) logoutf("Adding package directory: %s", dir);
+ break;
+ }
+ case 'g': break;
+ case 'd': dedicated = atoi(&argv[i][2]); if(dedicated<=0) dedicated = 2; break;
+ case 'w': scr_w = clamp(atoi(&argv[i][2]), SCR_MINW, SCR_MAXW); if(!findarg(argc, argv, "-h")) scr_h = -1; break;
+ case 'h': scr_h = clamp(atoi(&argv[i][2]), SCR_MINH, SCR_MAXH); if(!findarg(argc, argv, "-w")) scr_w = -1; break;
+ case 'z': depthbits = atoi(&argv[i][2]); break;
+ case 'b': /* compat, ignore */ break;
+ case 'a': fsaa = atoi(&argv[i][2]); break;
+ case 'v': /* compat, ignore */ break;
+ case 't': fullscreen = atoi(&argv[i][2]); break;
+ case 's': /* compat, ignore */ break;
+ case 'f': /* compat, ignore */ break;
+ case 'l':
+ {
+ char pkgdir[] = "packages/";
+ load = strstr(path(&argv[i][2]), path(pkgdir));
+ if(load) load += sizeof(pkgdir)-1;
+ else load = &argv[i][2];
+ break;
+ }
+ case 'x': initscript = &argv[i][2]; break;
+ default: if(!serveroption(argv[i])) gameargs.add(argv[i]); break;
+ }
+ else gameargs.add(argv[i]);
+ }
+ initing = NOT_INITING;
+
+ numcpus = clamp(SDL_GetCPUCount(), 1, 16);
+
+ if(dedicated <= 1)
+ {
+ logoutf("init: sdl");
+
+ if(SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO|SDL_INIT_AUDIO)<0) fatal("Unable to initialize SDL: %s", SDL_GetError());
#ifdef SDL_VIDEO_DRIVER_X11
- SDL_version version;
- SDL_GetVersion(&version);
- if (SDL_VERSIONNUM(version.major, version.minor, version.patch) <= SDL_VERSIONNUM(2, 0, 12))
- sdl_xgrab_bug = 1;
+ SDL_version version;
+ SDL_GetVersion(&version);
+ if (SDL_VERSIONNUM(version.major, version.minor, version.patch) <= SDL_VERSIONNUM(2, 0, 12))
+ sdl_xgrab_bug = 1;
#endif
- }
-
- logoutf("init: net");
- if(enet_initialize()<0) fatal("Unable to initialise network module");
- atexit(enet_deinitialize);
- enet_time_set(0);
-
- logoutf("init: game");
- game::parseoptions(gameargs);
- initserver(dedicated>0, dedicated>1); // never returns if dedicated
- ASSERT(dedicated <= 1);
- game::initclient();
-
- logoutf("init: video");
- SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "0");
- #if !defined(WIN32) && !defined(__APPLE__)
- SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
- #endif
- setupscreen();
- SDL_ShowCursor(SDL_FALSE);
- SDL_StopTextInput(); // workaround for spurious text-input events getting sent on first text input toggle?
-
- logoutf("init: gl");
- gl_checkextensions();
- gl_init();
- notexture = textureload("packages/textures/texture_error.png");
- if(!notexture) fatal("could not find core textures");
-
- logoutf("init: console");
- if(!execfile("data/stdlib.cfg", false)) fatal("cannot find data files (you are running from the wrong folder, try .bat file in the main folder)"); // this is the first file we load.
- if(!execfile("packages/fonts/default.cfg", false)) fatal("cannot find font definitions");
- if(!setfont("default")) fatal("no default font specified");
-
- inbetweenframes = true;
- renderbackground("initializing...");
-
- logoutf("init: world");
- camera1 = player = game::iterdynents(0);
- emptymap(0, true, NULL, false);
-
- logoutf("init: sound");
- initsound();
-
- logoutf("init: cfg");
- initing = INIT_LOAD;
- execfile("data/keymap.cfg");
- execfile("data/stdedit.cfg");
- execfile("data/sounds.cfg");
- execfile("data/menus.cfg");
- execfile("data/heightmap.cfg");
- execfile("data/brushes.cfg");
- execfile("data/game.cfg");
- execfile("data/custom_maps_menu.cfg");
- if(game::savedservers()) execfile(game::savedservers(), false);
-
- identflags |= IDF_PERSIST;
-
- if(!execfile(game::savedconfig(), false))
- {
- execfile(game::defaultconfig());
- writecfg(game::restoreconfig());
- }
- execfile(game::autoexec(), false);
-
- identflags &= ~IDF_PERSIST;
-
- initing = INIT_GAME;
- game::loadconfigs();
-
- initing = NOT_INITING;
-
- logoutf("init: render");
- restoregamma();
- restorevsync();
- loadshaders();
- initparticles();
- initdecals();
-
- identflags |= IDF_PERSIST;
-
- logoutf("init: mainloop");
-
- if(execfile("once.cfg", false)) remove(findfile("once.cfg", "rb"));
-
- if(load)
- {
- logoutf("init: localconnect");
- //localconnect();
- game::changemap(load);
- }
-
- if(initscript) execute(initscript);
-
- resetfpshistory();
-
- inputgrab(grabinput = true);
- ignoremousemotion();
-
- for(;;)
- {
- static int frames = 0;
- int millis = getclockmillis();
- limitfps(millis, totalmillis);
- elapsedtime = millis - totalmillis;
- static int timeerr = 0;
- int scaledtime = game::scaletime(elapsedtime) + timeerr;
- curtime = scaledtime/100;
- timeerr = scaledtime%100;
- if(!multiplayer(false) && curtime>200) curtime = 200;
- if(game::ispaused()) curtime = 0;
- lastmillis += curtime;
- totalmillis = millis;
- updatetime();
+ }
+
+ logoutf("init: net");
+ if(enet_initialize()<0) fatal("Unable to initialise network module");
+ atexit(enet_deinitialize);
+ enet_time_set(0);
+
+ logoutf("init: game");
+ game::parseoptions(gameargs);
+ initserver(dedicated>0, dedicated>1); // never returns if dedicated
+ ASSERT(dedicated <= 1);
+ game::initclient();
+
+ logoutf("init: video");
+ SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "0");
+ SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
+ setupscreen();
+ SDL_ShowCursor(SDL_FALSE);
+ SDL_StopTextInput(); // workaround for spurious text-input events getting sent on first text input toggle?
+
+ logoutf("init: gl");
+ gl_checkextensions();
+ gl_init();
+ notexture = textureload("packages/textures/texture_error.png");
+ if(!notexture) fatal("could not find core textures");
+
+ logoutf("init: console");
+ if(!execfile("data/stdlib.cfg", false)) fatal("cannot find data files (you are running from the wrong folder, try .bat file in the main folder)"); // this is the first file we load.
+ if(!execfile("packages/fonts/default.cfg", false)) fatal("cannot find font definitions");
+ if(!setfont("default")) fatal("no default font specified");
+
+ inbetweenframes = true;
+ renderbackground("initializing...");
+
+ logoutf("init: world");
+ camera1 = player = game::iterdynents(0);
+ emptymap(0, true, NULL, false);
+
+ logoutf("init: sound");
+ initsound();
+
+ logoutf("init: cfg");
+ initing = INIT_LOAD;
+ execfile("data/keymap.cfg");
+ execfile("data/stdedit.cfg");
+ execfile("data/sounds.cfg");
+ execfile("data/menus.cfg");
+ execfile("data/heightmap.cfg");
+ execfile("data/brushes.cfg");
+ execfile("data/game.cfg");
+ execfile("data/custom_maps_menu.cfg");
+ if(game::savedservers()) execfile(game::savedservers(), false);
+
+ identflags |= IDF_PERSIST;
+
+ if(!execfile(game::savedconfig(), false))
+ {
+ execfile(game::defaultconfig());
+ writecfg(game::restoreconfig());
+ }
+ execfile(game::autoexec(), false);
+
+ identflags &= ~IDF_PERSIST;
+
+ initing = INIT_GAME;
+ game::loadconfigs();
+
+ initing = NOT_INITING;
+
+ logoutf("init: render");
+ restoregamma();
+ restorevsync();
+ loadshaders();
+ initparticles();
+ initdecals();
+
+ identflags |= IDF_PERSIST;
- checkinput();
- menuprocess();
- tryedit();
+ logoutf("init: mainloop");
+
+ if(execfile("once.cfg", false)) remove(findfile("once.cfg", "rb"));
+
+ if(load)
+ {
+ logoutf("init: localconnect");
+ //localconnect();
+ game::changemap(load);
+ }
+
+ if(initscript) execute(initscript);
+
+ resetfpshistory();
+
+ inputgrab(grabinput = true);
+ ignoremousemotion();
+
+ for(;;)
+ {
+ static int frames = 0;
+ int millis = getclockmillis();
+ limitfps(millis, totalmillis);
+ elapsedtime = millis - totalmillis;
+ static int timeerr = 0;
+ int scaledtime = game::scaletime(elapsedtime) + timeerr;
+ curtime = scaledtime/100;
+ timeerr = scaledtime%100;
+ if(!multiplayer(false) && curtime>200) curtime = 200;
+ if(game::ispaused()) curtime = 0;
+ lastmillis += curtime;
+ totalmillis = millis;
+ updatetime();
- if(lastmillis) game::updateworld();
+ checkinput();
+ menuprocess();
+ tryedit();
- checksleep(lastmillis);
+ if(lastmillis) game::updateworld();
- serverslice(false, 0);
+ checksleep(lastmillis);
- if(frames) updatefpshistory(elapsedtime);
- frames++;
+ serverslice(false, 0);
- // miscellaneous general game effects
- recomputecamera();
- updateparticles();
- updatesounds();
+ if(frames) updatefpshistory(elapsedtime);
+ frames++;
- if(minimized) continue;
+ // miscellaneous general game effects
+ recomputecamera();
+ updateparticles();
+ updatesounds();
- inbetweenframes = false;
- if(mainmenu) gl_drawmainmenu();
- else gl_drawframe();
- swapbuffers();
- renderedframe = inbetweenframes = true;
- }
+ if(minimized) continue;
- ASSERT(0);
- return EXIT_FAILURE;
+ inbetweenframes = false;
+ if(mainmenu) gl_drawmainmenu();
+ else gl_drawframe();
+ swapbuffers();
+ renderedframe = inbetweenframes = true;
+ }
- #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__)
- } __except(stackdumper(0, GetExceptionInformation()), EXCEPTION_CONTINUE_SEARCH) { return 0; }
- #endif
+ ASSERT(0);
+ return EXIT_FAILURE;
}
diff --git a/src/engine/master.cpp b/src/engine/master.cpp
index 6eb6fe6..2bf1dba 100644
--- a/src/engine/master.cpp
+++ b/src/engine/master.cpp
@@ -24,24 +24,24 @@ FILE *logfile = NULL;
struct userinfo
{
- char *name;
- void *pubkey;
+ char *name;
+ void *pubkey;
};
hashnameset<userinfo> users;
void adduser(char *name, char *pubkey)
{
- name = newstring(name);
- userinfo &u = users[name];
- u.name = name;
- u.pubkey = parsepubkey(pubkey);
+ name = newstring(name);
+ userinfo &u = users[name];
+ u.name = name;
+ u.pubkey = parsepubkey(pubkey);
}
COMMAND(adduser, "ss");
void clearusers()
{
- enumerate(users, userinfo, u, { delete[] u.name; freepubkey(u.pubkey); });
- users.clear();
+ enumerate(users, userinfo, u, { delete[] u.name; freepubkey(u.pubkey); });
+ users.clear();
}
COMMAND(clearusers, "");
@@ -49,17 +49,17 @@ vector<ipmask> bans, servbans, gbans;
void clearbans()
{
- bans.shrink(0);
- servbans.shrink(0);
- gbans.shrink(0);
+ bans.shrink(0);
+ servbans.shrink(0);
+ gbans.shrink(0);
}
COMMAND(clearbans, "");
void addban(vector<ipmask> &bans, const char *name)
{
- ipmask ban;
- ban.parse(name);
- bans.add(ban);
+ ipmask ban;
+ ban.parse(name);
+ bans.add(ban);
}
ICOMMAND(ban, "s", (char *name), addban(bans, name));
ICOMMAND(servban, "s", (char *name), addban(servbans, name));
@@ -67,73 +67,73 @@ ICOMMAND(gban, "s", (char *name), addban(gbans, name));
bool checkban(vector<ipmask> &bans, enet_uint32 host)
{
- loopv(bans) if(bans[i].check(host)) return true;
- return false;
+ loopv(bans) if(bans[i].check(host)) return true;
+ return false;
}
struct authreq
{
- enet_uint32 reqtime;
- uint id;
- void *answer;
+ enet_uint32 reqtime;
+ uint id;
+ void *answer;
};
struct gameserver
{
- ENetAddress address;
- string ip;
- int port, numpings;
- enet_uint32 lastping, lastpong;
+ ENetAddress address;
+ string ip;
+ int port, numpings;
+ enet_uint32 lastping, lastpong;
};
vector<gameserver *> gameservers;
struct messagebuf
{
- vector<messagebuf *> &owner;
- vector<char> buf;
- int refs;
+ vector<messagebuf *> &owner;
+ vector<char> buf;
+ int refs;
- messagebuf(vector<messagebuf *> &owner) : owner(owner), refs(0) {}
+ messagebuf(vector<messagebuf *> &owner) : owner(owner), refs(0) {}
- const char *getbuf() { return buf.getbuf(); }
- int length() { return buf.length(); }
- void purge();
+ const char *getbuf() { return buf.getbuf(); }
+ int length() { return buf.length(); }
+ void purge();
- bool equals(const messagebuf &m) const
- {
- return buf.length() == m.buf.length() && !memcmp(buf.getbuf(), m.buf.getbuf(), buf.length());
- }
+ bool equals(const messagebuf &m) const
+ {
+ return buf.length() == m.buf.length() && !memcmp(buf.getbuf(), m.buf.getbuf(), buf.length());
+ }
- bool endswith(const messagebuf &m) const
- {
- return buf.length() >= m.buf.length() && !memcmp(&buf[buf.length() - m.buf.length()], m.buf.getbuf(), m.buf.length());
- }
+ bool endswith(const messagebuf &m) const
+ {
+ return buf.length() >= m.buf.length() && !memcmp(&buf[buf.length() - m.buf.length()], m.buf.getbuf(), m.buf.length());
+ }
- void concat(const messagebuf &m)
- {
- if(buf.length() && buf.last() == '\0') buf.pop();
- buf.put(m.buf.getbuf(), m.buf.length());
- }
+ void concat(const messagebuf &m)
+ {
+ if(buf.length() && buf.last() == '\0') buf.pop();
+ buf.put(m.buf.getbuf(), m.buf.length());
+ }
};
vector<messagebuf *> gameserverlists, gbanlists;
bool updateserverlist = true;
struct client
{
- ENetAddress address;
- ENetSocket socket;
- char input[INPUT_LIMIT];
- messagebuf *message;
- vector<char> output;
- int inputpos, outputpos;
- enet_uint32 connecttime, lastinput;
- int servport;
- enet_uint32 lastauth;
- vector<authreq> authreqs;
- bool shouldpurge;
- bool registeredserver;
-
- client() : message(NULL), inputpos(0), outputpos(0), servport(-1), lastauth(0), shouldpurge(false), registeredserver(false) {}
+ ENetAddress address;
+ ENetSocket socket;
+ char input[INPUT_LIMIT];
+ messagebuf *message;
+ vector<char> output;
+ int inputpos, outputpos;
+ enet_uint32 connecttime, lastinput;
+ int servport;
+ enet_uint32 lastauth;
+ vector<authreq> authreqs;
+ bool shouldpurge;
+ bool registeredserver;
+
+ client() : message(NULL), inputpos(0), outputpos(0), servport(-1), lastauth(0), shouldpurge(false), registeredserver(false) {}
};
vector<client *> clients;
@@ -144,567 +144,567 @@ enet_uint32 servtime = 0;
void fatal(const char *fmt, ...)
{
- va_list args;
- va_start(args, fmt);
- vfprintf(logfile, fmt, args);
- fputc('\n', logfile);
- va_end(args);
- exit(EXIT_FAILURE);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(logfile, fmt, args);
+ fputc('\n', logfile);
+ va_end(args);
+ exit(EXIT_FAILURE);
}
void conoutfv(int type, const char *fmt, va_list args)
{
- vfprintf(logfile, fmt, args);
- fputc('\n', logfile);
+ vfprintf(logfile, fmt, args);
+ fputc('\n', logfile);
}
void purgeclient(int n)
{
- client &c = *clients[n];
- if(c.message) c.message->purge();
- enet_socket_destroy(c.socket);
- delete clients[n];
- clients.remove(n);
+ client &c = *clients[n];
+ if(c.message) c.message->purge();
+ enet_socket_destroy(c.socket);
+ delete clients[n];
+ clients.remove(n);
}
void output(client &c, const char *msg, int len = 0)
{
- if(!len) len = strlen(msg);
- c.output.put(msg, len);
+ if(!len) len = strlen(msg);
+ c.output.put(msg, len);
}
void outputf(client &c, const char *fmt, ...)
{
- string msg;
- va_list args;
- va_start(args, fmt);
- vformatstring(msg, fmt, args);
- va_end(args);
+ string msg;
+ va_list args;
+ va_start(args, fmt);
+ vformatstring(msg, fmt, args);
+ va_end(args);
- output(c, msg);
+ output(c, msg);
}
ENetSocket pingsocket = ENET_SOCKET_NULL;
bool setuppingsocket(ENetAddress *address)
{
- if(pingsocket != ENET_SOCKET_NULL) return true;
- pingsocket = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
- if(pingsocket == ENET_SOCKET_NULL) return false;
- if(address && enet_socket_bind(pingsocket, address) < 0) return false;
- enet_socket_set_option(pingsocket, ENET_SOCKOPT_NONBLOCK, 1);
- return true;
+ if(pingsocket != ENET_SOCKET_NULL) return true;
+ pingsocket = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
+ if(pingsocket == ENET_SOCKET_NULL) return false;
+ if(address && enet_socket_bind(pingsocket, address) < 0) return false;
+ enet_socket_set_option(pingsocket, ENET_SOCKOPT_NONBLOCK, 1);
+ return true;
}
void setupserver(int port, const char *ip = NULL)
{
- ENetAddress address;
- address.host = ENET_HOST_ANY;
- address.port = port;
-
- if(ip)
- {
- if(enet_address_set_host(&address, ip)<0)
- fatal("failed to resolve server address: %s", ip);
- }
- serversocket = enet_socket_create(ENET_SOCKET_TYPE_STREAM);
- if(serversocket==ENET_SOCKET_NULL ||
- enet_socket_set_option(serversocket, ENET_SOCKOPT_REUSEADDR, 1) < 0 ||
- enet_socket_bind(serversocket, &address) < 0 ||
- enet_socket_listen(serversocket, -1) < 0)
- fatal("failed to create server socket");
- if(enet_socket_set_option(serversocket, ENET_SOCKOPT_NONBLOCK, 1)<0)
- fatal("failed to make server socket non-blocking");
- if(!setuppingsocket(&address))
- fatal("failed to create ping socket");
-
- enet_time_set(0);
-
- starttime = time(NULL);
- char *ct = ctime(&starttime);
- if(strchr(ct, '\n')) *strchr(ct, '\n') = '\0';
- conoutf("*** Starting master server on %s %d at %s ***", ip ? ip : "localhost", port, ct);
+ ENetAddress address;
+ address.host = ENET_HOST_ANY;
+ address.port = port;
+
+ if(ip)
+ {
+ if(enet_address_set_host(&address, ip)<0)
+ fatal("failed to resolve server address: %s", ip);
+ }
+ serversocket = enet_socket_create(ENET_SOCKET_TYPE_STREAM);
+ if(serversocket==ENET_SOCKET_NULL ||
+ enet_socket_set_option(serversocket, ENET_SOCKOPT_REUSEADDR, 1) < 0 ||
+ enet_socket_bind(serversocket, &address) < 0 ||
+ enet_socket_listen(serversocket, -1) < 0)
+ fatal("failed to create server socket");
+ if(enet_socket_set_option(serversocket, ENET_SOCKOPT_NONBLOCK, 1)<0)
+ fatal("failed to make server socket non-blocking");
+ if(!setuppingsocket(&address))
+ fatal("failed to create ping socket");
+
+ enet_time_set(0);
+
+ starttime = time(NULL);
+ char *ct = ctime(&starttime);
+ if(strchr(ct, '\n')) *strchr(ct, '\n') = '\0';
+ conoutf("*** Starting master server on %s %d at %s ***", ip ? ip : "localhost", port, ct);
}
void genserverlist()
{
- if(!updateserverlist) return;
- while(gameserverlists.length() && gameserverlists.last()->refs<=0)
- delete gameserverlists.pop();
- messagebuf *l = new messagebuf(gameserverlists);
- loopv(gameservers)
- {
- gameserver &s = *gameservers[i];
- if(!s.lastpong) continue;
- defformatstring(cmd, "addserver %s %d\n", s.ip, s.port);
- l->buf.put(cmd, strlen(cmd));
- }
- l->buf.add('\0');
- gameserverlists.add(l);
- updateserverlist = false;
+ if(!updateserverlist) return;
+ while(gameserverlists.length() && gameserverlists.last()->refs<=0)
+ delete gameserverlists.pop();
+ messagebuf *l = new messagebuf(gameserverlists);
+ loopv(gameservers)
+ {
+ gameserver &s = *gameservers[i];
+ if(!s.lastpong) continue;
+ defformatstring(cmd, "addserver %s %d\n", s.ip, s.port);
+ l->buf.put(cmd, strlen(cmd));
+ }
+ l->buf.add('\0');
+ gameserverlists.add(l);
+ updateserverlist = false;
}
void gengbanlist()
{
- messagebuf *l = new messagebuf(gbanlists);
- const char *header = "cleargbans\n";
- l->buf.put(header, strlen(header));
- string cmd = "addgban ";
- int cmdlen = strlen(cmd);
- loopv(gbans)
- {
- ipmask &b = gbans[i];
- l->buf.put(cmd, cmdlen + b.print(&cmd[cmdlen]));
- l->buf.add('\n');
- }
- if(gbanlists.length() && gbanlists.last()->equals(*l))
- {
- delete l;
- return;
- }
- while(gbanlists.length() && gbanlists.last()->refs<=0)
- delete gbanlists.pop();
- loopv(gbanlists)
- {
- messagebuf *m = gbanlists[i];
- if(m->refs > 0 && !m->endswith(*l)) m->concat(*l);
- }
- gbanlists.add(l);
- loopv(clients)
- {
- client &c = *clients[i];
- if(c.servport >= 0 && !c.message)
- {
- c.message = l;
- c.message->refs++;
- }
- }
+ messagebuf *l = new messagebuf(gbanlists);
+ const char *header = "cleargbans\n";
+ l->buf.put(header, strlen(header));
+ string cmd = "addgban ";
+ int cmdlen = strlen(cmd);
+ loopv(gbans)
+ {
+ ipmask &b = gbans[i];
+ l->buf.put(cmd, cmdlen + b.print(&cmd[cmdlen]));
+ l->buf.add('\n');
+ }
+ if(gbanlists.length() && gbanlists.last()->equals(*l))
+ {
+ delete l;
+ return;
+ }
+ while(gbanlists.length() && gbanlists.last()->refs<=0)
+ delete gbanlists.pop();
+ loopv(gbanlists)
+ {
+ messagebuf *m = gbanlists[i];
+ if(m->refs > 0 && !m->endswith(*l)) m->concat(*l);
+ }
+ gbanlists.add(l);
+ loopv(clients)
+ {
+ client &c = *clients[i];
+ if(c.servport >= 0 && !c.message)
+ {
+ c.message = l;
+ c.message->refs++;
+ }
+ }
}
void addgameserver(client &c)
{
- if(gameservers.length() >= SERVER_LIMIT) return;
- int dups = 0;
- loopv(gameservers)
- {
- gameserver &s = *gameservers[i];
- if(s.address.host != c.address.host) continue;
- ++dups;
- if(s.port == c.servport)
- {
- s.lastping = 0;
- s.numpings = 0;
- return;
- }
- }
- if(dups >= SERVER_DUP_LIMIT)
- {
- outputf(c, "failreg too many servers on ip\n");
- return;
- }
- string hostname;
- if(enet_address_get_host_ip(&c.address, hostname, sizeof(hostname)) < 0)
- {
- outputf(c, "failreg failed resolving ip\n");
- return;
- }
- gameserver &s = *gameservers.add(new gameserver);
- s.address.host = c.address.host;
- s.address.port = c.servport+1;
- copystring(s.ip, hostname);
- s.port = c.servport;
- s.numpings = 0;
- s.lastping = s.lastpong = 0;
+ if(gameservers.length() >= SERVER_LIMIT) return;
+ int dups = 0;
+ loopv(gameservers)
+ {
+ gameserver &s = *gameservers[i];
+ if(s.address.host != c.address.host) continue;
+ ++dups;
+ if(s.port == c.servport)
+ {
+ s.lastping = 0;
+ s.numpings = 0;
+ return;
+ }
+ }
+ if(dups >= SERVER_DUP_LIMIT)
+ {
+ outputf(c, "failreg too many servers on ip\n");
+ return;
+ }
+ string hostname;
+ if(enet_address_get_host_ip(&c.address, hostname, sizeof(hostname)) < 0)
+ {
+ outputf(c, "failreg failed resolving ip\n");
+ return;
+ }
+ gameserver &s = *gameservers.add(new gameserver);
+ s.address.host = c.address.host;
+ s.address.port = c.servport+1;
+ copystring(s.ip, hostname);
+ s.port = c.servport;
+ s.numpings = 0;
+ s.lastping = s.lastpong = 0;
}
client *findclient(gameserver &s)
{
- loopv(clients)
- {
- client &c = *clients[i];
- if(s.address.host == c.address.host && s.port == c.servport)
- return &c;
- }
- return NULL;
+ loopv(clients)
+ {
+ client &c = *clients[i];
+ if(s.address.host == c.address.host && s.port == c.servport)
+ return &c;
+ }
+ return NULL;
}
void servermessage(gameserver &s, const char *msg)
{
- client *c = findclient(s);
- if(c) outputf(*c, msg);
+ client *c = findclient(s);
+ if(c) outputf(*c, msg);
}
void checkserverpongs()
{
- ENetBuffer buf;
- ENetAddress addr;
- static uchar pong[MAXTRANS];
- for(;;)
- {
- buf.data = pong;
- buf.dataLength = sizeof(pong);
- int len = enet_socket_receive(pingsocket, &addr, &buf, 1);
- if(len <= 0) break;
- loopv(gameservers)
- {
- gameserver &s = *gameservers[i];
- if(s.address.host == addr.host && s.address.port == addr.port)
- {
- if(s.lastping && (!s.lastpong || ENET_TIME_GREATER(s.lastping, s.lastpong)))
- {
- client *c = findclient(s);
- if(c)
- {
- c->registeredserver = true;
- outputf(*c, "succreg\n");
- if(!c->message && gbanlists.length())
- {
- c->message = gbanlists.last();
- c->message->refs++;
- }
- }
- }
- if(!s.lastpong) updateserverlist = true;
- s.lastpong = servtime ? servtime : 1;
- break;
- }
- }
- }
+ ENetBuffer buf;
+ ENetAddress addr;
+ static uchar pong[MAXTRANS];
+ for(;;)
+ {
+ buf.data = pong;
+ buf.dataLength = sizeof(pong);
+ int len = enet_socket_receive(pingsocket, &addr, &buf, 1);
+ if(len <= 0) break;
+ loopv(gameservers)
+ {
+ gameserver &s = *gameservers[i];
+ if(s.address.host == addr.host && s.address.port == addr.port)
+ {
+ if(s.lastping && (!s.lastpong || ENET_TIME_GREATER(s.lastping, s.lastpong)))
+ {
+ client *c = findclient(s);
+ if(c)
+ {
+ c->registeredserver = true;
+ outputf(*c, "succreg\n");
+ if(!c->message && gbanlists.length())
+ {
+ c->message = gbanlists.last();
+ c->message->refs++;
+ }
+ }
+ }
+ if(!s.lastpong) updateserverlist = true;
+ s.lastpong = servtime ? servtime : 1;
+ break;
+ }
+ }
+ }
}
void bangameservers()
{
- loopvrev(gameservers) if(checkban(servbans, gameservers[i]->address.host))
- {
- delete gameservers.remove(i);
- updateserverlist = true;
- }
+ loopvrev(gameservers) if(checkban(servbans, gameservers[i]->address.host))
+ {
+ delete gameservers.remove(i);
+ updateserverlist = true;
+ }
}
void checkgameservers()
{
- ENetBuffer buf;
- loopv(gameservers)
- {
- gameserver &s = *gameservers[i];
- if(s.lastping && s.lastpong && ENET_TIME_LESS_EQUAL(s.lastping, s.lastpong))
- {
- if(ENET_TIME_DIFFERENCE(servtime, s.lastpong) > KEEPALIVE_TIME)
- {
- delete gameservers.remove(i--);
- updateserverlist = true;
- }
- }
- else if(!s.lastping || ENET_TIME_DIFFERENCE(servtime, s.lastping) > PING_TIME)
- {
- if(s.numpings >= PING_RETRY)
- {
- servermessage(s, "failreg failed pinging server\n");
- delete gameservers.remove(i--);
- updateserverlist = true;
- }
- else
- {
- static const uchar ping[] = { 1 };
- buf.data = (void *)ping;
- buf.dataLength = sizeof(ping);
- s.numpings++;
- s.lastping = servtime ? servtime : 1;
- enet_socket_send(pingsocket, &s.address, &buf, 1);
- }
- }
- }
+ ENetBuffer buf;
+ loopv(gameservers)
+ {
+ gameserver &s = *gameservers[i];
+ if(s.lastping && s.lastpong && ENET_TIME_LESS_EQUAL(s.lastping, s.lastpong))
+ {
+ if(ENET_TIME_DIFFERENCE(servtime, s.lastpong) > KEEPALIVE_TIME)
+ {
+ delete gameservers.remove(i--);
+ updateserverlist = true;
+ }
+ }
+ else if(!s.lastping || ENET_TIME_DIFFERENCE(servtime, s.lastping) > PING_TIME)
+ {
+ if(s.numpings >= PING_RETRY)
+ {
+ servermessage(s, "failreg failed pinging server\n");
+ delete gameservers.remove(i--);
+ updateserverlist = true;
+ }
+ else
+ {
+ static const uchar ping[] = { 1 };
+ buf.data = (void *)ping;
+ buf.dataLength = sizeof(ping);
+ s.numpings++;
+ s.lastping = servtime ? servtime : 1;
+ enet_socket_send(pingsocket, &s.address, &buf, 1);
+ }
+ }
+ }
}
void messagebuf::purge()
{
- refs = max(refs - 1, 0);
- if(refs<=0 && owner.last()!=this)
- {
- owner.removeobj(this);
- delete this;
- }
+ refs = max(refs - 1, 0);
+ if(refs<=0 && owner.last()!=this)
+ {
+ owner.removeobj(this);
+ delete this;
+ }
}
void purgeauths(client &c)
{
- int expired = 0;
- loopv(c.authreqs)
- {
- if(ENET_TIME_DIFFERENCE(servtime, c.authreqs[i].reqtime) >= AUTH_TIME)
- {
- outputf(c, "failauth %u\n", c.authreqs[i].id);
- freechallenge(c.authreqs[i].answer);
- expired = i + 1;
- }
- else break;
- }
- if(expired > 0) c.authreqs.remove(0, expired);
+ int expired = 0;
+ loopv(c.authreqs)
+ {
+ if(ENET_TIME_DIFFERENCE(servtime, c.authreqs[i].reqtime) >= AUTH_TIME)
+ {
+ outputf(c, "failauth %u\n", c.authreqs[i].id);
+ freechallenge(c.authreqs[i].answer);
+ expired = i + 1;
+ }
+ else break;
+ }
+ if(expired > 0) c.authreqs.remove(0, expired);
}
void reqauth(client &c, uint id, char *name)
{
- if(ENET_TIME_DIFFERENCE(servtime, c.lastauth) < AUTH_THROTTLE)
- return;
-
- c.lastauth = servtime;
-
- purgeauths(c);
-
- time_t t = time(NULL);
- char *ct = ctime(&t);
- if(ct)
- {
- char *newline = strchr(ct, '\n');
- if(newline) *newline = '\0';
- }
- string ip;
- if(enet_address_get_host_ip(&c.address, ip, sizeof(ip)) < 0) copystring(ip, "-");
- conoutf("%s: attempting \"%s\" as %u from %s", ct ? ct : "-", name, id, ip);
-
- userinfo *u = users.access(name);
- if(!u)
- {
- outputf(c, "failauth %u\n", id);
- return;
- }
-
- if(c.authreqs.length() >= AUTH_LIMIT)
- {
- outputf(c, "failauth %u\n", c.authreqs[0].id);
- freechallenge(c.authreqs[0].answer);
- c.authreqs.remove(0);
- }
-
- authreq &a = c.authreqs.add();
- a.reqtime = servtime;
- a.id = id;
- uint seed[3] = { uint(starttime), servtime, randomMT() };
- static vector<char> buf;
- buf.setsize(0);
- a.answer = genchallenge(u->pubkey, seed, sizeof(seed), buf);
-
- outputf(c, "chalauth %u %s\n", id, buf.getbuf());
+ if(ENET_TIME_DIFFERENCE(servtime, c.lastauth) < AUTH_THROTTLE)
+ return;
+
+ c.lastauth = servtime;
+
+ purgeauths(c);
+
+ time_t t = time(NULL);
+ char *ct = ctime(&t);
+ if(ct)
+ {
+ char *newline = strchr(ct, '\n');
+ if(newline) *newline = '\0';
+ }
+ string ip;
+ if(enet_address_get_host_ip(&c.address, ip, sizeof(ip)) < 0) copystring(ip, "-");
+ conoutf("%s: attempting \"%s\" as %u from %s", ct ? ct : "-", name, id, ip);
+
+ userinfo *u = users.access(name);
+ if(!u)
+ {
+ outputf(c, "failauth %u\n", id);
+ return;
+ }
+
+ if(c.authreqs.length() >= AUTH_LIMIT)
+ {
+ outputf(c, "failauth %u\n", c.authreqs[0].id);
+ freechallenge(c.authreqs[0].answer);
+ c.authreqs.remove(0);
+ }
+
+ authreq &a = c.authreqs.add();
+ a.reqtime = servtime;
+ a.id = id;
+ uint seed[3] = { uint(starttime), servtime, randomMT() };
+ static vector<char> buf;
+ buf.setsize(0);
+ a.answer = genchallenge(u->pubkey, seed, sizeof(seed), buf);
+
+ outputf(c, "chalauth %u %s\n", id, buf.getbuf());
}
void confauth(client &c, uint id, const char *val)
{
- purgeauths(c);
-
- loopv(c.authreqs) if(c.authreqs[i].id == id)
- {
- string ip;
- if(enet_address_get_host_ip(&c.address, ip, sizeof(ip)) < 0) copystring(ip, "-");
- if(checkchallenge(val, c.authreqs[i].answer))
- {
- outputf(c, "succauth %u\n", id);
- conoutf("succeeded %u from %s", id, ip);
- }
- else
- {
- outputf(c, "failauth %u\n", id);
- conoutf("failed %u from %s", id, ip);
- }
- freechallenge(c.authreqs[i].answer);
- c.authreqs.remove(i--);
- return;
- }
- outputf(c, "failauth %u\n", id);
+ purgeauths(c);
+
+ loopv(c.authreqs) if(c.authreqs[i].id == id)
+ {
+ string ip;
+ if(enet_address_get_host_ip(&c.address, ip, sizeof(ip)) < 0) copystring(ip, "-");
+ if(checkchallenge(val, c.authreqs[i].answer))
+ {
+ outputf(c, "succauth %u\n", id);
+ conoutf("succeeded %u from %s", id, ip);
+ }
+ else
+ {
+ outputf(c, "failauth %u\n", id);
+ conoutf("failed %u from %s", id, ip);
+ }
+ freechallenge(c.authreqs[i].answer);
+ c.authreqs.remove(i--);
+ return;
+ }
+ outputf(c, "failauth %u\n", id);
}
bool checkclientinput(client &c)
{
- if(c.inputpos<0) return true;
- char *end = (char *)memchr(c.input, '\n', c.inputpos);
- while(end)
- {
- *end++ = '\0';
- c.lastinput = servtime;
-
- int port;
- uint id;
- string user, val;
- if(!strncmp(c.input, "list", 4) && (!c.input[4] || c.input[4] == '\n' || c.input[4] == '\r'))
- {
- genserverlist();
- if(gameserverlists.empty() || c.message) return false;
- c.message = gameserverlists.last();
- c.message->refs++;
- c.output.setsize(0);
- c.outputpos = 0;
- c.shouldpurge = true;
- return true;
- }
- else if(sscanf(c.input, "regserv %d", &port) == 1)
- {
- if(checkban(servbans, c.address.host)) return false;
- if(port < 0 || port > 0xFFFF-1 || (c.servport >= 0 && port != c.servport)) outputf(c, "failreg invalid port\n");
- else
- {
- c.servport = port;
- addgameserver(c);
- }
- }
- else if(sscanf(c.input, "reqauth %u %100s", &id, user) == 2)
- {
- reqauth(c, id, user);
- }
- else if(sscanf(c.input, "confauth %u %100s", &id, val) == 2)
- {
- confauth(c, id, val);
- }
- c.inputpos = &c.input[c.inputpos] - end;
- memmove(c.input, end, c.inputpos);
-
- end = (char *)memchr(c.input, '\n', c.inputpos);
- }
- return c.inputpos<(int)sizeof(c.input);
+ if(c.inputpos<0) return true;
+ char *end = (char *)memchr(c.input, '\n', c.inputpos);
+ while(end)
+ {
+ *end++ = '\0';
+ c.lastinput = servtime;
+
+ int port;
+ uint id;
+ string user, val;
+ if(!strncmp(c.input, "list", 4) && (!c.input[4] || c.input[4] == '\n' || c.input[4] == '\r'))
+ {
+ genserverlist();
+ if(gameserverlists.empty() || c.message) return false;
+ c.message = gameserverlists.last();
+ c.message->refs++;
+ c.output.setsize(0);
+ c.outputpos = 0;
+ c.shouldpurge = true;
+ return true;
+ }
+ else if(sscanf(c.input, "regserv %d", &port) == 1)
+ {
+ if(checkban(servbans, c.address.host)) return false;
+ if(port < 0 || port > 0xFFFF-1 || (c.servport >= 0 && port != c.servport)) outputf(c, "failreg invalid port\n");
+ else
+ {
+ c.servport = port;
+ addgameserver(c);
+ }
+ }
+ else if(sscanf(c.input, "reqauth %u %100s", &id, user) == 2)
+ {
+ reqauth(c, id, user);
+ }
+ else if(sscanf(c.input, "confauth %u %100s", &id, val) == 2)
+ {
+ confauth(c, id, val);
+ }
+ c.inputpos = &c.input[c.inputpos] - end;
+ memmove(c.input, end, c.inputpos);
+
+ end = (char *)memchr(c.input, '\n', c.inputpos);
+ }
+ return c.inputpos<(int)sizeof(c.input);
}
ENetSocketSet readset, writeset;
void checkclients()
{
- ENetSocketSet readset, writeset;
- ENetSocket maxsock = max(serversocket, pingsocket);
- ENET_SOCKETSET_EMPTY(readset);
- ENET_SOCKETSET_EMPTY(writeset);
- ENET_SOCKETSET_ADD(readset, serversocket);
- ENET_SOCKETSET_ADD(readset, pingsocket);
- loopv(clients)
- {
- client &c = *clients[i];
- if(c.authreqs.length()) purgeauths(c);
- if(c.message || c.output.length()) ENET_SOCKETSET_ADD(writeset, c.socket);
- else ENET_SOCKETSET_ADD(readset, c.socket);
- maxsock = max(maxsock, c.socket);
- }
- if(enet_socketset_select(maxsock, &readset, &writeset, 1000)<=0) return;
-
- if(ENET_SOCKETSET_CHECK(readset, pingsocket)) checkserverpongs();
- if(ENET_SOCKETSET_CHECK(readset, serversocket))
- {
- ENetAddress address;
- ENetSocket clientsocket = enet_socket_accept(serversocket, &address);
- if(clients.length()>=CLIENT_LIMIT || checkban(bans, address.host)) enet_socket_destroy(clientsocket);
- else if(clientsocket!=ENET_SOCKET_NULL)
- {
- int dups = 0, oldest = -1;
- loopv(clients) if(clients[i]->address.host == address.host)
- {
- dups++;
- if(oldest<0 || clients[i]->connecttime < clients[oldest]->connecttime) oldest = i;
- }
- if(dups >= DUP_LIMIT) purgeclient(oldest);
-
- client *c = new client;
- c->address = address;
- c->socket = clientsocket;
- c->connecttime = servtime;
- c->lastinput = servtime;
- clients.add(c);
- }
- }
-
- loopv(clients)
- {
- client &c = *clients[i];
- if((c.message || c.output.length()) && ENET_SOCKETSET_CHECK(writeset, c.socket))
- {
- const char *data = c.output.length() ? c.output.getbuf() : c.message->getbuf();
- int len = c.output.length() ? c.output.length() : c.message->length();
- ENetBuffer buf;
- buf.data = (void *)&data[c.outputpos];
- buf.dataLength = len-c.outputpos;
- int res = enet_socket_send(c.socket, NULL, &buf, 1);
- if(res>=0)
- {
- c.outputpos += res;
- if(c.outputpos>=len)
- {
- if(c.output.length()) c.output.setsize(0);
- else
- {
- c.message->purge();
- c.message = NULL;
- }
- c.outputpos = 0;
- if(!c.message && c.output.empty() && c.shouldpurge)
- {
- purgeclient(i--);
- continue;
- }
- }
- }
- else { purgeclient(i--); continue; }
- }
- if(ENET_SOCKETSET_CHECK(readset, c.socket))
- {
- ENetBuffer buf;
- buf.data = &c.input[c.inputpos];
- buf.dataLength = sizeof(c.input) - c.inputpos;
- int res = enet_socket_receive(c.socket, NULL, &buf, 1);
- if(res>0)
- {
- c.inputpos += res;
- c.input[min(c.inputpos, (int)sizeof(c.input)-1)] = '\0';
- if(!checkclientinput(c)) { purgeclient(i--); continue; }
- }
- else { purgeclient(i--); continue; }
- }
- if(c.output.length() > OUTPUT_LIMIT) { purgeclient(i--); continue; }
- if(ENET_TIME_DIFFERENCE(servtime, c.lastinput) >= (c.registeredserver ? KEEPALIVE_TIME : CLIENT_TIME)) { purgeclient(i--); continue; }
- }
+ ENetSocketSet readset, writeset;
+ ENetSocket maxsock = max(serversocket, pingsocket);
+ ENET_SOCKETSET_EMPTY(readset);
+ ENET_SOCKETSET_EMPTY(writeset);
+ ENET_SOCKETSET_ADD(readset, serversocket);
+ ENET_SOCKETSET_ADD(readset, pingsocket);
+ loopv(clients)
+ {
+ client &c = *clients[i];
+ if(c.authreqs.length()) purgeauths(c);
+ if(c.message || c.output.length()) ENET_SOCKETSET_ADD(writeset, c.socket);
+ else ENET_SOCKETSET_ADD(readset, c.socket);
+ maxsock = max(maxsock, c.socket);
+ }
+ if(enet_socketset_select(maxsock, &readset, &writeset, 1000)<=0) return;
+
+ if(ENET_SOCKETSET_CHECK(readset, pingsocket)) checkserverpongs();
+ if(ENET_SOCKETSET_CHECK(readset, serversocket))
+ {
+ ENetAddress address;
+ ENetSocket clientsocket = enet_socket_accept(serversocket, &address);
+ if(clients.length()>=CLIENT_LIMIT || checkban(bans, address.host)) enet_socket_destroy(clientsocket);
+ else if(clientsocket!=ENET_SOCKET_NULL)
+ {
+ int dups = 0, oldest = -1;
+ loopv(clients) if(clients[i]->address.host == address.host)
+ {
+ dups++;
+ if(oldest<0 || clients[i]->connecttime < clients[oldest]->connecttime) oldest = i;
+ }
+ if(dups >= DUP_LIMIT) purgeclient(oldest);
+
+ client *c = new client;
+ c->address = address;
+ c->socket = clientsocket;
+ c->connecttime = servtime;
+ c->lastinput = servtime;
+ clients.add(c);
+ }
+ }
+
+ loopv(clients)
+ {
+ client &c = *clients[i];
+ if((c.message || c.output.length()) && ENET_SOCKETSET_CHECK(writeset, c.socket))
+ {
+ const char *data = c.output.length() ? c.output.getbuf() : c.message->getbuf();
+ int len = c.output.length() ? c.output.length() : c.message->length();
+ ENetBuffer buf;
+ buf.data = (void *)&data[c.outputpos];
+ buf.dataLength = len-c.outputpos;
+ int res = enet_socket_send(c.socket, NULL, &buf, 1);
+ if(res>=0)
+ {
+ c.outputpos += res;
+ if(c.outputpos>=len)
+ {
+ if(c.output.length()) c.output.setsize(0);
+ else
+ {
+ c.message->purge();
+ c.message = NULL;
+ }
+ c.outputpos = 0;
+ if(!c.message && c.output.empty() && c.shouldpurge)
+ {
+ purgeclient(i--);
+ continue;
+ }
+ }
+ }
+ else { purgeclient(i--); continue; }
+ }
+ if(ENET_SOCKETSET_CHECK(readset, c.socket))
+ {
+ ENetBuffer buf;
+ buf.data = &c.input[c.inputpos];
+ buf.dataLength = sizeof(c.input) - c.inputpos;
+ int res = enet_socket_receive(c.socket, NULL, &buf, 1);
+ if(res>0)
+ {
+ c.inputpos += res;
+ c.input[min(c.inputpos, (int)sizeof(c.input)-1)] = '\0';
+ if(!checkclientinput(c)) { purgeclient(i--); continue; }
+ }
+ else { purgeclient(i--); continue; }
+ }
+ if(c.output.length() > OUTPUT_LIMIT) { purgeclient(i--); continue; }
+ if(ENET_TIME_DIFFERENCE(servtime, c.lastinput) >= (c.registeredserver ? KEEPALIVE_TIME : CLIENT_TIME)) { purgeclient(i--); continue; }
+ }
}
void banclients()
{
- loopvrev(clients) if(checkban(bans, clients[i]->address.host)) purgeclient(i);
+ loopvrev(clients) if(checkban(bans, clients[i]->address.host)) purgeclient(i);
}
volatile int reloadcfg = 1;
void reloadsignal(int signum)
{
- reloadcfg = 1;
+ reloadcfg = 1;
}
int main(int argc, char **argv)
{
- if(enet_initialize()<0) fatal("Unable to initialise network module");
- atexit(enet_deinitialize);
-
- const char *dir = "", *ip = NULL;
- int port = 28787;
- if(argc>=2) dir = argv[1];
- if(argc>=3) port = atoi(argv[2]);
- if(argc>=4) ip = argv[3];
- defformatstring(logname, "%smaster.log", dir);
- defformatstring(cfgname, "%smaster.cfg", dir);
- path(logname);
- path(cfgname);
- logfile = fopen(logname, "a");
- if(!logfile) logfile = stdout;
- setvbuf(logfile, NULL, _IOLBF, BUFSIZ);
- signal(SIGUSR1, reloadsignal);
- setupserver(port, ip);
- for(;;)
- {
- if(reloadcfg)
- {
- conoutf("reloading master.cfg");
- execfile(cfgname);
- bangameservers();
- banclients();
- gengbanlist();
- reloadcfg = 0;
- }
-
- servtime = enet_time_get();
- checkclients();
- checkgameservers();
- }
-
- return EXIT_SUCCESS;
+ if(enet_initialize()<0) fatal("Unable to initialise network module");
+ atexit(enet_deinitialize);
+
+ const char *dir = "", *ip = NULL;
+ int port = 28787;
+ if(argc>=2) dir = argv[1];
+ if(argc>=3) port = atoi(argv[2]);
+ if(argc>=4) ip = argv[3];
+ defformatstring(logname, "%smaster.log", dir);
+ defformatstring(cfgname, "%smaster.cfg", dir);
+ path(logname);
+ path(cfgname);
+ logfile = fopen(logname, "a");
+ if(!logfile) logfile = stdout;
+ setvbuf(logfile, NULL, _IOLBF, BUFSIZ);
+ signal(SIGUSR1, reloadsignal);
+ setupserver(port, ip);
+ for(;;)
+ {
+ if(reloadcfg)
+ {
+ conoutf("reloading master.cfg");
+ execfile(cfgname);
+ bangameservers();
+ banclients();
+ gengbanlist();
+ reloadcfg = 0;
+ }
+
+ servtime = enet_time_get();
+ checkclients();
+ checkgameservers();
+ }
+
+ return EXIT_SUCCESS;
}
diff --git a/src/engine/material.cpp b/src/engine/material.cpp
index d56c7e2..54bc4ae 100644
--- a/src/engine/material.cpp
+++ b/src/engine/material.cpp
@@ -1,450 +1,450 @@
#include "engine.h"
struct QuadNode {
- int x, y, size;
- uint filled;
- QuadNode *child[4];
-
- QuadNode(int x, int y, int size) : x(x), y(y), size(size), filled(0) { loopi(4) child[i] = 0; }
-
- void clear() { loopi(4) DELETEP(child[i]); }
-
- ~QuadNode() { clear(); }
-
- void insert(int mx, int my, int msize) {
- if(size == msize) {
- filled = 0xF;
- return;
- }
- int csize = size>>1, i = 0;
- if(mx >= x+csize) i |= 1;
- if(my >= y+csize) i |= 2;
- if(csize == msize) {
- filled |= (1 << i);
- return;
- }
- if(!child[i]) child[i] = new QuadNode(i&1 ? x+csize : x, i&2 ? y+csize : y, csize);
- child[i]->insert(mx, my, msize);
- loopj(4) if(child[j])
- {
- if(child[j]->filled == 0xF) {
- DELETEP(child[j]);
- filled |= (1 << j);
- }
- }
- }
-
- void genmatsurf(ushort mat, uchar orient, uchar visible, int x, int y, int z, int size, materialsurface *&matbuf) {
- materialsurface &m = *matbuf++;
- m.material = mat;
- m.orient = orient;
- m.visible = visible;
- m.csize = size;
- m.rsize = size;
- int dim = dimension(orient);
- m.o[C[dim]] = x;
- m.o[R[dim]] = y;
- m.o[dim] = z;
- }
-
- void genmatsurfs(ushort mat, uchar orient, uchar flags, int z, materialsurface *&matbuf) {
- if(filled == 0xF) genmatsurf(mat, orient, flags, x, y, z, size, matbuf);
- else if(filled)
- {
- int csize = size>>1;
- loopi(4) if(filled & (1 << i))
- genmatsurf(mat, orient, flags, i&1 ? x+csize : x, i&2 ? y+csize : y, z, csize, matbuf);
- }
- loopi(4) if(child[i]) child[i]->genmatsurfs(mat, orient, flags, z, matbuf);
- }
+ int x, y, size;
+ uint filled;
+ QuadNode *child[4];
+
+ QuadNode(int x, int y, int size) : x(x), y(y), size(size), filled(0) { loopi(4) child[i] = 0; }
+
+ void clear() { loopi(4) DELETEP(child[i]); }
+
+ ~QuadNode() { clear(); }
+
+ void insert(int mx, int my, int msize) {
+ if(size == msize) {
+ filled = 0xF;
+ return;
+ }
+ int csize = size>>1, i = 0;
+ if(mx >= x+csize) i |= 1;
+ if(my >= y+csize) i |= 2;
+ if(csize == msize) {
+ filled |= (1 << i);
+ return;
+ }
+ if(!child[i]) child[i] = new QuadNode(i&1 ? x+csize : x, i&2 ? y+csize : y, csize);
+ child[i]->insert(mx, my, msize);
+ loopj(4) if(child[j])
+ {
+ if(child[j]->filled == 0xF) {
+ DELETEP(child[j]);
+ filled |= (1 << j);
+ }
+ }
+ }
+
+ void genmatsurf(ushort mat, uchar orient, uchar visible, int x, int y, int z, int size, materialsurface *&matbuf) {
+ materialsurface &m = *matbuf++;
+ m.material = mat;
+ m.orient = orient;
+ m.visible = visible;
+ m.csize = size;
+ m.rsize = size;
+ int dim = dimension(orient);
+ m.o[C[dim]] = x;
+ m.o[R[dim]] = y;
+ m.o[dim] = z;
+ }
+
+ void genmatsurfs(ushort mat, uchar orient, uchar flags, int z, materialsurface *&matbuf) {
+ if(filled == 0xF) genmatsurf(mat, orient, flags, x, y, z, size, matbuf);
+ else if(filled)
+ {
+ int csize = size>>1;
+ loopi(4) if(filled & (1 << i))
+ genmatsurf(mat, orient, flags, i&1 ? x+csize : x, i&2 ? y+csize : y, z, csize, matbuf);
+ }
+ loopi(4) if(child[i]) child[i]->genmatsurfs(mat, orient, flags, z, matbuf);
+ }
};
static float wfwave;
static const bvec4 matnormals[6] = {
- bvec4(0x80, 0, 0),
- bvec4(0x7F, 0, 0),
- bvec4(0, 0x80, 0),
- bvec4(0, 0x7F, 0),
- bvec4(0, 0, 0x80),
- bvec4(0, 0, 0x7F)
+ bvec4(0x80, 0, 0),
+ bvec4(0x7F, 0, 0),
+ bvec4(0, 0x80, 0),
+ bvec4(0, 0x7F, 0),
+ bvec4(0, 0, 0x80),
+ bvec4(0, 0, 0x7F)
};
static void renderwaterfall(const materialsurface &m, float offset)
{
- if(gle::attribbuf.empty())
- {
- gle::defvertex();
- gle::defnormal(4, GL_BYTE);
- gle::begin(GL_QUADS);
- }
- float x = m.o.x, y = m.o.y, zmin = m.o.z, zmax = zmin;
- if(m.ends&1) zmin += -WATER_OFFSET-WATER_AMPLITUDE;
- if(m.ends&2) zmax += wfwave;
- int csize = m.csize, rsize = m.rsize;
- switch(m.orient)
- {
- #define GENFACEORIENT(orient, v0, v1, v2, v3) \
- case orient: v0 v1 v2 v3 break;
- #define GENFACEVERT(orient, vert, mx,my,mz, sx,sy,sz) \
- { \
- gle::attribf(mx sx, my sy, mz sz); \
- gle::attrib(matnormals[orient]); \
- }
- GENFACEVERTSXY(x, x, y, y, zmin, zmax, /**/, + csize, /**/, + rsize, + offset, - offset)
- #undef GENFACEORIENT
- #undef GENFACEVERT
- }
+ if(gle::attribbuf.empty())
+ {
+ gle::defvertex();
+ gle::defnormal(4, GL_BYTE);
+ gle::begin(GL_QUADS);
+ }
+ float x = m.o.x, y = m.o.y, zmin = m.o.z, zmax = zmin;
+ if(m.ends&1) zmin += -WATER_OFFSET-WATER_AMPLITUDE;
+ if(m.ends&2) zmax += wfwave;
+ int csize = m.csize, rsize = m.rsize;
+ switch(m.orient)
+ {
+ #define GENFACEORIENT(orient, v0, v1, v2, v3) \
+ case orient: v0 v1 v2 v3 break;
+ #define GENFACEVERT(orient, vert, mx,my,mz, sx,sy,sz) \
+ { \
+ gle::attribf(mx sx, my sy, mz sz); \
+ gle::attrib(matnormals[orient]); \
+ }
+ GENFACEVERTSXY(x, x, y, y, zmin, zmax, /**/, + csize, /**/, + rsize, + offset, - offset)
+ #undef GENFACEORIENT
+ #undef GENFACEVERT
+ }
}
static void drawmaterial(const materialsurface &m, float offset, const bvec4 &color)
{
- if(gle::attribbuf.empty())
- {
- gle::defvertex();
- gle::defcolor(4, GL_UNSIGNED_BYTE);
- gle::begin(GL_QUADS);
- }
- float x = m.o.x, y = m.o.y, z = m.o.z, csize = m.csize, rsize = m.rsize;
- switch(m.orient)
- {
- #define GENFACEORIENT(orient, v0, v1, v2, v3) \
- case orient: v0 v1 v2 v3 break;
- #define GENFACEVERT(orient, vert, mx,my,mz, sx,sy,sz) \
- { \
- gle::attribf(mx sx, my sy, mz sz); \
- gle::attrib(color); \
- }
- GENFACEVERTS(x, x, y, y, z, z, /**/, + csize, /**/, + rsize, + offset, - offset)
- #undef GENFACEORIENT
- #undef GENFACEVERT
- }
+ if(gle::attribbuf.empty())
+ {
+ gle::defvertex();
+ gle::defcolor(4, GL_UNSIGNED_BYTE);
+ gle::begin(GL_QUADS);
+ }
+ float x = m.o.x, y = m.o.y, z = m.o.z, csize = m.csize, rsize = m.rsize;
+ switch(m.orient)
+ {
+ #define GENFACEORIENT(orient, v0, v1, v2, v3) \
+ case orient: v0 v1 v2 v3 break;
+ #define GENFACEVERT(orient, vert, mx,my,mz, sx,sy,sz) \
+ { \
+ gle::attribf(mx sx, my sy, mz sz); \
+ gle::attrib(color); \
+ }
+ GENFACEVERTS(x, x, y, y, z, z, /**/, + csize, /**/, + rsize, + offset, - offset)
+ #undef GENFACEORIENT
+ #undef GENFACEVERT
+ }
}
const struct material
{
- const char *name;
- ushort id;
+ const char *name;
+ ushort id;
} materials[] =
{
- {"air", MAT_AIR},
- {"water", MAT_WATER}, {"water1", MAT_WATER}, {"water2", MAT_WATER+1}, {"water3", MAT_WATER+2}, {"water4", MAT_WATER+3},
- {"glass", MAT_GLASS}, {"glass1", MAT_GLASS}, {"glass2", MAT_GLASS+1}, {"glass3", MAT_GLASS+2}, {"glass4", MAT_GLASS+3},
- {"lava", MAT_LAVA}, {"lava1", MAT_LAVA}, {"lava2", MAT_LAVA+1}, {"lava3", MAT_LAVA+2}, {"lava4", MAT_LAVA+3},
- {"clip", MAT_CLIP},
- {"noclip", MAT_NOCLIP},
- {"gameclip", MAT_GAMECLIP},
- {"death", MAT_DEATH},
- {"alpha", MAT_ALPHA}
+ {"air", MAT_AIR},
+ {"water", MAT_WATER}, {"water1", MAT_WATER}, {"water2", MAT_WATER+1}, {"water3", MAT_WATER+2}, {"water4", MAT_WATER+3},
+ {"glass", MAT_GLASS}, {"glass1", MAT_GLASS}, {"glass2", MAT_GLASS+1}, {"glass3", MAT_GLASS+2}, {"glass4", MAT_GLASS+3},
+ {"lava", MAT_LAVA}, {"lava1", MAT_LAVA}, {"lava2", MAT_LAVA+1}, {"lava3", MAT_LAVA+2}, {"lava4", MAT_LAVA+3},
+ {"clip", MAT_CLIP},
+ {"noclip", MAT_NOCLIP},
+ {"gameclip", MAT_GAMECLIP},
+ {"death", MAT_DEATH},
+ {"alpha", MAT_ALPHA}
};
int findmaterial(const char *name)
{
- loopi(sizeof(materials)/sizeof(material))
- {
- if(!strcmp(materials[i].name, name)) return materials[i].id;
- }
- return -1;
+ loopi(sizeof(materials)/sizeof(material))
+ {
+ if(!strcmp(materials[i].name, name)) return materials[i].id;
+ }
+ return -1;
}
const char *findmaterialname(int mat)
{
- loopi(sizeof(materials)/sizeof(materials[0])) if(materials[i].id == mat) return materials[i].name;
- return NULL;
+ loopi(sizeof(materials)/sizeof(materials[0])) if(materials[i].id == mat) return materials[i].name;
+ return NULL;
}
const char *getmaterialdesc(int mat, const char *prefix)
{
- static const ushort matmasks[] = { MATF_VOLUME|MATF_INDEX, MATF_CLIP, MAT_DEATH, MAT_ALPHA };
- static string desc;
- desc[0] = '\0';
- loopi(sizeof(matmasks)/sizeof(matmasks[0])) if(mat&matmasks[i])
- {
- const char *matname = findmaterialname(mat&matmasks[i]);
- if(matname)
- {
- concatstring(desc, desc[0] ? ", " : prefix);
- concatstring(desc, matname);
- }
- }
- return desc;
+ static const ushort matmasks[] = { MATF_VOLUME|MATF_INDEX, MATF_CLIP, MAT_DEATH, MAT_ALPHA };
+ static string desc;
+ desc[0] = '\0';
+ loopi(sizeof(matmasks)/sizeof(matmasks[0])) if(mat&matmasks[i])
+ {
+ const char *matname = findmaterialname(mat&matmasks[i]);
+ if(matname)
+ {
+ concatstring(desc, desc[0] ? ", " : prefix);
+ concatstring(desc, matname);
+ }
+ }
+ return desc;
}
int visiblematerial(const cube &c, int orient, const ivec &co, int size, ushort matmask)
{
- ushort mat = c.material&matmask;
- switch(mat)
- {
- case MAT_AIR:
- break;
-
- case MAT_LAVA:
- case MAT_WATER:
- if(visibleface(c, orient, co, size, mat, MAT_AIR, matmask))
- return (orient != O_BOTTOM ? MATSURF_VISIBLE : MATSURF_EDIT_ONLY);
- break;
-
- case MAT_GLASS:
- if(visibleface(c, orient, co, size, MAT_GLASS, MAT_AIR, matmask))
- return MATSURF_VISIBLE;
- break;
-
- default:
- if(visibleface(c, orient, co, size, mat, MAT_AIR, matmask))
- return MATSURF_EDIT_ONLY;
- break;
- }
- return MATSURF_NOT_VISIBLE;
+ ushort mat = c.material&matmask;
+ switch(mat)
+ {
+ case MAT_AIR:
+ break;
+
+ case MAT_LAVA:
+ case MAT_WATER:
+ if(visibleface(c, orient, co, size, mat, MAT_AIR, matmask))
+ return (orient != O_BOTTOM ? MATSURF_VISIBLE : MATSURF_EDIT_ONLY);
+ break;
+
+ case MAT_GLASS:
+ if(visibleface(c, orient, co, size, MAT_GLASS, MAT_AIR, matmask))
+ return MATSURF_VISIBLE;
+ break;
+
+ default:
+ if(visibleface(c, orient, co, size, mat, MAT_AIR, matmask))
+ return MATSURF_EDIT_ONLY;
+ break;
+ }
+ return MATSURF_NOT_VISIBLE;
}
void genmatsurfs(const cube &c, const ivec &co, int size, vector<materialsurface> &matsurfs)
{
- loopi(6)
- {
- static const ushort matmasks[] = { MATF_VOLUME|MATF_INDEX, MATF_CLIP, MAT_DEATH, MAT_ALPHA };
- loopj(sizeof(matmasks)/sizeof(matmasks[0]))
- {
- int matmask = matmasks[j];
- int vis = visiblematerial(c, i, co, size, matmask&~MATF_INDEX);
- if(vis != MATSURF_NOT_VISIBLE)
- {
- materialsurface m;
- m.material = c.material&matmask;
- m.orient = i;
- m.visible = vis;
- m.o = co;
- m.csize = m.rsize = size;
- if(dimcoord(i)) m.o[dimension(i)] += size;
- matsurfs.add(m);
- break;
- }
- }
- }
+ loopi(6)
+ {
+ static const ushort matmasks[] = { MATF_VOLUME|MATF_INDEX, MATF_CLIP, MAT_DEATH, MAT_ALPHA };
+ loopj(sizeof(matmasks)/sizeof(matmasks[0]))
+ {
+ int matmask = matmasks[j];
+ int vis = visiblematerial(c, i, co, size, matmask&~MATF_INDEX);
+ if(vis != MATSURF_NOT_VISIBLE)
+ {
+ materialsurface m;
+ m.material = c.material&matmask;
+ m.orient = i;
+ m.visible = vis;
+ m.o = co;
+ m.csize = m.rsize = size;
+ if(dimcoord(i)) m.o[dimension(i)] += size;
+ matsurfs.add(m);
+ break;
+ }
+ }
+ }
}
static inline bool mergematcmp(const materialsurface &x, const materialsurface &y)
{
- int dim = dimension(x.orient), c = C[dim], r = R[dim];
- if(x.o[r] + x.rsize < y.o[r] + y.rsize) return true;
- if(x.o[r] + x.rsize > y.o[r] + y.rsize) return false;
- return x.o[c] < y.o[c];
+ int dim = dimension(x.orient), c = C[dim], r = R[dim];
+ if(x.o[r] + x.rsize < y.o[r] + y.rsize) return true;
+ if(x.o[r] + x.rsize > y.o[r] + y.rsize) return false;
+ return x.o[c] < y.o[c];
}
static int mergematr(materialsurface *m, int sz, materialsurface &n)
{
- int dim = dimension(n.orient), c = C[dim], r = R[dim];
- for(int i = sz-1; i >= 0; --i)
- {
- if(m[i].o[r] + m[i].rsize < n.o[r]) break;
- if(m[i].o[r] + m[i].rsize == n.o[r] && m[i].o[c] == n.o[c] && m[i].csize == n.csize)
- {
- n.o[r] = m[i].o[r];
- n.rsize += m[i].rsize;
- memmove(&m[i], &m[i+1], (sz - (i+1)) * sizeof(materialsurface));
- return 1;
- }
- }
- return 0;
+ int dim = dimension(n.orient), c = C[dim], r = R[dim];
+ for(int i = sz-1; i >= 0; --i)
+ {
+ if(m[i].o[r] + m[i].rsize < n.o[r]) break;
+ if(m[i].o[r] + m[i].rsize == n.o[r] && m[i].o[c] == n.o[c] && m[i].csize == n.csize)
+ {
+ n.o[r] = m[i].o[r];
+ n.rsize += m[i].rsize;
+ memmove(&m[i], &m[i+1], (sz - (i+1)) * sizeof(materialsurface));
+ return 1;
+ }
+ }
+ return 0;
}
static int mergematc(materialsurface &m, materialsurface &n)
{
- int dim = dimension(n.orient), c = C[dim], r = R[dim];
- if(m.o[r] == n.o[r] && m.rsize == n.rsize && m.o[c] + m.csize == n.o[c])
- {
- n.o[c] = m.o[c];
- n.csize += m.csize;
- return 1;
- }
- return 0;
+ int dim = dimension(n.orient), c = C[dim], r = R[dim];
+ if(m.o[r] == n.o[r] && m.rsize == n.rsize && m.o[c] + m.csize == n.o[c])
+ {
+ n.o[c] = m.o[c];
+ n.csize += m.csize;
+ return 1;
+ }
+ return 0;
}
static int mergemat(materialsurface *m, int sz, materialsurface &n)
{
- for(bool merged = false; sz; merged = true)
- {
- int rmerged = mergematr(m, sz, n);
- sz -= rmerged;
- if(!rmerged && merged) break;
- if(!sz) break;
- int cmerged = mergematc(m[sz-1], n);
- sz -= cmerged;
- if(!cmerged) break;
- }
- m[sz++] = n;
- return sz;
+ for(bool merged = false; sz; merged = true)
+ {
+ int rmerged = mergematr(m, sz, n);
+ sz -= rmerged;
+ if(!rmerged && merged) break;
+ if(!sz) break;
+ int cmerged = mergematc(m[sz-1], n);
+ sz -= cmerged;
+ if(!cmerged) break;
+ }
+ m[sz++] = n;
+ return sz;
}
static int mergemats(materialsurface *m, int sz)
{
- quicksort(m, sz, mergematcmp);
+ quicksort(m, sz, mergematcmp);
- int nsz = 0;
- loopi(sz) nsz = mergemat(m, nsz, m[i]);
- return nsz;
+ int nsz = 0;
+ loopi(sz) nsz = mergemat(m, nsz, m[i]);
+ return nsz;
}
static inline bool optmatcmp(const materialsurface &x, const materialsurface &y)
{
- if(x.material < y.material) return true;
- if(x.material > y.material) return false;
- if(x.orient > y.orient) return true;
- if(x.orient < y.orient) return false;
- int dim = dimension(x.orient);
- return x.o[dim] < y.o[dim];
+ if(x.material < y.material) return true;
+ if(x.material > y.material) return false;
+ if(x.orient > y.orient) return true;
+ if(x.orient < y.orient) return false;
+ int dim = dimension(x.orient);
+ return x.o[dim] < y.o[dim];
}
VARF(optmats, 0, 1, 1, allchanged());
int optimizematsurfs(materialsurface *matbuf, int matsurfs)
{
- quicksort(matbuf, matsurfs, optmatcmp);
- if(!optmats) return matsurfs;
- materialsurface *cur = matbuf, *end = matbuf+matsurfs;
- while(cur < end)
- {
- materialsurface *start = cur++;
- int dim = dimension(start->orient);
- while(cur < end &&
- cur->material == start->material &&
- cur->orient == start->orient &&
- cur->visible == start->visible &&
- cur->o[dim] == start->o[dim])
- ++cur;
- if(!isliquid(start->material&MATF_VOLUME) || start->orient != O_TOP || !vertwater)
- {
- if(start!=matbuf) memmove(matbuf, start, (cur-start)*sizeof(materialsurface));
- matbuf += mergemats(matbuf, cur-start);
- }
- else if(cur-start>=4)
- {
- QuadNode vmats(0, 0, worldsize);
- loopi(cur-start) vmats.insert(start[i].o[C[dim]], start[i].o[R[dim]], start[i].csize);
- vmats.genmatsurfs(start->material, start->orient, start->visible, start->o[dim], matbuf);
- }
- else
- {
- if(start!=matbuf) memmove(matbuf, start, (cur-start)*sizeof(materialsurface));
- matbuf += cur-start;
- }
- }
- return matsurfs - (end-matbuf);
+ quicksort(matbuf, matsurfs, optmatcmp);
+ if(!optmats) return matsurfs;
+ materialsurface *cur = matbuf, *end = matbuf+matsurfs;
+ while(cur < end)
+ {
+ materialsurface *start = cur++;
+ int dim = dimension(start->orient);
+ while(cur < end &&
+ cur->material == start->material &&
+ cur->orient == start->orient &&
+ cur->visible == start->visible &&
+ cur->o[dim] == start->o[dim])
+ ++cur;
+ if(!isliquid(start->material&MATF_VOLUME) || start->orient != O_TOP || !vertwater)
+ {
+ if(start!=matbuf) memmove(matbuf, start, (cur-start)*sizeof(materialsurface));
+ matbuf += mergemats(matbuf, cur-start);
+ }
+ else if(cur-start>=4)
+ {
+ QuadNode vmats(0, 0, worldsize);
+ loopi(cur-start) vmats.insert(start[i].o[C[dim]], start[i].o[R[dim]], start[i].csize);
+ vmats.genmatsurfs(start->material, start->orient, start->visible, start->o[dim], matbuf);
+ }
+ else
+ {
+ if(start!=matbuf) memmove(matbuf, start, (cur-start)*sizeof(materialsurface));
+ matbuf += cur-start;
+ }
+ }
+ return matsurfs - (end-matbuf);
}
struct waterinfo
{
- materialsurface *m;
- double depth, area;
+ materialsurface *m;
+ double depth, area;
};
void setupmaterials(int start, int len)
{
- int hasmat = 0;
- vector<waterinfo> water;
- unionfind uf;
- if(!len) len = valist.length();
- for(int i = start; i < len; i++)
- {
- vtxarray *va = valist[i];
- materialsurface *skip = NULL;
- loopj(va->matsurfs)
- {
- materialsurface &m = va->matbuf[j];
- int matvol = m.material&MATF_VOLUME;
- if(matvol==MAT_WATER && m.orient==O_TOP)
- {
- m.index = water.length();
- loopvk(water)
- {
- materialsurface &n = *water[k].m;
- if(m.material!=n.material || m.o.z!=n.o.z) continue;
- if(n.o.x+n.rsize==m.o.x || m.o.x+m.rsize==n.o.x)
- {
- if(n.o.y+n.csize>m.o.y && n.o.y<m.o.y+m.csize) uf.unite(m.index, n.index);
- }
- else if(n.o.y+n.csize==m.o.y || m.o.y+m.csize==n.o.y)
- {
- if(n.o.x+n.rsize>m.o.x && n.o.x<m.o.x+m.rsize) uf.unite(m.index, n.index);
- }
- }
- waterinfo &wi = water.add();
- wi.m = &m;
- vec center(m.o.x+m.rsize/2, m.o.y+m.csize/2, m.o.z-WATER_OFFSET);
- m.light = brightestlight(center, vec(0, 0, 1));
- float depth = raycube(center, vec(0, 0, -1), 10000);
- wi.depth = double(depth)*m.rsize*m.csize;
- wi.area = m.rsize*m.csize;
- }
- else if(isliquid(matvol) && m.orient!=O_BOTTOM && m.orient!=O_TOP)
- {
- m.ends = 0;
- int dim = dimension(m.orient), coord = dimcoord(m.orient);
- ivec o(m.o);
- o.z -= 1;
- o[dim] += coord ? 1 : -1;
- int minc = o[dim^1], maxc = minc + (C[dim]==2 ? m.rsize : m.csize);
- ivec co;
- int csize;
- while(o[dim^1] < maxc)
- {
- cube &c = lookupcube(o, 0, co, csize);
- if(isliquid(c.material&MATF_VOLUME)) { m.ends |= 1; break; }
- o[dim^1] += csize;
- }
- o[dim^1] = minc;
- o.z += R[dim]==2 ? m.rsize : m.csize;
- o[dim] -= coord ? 2 : -2;
- while(o[dim^1] < maxc)
- {
- cube &c = lookupcube(o, 0, co, csize);
- if(visiblematerial(c, O_TOP, co, csize)) { m.ends |= 2; break; }
- o[dim^1] += csize;
- }
- }
- else if(matvol==MAT_GLASS)
- {
- int dim = dimension(m.orient);
- vec center(m.o);
- center[R[dim]] += m.rsize/2;
- center[C[dim]] += m.csize/2;
- m.envmap = closestenvmap(center);
- }
- if(matvol) hasmat |= 1<<m.material;
- m.skip = 0;
- if(skip && m.material == skip->material && m.orient == skip->orient && skip->skip < 0xFFFF)
- skip->skip++;
- else
- skip = &m;
- }
- }
- loopv(water)
- {
- int root = uf.find(i);
- if(i==root) continue;
- materialsurface &m = *water[i].m, &n = *water[root].m;
- if(m.light && (!m.light->attr1 || !n.light || (n.light->attr1 && m.light->attr1 > n.light->attr1))) n.light = m.light;
- water[root].depth += water[i].depth;
- water[root].area += water[i].area;
- }
- loopv(water)
- {
- int root = uf.find(i);
- water[i].m->light = water[root].m->light;
- water[i].m->depth = (short)(water[root].depth/water[root].area);
- }
- if(hasmat&(0xF<<MAT_WATER))
- {
- loadcaustics(true);
- preloadwatershaders(true);
- loopi(4) if(hasmat&(1<<(MAT_WATER+i))) lookupmaterialslot(MAT_WATER+i);
- }
- if(hasmat&(0xF<<MAT_LAVA))
- {
- useshaderbyname("lava");
- useshaderbyname("lavaglare");
- loopi(4) if(hasmat&(1<<(MAT_LAVA+i))) lookupmaterialslot(MAT_LAVA+i);
- }
- if(hasmat&(0xF<<MAT_GLASS)) useshaderbyname("glass");
+ int hasmat = 0;
+ vector<waterinfo> water;
+ unionfind uf;
+ if(!len) len = valist.length();
+ for(int i = start; i < len; i++)
+ {
+ vtxarray *va = valist[i];
+ materialsurface *skip = NULL;
+ loopj(va->matsurfs)
+ {
+ materialsurface &m = va->matbuf[j];
+ int matvol = m.material&MATF_VOLUME;
+ if(matvol==MAT_WATER && m.orient==O_TOP)
+ {
+ m.index = water.length();
+ loopvk(water)
+ {
+ materialsurface &n = *water[k].m;
+ if(m.material!=n.material || m.o.z!=n.o.z) continue;
+ if(n.o.x+n.rsize==m.o.x || m.o.x+m.rsize==n.o.x)
+ {
+ if(n.o.y+n.csize>m.o.y && n.o.y<m.o.y+m.csize) uf.unite(m.index, n.index);
+ }
+ else if(n.o.y+n.csize==m.o.y || m.o.y+m.csize==n.o.y)
+ {
+ if(n.o.x+n.rsize>m.o.x && n.o.x<m.o.x+m.rsize) uf.unite(m.index, n.index);
+ }
+ }
+ waterinfo &wi = water.add();
+ wi.m = &m;
+ vec center(m.o.x+m.rsize/2, m.o.y+m.csize/2, m.o.z-WATER_OFFSET);
+ m.light = brightestlight(center, vec(0, 0, 1));
+ float depth = raycube(center, vec(0, 0, -1), 10000);
+ wi.depth = double(depth)*m.rsize*m.csize;
+ wi.area = m.rsize*m.csize;
+ }
+ else if(isliquid(matvol) && m.orient!=O_BOTTOM && m.orient!=O_TOP)
+ {
+ m.ends = 0;
+ int dim = dimension(m.orient), coord = dimcoord(m.orient);
+ ivec o(m.o);
+ o.z -= 1;
+ o[dim] += coord ? 1 : -1;
+ int minc = o[dim^1], maxc = minc + (C[dim]==2 ? m.rsize : m.csize);
+ ivec co;
+ int csize;
+ while(o[dim^1] < maxc)
+ {
+ cube &c = lookupcube(o, 0, co, csize);
+ if(isliquid(c.material&MATF_VOLUME)) { m.ends |= 1; break; }
+ o[dim^1] += csize;
+ }
+ o[dim^1] = minc;
+ o.z += R[dim]==2 ? m.rsize : m.csize;
+ o[dim] -= coord ? 2 : -2;
+ while(o[dim^1] < maxc)
+ {
+ cube &c = lookupcube(o, 0, co, csize);
+ if(visiblematerial(c, O_TOP, co, csize)) { m.ends |= 2; break; }
+ o[dim^1] += csize;
+ }
+ }
+ else if(matvol==MAT_GLASS)
+ {
+ int dim = dimension(m.orient);
+ vec center(m.o);
+ center[R[dim]] += m.rsize/2;
+ center[C[dim]] += m.csize/2;
+ m.envmap = closestenvmap(center);
+ }
+ if(matvol) hasmat |= 1<<m.material;
+ m.skip = 0;
+ if(skip && m.material == skip->material && m.orient == skip->orient && skip->skip < 0xFFFF)
+ skip->skip++;
+ else
+ skip = &m;
+ }
+ }
+ loopv(water)
+ {
+ int root = uf.find(i);
+ if(i==root) continue;
+ materialsurface &m = *water[i].m, &n = *water[root].m;
+ if(m.light && (!m.light->attr1 || !n.light || (n.light->attr1 && m.light->attr1 > n.light->attr1))) n.light = m.light;
+ water[root].depth += water[i].depth;
+ water[root].area += water[i].area;
+ }
+ loopv(water)
+ {
+ int root = uf.find(i);
+ water[i].m->light = water[root].m->light;
+ water[i].m->depth = (short)(water[root].depth/water[root].area);
+ }
+ if(hasmat&(0xF<<MAT_WATER))
+ {
+ loadcaustics(true);
+ preloadwatershaders(true);
+ loopi(4) if(hasmat&(1<<(MAT_WATER+i))) lookupmaterialslot(MAT_WATER+i);
+ }
+ if(hasmat&(0xF<<MAT_LAVA))
+ {
+ useshaderbyname("lava");
+ useshaderbyname("lavaglare");
+ loopi(4) if(hasmat&(1<<(MAT_LAVA+i))) lookupmaterialslot(MAT_LAVA+i);
+ }
+ if(hasmat&(0xF<<MAT_GLASS)) useshaderbyname("glass");
}
VARP(showmat, 0, 1, 1);
@@ -455,110 +455,110 @@ static bool sortedit;
static inline bool vismatcmp(const materialsurface *xm, const materialsurface *ym)
{
- const materialsurface &x = *xm, &y = *ym;
- if(!sortedit)
- {
- if((x.material&MATF_VOLUME) == MAT_LAVA) { if((y.material&MATF_VOLUME) != MAT_LAVA) return true; }
- else if((y.material&MATF_VOLUME) == MAT_LAVA) return false;
- }
- int xdim = dimension(x.orient), ydim = dimension(y.orient);
- loopi(3)
- {
- int dim = sortdim[i], xmin, xmax, ymin, ymax;
- xmin = xmax = x.o[dim];
- if(dim==C[xdim]) xmax += x.csize;
- else if(dim==R[xdim]) xmax += x.rsize;
- ymin = ymax = y.o[dim];
- if(dim==C[ydim]) ymax += y.csize;
- else if(dim==R[ydim]) ymax += y.rsize;
- if(xmax > ymin && ymax > xmin) continue;
- int c = sortorigin[dim];
- if(c > xmin && c < xmax) return sortedit;
- if(c > ymin && c < ymax) return !sortedit;
- xmin = abs(xmin - c);
- xmax = abs(xmax - c);
- ymin = abs(ymin - c);
- ymax = abs(ymax - c);
- if(max(xmin, xmax) <= min(ymin, ymax)) return sortedit;
- else if(max(ymin, ymax) <= min(xmin, xmax)) return !sortedit;
- }
- if(x.material < y.material) return sortedit;
- if(x.material > y.material) return !sortedit;
- return false;
+ const materialsurface &x = *xm, &y = *ym;
+ if(!sortedit)
+ {
+ if((x.material&MATF_VOLUME) == MAT_LAVA) { if((y.material&MATF_VOLUME) != MAT_LAVA) return true; }
+ else if((y.material&MATF_VOLUME) == MAT_LAVA) return false;
+ }
+ int xdim = dimension(x.orient), ydim = dimension(y.orient);
+ loopi(3)
+ {
+ int dim = sortdim[i], xmin, xmax, ymin, ymax;
+ xmin = xmax = x.o[dim];
+ if(dim==C[xdim]) xmax += x.csize;
+ else if(dim==R[xdim]) xmax += x.rsize;
+ ymin = ymax = y.o[dim];
+ if(dim==C[ydim]) ymax += y.csize;
+ else if(dim==R[ydim]) ymax += y.rsize;
+ if(xmax > ymin && ymax > xmin) continue;
+ int c = sortorigin[dim];
+ if(c > xmin && c < xmax) return sortedit;
+ if(c > ymin && c < ymax) return !sortedit;
+ xmin = abs(xmin - c);
+ xmax = abs(xmax - c);
+ ymin = abs(ymin - c);
+ ymax = abs(ymax - c);
+ if(max(xmin, xmax) <= min(ymin, ymax)) return sortedit;
+ else if(max(ymin, ymax) <= min(xmin, xmax)) return !sortedit;
+ }
+ if(x.material < y.material) return sortedit;
+ if(x.material > y.material) return !sortedit;
+ return false;
}
void sortmaterials(vector<materialsurface *> &vismats)
{
- sortorigin = ivec(camera1->o);
- if(reflecting) sortorigin.z = int(reflectz - (camera1->o.z - reflectz));
- vec dir;
- vecfromyawpitch(camera1->yaw, reflecting ? -camera1->pitch : camera1->pitch, 1, 0, dir);
- loopi(3) { dir[i] = fabs(dir[i]); sortdim[i] = i; }
- if(dir[sortdim[2]] > dir[sortdim[1]]) swap(sortdim[2], sortdim[1]);
- if(dir[sortdim[1]] > dir[sortdim[0]]) swap(sortdim[1], sortdim[0]);
- if(dir[sortdim[2]] > dir[sortdim[1]]) swap(sortdim[2], sortdim[1]);
-
- for(vtxarray *va = reflecting ? reflectedva : visibleva; va; va = reflecting ? va->rnext : va->next)
- {
- if(!va->matsurfs || va->occluded >= OCCLUDE_BB) continue;
- if(reflecting || refracting>0 ? va->o.z+va->size <= reflectz : va->o.z >= reflectz) continue;
- loopi(va->matsurfs)
- {
- materialsurface &m = va->matbuf[i];
- if(!editmode || !showmat || drawtex)
- {
- int matvol = m.material&MATF_VOLUME;
- if(matvol==MAT_WATER && (m.orient==O_TOP || (refracting<0 && reflectz>worldsize))) { i += m.skip; continue; }
- if(m.visible == MATSURF_EDIT_ONLY) { i += m.skip; continue; }
- if(glaring && matvol!=MAT_LAVA) { i += m.skip; continue; }
- }
- else if(glaring) continue;
- vismats.add(&m);
- }
- }
- sortedit = editmode && showmat && !drawtex;
- vismats.sort(vismatcmp);
+ sortorigin = ivec(camera1->o);
+ if(reflecting) sortorigin.z = int(reflectz - (camera1->o.z - reflectz));
+ vec dir;
+ vecfromyawpitch(camera1->yaw, reflecting ? -camera1->pitch : camera1->pitch, 1, 0, dir);
+ loopi(3) { dir[i] = fabs(dir[i]); sortdim[i] = i; }
+ if(dir[sortdim[2]] > dir[sortdim[1]]) swap(sortdim[2], sortdim[1]);
+ if(dir[sortdim[1]] > dir[sortdim[0]]) swap(sortdim[1], sortdim[0]);
+ if(dir[sortdim[2]] > dir[sortdim[1]]) swap(sortdim[2], sortdim[1]);
+
+ for(vtxarray *va = reflecting ? reflectedva : visibleva; va; va = reflecting ? va->rnext : va->next)
+ {
+ if(!va->matsurfs || va->occluded >= OCCLUDE_BB) continue;
+ if(reflecting || refracting>0 ? va->o.z+va->size <= reflectz : va->o.z >= reflectz) continue;
+ loopi(va->matsurfs)
+ {
+ materialsurface &m = va->matbuf[i];
+ if(!editmode || !showmat || drawtex)
+ {
+ int matvol = m.material&MATF_VOLUME;
+ if(matvol==MAT_WATER && (m.orient==O_TOP || (refracting<0 && reflectz>worldsize))) { i += m.skip; continue; }
+ if(m.visible == MATSURF_EDIT_ONLY) { i += m.skip; continue; }
+ if(glaring && matvol!=MAT_LAVA) { i += m.skip; continue; }
+ }
+ else if(glaring) continue;
+ vismats.add(&m);
+ }
+ }
+ sortedit = editmode && showmat && !drawtex;
+ vismats.sort(vismatcmp);
}
void rendermatgrid(vector<materialsurface *> &vismats)
{
- enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- int lastmat = -1;
- bvec4 color(0, 0, 0, 0);
- loopvrev(vismats)
- {
- materialsurface &m = *vismats[i];
- if(m.material != lastmat)
- {
- switch(m.material&~MATF_INDEX)
- {
- case MAT_WATER: color = bvec4( 0, 0, 85, 255); break; // blue
- case MAT_CLIP: color = bvec4(85, 0, 0, 255); break; // red
- case MAT_GLASS: color = bvec4( 0, 85, 85, 255); break; // cyan
- case MAT_NOCLIP: color = bvec4( 0, 85, 0, 255); break; // green
- case MAT_LAVA: color = bvec4(85, 40, 0, 255); break; // orange
- case MAT_GAMECLIP: color = bvec4(85, 85, 0, 255); break; // yellow
- case MAT_DEATH: color = bvec4(40, 40, 40, 255); break; // black
- case MAT_ALPHA: color = bvec4(85, 0, 85, 255); break; // pink
- default: continue;
- }
- lastmat = m.material;
- }
- drawmaterial(m, -0.1f, color);
- }
- xtraverts += gle::end();
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
+ enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ int lastmat = -1;
+ bvec4 color(0, 0, 0, 0);
+ loopvrev(vismats)
+ {
+ materialsurface &m = *vismats[i];
+ if(m.material != lastmat)
+ {
+ switch(m.material&~MATF_INDEX)
+ {
+ case MAT_WATER: color = bvec4( 0, 0, 85, 255); break; // blue
+ case MAT_CLIP: color = bvec4(85, 0, 0, 255); break; // red
+ case MAT_GLASS: color = bvec4( 0, 85, 85, 255); break; // cyan
+ case MAT_NOCLIP: color = bvec4( 0, 85, 0, 255); break; // green
+ case MAT_LAVA: color = bvec4(85, 40, 0, 255); break; // orange
+ case MAT_GAMECLIP: color = bvec4(85, 85, 0, 255); break; // yellow
+ case MAT_DEATH: color = bvec4(40, 40, 40, 255); break; // black
+ case MAT_ALPHA: color = bvec4(85, 0, 85, 255); break; // pink
+ default: continue;
+ }
+ lastmat = m.material;
+ }
+ drawmaterial(m, -0.1f, color);
+ }
+ xtraverts += gle::end();
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
}
#define GLASSVARS(name) \
- bvec name##color(0x20, 0x80, 0xC0); \
- HVARFR(name##colour, 0, 0x2080C0, 0xFFFFFF, \
- { \
- if(!name##colour) name##colour = 0x2080C0; \
- name##color = bvec((name##colour>>16)&0xFF, (name##colour>>8)&0xFF, name##colour&0xFF); \
- });
+ bvec name##color(0x20, 0x80, 0xC0); \
+ HVARFR(name##colour, 0, 0x2080C0, 0xFFFFFF, \
+ { \
+ if(!name##colour) name##colour = 0x2080C0; \
+ name##color = bvec((name##colour>>16)&0xFF, (name##colour>>8)&0xFF, name##colour&0xFF); \
+ });
GLASSVARS(glass)
GLASSVARS(glass2)
@@ -572,301 +572,300 @@ VARP(glassenv, 0, 1, 1);
static void drawglass(const materialsurface &m, float offset)
{
- if(gle::attribbuf.empty())
- {
- gle::defvertex();
- gle::defnormal(4, GL_BYTE);
- gle::begin(GL_QUADS);
- }
- float x = m.o.x, y = m.o.y, z = m.o.z, csize = m.csize, rsize = m.rsize;
- switch(m.orient)
- {
- #define GENFACEORIENT(orient, v0, v1, v2, v3) \
- case orient: v0 v1 v2 v3 break;
- #define GENFACEVERT(orient, vert, mx,my,mz, sx,sy,sz) \
- { \
- gle::attribf(mx sx, my sy, mz sz); \
- gle::attrib(matnormals[orient]); \
- }
- GENFACEVERTS(x, x, y, y, z, z, /**/, + csize, /**/, + rsize, + offset, - offset)
- #undef GENFACEORIENT
- #undef GENFACEVERT
- }
+ if(gle::attribbuf.empty())
+ {
+ gle::defvertex();
+ gle::defnormal(4, GL_BYTE);
+ gle::begin(GL_QUADS);
+ }
+ float x = m.o.x, y = m.o.y, z = m.o.z, csize = m.csize, rsize = m.rsize;
+ switch(m.orient)
+ {
+ #define GENFACEORIENT(orient, v0, v1, v2, v3) \
+ case orient: v0 v1 v2 v3 break;
+ #define GENFACEVERT(orient, vert, mx,my,mz, sx,sy,sz) \
+ { \
+ gle::attribf(mx sx, my sy, mz sz); \
+ gle::attrib(matnormals[orient]); \
+ }
+ GENFACEVERTS(x, x, y, y, z, z, /**/, + csize, /**/, + rsize, + offset, - offset)
+ #undef GENFACEORIENT
+ #undef GENFACEVERT
+ }
}
VARFP(waterfallenv, 0, 1, 1, preloadwatershaders());
static inline void changematerial(int mat, int orient)
{
- switch(mat&~MATF_INDEX)
- {
- case MAT_LAVA:
- if(orient==O_TOP) flushlava();
- else xtraverts += gle::end();
- break;
- default:
- xtraverts += gle::end();
- break;
- }
+ switch(mat&~MATF_INDEX)
+ {
+ case MAT_LAVA:
+ if(orient==O_TOP) flushlava();
+ else xtraverts += gle::end();
+ break;
+ default:
+ xtraverts += gle::end();
+ break;
+ }
}
void rendermaterials()
{
- vector<materialsurface *> vismats;
- sortmaterials(vismats);
- if(vismats.empty()) return;
-
- glDisable(GL_CULL_FACE);
-
- MSlot *mslot = NULL;
- int lastorient = -1, lastmat = -1, usedwaterfall = -1;
- bool depth = true, blended = false;
- ushort envmapped = EMID_NONE;
-
- GLOBALPARAM(camera, camera1->o);
-
- int lastfogtype = 1;
- if(editmode && showmat && !drawtex)
- {
- glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
- glEnable(GL_BLEND); blended = true;
- foggednotextureshader->set();
- zerofogcolor(); lastfogtype = 0;
- bvec4 color(0, 0, 0, 0);
- loopv(vismats)
- {
- const materialsurface &m = *vismats[i];
- if(lastmat!=m.material)
- {
- switch(m.material&~MATF_INDEX)
- {
- case MAT_WATER: color = bvec4(255, 128, 0, 255); break; // blue
- case MAT_CLIP: color = bvec4( 0, 255, 255, 255); break; // red
- case MAT_GLASS: color = bvec4(255, 0, 0, 255); break; // cyan
- case MAT_NOCLIP: color = bvec4(255, 0, 255, 255); break; // green
- case MAT_LAVA: color = bvec4( 0, 128, 255, 255); break; // orange
- case MAT_GAMECLIP: color = bvec4( 0, 0, 255, 255); break; // yellow
- case MAT_DEATH: color = bvec4(192, 192, 192, 255); break; // black
- case MAT_ALPHA: color = bvec4( 0, 255, 0, 255); break; // pink
- default: continue;
- }
- lastmat = m.material;
- }
- drawmaterial(m, -0.1f, color);
- }
- xtraverts += gle::end();
- }
- else loopv(vismats)
- {
- const materialsurface &m = *vismats[i];
- int matvol = m.material&~MATF_INDEX;
- if(lastmat!=m.material || lastorient!=m.orient || (matvol==MAT_GLASS && envmapped && m.envmap != envmapped))
- {
- int fogtype = lastfogtype;
- switch(matvol)
- {
- case MAT_WATER:
- if(m.orient == O_TOP) continue;
- if(lastmat == m.material) break;
- mslot = &lookupmaterialslot(m.material, false);
- if(!mslot->loaded || !mslot->sts.inrange(1)) continue;
- else
- {
- changematerial(lastmat, lastorient);
- glBindTexture(GL_TEXTURE_2D, mslot->sts[1].t->id);
-
- bvec wfcol = getwaterfallcolor(m.material);
- if(wfcol.iszero()) wfcol = getwatercolor(m.material);
- gle::color(wfcol, 192);
-
- int wfog = getwaterfog(m.material);
- if(!wfog && !waterfallenv)
- {
- foggednotextureshader->set();
- fogtype = 1;
- if(blended) { glDisable(GL_BLEND); blended = false; }
- if(!depth) { glDepthMask(GL_TRUE); depth = true; }
- }
- else if((!waterfallrefract || reflecting || refracting) && !waterfallenv)
- {
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
- SETSHADER(waterfall);
- fogtype = 0;
- if(!blended) { glEnable(GL_BLEND); blended = true; }
- if(depth) { glDepthMask(GL_FALSE); depth = false; }
- }
- else
- {
- fogtype = 1;
-
- if(waterfallrefract && wfog && !reflecting && !refracting)
- {
- if(waterfallenv) SETSHADER(waterfallenvrefract);
- else SETSHADER(waterfallrefract);
- if(blended) { glDisable(GL_BLEND); blended = false; }
- if(!depth) { glDepthMask(GL_TRUE); depth = true; }
- }
- else
- {
- SETSHADER(waterfallenv);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- if(wfog)
- {
- if(!blended) { glEnable(GL_BLEND); blended = true; }
- if(depth) { glDepthMask(GL_FALSE); depth = false; }
- }
- else
- {
- if(blended) { glDisable(GL_BLEND); blended = false; }
- if(!depth) { glDepthMask(GL_TRUE); depth = true; }
- }
- }
-
- if(usedwaterfall != m.material)
- {
- Texture *dudv = mslot->sts.inrange(5) ? mslot->sts[5].t : notexture;
- float scale = TEX_SCALE/(dudv->ys*mslot->scale);
- LOCALPARAMF(dudvoffset, 0, scale*16*lastmillis/1000.0f);
-
- glActiveTexture_(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, mslot->sts.inrange(4) ? mslot->sts[4].t->id : notexture->id);
- glActiveTexture_(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, mslot->sts.inrange(5) ? mslot->sts[5].t->id : notexture->id);
- if(waterfallenv)
- {
- glActiveTexture_(GL_TEXTURE3);
- glBindTexture(GL_TEXTURE_CUBE_MAP, lookupenvmap(*mslot));
- }
- if(waterfallrefract && (!reflecting || !refracting) && usedwaterfall < 0)
- {
- glActiveTexture_(GL_TEXTURE4);
- extern void setupwaterfallrefract();
- setupwaterfallrefract();
- }
- glActiveTexture_(GL_TEXTURE0);
-
- usedwaterfall = m.material;
- }
- }
- float angle = fmod(float(lastmillis/600.0f/(2*M_PI)), 1.0f),
- s = angle - int(angle) - 0.5f;
- s *= 8 - fabs(s)*16;
- wfwave = vertwater ? WATER_AMPLITUDE*s-WATER_OFFSET : -WATER_OFFSET;
- float scroll = 16.0f*lastmillis/1000.0f;
- float xscale = TEX_SCALE/(mslot->sts[1].t->xs*mslot->scale);
- float yscale = -TEX_SCALE/(mslot->sts[1].t->ys*mslot->scale);
- LOCALPARAMF(waterfalltexgen, xscale, yscale, 0.0f, scroll);
- }
- break;
-
- case MAT_LAVA:
- if(lastmat==m.material && lastorient!=O_TOP && m.orient!=O_TOP) break;
- mslot = &lookupmaterialslot(m.material, false);
- if(!mslot->loaded) continue;
- else
- {
- int subslot = m.orient==O_TOP ? 0 : 1;
- if(!mslot->sts.inrange(subslot)) continue;
- changematerial(lastmat, lastorient);
- glBindTexture(GL_TEXTURE_2D, mslot->sts[subslot].t->id);
- }
- if(lastmat!=m.material)
- {
- if(!depth) { glDepthMask(GL_TRUE); depth = true; }
- if(blended) { glDisable(GL_BLEND); blended = false; }
- float t = lastmillis/2000.0f;
- t -= floor(t);
- t = 1.0f - 2*fabs(t-0.5f);
- extern int glare;
- if(glare) t = 0.625f + 0.075f*t;
- else t = 0.5f + 0.5f*t;
- gle::colorf(t, t, t);
- if(glaring) SETSHADER(lavaglare); else SETSHADER(lava);
- fogtype = 1;
- }
- if(m.orient!=O_TOP)
- {
- float angle = fmod(float(lastmillis/2000.0f/(2*M_PI)), 1.0f),
- s = angle - int(angle) - 0.5f;
- s *= 8 - fabs(s)*16;
- wfwave = vertwater ? WATER_AMPLITUDE*s-WATER_OFFSET : -WATER_OFFSET;
- float scroll = 16.0f*lastmillis/3000.0f;
- float xscale = TEX_SCALE/(mslot->sts[1].t->xs*mslot->scale);
- float yscale = -TEX_SCALE/(mslot->sts[1].t->ys*mslot->scale);
- LOCALPARAMF(lavatexgen, xscale, yscale, 0.0f, scroll);
- }
- else setuplava(mslot->sts[0].t, mslot->scale);
- break;
-
- case MAT_GLASS:
- if((m.envmap==EMID_NONE || !glassenv || envmapped==m.envmap) && lastmat==m.material) break;
- changematerial(lastmat, lastorient);
- if(m.envmap!=EMID_NONE && glassenv && envmapped!=m.envmap)
- {
- glBindTexture(GL_TEXTURE_CUBE_MAP, lookupenvmap(m.envmap));
- envmapped = m.envmap;
- }
- if(lastmat!=m.material)
- {
- if(!blended) { glEnable(GL_BLEND); blended = true; }
- if(depth) { glDepthMask(GL_FALSE); depth = false; }
- const bvec &gcol = getglasscolor(m.material);
- if(m.envmap!=EMID_NONE && glassenv)
- {
- glBlendFunc(GL_ONE, GL_SRC_ALPHA);
- gle::color(gcol);
- SETSHADER(glass);
- }
- else
- {
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- gle::color(gcol, 40);
- foggednotextureshader->set();
- fogtype = 1;
- }
- }
- break;
-
- default: continue;
- }
- lastmat = m.material;
- lastorient = m.orient;
- if(fogtype!=lastfogtype)
- {
- if(fogtype) resetfogcolor();
- else zerofogcolor();
- lastfogtype = fogtype;
- }
- }
- switch(matvol)
- {
- case MAT_WATER:
- renderwaterfall(m, 0.1f);
- break;
-
- case MAT_LAVA:
- if(m.orient==O_TOP) renderlava(m);
- else renderwaterfall(m, 0.1f);
- break;
-
- case MAT_GLASS:
- drawglass(m, 0.1f);
- break;
- }
- }
-
- if(lastorient >= 0) changematerial(lastmat, lastorient);
-
- if(!depth) glDepthMask(GL_TRUE);
- if(blended) glDisable(GL_BLEND);
- if(!lastfogtype) resetfogcolor();
- extern int wireframe;
- if(editmode && showmat && !drawtex && !wireframe)
- {
- foggednotextureshader->set();
- rendermatgrid(vismats);
- }
-
- glEnable(GL_CULL_FACE);
+ vector<materialsurface *> vismats;
+ sortmaterials(vismats);
+ if(vismats.empty()) return;
+
+ glDisable(GL_CULL_FACE);
+
+ MSlot *mslot = NULL;
+ int lastorient = -1, lastmat = -1, usedwaterfall = -1;
+ bool depth = true, blended = false;
+ ushort envmapped = EMID_NONE;
+
+ GLOBALPARAM(camera, camera1->o);
+
+ int lastfogtype = 1;
+ if(editmode && showmat && !drawtex)
+ {
+ glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
+ glEnable(GL_BLEND); blended = true;
+ foggednotextureshader->set();
+ zerofogcolor(); lastfogtype = 0;
+ bvec4 color(0, 0, 0, 0);
+ loopv(vismats)
+ {
+ const materialsurface &m = *vismats[i];
+ if(lastmat!=m.material)
+ {
+ switch(m.material&~MATF_INDEX)
+ {
+ case MAT_WATER: color = bvec4(255, 128, 0, 255); break; // blue
+ case MAT_CLIP: color = bvec4( 0, 255, 255, 255); break; // red
+ case MAT_GLASS: color = bvec4(255, 0, 0, 255); break; // cyan
+ case MAT_NOCLIP: color = bvec4(255, 0, 255, 255); break; // green
+ case MAT_LAVA: color = bvec4( 0, 128, 255, 255); break; // orange
+ case MAT_GAMECLIP: color = bvec4( 0, 0, 255, 255); break; // yellow
+ case MAT_DEATH: color = bvec4(192, 192, 192, 255); break; // black
+ case MAT_ALPHA: color = bvec4( 0, 255, 0, 255); break; // pink
+ default: continue;
+ }
+ lastmat = m.material;
+ }
+ drawmaterial(m, -0.1f, color);
+ }
+ xtraverts += gle::end();
+ }
+ else loopv(vismats)
+ {
+ const materialsurface &m = *vismats[i];
+ int matvol = m.material&~MATF_INDEX;
+ if(lastmat!=m.material || lastorient!=m.orient || (matvol==MAT_GLASS && envmapped && m.envmap != envmapped))
+ {
+ int fogtype = lastfogtype;
+ switch(matvol)
+ {
+ case MAT_WATER:
+ if(m.orient == O_TOP) continue;
+ if(lastmat == m.material) break;
+ mslot = &lookupmaterialslot(m.material, false);
+ if(!mslot->loaded || !mslot->sts.inrange(1)) continue;
+ else
+ {
+ changematerial(lastmat, lastorient);
+ glBindTexture(GL_TEXTURE_2D, mslot->sts[1].t->id);
+
+ bvec wfcol = getwaterfallcolor(m.material);
+ if(wfcol.iszero()) wfcol = getwatercolor(m.material);
+ gle::color(wfcol, 192);
+
+ int wfog = getwaterfog(m.material);
+ if(!wfog && !waterfallenv)
+ {
+ foggednotextureshader->set();
+ fogtype = 1;
+ if(blended) { glDisable(GL_BLEND); blended = false; }
+ if(!depth) { glDepthMask(GL_TRUE); depth = true; }
+ }
+ else if((!waterfallrefract || reflecting || refracting) && !waterfallenv)
+ {
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
+ SETSHADER(waterfall);
+ fogtype = 0;
+ if(!blended) { glEnable(GL_BLEND); blended = true; }
+ if(depth) { glDepthMask(GL_FALSE); depth = false; }
+ }
+ else
+ {
+ fogtype = 1;
+
+ if(waterfallrefract && wfog && !reflecting && !refracting)
+ {
+ if(waterfallenv) SETSHADER(waterfallenvrefract);
+ else SETSHADER(waterfallrefract);
+ if(blended) { glDisable(GL_BLEND); blended = false; }
+ if(!depth) { glDepthMask(GL_TRUE); depth = true; }
+ }
+ else
+ {
+ SETSHADER(waterfallenv);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ if(wfog)
+ {
+ if(!blended) { glEnable(GL_BLEND); blended = true; }
+ if(depth) { glDepthMask(GL_FALSE); depth = false; }
+ }
+ else
+ {
+ if(blended) { glDisable(GL_BLEND); blended = false; }
+ if(!depth) { glDepthMask(GL_TRUE); depth = true; }
+ }
+ }
+
+ if(usedwaterfall != m.material)
+ {
+ Texture *dudv = mslot->sts.inrange(5) ? mslot->sts[5].t : notexture;
+ float scale = TEX_SCALE/(dudv->ys*mslot->scale);
+ LOCALPARAMF(dudvoffset, 0, scale*16*lastmillis/1000.0f);
+
+ glActiveTexture_(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mslot->sts.inrange(4) ? mslot->sts[4].t->id : notexture->id);
+ glActiveTexture_(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, mslot->sts.inrange(5) ? mslot->sts[5].t->id : notexture->id);
+ if(waterfallenv)
+ {
+ glActiveTexture_(GL_TEXTURE3);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, lookupenvmap(*mslot));
+ }
+ if(waterfallrefract && (!reflecting || !refracting) && usedwaterfall < 0)
+ {
+ glActiveTexture_(GL_TEXTURE4);
+ extern void setupwaterfallrefract();
+ setupwaterfallrefract();
+ }
+ glActiveTexture_(GL_TEXTURE0);
+
+ usedwaterfall = m.material;
+ }
+ }
+ float angle = fmod(float(lastmillis/600.0f/(2*M_PI)), 1.0f),
+ s = angle - int(angle) - 0.5f;
+ s *= 8 - fabs(s)*16;
+ wfwave = vertwater ? WATER_AMPLITUDE*s-WATER_OFFSET : -WATER_OFFSET;
+ float scroll = 16.0f*lastmillis/1000.0f;
+ float xscale = TEX_SCALE/(mslot->sts[1].t->xs*mslot->scale);
+ float yscale = -TEX_SCALE/(mslot->sts[1].t->ys*mslot->scale);
+ LOCALPARAMF(waterfalltexgen, xscale, yscale, 0.0f, scroll);
+ }
+ break;
+
+ case MAT_LAVA:
+ if(lastmat==m.material && lastorient!=O_TOP && m.orient!=O_TOP) break;
+ mslot = &lookupmaterialslot(m.material, false);
+ if(!mslot->loaded) continue;
+ else
+ {
+ int subslot = m.orient==O_TOP ? 0 : 1;
+ if(!mslot->sts.inrange(subslot)) continue;
+ changematerial(lastmat, lastorient);
+ glBindTexture(GL_TEXTURE_2D, mslot->sts[subslot].t->id);
+ }
+ if(lastmat!=m.material)
+ {
+ if(!depth) { glDepthMask(GL_TRUE); depth = true; }
+ if(blended) { glDisable(GL_BLEND); blended = false; }
+ float t = lastmillis/2000.0f;
+ t -= floor(t);
+ t = 1.0f - 2*fabs(t-0.5f);
+ extern int glare;
+ if(glare) t = 0.625f + 0.075f*t;
+ else t = 0.5f + 0.5f*t;
+ gle::colorf(t, t, t);
+ if(glaring) SETSHADER(lavaglare); else SETSHADER(lava);
+ fogtype = 1;
+ }
+ if(m.orient!=O_TOP)
+ {
+ float angle = fmod(float(lastmillis/2000.0f/(2*M_PI)), 1.0f),
+ s = angle - int(angle) - 0.5f;
+ s *= 8 - fabs(s)*16;
+ wfwave = vertwater ? WATER_AMPLITUDE*s-WATER_OFFSET : -WATER_OFFSET;
+ float scroll = 16.0f*lastmillis/3000.0f;
+ float xscale = TEX_SCALE/(mslot->sts[1].t->xs*mslot->scale);
+ float yscale = -TEX_SCALE/(mslot->sts[1].t->ys*mslot->scale);
+ LOCALPARAMF(lavatexgen, xscale, yscale, 0.0f, scroll);
+ }
+ else setuplava(mslot->sts[0].t, mslot->scale);
+ break;
+
+ case MAT_GLASS:
+ if((m.envmap==EMID_NONE || !glassenv || envmapped==m.envmap) && lastmat==m.material) break;
+ changematerial(lastmat, lastorient);
+ if(m.envmap!=EMID_NONE && glassenv && envmapped!=m.envmap)
+ {
+ glBindTexture(GL_TEXTURE_CUBE_MAP, lookupenvmap(m.envmap));
+ envmapped = m.envmap;
+ }
+ if(lastmat!=m.material)
+ {
+ if(!blended) { glEnable(GL_BLEND); blended = true; }
+ if(depth) { glDepthMask(GL_FALSE); depth = false; }
+ const bvec &gcol = getglasscolor(m.material);
+ if(m.envmap!=EMID_NONE && glassenv)
+ {
+ glBlendFunc(GL_ONE, GL_SRC_ALPHA);
+ gle::color(gcol);
+ SETSHADER(glass);
+ }
+ else
+ {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ gle::color(gcol, 40);
+ foggednotextureshader->set();
+ fogtype = 1;
+ }
+ }
+ break;
+
+ default: continue;
+ }
+ lastmat = m.material;
+ lastorient = m.orient;
+ if(fogtype!=lastfogtype)
+ {
+ if(fogtype) resetfogcolor();
+ else zerofogcolor();
+ lastfogtype = fogtype;
+ }
+ }
+ switch(matvol)
+ {
+ case MAT_WATER:
+ renderwaterfall(m, 0.1f);
+ break;
+
+ case MAT_LAVA:
+ if(m.orient==O_TOP) renderlava(m);
+ else renderwaterfall(m, 0.1f);
+ break;
+
+ case MAT_GLASS:
+ drawglass(m, 0.1f);
+ break;
+ }
+ }
+
+ if(lastorient >= 0) changematerial(lastmat, lastorient);
+
+ if(!depth) glDepthMask(GL_TRUE);
+ if(blended) glDisable(GL_BLEND);
+ if(!lastfogtype) resetfogcolor();
+ extern int wireframe;
+ if(editmode && showmat && !drawtex && !wireframe)
+ {
+ foggednotextureshader->set();
+ rendermatgrid(vismats);
+ }
+
+ glEnable(GL_CULL_FACE);
}
-
diff --git a/src/engine/md3.h b/src/engine/md3.h
index 2ae5986..7296055 100644
--- a/src/engine/md3.h
+++ b/src/engine/md3.h
@@ -2,181 +2,181 @@ struct md3;
struct md3frame
{
- vec bbmin, bbmax, origin;
- float radius;
- uchar name[16];
+ vec bbmin, bbmax, origin;
+ float radius;
+ uchar name[16];
};
struct md3tag
{
- char name[64];
- float translation[3];
- float rotation[3][3];
+ char name[64];
+ float translation[3];
+ float rotation[3][3];
};
struct md3vertex
{
- short vertex[3];
- short normal;
+ short vertex[3];
+ short normal;
};
struct md3triangle
{
- int vertexindices[3];
+ int vertexindices[3];
};
struct md3header
{
- char id[4];
- int version;
- char name[64];
- int flags;
- int numframes, numtags, nummeshes, numskins;
- int ofs_frames, ofs_tags, ofs_meshes, ofs_eof; // offsets
+ char id[4];
+ int version;
+ char name[64];
+ int flags;
+ int numframes, numtags, nummeshes, numskins;
+ int ofs_frames, ofs_tags, ofs_meshes, ofs_eof; // offsets
};
struct md3meshheader
{
- char id[4];
- char name[64];
- int flags;
- int numframes, numshaders, numvertices, numtriangles;
- int ofs_triangles, ofs_shaders, ofs_uv, ofs_vertices, meshsize; // offsets
+ char id[4];
+ char name[64];
+ int flags;
+ int numframes, numshaders, numvertices, numtriangles;
+ int ofs_triangles, ofs_shaders, ofs_uv, ofs_vertices, meshsize; // offsets
};
struct md3 : vertloader<md3>
{
- md3(const char *name) : vertloader(name) {}
-
- static const char *formatname() { return "md3"; }
- bool flipy() const { return true; }
- int type() const { return MDL_MD3; }
-
- struct md3meshgroup : vertmeshgroup
- {
- bool load(const char *path)
- {
- stream *f = openfile(path, "rb");
- if(!f) return false;
- md3header header;
- f->read(&header, sizeof(md3header));
- lilswap(&header.version, 1);
- lilswap(&header.flags, 9);
- if(strncmp(header.id, "IDP3", 4) != 0 || header.version != 15) // header check
- {
- delete f;
- conoutf(CON_ERROR, "md3: corrupted header");
- return false;
- }
-
- name = newstring(path);
-
- numframes = header.numframes;
-
- int mesh_offset = header.ofs_meshes;
- loopi(header.nummeshes)
- {
- vertmesh &m = *new vertmesh;
- m.group = this;
- meshes.add(&m);
-
- md3meshheader mheader;
- f->seek(mesh_offset, SEEK_SET);
- f->read(&mheader, sizeof(md3meshheader));
- lilswap(&mheader.flags, 10);
-
- m.name = newstring(mheader.name);
-
- m.numtris = mheader.numtriangles;
- m.tris = new tri[m.numtris];
- f->seek(mesh_offset + mheader.ofs_triangles, SEEK_SET);
- loopj(m.numtris)
- {
- md3triangle tri = { 0 };
- f->read(&tri, sizeof(md3triangle)); // read the triangles
- lilswap(tri.vertexindices, 3);
- loopk(3) m.tris[j].vert[k] = (ushort)tri.vertexindices[k];
- }
-
- m.numverts = mheader.numvertices;
- m.tcverts = new tcvert[m.numverts];
- f->seek(mesh_offset + mheader.ofs_uv , SEEK_SET);
- f->read(m.tcverts, m.numverts*2*sizeof(float)); // read the UV data
- lilswap(&m.tcverts[0].tc.x, 2*m.numverts);
-
- m.verts = new vert[numframes*m.numverts];
- f->seek(mesh_offset + mheader.ofs_vertices, SEEK_SET);
- loopj(numframes*m.numverts)
- {
- md3vertex v = { {0}, 0 };
- f->read(&v, sizeof(md3vertex)); // read the vertices
- lilswap(v.vertex, 4);
-
- m.verts[j].pos = vec(v.vertex[0]/64.0f, -v.vertex[1]/64.0f, v.vertex[2]/64.0f);
-
- float lng = (v.normal&0xFF)*2*M_PI/255.0f; // decode vertex normals
- float lat = ((v.normal>>8)&0xFF)*2*M_PI/255.0f;
- m.verts[j].norm = vec(cosf(lat)*sinf(lng), -sinf(lat)*sinf(lng), cosf(lng));
- }
-
- mesh_offset += mheader.meshsize;
- }
-
- numtags = header.numtags;
- if(numtags)
- {
- tags = new tag[numframes*numtags];
- f->seek(header.ofs_tags, SEEK_SET);
- md3tag tag;
-
- loopi(header.numframes*header.numtags)
- {
- f->read(&tag, sizeof(md3tag));
- lilswap(tag.translation, 12);
- if(tag.name[0] && i<header.numtags) tags[i].name = newstring(tag.name);
- matrix4x3 &m = tags[i].transform;
- tag.translation[1] *= -1;
- // undo the -y
- loopj(3) tag.rotation[1][j] *= -1;
- // then restore it
- loopj(3) tag.rotation[j][1] *= -1;
- m.a = vec(tag.rotation[0]);
- m.b = vec(tag.rotation[1]);
- m.c = vec(tag.rotation[2]);
- m.d = vec(tag.translation);
- }
- }
-
- delete f;
- return true;
- }
- };
-
- meshgroup *loadmeshes(const char *name, va_list args)
- {
- md3meshgroup *group = new md3meshgroup;
- if(!group->load(name)) { delete group; return NULL; }
- return group;
- }
-
- bool loaddefaultparts()
- {
- const char *pname = parentdir(name);
- part &mdl = addpart();
- defformatstring(name1, "packages/models/%s/tris.md3", name);
- mdl.meshes = sharemeshes(path(name1));
- if(!mdl.meshes)
- {
- defformatstring(name2, "packages/models/%s/tris.md3", pname); // try md3 in parent folder (vert sharing)
- mdl.meshes = sharemeshes(path(name2));
- if(!mdl.meshes) return false;
- }
- Texture *tex, *masks;
- loadskin(name, pname, tex, masks);
- mdl.initskins(tex, masks);
- if(tex==notexture) conoutf(CON_ERROR, "could not load model skin for %s", name1);
- return true;
- }
+ md3(const char *name) : vertloader(name) {}
+
+ static const char *formatname() { return "md3"; }
+ bool flipy() const { return true; }
+ int type() const { return MDL_MD3; }
+
+ struct md3meshgroup : vertmeshgroup
+ {
+ bool load(const char *path)
+ {
+ stream *f = openfile(path, "rb");
+ if(!f) return false;
+ md3header header;
+ f->read(&header, sizeof(md3header));
+ lilswap(&header.version, 1);
+ lilswap(&header.flags, 9);
+ if(strncmp(header.id, "IDP3", 4) != 0 || header.version != 15) // header check
+ {
+ delete f;
+ conoutf(CON_ERROR, "md3: corrupted header");
+ return false;
+ }
+
+ name = newstring(path);
+
+ numframes = header.numframes;
+
+ int mesh_offset = header.ofs_meshes;
+ loopi(header.nummeshes)
+ {
+ vertmesh &m = *new vertmesh;
+ m.group = this;
+ meshes.add(&m);
+
+ md3meshheader mheader;
+ f->seek(mesh_offset, SEEK_SET);
+ f->read(&mheader, sizeof(md3meshheader));
+ lilswap(&mheader.flags, 10);
+
+ m.name = newstring(mheader.name);
+
+ m.numtris = mheader.numtriangles;
+ m.tris = new tri[m.numtris];
+ f->seek(mesh_offset + mheader.ofs_triangles, SEEK_SET);
+ loopj(m.numtris)
+ {
+ md3triangle tri = { 0 };
+ f->read(&tri, sizeof(md3triangle)); // read the triangles
+ lilswap(tri.vertexindices, 3);
+ loopk(3) m.tris[j].vert[k] = (ushort)tri.vertexindices[k];
+ }
+
+ m.numverts = mheader.numvertices;
+ m.tcverts = new tcvert[m.numverts];
+ f->seek(mesh_offset + mheader.ofs_uv , SEEK_SET);
+ f->read(m.tcverts, m.numverts*2*sizeof(float)); // read the UV data
+ lilswap(&m.tcverts[0].tc.x, 2*m.numverts);
+
+ m.verts = new vert[numframes*m.numverts];
+ f->seek(mesh_offset + mheader.ofs_vertices, SEEK_SET);
+ loopj(numframes*m.numverts)
+ {
+ md3vertex v = { {0}, 0 };
+ f->read(&v, sizeof(md3vertex)); // read the vertices
+ lilswap(v.vertex, 4);
+
+ m.verts[j].pos = vec(v.vertex[0]/64.0f, -v.vertex[1]/64.0f, v.vertex[2]/64.0f);
+
+ float lng = (v.normal&0xFF)*2*M_PI/255.0f; // decode vertex normals
+ float lat = ((v.normal>>8)&0xFF)*2*M_PI/255.0f;
+ m.verts[j].norm = vec(cosf(lat)*sinf(lng), -sinf(lat)*sinf(lng), cosf(lng));
+ }
+
+ mesh_offset += mheader.meshsize;
+ }
+
+ numtags = header.numtags;
+ if(numtags)
+ {
+ tags = new tag[numframes*numtags];
+ f->seek(header.ofs_tags, SEEK_SET);
+ md3tag tag;
+
+ loopi(header.numframes*header.numtags)
+ {
+ f->read(&tag, sizeof(md3tag));
+ lilswap(tag.translation, 12);
+ if(tag.name[0] && i<header.numtags) tags[i].name = newstring(tag.name);
+ matrix4x3 &m = tags[i].transform;
+ tag.translation[1] *= -1;
+ // undo the -y
+ loopj(3) tag.rotation[1][j] *= -1;
+ // then restore it
+ loopj(3) tag.rotation[j][1] *= -1;
+ m.a = vec(tag.rotation[0]);
+ m.b = vec(tag.rotation[1]);
+ m.c = vec(tag.rotation[2]);
+ m.d = vec(tag.translation);
+ }
+ }
+
+ delete f;
+ return true;
+ }
+ };
+
+ meshgroup *loadmeshes(const char *name, va_list args)
+ {
+ md3meshgroup *group = new md3meshgroup;
+ if(!group->load(name)) { delete group; return NULL; }
+ return group;
+ }
+
+ bool loaddefaultparts()
+ {
+ const char *pname = parentdir(name);
+ part &mdl = addpart();
+ defformatstring(name1, "packages/models/%s/tris.md3", name);
+ mdl.meshes = sharemeshes(path(name1));
+ if(!mdl.meshes)
+ {
+ defformatstring(name2, "packages/models/%s/tris.md3", pname); // try md3 in parent folder (vert sharing)
+ mdl.meshes = sharemeshes(path(name2));
+ if(!mdl.meshes) return false;
+ }
+ Texture *tex, *masks;
+ loadskin(name, pname, tex, masks);
+ mdl.initskins(tex, masks);
+ if(tex==notexture) conoutf(CON_ERROR, "could not load model skin for %s", name1);
+ return true;
+ }
};
vertcommands<md3> md3commands;
diff --git a/src/engine/md5.h b/src/engine/md5.h
index 0d59586..d948ccf 100644
--- a/src/engine/md5.h
+++ b/src/engine/md5.h
@@ -2,417 +2,417 @@ struct md5;
struct md5joint
{
- vec pos;
- quat orient;
+ vec pos;
+ quat orient;
};
struct md5weight
{
- int joint;
- float bias;
- vec pos;
-};
+ int joint;
+ float bias;
+ vec pos;
+};
struct md5vert
{
- vec2 tc;
- ushort start, count;
+ vec2 tc;
+ ushort start, count;
};
struct md5hierarchy
{
- string name;
- int parent, flags, start;
+ string name;
+ int parent, flags, start;
};
struct md5 : skelloader<md5>
{
- md5(const char *name) : skelloader(name) {}
-
- static const char *formatname() { return "md5"; }
- int type() const { return MDL_MD5; }
-
- struct md5mesh : skelmesh
- {
- md5weight *weightinfo;
- int numweights;
- md5vert *vertinfo;
-
- md5mesh() : weightinfo(NULL), numweights(0), vertinfo(NULL)
- {
- }
-
- ~md5mesh()
- {
- cleanup();
- }
-
- void cleanup()
- {
- DELETEA(weightinfo);
- DELETEA(vertinfo);
- }
-
- void buildverts(vector<md5joint> &joints)
- {
- loopi(numverts)
- {
- md5vert &v = vertinfo[i];
- vec pos(0, 0, 0);
- loopk(v.count)
- {
- md5weight &w = weightinfo[v.start+k];
- md5joint &j = joints[w.joint];
- vec wpos = j.orient.rotate(w.pos);
- wpos.add(j.pos);
- wpos.mul(w.bias);
- pos.add(wpos);
- }
- vert &vv = verts[i];
- vv.pos = pos;
- vv.tc = v.tc;
-
- blendcombo c;
- int sorted = 0;
- loopj(v.count)
- {
- md5weight &w = weightinfo[v.start+j];
- sorted = c.addweight(sorted, w.bias, w.joint);
- }
- c.finalize(sorted);
- vv.blend = addblendcombo(c);
- }
- }
-
- void load(stream *f, char *buf, size_t bufsize)
- {
- md5weight w;
- md5vert v;
- tri t;
- int index;
-
- while(f->getline(buf, bufsize) && buf[0]!='}')
- {
- if(strstr(buf, "// meshes:"))
- {
- char *start = strchr(buf, ':')+1;
- if(*start==' ') start++;
- char *end = start + strlen(start)-1;
- while(end >= start && isspace(*end)) end--;
- name = newstring(start, end+1-start);
- }
- else if(strstr(buf, "shader"))
- {
- char *start = strchr(buf, '"'), *end = start ? strchr(start+1, '"') : NULL;
- if(start && end)
- {
- char *texname = newstring(start+1, end-(start+1));
- part *p = loading->parts.last();
- p->initskins(notexture, notexture, group->meshes.length());
- skin &s = p->skins.last();
- s.tex = textureload(makerelpath(dir, texname), 0, true, false);
- delete[] texname;
- }
- }
- else if(sscanf(buf, " numverts %d", &numverts)==1)
- {
- numverts = max(numverts, 0);
- if(numverts)
- {
- vertinfo = new md5vert[numverts];
- verts = new vert[numverts];
- }
- }
- else if(sscanf(buf, " numtris %d", &numtris)==1)
- {
- numtris = max(numtris, 0);
- if(numtris) tris = new tri[numtris];
- }
- else if(sscanf(buf, " numweights %d", &numweights)==1)
- {
- numweights = max(numweights, 0);
- if(numweights) weightinfo = new md5weight[numweights];
- }
- else if(sscanf(buf, " vert %d ( %f %f ) %hu %hu", &index, &v.tc.x, &v.tc.y, &v.start, &v.count)==5)
- {
- if(index>=0 && index<numverts) vertinfo[index] = v;
- }
- else if(sscanf(buf, " tri %d %hu %hu %hu", &index, &t.vert[0], &t.vert[1], &t.vert[2])==4)
- {
- if(index>=0 && index<numtris) tris[index] = t;
- }
- else if(sscanf(buf, " weight %d %d %f ( %f %f %f ) ", &index, &w.joint, &w.bias, &w.pos.x, &w.pos.y, &w.pos.z)==6)
- {
- w.pos.y = -w.pos.y;
- if(index>=0 && index<numweights) weightinfo[index] = w;
- }
- }
- }
- };
-
- struct md5meshgroup : skelmeshgroup
- {
- md5meshgroup()
- {
- }
-
- bool loadmesh(const char *filename, float smooth)
- {
- stream *f = openfile(filename, "r");
- if(!f) return false;
-
- char buf[512];
- vector<md5joint> basejoints;
- while(f->getline(buf, sizeof(buf)))
- {
- int tmp;
- if(sscanf(buf, " MD5Version %d", &tmp)==1)
- {
- if(tmp!=10) { delete f; return false; }
- }
- else if(sscanf(buf, " numJoints %d", &tmp)==1)
- {
- if(tmp<1) { delete f; return false; }
- if(skel->numbones>0) continue;
- skel->numbones = tmp;
- skel->bones = new boneinfo[skel->numbones];
- }
- else if(sscanf(buf, " numMeshes %d", &tmp)==1)
- {
- if(tmp<1) { delete f; return false; }
- }
- else if(strstr(buf, "joints {"))
- {
- string name;
- int parent;
- md5joint j;
- while(f->getline(buf, sizeof(buf)) && buf[0]!='}')
- {
- char *curbuf = buf, *curname = name;
- bool allowspace = false;
- while(*curbuf && isspace(*curbuf)) curbuf++;
- if(*curbuf == '"') { curbuf++; allowspace = true; }
- while(*curbuf && curname < &name[sizeof(name)-1])
- {
- char c = *curbuf++;
- if(c == '"') break;
- if(isspace(c) && !allowspace) break;
- *curname++ = c;
- }
- *curname = '\0';
- if(sscanf(curbuf, " %d ( %f %f %f ) ( %f %f %f )",
- &parent, &j.pos.x, &j.pos.y, &j.pos.z,
- &j.orient.x, &j.orient.y, &j.orient.z)==7)
- {
- j.pos.y = -j.pos.y;
- j.orient.x = -j.orient.x;
- j.orient.z = -j.orient.z;
- if(basejoints.length()<skel->numbones)
- {
- if(!skel->bones[basejoints.length()].name)
- skel->bones[basejoints.length()].name = newstring(name);
- skel->bones[basejoints.length()].parent = parent;
- }
- j.orient.restorew();
- basejoints.add(j);
- }
- }
- if(basejoints.length()!=skel->numbones) { delete f; return false; }
- }
- else if(strstr(buf, "mesh {"))
- {
- md5mesh *m = new md5mesh;
- m->group = this;
- meshes.add(m);
- m->load(f, buf, sizeof(buf));
- if(!m->numtris || !m->numverts)
- {
- conoutf(CON_WARN, "empty mesh in %s", filename);
- meshes.removeobj(m);
- delete m;
- }
- }
- }
-
- if(skel->shared <= 1)
- {
- skel->linkchildren();
- loopv(basejoints)
- {
- boneinfo &b = skel->bones[i];
- b.base = dualquat(basejoints[i].orient, basejoints[i].pos);
- (b.invbase = b.base).invert();
- }
- }
-
- loopv(meshes)
- {
- md5mesh &m = *(md5mesh *)meshes[i];
- m.buildverts(basejoints);
- if(smooth <= 1) m.smoothnorms(smooth);
- else m.buildnorms();
- m.cleanup();
- }
-
- sortblendcombos();
-
- delete f;
- return true;
- }
-
- skelanimspec *loadanim(const char *filename)
- {
- skelanimspec *sa = skel->findskelanim(filename);
- if(sa) return sa;
-
- stream *f = openfile(filename, "r");
- if(!f) return NULL;
-
- vector<md5hierarchy> hierarchy;
- vector<md5joint> basejoints;
- int animdatalen = 0, animframes = 0;
- float *animdata = NULL;
- dualquat *animbones = NULL;
- char buf[512];
- while(f->getline(buf, sizeof(buf)))
- {
- int tmp;
- if(sscanf(buf, " MD5Version %d", &tmp)==1)
- {
- if(tmp!=10) { delete f; return NULL; }
- }
- else if(sscanf(buf, " numJoints %d", &tmp)==1)
- {
- if(tmp!=skel->numbones) { delete f; return NULL; }
- }
- else if(sscanf(buf, " numFrames %d", &animframes)==1)
- {
- if(animframes<1) { delete f; return NULL; }
- }
- else if(sscanf(buf, " frameRate %d", &tmp)==1);
- else if(sscanf(buf, " numAnimatedComponents %d", &animdatalen)==1)
- {
- if(animdatalen>0) animdata = new float[animdatalen];
- }
- else if(strstr(buf, "bounds {"))
- {
- while(f->getline(buf, sizeof(buf)) && buf[0]!='}');
- }
- else if(strstr(buf, "hierarchy {"))
- {
- while(f->getline(buf, sizeof(buf)) && buf[0]!='}')
- {
- md5hierarchy h;
- if(sscanf(buf, " %100s %d %d %d", h.name, &h.parent, &h.flags, &h.start)==4)
- hierarchy.add(h);
- }
- }
- else if(strstr(buf, "baseframe {"))
- {
- while(f->getline(buf, sizeof(buf)) && buf[0]!='}')
- {
- md5joint j;
- if(sscanf(buf, " ( %f %f %f ) ( %f %f %f )", &j.pos.x, &j.pos.y, &j.pos.z, &j.orient.x, &j.orient.y, &j.orient.z)==6)
- {
- j.pos.y = -j.pos.y;
- j.orient.x = -j.orient.x;
- j.orient.z = -j.orient.z;
- j.orient.restorew();
- basejoints.add(j);
- }
- }
- if(basejoints.length()!=skel->numbones) { delete f; if(animdata) delete[] animdata; return NULL; }
- animbones = new dualquat[(skel->numframes+animframes)*skel->numbones];
- if(skel->framebones)
- {
- memcpy(animbones, skel->framebones, skel->numframes*skel->numbones*sizeof(dualquat));
- delete[] skel->framebones;
- }
- skel->framebones = animbones;
- animbones += skel->numframes*skel->numbones;
-
- sa = &skel->addskelanim(filename);
- sa->frame = skel->numframes;
- sa->range = animframes;
-
- skel->numframes += animframes;
- }
- else if(sscanf(buf, " frame %d", &tmp)==1)
- {
- for(int numdata = 0; f->getline(buf, sizeof(buf)) && buf[0]!='}';)
- {
- for(char *src = buf, *next = src; numdata < animdatalen; numdata++, src = next)
- {
- animdata[numdata] = strtod(src, &next);
- if(next <= src) break;
- }
- }
- dualquat *frame = &animbones[tmp*skel->numbones];
- loopv(basejoints)
- {
- md5hierarchy &h = hierarchy[i];
- md5joint j = basejoints[i];
- if(h.start < animdatalen && h.flags)
- {
- float *jdata = &animdata[h.start];
- if(h.flags&1) j.pos.x = *jdata++;
- if(h.flags&2) j.pos.y = -*jdata++;
- if(h.flags&4) j.pos.z = *jdata++;
- if(h.flags&8) j.orient.x = -*jdata++;
- if(h.flags&16) j.orient.y = *jdata++;
- if(h.flags&32) j.orient.z = -*jdata++;
- j.orient.restorew();
- }
- frame[i] = dualquat(j.orient, j.pos);
- if(adjustments.inrange(i)) adjustments[i].adjust(frame[i]);
- frame[i].mul(skel->bones[i].invbase);
- if(h.parent >= 0) frame[i].mul(skel->bones[h.parent].base, dualquat(frame[i]));
- frame[i].fixantipodal(skel->framebones[i]);
- }
- }
- }
-
- if(animdata) delete[] animdata;
- delete f;
-
- return sa;
- }
-
- bool load(const char *meshfile, float smooth)
- {
- name = newstring(meshfile);
-
- if(!loadmesh(meshfile, smooth)) return false;
-
- return true;
- }
- };
-
- meshgroup *loadmeshes(const char *name, va_list args)
- {
- md5meshgroup *group = new md5meshgroup;
- group->shareskeleton(va_arg(args, char *));
- if(!group->load(name, va_arg(args, double))) { delete group; return NULL; }
- return group;
- }
-
- bool loaddefaultparts()
- {
- skelpart &mdl = addpart();
- mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0;
- adjustments.setsize(0);
- const char *fname = name + strlen(name);
- do --fname; while(fname >= name && *fname!='/' && *fname!='\\');
- fname++;
- defformatstring(meshname, "packages/models/%s/%s.md5mesh", name, fname);
- mdl.meshes = sharemeshes(path(meshname), NULL, 2.0);
- if(!mdl.meshes) return false;
- mdl.initanimparts();
- mdl.initskins();
- defformatstring(animname, "packages/models/%s/%s.md5anim", name, fname);
- ((md5meshgroup *)mdl.meshes)->loadanim(path(animname));
- return true;
- }
+ md5(const char *name) : skelloader(name) {}
+
+ static const char *formatname() { return "md5"; }
+ int type() const { return MDL_MD5; }
+
+ struct md5mesh : skelmesh
+ {
+ md5weight *weightinfo;
+ int numweights;
+ md5vert *vertinfo;
+
+ md5mesh() : weightinfo(NULL), numweights(0), vertinfo(NULL)
+ {
+ }
+
+ ~md5mesh()
+ {
+ cleanup();
+ }
+
+ void cleanup()
+ {
+ DELETEA(weightinfo);
+ DELETEA(vertinfo);
+ }
+
+ void buildverts(vector<md5joint> &joints)
+ {
+ loopi(numverts)
+ {
+ md5vert &v = vertinfo[i];
+ vec pos(0, 0, 0);
+ loopk(v.count)
+ {
+ md5weight &w = weightinfo[v.start+k];
+ md5joint &j = joints[w.joint];
+ vec wpos = j.orient.rotate(w.pos);
+ wpos.add(j.pos);
+ wpos.mul(w.bias);
+ pos.add(wpos);
+ }
+ vert &vv = verts[i];
+ vv.pos = pos;
+ vv.tc = v.tc;
+
+ blendcombo c;
+ int sorted = 0;
+ loopj(v.count)
+ {
+ md5weight &w = weightinfo[v.start+j];
+ sorted = c.addweight(sorted, w.bias, w.joint);
+ }
+ c.finalize(sorted);
+ vv.blend = addblendcombo(c);
+ }
+ }
+
+ void load(stream *f, char *buf, size_t bufsize)
+ {
+ md5weight w;
+ md5vert v;
+ tri t;
+ int index;
+
+ while(f->getline(buf, bufsize) && buf[0]!='}')
+ {
+ if(strstr(buf, "// meshes:"))
+ {
+ char *start = strchr(buf, ':')+1;
+ if(*start==' ') start++;
+ char *end = start + strlen(start)-1;
+ while(end >= start && isspace(*end)) end--;
+ name = newstring(start, end+1-start);
+ }
+ else if(strstr(buf, "shader"))
+ {
+ char *start = strchr(buf, '"'), *end = start ? strchr(start+1, '"') : NULL;
+ if(start && end)
+ {
+ char *texname = newstring(start+1, end-(start+1));
+ part *p = loading->parts.last();
+ p->initskins(notexture, notexture, group->meshes.length());
+ skin &s = p->skins.last();
+ s.tex = textureload(makerelpath(dir, texname), 0, true, false);
+ delete[] texname;
+ }
+ }
+ else if(sscanf(buf, " numverts %d", &numverts)==1)
+ {
+ numverts = max(numverts, 0);
+ if(numverts)
+ {
+ vertinfo = new md5vert[numverts];
+ verts = new vert[numverts];
+ }
+ }
+ else if(sscanf(buf, " numtris %d", &numtris)==1)
+ {
+ numtris = max(numtris, 0);
+ if(numtris) tris = new tri[numtris];
+ }
+ else if(sscanf(buf, " numweights %d", &numweights)==1)
+ {
+ numweights = max(numweights, 0);
+ if(numweights) weightinfo = new md5weight[numweights];
+ }
+ else if(sscanf(buf, " vert %d ( %f %f ) %hu %hu", &index, &v.tc.x, &v.tc.y, &v.start, &v.count)==5)
+ {
+ if(index>=0 && index<numverts) vertinfo[index] = v;
+ }
+ else if(sscanf(buf, " tri %d %hu %hu %hu", &index, &t.vert[0], &t.vert[1], &t.vert[2])==4)
+ {
+ if(index>=0 && index<numtris) tris[index] = t;
+ }
+ else if(sscanf(buf, " weight %d %d %f ( %f %f %f ) ", &index, &w.joint, &w.bias, &w.pos.x, &w.pos.y, &w.pos.z)==6)
+ {
+ w.pos.y = -w.pos.y;
+ if(index>=0 && index<numweights) weightinfo[index] = w;
+ }
+ }
+ }
+ };
+
+ struct md5meshgroup : skelmeshgroup
+ {
+ md5meshgroup()
+ {
+ }
+
+ bool loadmesh(const char *filename, float smooth)
+ {
+ stream *f = openfile(filename, "r");
+ if(!f) return false;
+
+ char buf[512];
+ vector<md5joint> basejoints;
+ while(f->getline(buf, sizeof(buf)))
+ {
+ int tmp;
+ if(sscanf(buf, " MD5Version %d", &tmp)==1)
+ {
+ if(tmp!=10) { delete f; return false; }
+ }
+ else if(sscanf(buf, " numJoints %d", &tmp)==1)
+ {
+ if(tmp<1) { delete f; return false; }
+ if(skel->numbones>0) continue;
+ skel->numbones = tmp;
+ skel->bones = new boneinfo[skel->numbones];
+ }
+ else if(sscanf(buf, " numMeshes %d", &tmp)==1)
+ {
+ if(tmp<1) { delete f; return false; }
+ }
+ else if(strstr(buf, "joints {"))
+ {
+ string name;
+ int parent;
+ md5joint j;
+ while(f->getline(buf, sizeof(buf)) && buf[0]!='}')
+ {
+ char *curbuf = buf, *curname = name;
+ bool allowspace = false;
+ while(*curbuf && isspace(*curbuf)) curbuf++;
+ if(*curbuf == '"') { curbuf++; allowspace = true; }
+ while(*curbuf && curname < &name[sizeof(name)-1])
+ {
+ char c = *curbuf++;
+ if(c == '"') break;
+ if(isspace(c) && !allowspace) break;
+ *curname++ = c;
+ }
+ *curname = '\0';
+ if(sscanf(curbuf, " %d ( %f %f %f ) ( %f %f %f )",
+ &parent, &j.pos.x, &j.pos.y, &j.pos.z,
+ &j.orient.x, &j.orient.y, &j.orient.z)==7)
+ {
+ j.pos.y = -j.pos.y;
+ j.orient.x = -j.orient.x;
+ j.orient.z = -j.orient.z;
+ if(basejoints.length()<skel->numbones)
+ {
+ if(!skel->bones[basejoints.length()].name)
+ skel->bones[basejoints.length()].name = newstring(name);
+ skel->bones[basejoints.length()].parent = parent;
+ }
+ j.orient.restorew();
+ basejoints.add(j);
+ }
+ }
+ if(basejoints.length()!=skel->numbones) { delete f; return false; }
+ }
+ else if(strstr(buf, "mesh {"))
+ {
+ md5mesh *m = new md5mesh;
+ m->group = this;
+ meshes.add(m);
+ m->load(f, buf, sizeof(buf));
+ if(!m->numtris || !m->numverts)
+ {
+ conoutf(CON_WARN, "empty mesh in %s", filename);
+ meshes.removeobj(m);
+ delete m;
+ }
+ }
+ }
+
+ if(skel->shared <= 1)
+ {
+ skel->linkchildren();
+ loopv(basejoints)
+ {
+ boneinfo &b = skel->bones[i];
+ b.base = dualquat(basejoints[i].orient, basejoints[i].pos);
+ (b.invbase = b.base).invert();
+ }
+ }
+
+ loopv(meshes)
+ {
+ md5mesh &m = *(md5mesh *)meshes[i];
+ m.buildverts(basejoints);
+ if(smooth <= 1) m.smoothnorms(smooth);
+ else m.buildnorms();
+ m.cleanup();
+ }
+
+ sortblendcombos();
+
+ delete f;
+ return true;
+ }
+
+ skelanimspec *loadanim(const char *filename)
+ {
+ skelanimspec *sa = skel->findskelanim(filename);
+ if(sa) return sa;
+
+ stream *f = openfile(filename, "r");
+ if(!f) return NULL;
+
+ vector<md5hierarchy> hierarchy;
+ vector<md5joint> basejoints;
+ int animdatalen = 0, animframes = 0;
+ float *animdata = NULL;
+ dualquat *animbones = NULL;
+ char buf[512];
+ while(f->getline(buf, sizeof(buf)))
+ {
+ int tmp;
+ if(sscanf(buf, " MD5Version %d", &tmp)==1)
+ {
+ if(tmp!=10) { delete f; return NULL; }
+ }
+ else if(sscanf(buf, " numJoints %d", &tmp)==1)
+ {
+ if(tmp!=skel->numbones) { delete f; return NULL; }
+ }
+ else if(sscanf(buf, " numFrames %d", &animframes)==1)
+ {
+ if(animframes<1) { delete f; return NULL; }
+ }
+ else if(sscanf(buf, " frameRate %d", &tmp)==1);
+ else if(sscanf(buf, " numAnimatedComponents %d", &animdatalen)==1)
+ {
+ if(animdatalen>0) animdata = new float[animdatalen];
+ }
+ else if(strstr(buf, "bounds {"))
+ {
+ while(f->getline(buf, sizeof(buf)) && buf[0]!='}');
+ }
+ else if(strstr(buf, "hierarchy {"))
+ {
+ while(f->getline(buf, sizeof(buf)) && buf[0]!='}')
+ {
+ md5hierarchy h;
+ if(sscanf(buf, " %100s %d %d %d", h.name, &h.parent, &h.flags, &h.start)==4)
+ hierarchy.add(h);
+ }
+ }
+ else if(strstr(buf, "baseframe {"))
+ {
+ while(f->getline(buf, sizeof(buf)) && buf[0]!='}')
+ {
+ md5joint j;
+ if(sscanf(buf, " ( %f %f %f ) ( %f %f %f )", &j.pos.x, &j.pos.y, &j.pos.z, &j.orient.x, &j.orient.y, &j.orient.z)==6)
+ {
+ j.pos.y = -j.pos.y;
+ j.orient.x = -j.orient.x;
+ j.orient.z = -j.orient.z;
+ j.orient.restorew();
+ basejoints.add(j);
+ }
+ }
+ if(basejoints.length()!=skel->numbones) { delete f; if(animdata) delete[] animdata; return NULL; }
+ animbones = new dualquat[(skel->numframes+animframes)*skel->numbones];
+ if(skel->framebones)
+ {
+ memcpy(animbones, skel->framebones, skel->numframes*skel->numbones*sizeof(dualquat));
+ delete[] skel->framebones;
+ }
+ skel->framebones = animbones;
+ animbones += skel->numframes*skel->numbones;
+
+ sa = &skel->addskelanim(filename);
+ sa->frame = skel->numframes;
+ sa->range = animframes;
+
+ skel->numframes += animframes;
+ }
+ else if(sscanf(buf, " frame %d", &tmp)==1)
+ {
+ for(int numdata = 0; f->getline(buf, sizeof(buf)) && buf[0]!='}';)
+ {
+ for(char *src = buf, *next = src; numdata < animdatalen; numdata++, src = next)
+ {
+ animdata[numdata] = strtod(src, &next);
+ if(next <= src) break;
+ }
+ }
+ dualquat *frame = &animbones[tmp*skel->numbones];
+ loopv(basejoints)
+ {
+ md5hierarchy &h = hierarchy[i];
+ md5joint j = basejoints[i];
+ if(h.start < animdatalen && h.flags)
+ {
+ float *jdata = &animdata[h.start];
+ if(h.flags&1) j.pos.x = *jdata++;
+ if(h.flags&2) j.pos.y = -*jdata++;
+ if(h.flags&4) j.pos.z = *jdata++;
+ if(h.flags&8) j.orient.x = -*jdata++;
+ if(h.flags&16) j.orient.y = *jdata++;
+ if(h.flags&32) j.orient.z = -*jdata++;
+ j.orient.restorew();
+ }
+ frame[i] = dualquat(j.orient, j.pos);
+ if(adjustments.inrange(i)) adjustments[i].adjust(frame[i]);
+ frame[i].mul(skel->bones[i].invbase);
+ if(h.parent >= 0) frame[i].mul(skel->bones[h.parent].base, dualquat(frame[i]));
+ frame[i].fixantipodal(skel->framebones[i]);
+ }
+ }
+ }
+
+ if(animdata) delete[] animdata;
+ delete f;
+
+ return sa;
+ }
+
+ bool load(const char *meshfile, float smooth)
+ {
+ name = newstring(meshfile);
+
+ if(!loadmesh(meshfile, smooth)) return false;
+
+ return true;
+ }
+ };
+
+ meshgroup *loadmeshes(const char *name, va_list args)
+ {
+ md5meshgroup *group = new md5meshgroup;
+ group->shareskeleton(va_arg(args, char *));
+ if(!group->load(name, va_arg(args, double))) { delete group; return NULL; }
+ return group;
+ }
+
+ bool loaddefaultparts()
+ {
+ skelpart &mdl = addpart();
+ mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0;
+ adjustments.setsize(0);
+ const char *fname = name + strlen(name);
+ do --fname; while(fname >= name && *fname!='/' && *fname!='\\');
+ fname++;
+ defformatstring(meshname, "packages/models/%s/%s.md5mesh", name, fname);
+ mdl.meshes = sharemeshes(path(meshname), NULL, 2.0);
+ if(!mdl.meshes) return false;
+ mdl.initanimparts();
+ mdl.initskins();
+ defformatstring(animname, "packages/models/%s/%s.md5anim", name, fname);
+ ((md5meshgroup *)mdl.meshes)->loadanim(path(animname));
+ return true;
+ }
};
skelcommands<md5> md5commands;
diff --git a/src/engine/menus.cpp b/src/engine/menus.cpp
index f2d8e3b..805a79b 100644
--- a/src/engine/menus.cpp
+++ b/src/engine/menus.cpp
@@ -14,101 +14,101 @@ VAR(guitabnum, 1, 0, 0);
struct menu : g3d_callback
{
- char *name, *header;
- uint *contents, *init, *onclear;
- bool showtab, keeptab;
- int menutab;
-
- menu() : name(NULL), header(NULL), contents(NULL), init(NULL), onclear(NULL), showtab(true), keeptab(false), menutab(1) {}
-
- void gui(g3d_gui &g, bool firstpass)
- {
- cgui = &g;
- guitabnum = menutab;
- cgui->start(menustart, 0.03f, showtab ? &menutab : NULL);
- if(showtab) cgui->tab(header ? header : name, GUI_TITLE_COLOR);
- execute(contents);
- cgui->end();
- cgui = NULL;
- guitabnum = 0;
- }
-
- virtual void clear()
- {
- if(onclear) { freecode(onclear); onclear = NULL; }
- }
+ char *name, *header;
+ uint *contents, *init, *onclear;
+ bool showtab, keeptab;
+ int menutab;
+
+ menu() : name(NULL), header(NULL), contents(NULL), init(NULL), onclear(NULL), showtab(true), keeptab(false), menutab(1) {}
+
+ void gui(g3d_gui &g, bool firstpass)
+ {
+ cgui = &g;
+ guitabnum = menutab;
+ cgui->start(menustart, 0.03f, showtab ? &menutab : NULL);
+ if(showtab) cgui->tab(header ? header : name, GUI_TITLE_COLOR);
+ execute(contents);
+ cgui->end();
+ cgui = NULL;
+ guitabnum = 0;
+ }
+
+ virtual void clear()
+ {
+ if(onclear) { freecode(onclear); onclear = NULL; }
+ }
};
struct delayedupdate
{
- enum
- {
- INT,
- FLOAT,
- STRING,
- ACTION
- } type;
- ident *id;
- union
- {
- int i;
- float f;
- char *s;
- } val;
- delayedupdate() : type(ACTION), id(NULL) { val.s = NULL; }
- ~delayedupdate() { if(type == STRING || type == ACTION) DELETEA(val.s); }
-
- void schedule(const char *s) { type = ACTION; val.s = newstring(s); }
- void schedule(ident *var, int i) { type = INT; id = var; val.i = i; }
- void schedule(ident *var, float f) { type = FLOAT; id = var; val.f = f; }
- void schedule(ident *var, char *s) { type = STRING; id = var; val.s = newstring(s); }
-
- int getint() const
- {
- switch(type)
- {
- case INT: return val.i;
- case FLOAT: return int(val.f);
- case STRING: return int(strtol(val.s, NULL, 0));
- default: return 0;
- }
- }
-
- float getfloat() const
- {
- switch(type)
- {
- case INT: return float(val.i);
- case FLOAT: return val.f;
- case STRING: return float(parsefloat(val.s));
- default: return 0;
- }
- }
-
- const char *getstring() const
- {
- switch(type)
- {
- case INT: return intstr(val.i);
- case FLOAT: return intstr(int(floor(val.f)));
- case STRING: return val.s;
- default: return "";
- }
- }
-
- void run()
- {
- if(type == ACTION) { if(val.s) execute(val.s); }
- else if(id) switch(id->type)
- {
- case ID_VAR: setvarchecked(id, getint()); break;
- case ID_FVAR: setfvarchecked(id, getfloat()); break;
- case ID_SVAR: setsvarchecked(id, getstring()); break;
- case ID_ALIAS: alias(id->name, getstring()); break;
- }
- }
+ enum
+ {
+ INT,
+ FLOAT,
+ STRING,
+ ACTION
+ } type;
+ ident *id;
+ union
+ {
+ int i;
+ float f;
+ char *s;
+ } val;
+ delayedupdate() : type(ACTION), id(NULL) { val.s = NULL; }
+ ~delayedupdate() { if(type == STRING || type == ACTION) DELETEA(val.s); }
+
+ void schedule(const char *s) { type = ACTION; val.s = newstring(s); }
+ void schedule(ident *var, int i) { type = INT; id = var; val.i = i; }
+ void schedule(ident *var, float f) { type = FLOAT; id = var; val.f = f; }
+ void schedule(ident *var, char *s) { type = STRING; id = var; val.s = newstring(s); }
+
+ int getint() const
+ {
+ switch(type)
+ {
+ case INT: return val.i;
+ case FLOAT: return int(val.f);
+ case STRING: return int(strtol(val.s, NULL, 0));
+ default: return 0;
+ }
+ }
+
+ float getfloat() const
+ {
+ switch(type)
+ {
+ case INT: return float(val.i);
+ case FLOAT: return val.f;
+ case STRING: return float(parsefloat(val.s));
+ default: return 0;
+ }
+ }
+
+ const char *getstring() const
+ {
+ switch(type)
+ {
+ case INT: return intstr(val.i);
+ case FLOAT: return intstr(int(floor(val.f)));
+ case STRING: return val.s;
+ default: return "";
+ }
+ }
+
+ void run()
+ {
+ if(type == ACTION) { if(val.s) execute(val.s); }
+ else if(id) switch(id->type)
+ {
+ case ID_VAR: setvarchecked(id, getint()); break;
+ case ID_FVAR: setfvarchecked(id, getfloat()); break;
+ case ID_SVAR: setsvarchecked(id, getstring()); break;
+ case ID_ALIAS: alias(id->name, getstring()); break;
+ }
+ }
};
-
+
static hashnameset<menu> guis;
static vector<menu *> guistack;
static vector<delayedupdate> updatelater;
@@ -118,375 +118,375 @@ VARP(menudistance, 16, 40, 256);
VARP(menuautoclose, 32, 120, 4096);
vec menuinfrontofplayer()
-{
- vec dir;
- vecfromyawpitch(camera1->yaw, 0, 1, 0, dir);
- dir.mul(menudistance).add(camera1->o);
- dir.z -= player->eyeheight-1;
- return dir;
+{
+ vec dir;
+ vecfromyawpitch(camera1->yaw, 0, 1, 0, dir);
+ dir.mul(menudistance).add(camera1->o);
+ dir.z -= player->eyeheight-1;
+ return dir;
}
void popgui()
{
- menu *m = guistack.pop();
- m->clear();
+ menu *m = guistack.pop();
+ m->clear();
}
void removegui(menu *m)
{
- loopv(guistack) if(guistack[i]==m)
- {
- guistack.remove(i);
- m->clear();
- return;
- }
-}
+ loopv(guistack) if(guistack[i]==m)
+ {
+ guistack.remove(i);
+ m->clear();
+ return;
+ }
+}
void pushgui(menu *m, int pos = -1)
{
- if(guistack.empty())
- {
- menupos = menuinfrontofplayer();
- g3d_resetcursor();
- }
- if(pos < 0) guistack.add(m);
- else guistack.insert(pos, m);
- if(pos < 0 || pos==guistack.length()-1)
- {
- if(!m->keeptab) m->menutab = 1;
- menustart = totalmillis;
- }
- if(m->init) execute(m->init);
+ if(guistack.empty())
+ {
+ menupos = menuinfrontofplayer();
+ g3d_resetcursor();
+ }
+ if(pos < 0) guistack.add(m);
+ else guistack.insert(pos, m);
+ if(pos < 0 || pos==guistack.length()-1)
+ {
+ if(!m->keeptab) m->menutab = 1;
+ menustart = totalmillis;
+ }
+ if(m->init) execute(m->init);
}
void restoregui(int pos)
{
- int clear = guistack.length()-pos-1;
- loopi(clear) popgui();
- menustart = totalmillis;
+ int clear = guistack.length()-pos-1;
+ loopi(clear) popgui();
+ menustart = totalmillis;
}
void showgui(const char *name)
{
- menu *m = guis.access(name);
- if(!m) return;
- int pos = guistack.find(m);
- if(pos<0) pushgui(m);
- else restoregui(pos);
+ menu *m = guis.access(name);
+ if(!m) return;
+ int pos = guistack.find(m);
+ if(pos<0) pushgui(m);
+ else restoregui(pos);
}
void hidegui(const char *name)
{
- menu *m = guis.access(name);
- if(m) removegui(m);
+ menu *m = guis.access(name);
+ if(m) removegui(m);
}
-
+
int cleargui(int n)
{
- int clear = guistack.length();
- if(mainmenu && !isconnected(true) && clear > 0 && guistack[0]->name && !strcmp(guistack[0]->name, "main"))
- {
- clear--;
- if(!clear) return 1;
- }
- if(n>0) clear = min(clear, n);
- loopi(clear) popgui();
- if(!guistack.empty()) restoregui(guistack.length()-1);
- return clear;
+ int clear = guistack.length();
+ if(mainmenu && !isconnected(true) && clear > 0 && guistack[0]->name && !strcmp(guistack[0]->name, "main"))
+ {
+ clear--;
+ if(!clear) return 1;
+ }
+ if(n>0) clear = min(clear, n);
+ loopi(clear) popgui();
+ if(!guistack.empty()) restoregui(guistack.length()-1);
+ return clear;
}
void clearguis(int level = -1)
{
- if(level < 0) level = guistack.length();
- loopvrev(guistack)
- {
- menu *m = guistack[i];
- if(m->onclear)
- {
- uint *action = m->onclear;
- m->onclear = NULL;
- execute(action);
- freecode(action);
- }
- }
- cleargui(level);
+ if(level < 0) level = guistack.length();
+ loopvrev(guistack)
+ {
+ menu *m = guistack[i];
+ if(m->onclear)
+ {
+ uint *action = m->onclear;
+ m->onclear = NULL;
+ execute(action);
+ freecode(action);
+ }
+ }
+ cleargui(level);
}
void guionclear(char *action)
{
- if(guistack.empty()) return;
- menu *m = guistack.last();
- if(m->onclear) { freecode(m->onclear); m->onclear = NULL; }
- if(action[0]) m->onclear = compilecode(action);
+ if(guistack.empty()) return;
+ menu *m = guistack.last();
+ if(m->onclear) { freecode(m->onclear); m->onclear = NULL; }
+ if(action[0]) m->onclear = compilecode(action);
}
void guistayopen(uint *contents)
{
- bool oldclearmenu = shouldclearmenu;
- shouldclearmenu = false;
- execute(contents);
- shouldclearmenu = oldclearmenu;
+ bool oldclearmenu = shouldclearmenu;
+ shouldclearmenu = false;
+ execute(contents);
+ shouldclearmenu = oldclearmenu;
}
void guinoautotab(uint *contents)
{
- if(!cgui) return;
- bool oldval = cgui->allowautotab(false);
- execute(contents);
- cgui->allowautotab(oldval);
+ if(!cgui) return;
+ bool oldval = cgui->allowautotab(false);
+ execute(contents);
+ cgui->allowautotab(oldval);
}
void guimerge(uint *contents)
{
- if(!cgui) return;
- bool oldval = cgui->mergehits(true);
- execute(contents);
- cgui->mergehits(oldval);
+ if(!cgui) return;
+ bool oldval = cgui->mergehits(true);
+ execute(contents);
+ cgui->mergehits(oldval);
}
//@DOC name and icon are optional
void guibutton(char *name, char *action, char *icon)
{
- if(!cgui) return;
- bool hideicon = !strcmp(icon, "0");
- int ret = cgui->button(name, GUI_BUTTON_COLOR, hideicon ? NULL : (icon[0] ? icon : (strstr(action, "showgui") ? "menu" : "action")));
- if(ret&G3D_UP)
- {
- updatelater.add().schedule(action[0] ? action : name);
- if(shouldclearmenu) clearlater = true;
- }
- else if(ret&G3D_ROLLOVER)
- {
- alias("guirollovername", name);
- alias("guirolloveraction", action);
- }
+ if(!cgui) return;
+ bool hideicon = !strcmp(icon, "0");
+ int ret = cgui->button(name, GUI_BUTTON_COLOR, hideicon ? NULL : (icon[0] ? icon : (strstr(action, "showgui") ? "menu" : "action")));
+ if(ret&G3D_UP)
+ {
+ updatelater.add().schedule(action[0] ? action : name);
+ if(shouldclearmenu) clearlater = true;
+ }
+ else if(ret&G3D_ROLLOVER)
+ {
+ alias("guirollovername", name);
+ alias("guirolloveraction", action);
+ }
}
void guiimage(char *path, char *action, float *scale, int *overlaid, char *alt, char *title)
{
- if(!cgui) return;
- Texture *t = textureload(path, 0, true, false);
- if(t==notexture)
- {
- if(alt[0]) t = textureload(alt, 0, true, false);
- if(t==notexture) return;
- }
- int ret = cgui->image(t, *scale, *overlaid!=0 ? title : NULL);
- if(ret&G3D_UP)
- {
- if(*action)
- {
- updatelater.add().schedule(action);
- if(shouldclearmenu) clearlater = true;
- }
- }
- else if(ret&G3D_ROLLOVER)
- {
- alias("guirolloverimgpath", path);
- alias("guirolloverimgaction", action);
- }
+ if(!cgui) return;
+ Texture *t = textureload(path, 0, true, false);
+ if(t==notexture)
+ {
+ if(alt[0]) t = textureload(alt, 0, true, false);
+ if(t==notexture) return;
+ }
+ int ret = cgui->image(t, *scale, *overlaid!=0 ? title : NULL);
+ if(ret&G3D_UP)
+ {
+ if(*action)
+ {
+ updatelater.add().schedule(action);
+ if(shouldclearmenu) clearlater = true;
+ }
+ }
+ else if(ret&G3D_ROLLOVER)
+ {
+ alias("guirolloverimgpath", path);
+ alias("guirolloverimgaction", action);
+ }
}
void guicolor(int *color)
{
- if(cgui)
- {
- defformatstring(desc, "0x%06X", *color);
- cgui->text(desc, *color, NULL);
- }
+ if(cgui)
+ {
+ defformatstring(desc, "0x%06X", *color);
+ cgui->text(desc, *color, NULL);
+ }
}
void guitextbox(char *text, int *width, int *height, int *color)
{
- if(cgui && text[0]) cgui->textbox(text, *width ? *width : 12, *height ? *height : 1, *color ? *color : 0xFFFFFF);
+ if(cgui && text[0]) cgui->textbox(text, *width ? *width : 12, *height ? *height : 1, *color ? *color : 0xFFFFFF);
}
void guitext(char *name, char *icon)
{
- bool hideicon = !strcmp(icon, "0");
- if(cgui) cgui->text(name, !hideicon && icon[0] ? GUI_BUTTON_COLOR : GUI_TEXT_COLOR, hideicon ? NULL : (icon[0] ? icon : "info"));
+ bool hideicon = !strcmp(icon, "0");
+ if(cgui) cgui->text(name, !hideicon && icon[0] ? GUI_BUTTON_COLOR : GUI_TEXT_COLOR, hideicon ? NULL : (icon[0] ? icon : "info"));
}
void guititle(char *name)
{
- if(cgui) cgui->title(name, GUI_TITLE_COLOR);
+ if(cgui) cgui->title(name, GUI_TITLE_COLOR);
}
void guitab(char *name)
{
- if(cgui) cgui->tab(name, GUI_TITLE_COLOR);
+ if(cgui) cgui->tab(name, GUI_TITLE_COLOR);
}
void guibar()
{
- if(cgui) cgui->separator();
+ if(cgui) cgui->separator();
}
void guistrut(float *strut, int *alt)
{
- if(cgui)
- {
- if(*alt) cgui->strut(*strut); else cgui->space(*strut);
- }
+ if(cgui)
+ {
+ if(*alt) cgui->strut(*strut); else cgui->space(*strut);
+ }
}
void guispring(int *weight)
{
- if(cgui) cgui->spring(max(*weight, 1));
+ if(cgui) cgui->spring(max(*weight, 1));
}
void guicolumn(int *col)
{
- if(cgui) cgui->column(*col);
+ if(cgui) cgui->column(*col);
}
template<class T> static void updateval(char *var, T val, char *onchange)
{
- ident *id = writeident(var);
- updatelater.add().schedule(id, val);
- if(onchange[0]) updatelater.add().schedule(onchange);
+ ident *id = writeident(var);
+ updatelater.add().schedule(id, val);
+ if(onchange[0]) updatelater.add().schedule(onchange);
}
static int getval(char *var)
{
- ident *id = readident(var);
- if(!id) return 0;
- switch(id->type)
- {
- case ID_VAR: return *id->storage.i;
- case ID_FVAR: return int(*id->storage.f);
- case ID_SVAR: return parseint(*id->storage.s);
- case ID_ALIAS: return id->getint();
- default: return 0;
- }
+ ident *id = readident(var);
+ if(!id) return 0;
+ switch(id->type)
+ {
+ case ID_VAR: return *id->storage.i;
+ case ID_FVAR: return int(*id->storage.f);
+ case ID_SVAR: return parseint(*id->storage.s);
+ case ID_ALIAS: return id->getint();
+ default: return 0;
+ }
}
static float getfval(char *var)
{
- ident *id = readident(var);
- if(!id) return 0;
- switch(id->type)
- {
- case ID_VAR: return *id->storage.i;
- case ID_FVAR: return *id->storage.f;
- case ID_SVAR: return parsefloat(*id->storage.s);
- case ID_ALIAS: return id->getfloat();
- default: return 0;
- }
+ ident *id = readident(var);
+ if(!id) return 0;
+ switch(id->type)
+ {
+ case ID_VAR: return *id->storage.i;
+ case ID_FVAR: return *id->storage.f;
+ case ID_SVAR: return parsefloat(*id->storage.s);
+ case ID_ALIAS: return id->getfloat();
+ default: return 0;
+ }
}
static const char *getsval(char *var)
{
- ident *id = readident(var);
- if(!id) return "";
- switch(id->type)
- {
- case ID_VAR: return intstr(*id->storage.i);
- case ID_FVAR: return floatstr(*id->storage.f);
- case ID_SVAR: return *id->storage.s;
- case ID_ALIAS: return id->getstr();
- default: return "";
- }
+ ident *id = readident(var);
+ if(!id) return "";
+ switch(id->type)
+ {
+ case ID_VAR: return intstr(*id->storage.i);
+ case ID_FVAR: return floatstr(*id->storage.f);
+ case ID_SVAR: return *id->storage.s;
+ case ID_ALIAS: return id->getstr();
+ default: return "";
+ }
}
void guislider(char *var, int *min, int *max, char *onchange)
{
if(!cgui) return;
- int oldval = getval(var), val = oldval, vmin = *max > INT_MIN ? *min : getvarmin(var), vmax = *max > INT_MIN ? *max : getvarmax(var);
- cgui->slider(val, vmin, vmax, GUI_TITLE_COLOR);
- if(val != oldval) updateval(var, val, onchange);
+ int oldval = getval(var), val = oldval, vmin = *max > INT_MIN ? *min : getvarmin(var), vmax = *max > INT_MIN ? *max : getvarmax(var);
+ cgui->slider(val, vmin, vmax, GUI_TITLE_COLOR);
+ if(val != oldval) updateval(var, val, onchange);
}
void guilistslider(char *var, char *list, char *onchange)
{
- if(!cgui) return;
- vector<int> vals;
- list += strspn(list, "\n\t ");
- while(*list)
- {
- vals.add(parseint(list));
- list += strcspn(list, "\n\t \0");
- list += strspn(list, "\n\t ");
- }
- if(vals.empty()) return;
- int val = getval(var), oldoffset = vals.length()-1, offset = oldoffset;
- loopv(vals) if(val <= vals[i]) { oldoffset = offset = i; break; }
- cgui->slider(offset, 0, vals.length()-1, GUI_TITLE_COLOR, intstr(val));
- if(offset != oldoffset) updateval(var, vals[offset], onchange);
+ if(!cgui) return;
+ vector<int> vals;
+ list += strspn(list, "\n\t ");
+ while(*list)
+ {
+ vals.add(parseint(list));
+ list += strcspn(list, "\n\t \0");
+ list += strspn(list, "\n\t ");
+ }
+ if(vals.empty()) return;
+ int val = getval(var), oldoffset = vals.length()-1, offset = oldoffset;
+ loopv(vals) if(val <= vals[i]) { oldoffset = offset = i; break; }
+ cgui->slider(offset, 0, vals.length()-1, GUI_TITLE_COLOR, intstr(val));
+ if(offset != oldoffset) updateval(var, vals[offset], onchange);
}
void guinameslider(char *var, char *names, char *list, char *onchange)
{
- if(!cgui) return;
- vector<int> vals;
- list += strspn(list, "\n\t ");
- while(*list)
- {
- vals.add(parseint(list));
- list += strcspn(list, "\n\t \0");
- list += strspn(list, "\n\t ");
- }
- if(vals.empty()) return;
- int val = getval(var), oldoffset = vals.length()-1, offset = oldoffset;
- loopv(vals) if(val <= vals[i]) { oldoffset = offset = i; break; }
- char *label = indexlist(names, offset);
- cgui->slider(offset, 0, vals.length()-1, GUI_TITLE_COLOR, label);
- if(offset != oldoffset) updateval(var, vals[offset], onchange);
- delete[] label;
+ if(!cgui) return;
+ vector<int> vals;
+ list += strspn(list, "\n\t ");
+ while(*list)
+ {
+ vals.add(parseint(list));
+ list += strcspn(list, "\n\t \0");
+ list += strspn(list, "\n\t ");
+ }
+ if(vals.empty()) return;
+ int val = getval(var), oldoffset = vals.length()-1, offset = oldoffset;
+ loopv(vals) if(val <= vals[i]) { oldoffset = offset = i; break; }
+ char *label = indexlist(names, offset);
+ cgui->slider(offset, 0, vals.length()-1, GUI_TITLE_COLOR, label);
+ if(offset != oldoffset) updateval(var, vals[offset], onchange);
+ delete[] label;
}
void guicheckbox(char *name, char *var, float *on, float *off, char *onchange)
{
- bool enabled = getfval(var)!=*off;
- if(cgui && cgui->button(name, GUI_BUTTON_COLOR, enabled ? "checkbox_on" : "checkbox_off")&G3D_UP)
- {
- updateval(var, enabled ? *off : (*on || *off ? *on : 1.0f), onchange);
- }
+ bool enabled = getfval(var)!=*off;
+ if(cgui && cgui->button(name, GUI_BUTTON_COLOR, enabled ? "checkbox_on" : "checkbox_off")&G3D_UP)
+ {
+ updateval(var, enabled ? *off : (*on || *off ? *on : 1.0f), onchange);
+ }
}
void guiradio(char *name, char *var, float *n, char *onchange)
{
- bool enabled = getfval(var)==*n;
- if(cgui && cgui->button(name, GUI_BUTTON_COLOR, enabled ? "radio_on" : "radio_off")&G3D_UP)
- {
- if(!enabled) updateval(var, *n, onchange);
- }
+ bool enabled = getfval(var)==*n;
+ if(cgui && cgui->button(name, GUI_BUTTON_COLOR, enabled ? "radio_on" : "radio_off")&G3D_UP)
+ {
+ if(!enabled) updateval(var, *n, onchange);
+ }
}
void guibitfield(char *name, char *var, int *mask, char *onchange)
{
- int val = getval(var);
- bool enabled = (val & *mask) != 0;
- if(cgui && cgui->button(name, GUI_BUTTON_COLOR, enabled ? "checkbox_on" : "checkbox_off")&G3D_UP)
- {
- updateval(var, enabled ? val & ~*mask : val | *mask, onchange);
- }
+ int val = getval(var);
+ bool enabled = (val & *mask) != 0;
+ if(cgui && cgui->button(name, GUI_BUTTON_COLOR, enabled ? "checkbox_on" : "checkbox_off")&G3D_UP)
+ {
+ updateval(var, enabled ? val & ~*mask : val | *mask, onchange);
+ }
}
//-ve length indicates a wrapped text field of any (approx 260 chars) length, |length| is the field width
void guifield(char *var, int *maxlength, char *onchange)
-{
- if(!cgui) return;
- const char *initval = getsval(var);
+{
+ if(!cgui) return;
+ const char *initval = getsval(var);
char *result = cgui->field(var, GUI_BUTTON_COLOR, *maxlength ? *maxlength : 12, 0, initval);
- if(result) updateval(var, result, onchange);
+ if(result) updateval(var, result, onchange);
}
//-ve maxlength indicates a wrapped text field of any (approx 260 chars) length, |maxlength| is the field width
void guieditor(char *name, int *maxlength, int *height, int *mode)
{
- if(!cgui) return;
- cgui->field(name, GUI_BUTTON_COLOR, *maxlength ? *maxlength : 12, *height, NULL, *mode<=0 ? EDITORFOREVER : *mode);
- //returns a non-NULL pointer (the currentline) when the user commits, could then manipulate via text* commands
+ if(!cgui) return;
+ cgui->field(name, GUI_BUTTON_COLOR, *maxlength ? *maxlength : 12, *height, NULL, *mode<=0 ? EDITORFOREVER : *mode);
+ //returns a non-NULL pointer (the currentline) when the user commits, could then manipulate via text* commands
}
//-ve length indicates a wrapped text field of any (approx 260 chars) length, |length| is the field width
void guikeyfield(char *var, int *maxlength, char *onchange)
{
- if(!cgui) return;
- const char *initval = getsval(var);
- char *result = cgui->keyfield(var, GUI_BUTTON_COLOR, *maxlength ? *maxlength : -8, 0, initval);
- if(result) updateval(var, result, onchange);
+ if(!cgui) return;
+ const char *initval = getsval(var);
+ char *result = cgui->keyfield(var, GUI_BUTTON_COLOR, *maxlength ? *maxlength : -8, 0, initval);
+ if(result) updateval(var, result, onchange);
}
//use text<action> to do more...
@@ -494,87 +494,87 @@ void guikeyfield(char *var, int *maxlength, char *onchange)
void guilist(uint *contents)
{
- if(!cgui) return;
- cgui->pushlist();
- execute(contents);
- cgui->poplist();
+ if(!cgui) return;
+ cgui->pushlist();
+ execute(contents);
+ cgui->poplist();
}
void guialign(int *align, uint *contents)
{
- if(!cgui) return;
- cgui->pushlist();
- if(*align >= 0) cgui->spring();
- execute(contents);
- if(*align == 0) cgui->spring();
- cgui->poplist();
+ if(!cgui) return;
+ cgui->pushlist();
+ if(*align >= 0) cgui->spring();
+ execute(contents);
+ if(*align == 0) cgui->spring();
+ cgui->poplist();
}
void newgui(char *name, char *contents, char *header, char *init)
{
- menu *m = guis.access(name);
- if(!m)
- {
- name = newstring(name);
- m = &guis[name];
- m->name = name;
- }
- else
- {
- DELETEA(m->header);
- freecode(m->contents);
- freecode(m->init);
- }
- if(header && header[0])
- {
- char *end = NULL;
- int val = strtol(header, &end, 0);
- if(end && !*end)
- {
- m->header = NULL;
- m->showtab = val != 0;
- }
- else
- {
- m->header = newstring(header);
- m->showtab = true;
- }
- }
- else
- {
- m->header = NULL;
- m->showtab = true;
- }
- m->contents = compilecode(contents);
- m->init = init && init[0] ? compilecode(init) : NULL;
+ menu *m = guis.access(name);
+ if(!m)
+ {
+ name = newstring(name);
+ m = &guis[name];
+ m->name = name;
+ }
+ else
+ {
+ DELETEA(m->header);
+ freecode(m->contents);
+ freecode(m->init);
+ }
+ if(header && header[0])
+ {
+ char *end = NULL;
+ int val = strtol(header, &end, 0);
+ if(end && !*end)
+ {
+ m->header = NULL;
+ m->showtab = val != 0;
+ }
+ else
+ {
+ m->header = newstring(header);
+ m->showtab = true;
+ }
+ }
+ else
+ {
+ m->header = NULL;
+ m->showtab = true;
+ }
+ m->contents = compilecode(contents);
+ m->init = init && init[0] ? compilecode(init) : NULL;
}
menu *guiserversmenu = NULL;
void guiservers(uint *header, int *pagemin, int *pagemax)
{
- extern const char *showservers(g3d_gui *cgui, uint *header, int pagemin, int pagemax);
- if(cgui)
- {
- const char *command = showservers(cgui, header, *pagemin, *pagemax > 0 ? *pagemax : INT_MAX);
- if(command)
- {
- updatelater.add().schedule(command);
- if(shouldclearmenu) clearlater = true;
- guiserversmenu = clearlater || guistack.empty() ? NULL : guistack.last();
- }
- }
+ extern const char *showservers(g3d_gui *cgui, uint *header, int pagemin, int pagemax);
+ if(cgui)
+ {
+ const char *command = showservers(cgui, header, *pagemin, *pagemax > 0 ? *pagemax : INT_MAX);
+ if(command)
+ {
+ updatelater.add().schedule(command);
+ if(shouldclearmenu) clearlater = true;
+ guiserversmenu = clearlater || guistack.empty() ? NULL : guistack.last();
+ }
+ }
}
void notifywelcome()
{
- if(guiserversmenu)
- {
- if(guistack.length() && guistack.last() == guiserversmenu) clearguis();
- guiserversmenu = NULL;
- }
+ if(guiserversmenu)
+ {
+ if(guistack.length() && guistack.last() == guiserversmenu) clearguis();
+ guiserversmenu = NULL;
+ }
}
-
+
COMMAND(newgui, "ssss");
COMMAND(guibutton, "sss");
COMMAND(guitext, "ss");
@@ -610,113 +610,113 @@ COMMAND(guitextbox, "siii");
void guiplayerpreview(int *model, int *team, int *weap, char *action, float *scale, int *overlaid, char *title)
{
- if(!cgui) return;
- int ret = cgui->playerpreview(*model, *team, *weap, *scale, *overlaid!=0 ? title : NULL);
- if(ret&G3D_UP)
- {
- if(*action)
- {
- updatelater.add().schedule(action);
- if(shouldclearmenu) clearlater = true;
- }
- }
+ if(!cgui) return;
+ int ret = cgui->playerpreview(*model, *team, *weap, *scale, *overlaid!=0 ? title : NULL);
+ if(ret&G3D_UP)
+ {
+ if(*action)
+ {
+ updatelater.add().schedule(action);
+ if(shouldclearmenu) clearlater = true;
+ }
+ }
}
COMMAND(guiplayerpreview, "iiisfis");
void guimodelpreview(char *model, char *animspec, char *action, float *scale, int *overlaid, char *title, int *throttle)
{
- if(!cgui) return;
- int anim = ANIM_ALL;
- if(animspec[0])
- {
- if(isdigit(animspec[0]))
- {
- anim = parseint(animspec);
- if(anim >= 0) anim %= ANIM_INDEX;
- else anim = ANIM_ALL;
- }
- else
- {
- vector<int> anims;
- findanims(animspec, anims);
- if(anims.length()) anim = anims[0];
- }
- }
- int ret = cgui->modelpreview(model, anim|ANIM_LOOP, *scale, *overlaid!=0 ? title : NULL, *throttle!=0);
- if(ret&G3D_UP)
- {
- if(*action)
- {
- updatelater.add().schedule(action);
- if(shouldclearmenu) clearlater = true;
- }
- }
- else if(ret&G3D_ROLLOVER)
- {
- alias("guirolloverpreviewname", model);
- alias("guirolloverpreviewaction", action);
- }
+ if(!cgui) return;
+ int anim = ANIM_ALL;
+ if(animspec[0])
+ {
+ if(isdigit(animspec[0]))
+ {
+ anim = parseint(animspec);
+ if(anim >= 0) anim %= ANIM_INDEX;
+ else anim = ANIM_ALL;
+ }
+ else
+ {
+ vector<int> anims;
+ findanims(animspec, anims);
+ if(anims.length()) anim = anims[0];
+ }
+ }
+ int ret = cgui->modelpreview(model, anim|ANIM_LOOP, *scale, *overlaid!=0 ? title : NULL, *throttle!=0);
+ if(ret&G3D_UP)
+ {
+ if(*action)
+ {
+ updatelater.add().schedule(action);
+ if(shouldclearmenu) clearlater = true;
+ }
+ }
+ else if(ret&G3D_ROLLOVER)
+ {
+ alias("guirolloverpreviewname", model);
+ alias("guirolloverpreviewaction", action);
+ }
}
COMMAND(guimodelpreview, "sssfisi");
void guiprefabpreview(char *prefab, int *color, char *action, float *scale, int *overlaid, char *title, int *throttle)
{
- if(!cgui) return;
- int ret = cgui->prefabpreview(prefab, vec::hexcolor(*color), *scale, *overlaid!=0 ? title : NULL, *throttle!=0);
- if(ret&G3D_UP)
- {
- if(*action)
- {
- updatelater.add().schedule(action);
- if(shouldclearmenu) clearlater = true;
- }
- }
- else if(ret&G3D_ROLLOVER)
- {
- alias("guirolloverpreviewname", prefab);
- alias("guirolloverpreviewaction", action);
- }
+ if(!cgui) return;
+ int ret = cgui->prefabpreview(prefab, vec::hexcolor(*color), *scale, *overlaid!=0 ? title : NULL, *throttle!=0);
+ if(ret&G3D_UP)
+ {
+ if(*action)
+ {
+ updatelater.add().schedule(action);
+ if(shouldclearmenu) clearlater = true;
+ }
+ }
+ else if(ret&G3D_ROLLOVER)
+ {
+ alias("guirolloverpreviewname", prefab);
+ alias("guirolloverpreviewaction", action);
+ }
}
COMMAND(guiprefabpreview, "sisfisi");
struct change
{
- int type;
- const char *desc;
+ int type;
+ const char *desc;
- change() {}
- change(int type, const char *desc) : type(type), desc(desc) {}
+ change() {}
+ change(int type, const char *desc) : type(type), desc(desc) {}
};
static vector<change> needsapply;
static struct applymenu : menu
{
- void gui(g3d_gui &g, bool firstpass)
- {
- if(guistack.empty()) return;
- g.start(menustart, 0.03f);
- g.text("the following settings have changed:", GUI_TEXT_COLOR, "info");
- loopv(needsapply) g.text(needsapply[i].desc, GUI_TEXT_COLOR, "info");
- g.separator();
- g.text("apply changes now?", GUI_TEXT_COLOR, "info");
- if(g.button("yes", GUI_BUTTON_COLOR, "action")&G3D_UP)
- {
- int changetypes = 0;
- loopv(needsapply) changetypes |= needsapply[i].type;
- if(changetypes&CHANGE_GFX) updatelater.add().schedule("resetgl");
- if(changetypes&CHANGE_SOUND) updatelater.add().schedule("resetsound");
- clearlater = true;
- }
- if(g.button("no", GUI_BUTTON_COLOR, "action")&G3D_UP)
- clearlater = true;
- g.end();
- }
-
- void clear()
- {
- menu::clear();
- needsapply.shrink(0);
- }
+ void gui(g3d_gui &g, bool firstpass)
+ {
+ if(guistack.empty()) return;
+ g.start(menustart, 0.03f);
+ g.text("the following settings have changed:", GUI_TEXT_COLOR, "info");
+ loopv(needsapply) g.text(needsapply[i].desc, GUI_TEXT_COLOR, "info");
+ g.separator();
+ g.text("apply changes now?", GUI_TEXT_COLOR, "info");
+ if(g.button("yes", GUI_BUTTON_COLOR, "action")&G3D_UP)
+ {
+ int changetypes = 0;
+ loopv(needsapply) changetypes |= needsapply[i].type;
+ if(changetypes&CHANGE_GFX) updatelater.add().schedule("resetgl");
+ if(changetypes&CHANGE_SOUND) updatelater.add().schedule("resetsound");
+ clearlater = true;
+ }
+ if(g.button("no", GUI_BUTTON_COLOR, "action")&G3D_UP)
+ clearlater = true;
+ g.end();
+ }
+
+ void clear()
+ {
+ menu::clear();
+ needsapply.shrink(0);
+ }
} applymenu;
VARP(applydialog, 0, 1, 1);
@@ -725,59 +725,59 @@ static bool processingmenu = false;
void addchange(const char *desc, int type)
{
- if(!applydialog) return;
- loopv(needsapply) if(!strcmp(needsapply[i].desc, desc)) return;
- needsapply.add(change(type, desc));
- if(needsapply.length() && guistack.find(&applymenu) < 0)
- pushgui(&applymenu, processingmenu ? max(guistack.length()-1, 0) : -1);
+ if(!applydialog) return;
+ loopv(needsapply) if(!strcmp(needsapply[i].desc, desc)) return;
+ needsapply.add(change(type, desc));
+ if(needsapply.length() && guistack.find(&applymenu) < 0)
+ pushgui(&applymenu, processingmenu ? max(guistack.length()-1, 0) : -1);
}
void clearchanges(int type)
{
- loopv(needsapply)
- {
- if(needsapply[i].type&type)
- {
- needsapply[i].type &= ~type;
- if(!needsapply[i].type) needsapply.remove(i--);
- }
- }
- if(needsapply.empty()) removegui(&applymenu);
+ loopv(needsapply)
+ {
+ if(needsapply[i].type&type)
+ {
+ needsapply[i].type &= ~type;
+ if(!needsapply[i].type) needsapply.remove(i--);
+ }
+ }
+ if(needsapply.empty()) removegui(&applymenu);
}
void menuprocess()
{
- processingmenu = true;
- int wasmain = mainmenu, level = guistack.length();
- loopv(updatelater) updatelater[i].run();
- updatelater.shrink(0);
- if(wasmain > mainmenu || clearlater)
- {
- if(wasmain > mainmenu || level==guistack.length()) clearguis(level);
- clearlater = false;
- }
- if(mainmenu && !isconnected(true) && guistack.empty()) showgui("main");
- processingmenu = false;
+ processingmenu = true;
+ int wasmain = mainmenu, level = guistack.length();
+ loopv(updatelater) updatelater[i].run();
+ updatelater.shrink(0);
+ if(wasmain > mainmenu || clearlater)
+ {
+ if(wasmain > mainmenu || level==guistack.length()) clearguis(level);
+ clearlater = false;
+ }
+ if(mainmenu && !isconnected(true) && guistack.empty()) showgui("main");
+ processingmenu = false;
}
VAR(mainmenu, 1, 1, 0);
void clearmainmenu()
{
- if(mainmenu && isconnected())
- {
- mainmenu = 0;
- if(!processingmenu) cleargui();
- }
+ if(mainmenu && isconnected())
+ {
+ mainmenu = 0;
+ if(!processingmenu) cleargui();
+ }
}
void g3d_mainmenu()
{
- if(!guistack.empty())
- {
- extern int usegui2d;
- if(!mainmenu && !usegui2d && camera1->o.dist(menupos) > menuautoclose) cleargui();
- else g3d_addgui(guistack.last(), menupos, GUI_2D | GUI_FOLLOW);
- }
+ if(!guistack.empty())
+ {
+ extern int usegui2d;
+ if(!mainmenu && !usegui2d && camera1->o.dist(menupos) > menuautoclose) cleargui();
+ else g3d_addgui(guistack.last(), menupos, GUI_2D | GUI_FOLLOW);
+ }
}
diff --git a/src/engine/model.h b/src/engine/model.h
index d314d4b..de850ef 100644
--- a/src/engine/model.h
+++ b/src/engine/model.h
@@ -2,89 +2,89 @@ enum { MDL_MD3, MDL_MD5, MDL_IQM, NUMMODELTYPES };
struct model
{
- char *name;
- float spinyaw, spinpitch, offsetyaw, offsetpitch;
- bool collide, ellipsecollide, shadow, alphadepth, depthoffset;
- float scale;
- vec translate;
- BIH *bih;
- vec bbcenter, bbradius, bbextend, collidecenter, collideradius;
- float rejectradius, eyeheight, collidexyradius, collideheight;
- int batch;
+ char *name;
+ float spinyaw, spinpitch, offsetyaw, offsetpitch;
+ bool collide, ellipsecollide, shadow, alphadepth, depthoffset;
+ float scale;
+ vec translate;
+ BIH *bih;
+ vec bbcenter, bbradius, bbextend, collidecenter, collideradius;
+ float rejectradius, eyeheight, collidexyradius, collideheight;
+ int batch;
- model(const char *name) : name(name ? newstring(name) : NULL), spinyaw(0), spinpitch(0), offsetyaw(0), offsetpitch(0), collide(true), ellipsecollide(false), shadow(true), alphadepth(true), depthoffset(false), scale(1.0f), translate(0, 0, 0), bih(0), bbcenter(0, 0, 0), bbradius(-1, -1, -1), bbextend(0, 0, 0), collidecenter(0, 0, 0), collideradius(-1, -1, -1), rejectradius(-1), eyeheight(0.9f), collidexyradius(0), collideheight(0), batch(-1) {}
- virtual ~model() { DELETEA(name); DELETEP(bih); }
- virtual void calcbb(vec &center, vec &radius) = 0;
- virtual void render(int anim, int basetime, int basetime2, const vec &o, float yaw, float pitch, dynent *d, modelattach *a = NULL, const vec &color = vec(0, 0, 0), const vec &dir = vec(0, 0, 0), float transparent = 1) = 0;
- virtual bool load() = 0;
- virtual int type() const = 0;
- virtual BIH *setBIH() { return 0; }
- virtual bool envmapped() { return false; }
- virtual bool skeletal() const { return false; }
+ model(const char *name) : name(name ? newstring(name) : NULL), spinyaw(0), spinpitch(0), offsetyaw(0), offsetpitch(0), collide(true), ellipsecollide(false), shadow(true), alphadepth(true), depthoffset(false), scale(1.0f), translate(0, 0, 0), bih(0), bbcenter(0, 0, 0), bbradius(-1, -1, -1), bbextend(0, 0, 0), collidecenter(0, 0, 0), collideradius(-1, -1, -1), rejectradius(-1), eyeheight(0.9f), collidexyradius(0), collideheight(0), batch(-1) {}
+ virtual ~model() { DELETEA(name); DELETEP(bih); }
+ virtual void calcbb(vec &center, vec &radius) = 0;
+ virtual void render(int anim, int basetime, int basetime2, const vec &o, float yaw, float pitch, dynent *d, modelattach *a = NULL, const vec &color = vec(0, 0, 0), const vec &dir = vec(0, 0, 0), float transparent = 1) = 0;
+ virtual bool load() = 0;
+ virtual int type() const = 0;
+ virtual BIH *setBIH() { return 0; }
+ virtual bool envmapped() { return false; }
+ virtual bool skeletal() const { return false; }
- virtual void setshader(Shader *shader) {}
- virtual void setenvmap(float envmapmin, float envmapmax, Texture *envmap) {}
- virtual void setspec(float spec) {}
- virtual void setambient(float ambient) {}
- virtual void setglow(float glow, float glowdelta, float glowpulse) {}
- virtual void setglare(float specglare, float glowglare) {}
- virtual void setalphatest(float alpha) {}
- virtual void setalphablend(bool blend) {}
- virtual void setfullbright(float fullbright) {}
- virtual void setcullface(bool cullface) {}
+ virtual void setshader(Shader *shader) {}
+ virtual void setenvmap(float envmapmin, float envmapmax, Texture *envmap) {}
+ virtual void setspec(float spec) {}
+ virtual void setambient(float ambient) {}
+ virtual void setglow(float glow, float glowdelta, float glowpulse) {}
+ virtual void setglare(float specglare, float glowglare) {}
+ virtual void setalphatest(float alpha) {}
+ virtual void setalphablend(bool blend) {}
+ virtual void setfullbright(float fullbright) {}
+ virtual void setcullface(bool cullface) {}
- virtual void preloadBIH() { if(!bih) setBIH(); }
- virtual void preloadshaders(bool force = false) {}
- virtual void preloadmeshes() {}
- virtual void cleanup() {}
+ virtual void preloadBIH() { if(!bih) setBIH(); }
+ virtual void preloadshaders(bool force = false) {}
+ virtual void preloadmeshes() {}
+ virtual void cleanup() {}
- virtual void startrender() {}
- virtual void endrender() {}
+ virtual void startrender() {}
+ virtual void endrender() {}
- void boundbox(vec &center, vec &radius)
- {
- if(bbradius.x < 0)
- {
- calcbb(bbcenter, bbradius);
- bbradius.add(bbextend);
- }
- center = bbcenter;
- radius = bbradius;
- }
+ void boundbox(vec &center, vec &radius)
+ {
+ if(bbradius.x < 0)
+ {
+ calcbb(bbcenter, bbradius);
+ bbradius.add(bbextend);
+ }
+ center = bbcenter;
+ radius = bbradius;
+ }
- float collisionbox(vec &center, vec &radius)
- {
- if(collideradius.x < 0)
- {
- boundbox(collidecenter, collideradius);
- if(collidexyradius)
- {
- collidecenter.x = collidecenter.y = 0;
- collideradius.x = collideradius.y = collidexyradius;
- }
- if(collideheight)
- {
- collidecenter.z = collideradius.z = collideheight/2;
- }
- rejectradius = vec(collidecenter).abs().add(collideradius).magnitude();
- }
- center = collidecenter;
- radius = collideradius;
- return rejectradius;
- }
+ float collisionbox(vec &center, vec &radius)
+ {
+ if(collideradius.x < 0)
+ {
+ boundbox(collidecenter, collideradius);
+ if(collidexyradius)
+ {
+ collidecenter.x = collidecenter.y = 0;
+ collideradius.x = collideradius.y = collidexyradius;
+ }
+ if(collideheight)
+ {
+ collidecenter.z = collideradius.z = collideheight/2;
+ }
+ rejectradius = vec(collidecenter).abs().add(collideradius).magnitude();
+ }
+ center = collidecenter;
+ radius = collideradius;
+ return rejectradius;
+ }
- float boundsphere(vec &center)
- {
- vec radius;
- boundbox(center, radius);
- return radius.magnitude();
- }
+ float boundsphere(vec &center)
+ {
+ vec radius;
+ boundbox(center, radius);
+ return radius.magnitude();
+ }
- float above()
- {
- vec center, radius;
- boundbox(center, radius);
- return center.z+radius.z;
- }
+ float above()
+ {
+ vec center, radius;
+ boundbox(center, radius);
+ return center.z+radius.z;
+ }
};
diff --git a/src/engine/mpr.h b/src/engine/mpr.h
index b4cfb59..6288e4f 100644
--- a/src/engine/mpr.h
+++ b/src/engine/mpr.h
@@ -2,574 +2,574 @@
namespace mpr
{
- struct CubePlanes
- {
- const clipplanes &p;
-
- CubePlanes(const clipplanes &p) : p(p) {}
-
- vec center() const { return p.o; }
-
- vec supportpoint(const vec &n) const
- {
- int besti = 7;
- float bestd = n.dot(p.v[7]);
- loopi(7)
- {
- float d = n.dot(p.v[i]);
- if(d > bestd) { besti = i; bestd = d; }
- }
- return p.v[besti];
- }
- };
-
- struct SolidCube
- {
- vec o;
- int size;
-
- SolidCube(float x, float y, float z, int size) : o(x, y, z), size(size) {}
- SolidCube(const vec &o, int size) : o(o), size(size) {}
- SolidCube(const ivec &o, int size) : o(o), size(size) {}
-
- vec center() const { return vec(o).add(size/2); }
-
- vec supportpoint(const vec &n) const
- {
- vec p(o);
- if(n.x > 0) p.x += size;
- if(n.y > 0) p.y += size;
- if(n.z > 0) p.z += size;
- return p;
- }
- };
-
- struct Ent
- {
- physent *ent;
-
- Ent(physent *ent) : ent(ent) {}
-
- vec center() const { return vec(ent->o.x, ent->o.y, ent->o.z + (ent->aboveeye - ent->eyeheight)/2); }
- };
-
- struct EntOBB : Ent
- {
- matrix3 orient;
- float zmargin;
-
- EntOBB(physent *ent, float zmargin = 0) : Ent(ent), zmargin(zmargin)
- {
- orient.setyaw(ent->yaw*RAD);
- }
-
- vec center() const { return vec(ent->o.x, ent->o.y, ent->o.z + (ent->aboveeye - ent->eyeheight - zmargin)/2); }
-
- vec contactface(const vec &wn, const vec &wdir) const
- {
- vec n = orient.transform(wn).div(vec(ent->xradius, ent->yradius, (ent->aboveeye + ent->eyeheight + zmargin)/2)),
- dir = orient.transform(wdir),
- an(fabs(n.x), fabs(n.y), dir.z ? fabs(n.z) : 0),
- fn(0, 0, 0);
- if(an.x > an.y)
- {
- if(an.x > an.z) fn.x = n.x*dir.x < 0 ? (n.x > 0 ? 1 : -1) : 0;
- else if(an.z > 0) fn.z = n.z*dir.z < 0 ? (n.z > 0 ? 1 : -1) : 0;
- }
- else if(an.y > an.z) fn.y = n.y*dir.y < 0 ? (n.y > 0 ? 1 : -1) : 0;
- else if(an.z > 0) fn.z = n.z*dir.z < 0 ? (n.z > 0 ? 1 : -1) : 0;
- return orient.transposedtransform(fn);
- }
-
- vec localsupportpoint(const vec &ln) const
- {
- return vec(ln.x > 0 ? ent->xradius : -ent->xradius,
- ln.y > 0 ? ent->yradius : -ent->yradius,
- ln.z > 0 ? ent->aboveeye : -ent->eyeheight - zmargin);
- }
-
- vec supportpoint(const vec &n) const
- {
- return orient.transposedtransform(localsupportpoint(orient.transform(n))).add(ent->o);
- }
-
- float supportcoordneg(float a, float b, float c) const
- {
- return localsupportpoint(vec(-a, -b, -c)).dot(vec(a, b, c));
- }
- float supportcoord(float a, float b, float c) const
- {
- return localsupportpoint(vec(a, b, c)).dot(vec(a, b, c));
- }
-
- float left() const { return supportcoordneg(orient.a.x, orient.b.x, orient.c.x) + ent->o.x; }
- float right() const { return supportcoord(orient.a.x, orient.b.x, orient.c.x) + ent->o.x; }
- float back() const { return supportcoordneg(orient.a.y, orient.b.y, orient.c.y) + ent->o.y; }
- float front() const { return supportcoord(orient.a.y, orient.b.y, orient.c.y) + ent->o.y; }
- float bottom() const { return ent->o.z - ent->eyeheight - zmargin; }
- float top() const { return ent->o.z + ent->aboveeye; }
- };
-
- struct EntFuzzy : Ent
- {
- EntFuzzy(physent *ent) : Ent(ent) {}
-
- float left() const { return ent->o.x - ent->radius; }
- float right() const { return ent->o.x + ent->radius; }
- float back() const { return ent->o.y - ent->radius; }
- float front() const { return ent->o.y + ent->radius; }
- float bottom() const { return ent->o.z - ent->eyeheight; }
- float top() const { return ent->o.z + ent->aboveeye; }
- };
-
- struct EntCylinder : EntFuzzy
- {
- float zmargin;
-
- EntCylinder(physent *ent, float zmargin = 0) : EntFuzzy(ent), zmargin(zmargin) {}
-
- vec center() const { return vec(ent->o.x, ent->o.y, ent->o.z + (ent->aboveeye - ent->eyeheight - zmargin)/2); }
-
- float bottom() const { return ent->o.z - ent->eyeheight - zmargin; }
-
- vec contactface(const vec &n, const vec &dir) const
- {
- float dxy = n.dot2(n)/(ent->radius*ent->radius), dz = n.z*n.z*4/(ent->aboveeye + ent->eyeheight + zmargin);
- vec fn(0, 0, 0);
- if(dz > dxy && dir.z) fn.z = n.z*dir.z < 0 ? (n.z > 0 ? 1 : -1) : 0;
- else if(n.dot2(dir) < 0)
- {
- fn.x = n.x;
- fn.y = n.y;
- fn.normalize();
- }
- return fn;
- }
-
- vec supportpoint(const vec &n) const
- {
- vec p(ent->o);
- if(n.z > 0) p.z += ent->aboveeye;
- else p.z -= ent->eyeheight + zmargin;
- if(n.x || n.y)
- {
- float r = ent->radius / n.magnitude2();
- p.x += n.x*r;
- p.y += n.y*r;
- }
- return p;
- }
- };
-
- struct EntCapsule : EntFuzzy
- {
- EntCapsule(physent *ent) : EntFuzzy(ent) {}
-
- vec supportpoint(const vec &n) const
- {
- vec p(ent->o);
- if(n.z > 0) p.z += ent->aboveeye - ent->radius;
- else p.z -= ent->eyeheight - ent->radius;
- p.add(vec(n).mul(ent->radius / n.magnitude()));
- return p;
- }
- };
-
- struct EntEllipsoid : EntFuzzy
- {
- EntEllipsoid(physent *ent) : EntFuzzy(ent) {}
-
- vec supportpoint(const vec &dir) const
- {
- vec p(ent->o), n = vec(dir).normalize();
- p.x += ent->radius*n.x;
- p.y += ent->radius*n.y;
- p.z += (ent->aboveeye + ent->eyeheight)/2*(1 + n.z) - ent->eyeheight;
- return p;
- }
- };
-
- struct Model
- {
- vec o, radius;
- matrix3 orient;
-
- Model(const vec &ent, const vec &center, const vec &radius, int yaw) : o(ent), radius(radius)
- {
- orient.setyaw(yaw*RAD);
- o.add(orient.transposedtransform(center));
- }
-
- vec center() const { return o; }
- };
-
- struct ModelOBB : Model
- {
- ModelOBB(const vec &ent, const vec &center, const vec &radius, int yaw) :
- Model(ent, center, radius, yaw)
- {}
-
- vec contactface(const vec &wn, const vec &wdir) const
- {
- vec n = orient.transform(wn).div(radius), dir = orient.transform(wdir),
- an(fabs(n.x), fabs(n.y), dir.z ? fabs(n.z) : 0),
- fn(0, 0, 0);
- if(an.x > an.y)
- {
- if(an.x > an.z) fn.x = n.x*dir.x < 0 ? (n.x > 0 ? 1 : -1) : 0;
- else if(an.z > 0) fn.z = n.z*dir.z < 0 ? (n.z > 0 ? 1 : -1) : 0;
- }
- else if(an.y > an.z) fn.y = n.y*dir.y < 0 ? (n.y > 0 ? 1 : -1) : 0;
- else if(an.z > 0) fn.z = n.z*dir.z < 0 ? (n.z > 0 ? 1 : -1) : 0;
- return orient.transposedtransform(fn);
- }
-
- vec supportpoint(const vec &n) const
- {
- vec ln = orient.transform(n), p(0, 0, 0);
- if(ln.x > 0) p.x += radius.x;
- else p.x -= radius.x;
- if(ln.y > 0) p.y += radius.y;
- else p.y -= radius.y;
- if(ln.z > 0) p.z += radius.z;
- else p.z -= radius.z;
- return orient.transposedtransform(p).add(o);
- }
- };
-
- struct ModelEllipse : Model
- {
- ModelEllipse(const vec &ent, const vec &center, const vec &radius, int yaw) :
- Model(ent, center, radius, yaw)
- {}
-
- vec contactface(const vec &wn, const vec &wdir) const
- {
- vec n = orient.transform(wn).div(radius), dir = orient.transform(wdir);
- float dxy = n.dot2(n), dz = n.z*n.z;
- vec fn(0, 0, 0);
- if(dz > dxy && dir.z) fn.z = n.z*dir.z < 0 ? (n.z > 0 ? 1 : -1) : 0;
- else if(n.dot2(dir) < 0)
- {
- fn.x = n.x*radius.y;
- fn.y = n.y*radius.x;
- fn.normalize();
- }
- return orient.transposedtransform(fn);
- }
-
- vec supportpoint(const vec &n) const
- {
- vec ln = orient.transform(n), p(0, 0, 0);
- if(ln.z > 0) p.z += radius.z;
- else p.z -= radius.z;
- if(ln.x || ln.y)
- {
- float r = ln.magnitude2();
- p.x += ln.x*radius.x/r;
- p.y += ln.y*radius.y/r;
- }
- return orient.transposedtransform(p).add(o);
- }
- };
-
- const float boundarytolerance = 1e-3f;
-
- template<class T, class U>
- bool collide(const T &p1, const U &p2)
- {
- // v0 = center of Minkowski difference
- vec v0 = p2.center().sub(p1.center());
- if(v0.iszero()) return true; // v0 and origin overlap ==> hit
-
- // v1 = support in direction of origin
- vec n = vec(v0).neg();
- vec v1 = p2.supportpoint(n).sub(p1.supportpoint(vec(n).neg()));
- if(v1.dot(n) <= 0) return false; // origin outside v1 support plane ==> miss
-
- // v2 = support perpendicular to plane containing origin, v0 and v1
- n.cross(v1, v0);
- if(n.iszero()) return true; // v0, v1 and origin colinear (and origin inside v1 support plane) == > hit
- vec v2 = p2.supportpoint(n).sub(p1.supportpoint(vec(n).neg()));
- if(v2.dot(n) <= 0) return false; // origin outside v2 support plane ==> miss
-
- // v3 = support perpendicular to plane containing v0, v1 and v2
- n.cross(v0, v1, v2);
-
- // If the origin is on the - side of the plane, reverse the direction of the plane
- if(n.dot(v0) > 0)
- {
- swap(v1, v2);
- n.neg();
- }
-
- ///
- // Phase One: Find a valid portal
-
- loopi(100)
- {
- // Obtain the next support point
- vec v3 = p2.supportpoint(n).sub(p1.supportpoint(vec(n).neg()));
- if(v3.dot(n) <= 0) return false; // origin outside v3 support plane ==> miss
-
- // If origin is outside (v1,v0,v3), then portal is invalid -- eliminate v2 and find new support outside face
- vec v3xv0;
- v3xv0.cross(v3, v0);
- if(v1.dot(v3xv0) < 0)
- {
- v2 = v3;
- n.cross(v0, v1, v3);
- continue;
- }
-
- // If origin is outside (v3,v0,v2), then portal is invalid -- eliminate v1 and find new support outside face
- if(v2.dot(v3xv0) > 0)
- {
- v1 = v3;
- n.cross(v0, v3, v2);
- continue;
- }
-
- ///
- // Phase Two: Refine the portal
-
- for(int j = 0;; j++)
- {
- // Compute outward facing normal of the portal
- n.cross(v1, v2, v3);
-
- // If the origin is inside the portal, we have a hit
- if(n.dot(v1) >= 0) return true;
-
- n.normalize();
-
- // Find the support point in the direction of the portal's normal
- vec v4 = p2.supportpoint(n).sub(p1.supportpoint(vec(n).neg()));
-
- // If the origin is outside the support plane or the boundary is thin enough, we have a miss
- if(v4.dot(n) <= 0 || vec(v4).sub(v3).dot(n) <= boundarytolerance || j > 100) return false;
-
- // Test origin against the three planes that separate the new portal candidates: (v1,v4,v0) (v2,v4,v0) (v3,v4,v0)
- // Note: We're taking advantage of the triple product identities here as an optimization
- // (v1 % v4) * v0 == v1 * (v4 % v0) > 0 if origin inside (v1, v4, v0)
- // (v2 % v4) * v0 == v2 * (v4 % v0) > 0 if origin inside (v2, v4, v0)
- // (v3 % v4) * v0 == v3 * (v4 % v0) > 0 if origin inside (v3, v4, v0)
- vec v4xv0;
- v4xv0.cross(v4, v0);
- if(v1.dot(v4xv0) > 0)
- {
- if(v2.dot(v4xv0) > 0) v1 = v4; // Inside v1 & inside v2 ==> eliminate v1
- else v3 = v4; // Inside v1 & outside v2 ==> eliminate v3
- }
- else
- {
- if(v3.dot(v4xv0) > 0) v2 = v4; // Outside v1 & inside v3 ==> eliminate v2
- else v1 = v4; // Outside v1 & outside v3 ==> eliminate v1
- }
- }
- }
- return false;
- }
-
- template<class T, class U>
- bool collide(const T &p1, const U &p2, vec *contactnormal, vec *contactpoint1, vec *contactpoint2)
- {
- // v0 = center of Minkowski sum
- vec v01 = p1.center();
- vec v02 = p2.center();
- vec v0 = vec(v02).sub(v01);
-
- // Avoid case where centers overlap -- any direction is fine in this case
- if(v0.iszero()) v0 = vec(0, 0, 1e-5f);
-
- // v1 = support in direction of origin
- vec n = vec(v0).neg();
- vec v11 = p1.supportpoint(vec(n).neg());
- vec v12 = p2.supportpoint(n);
- vec v1 = vec(v12).sub(v11);
- if(v1.dot(n) <= 0)
- {
- if(contactnormal) *contactnormal = n;
- return false;
- }
-
- // v2 - support perpendicular to v1,v0
- n.cross(v1, v0);
- if(n.iszero())
- {
- n = vec(v1).sub(v0);
- n.normalize();
- if(contactnormal) *contactnormal = n;
- if(contactpoint1) *contactpoint1 = v11;
- if(contactpoint2) *contactpoint2 = v12;
- return true;
- }
- vec v21 = p1.supportpoint(vec(n).neg());
- vec v22 = p2.supportpoint(n);
- vec v2 = vec(v22).sub(v21);
- if(v2.dot(n) <= 0)
- {
- if(contactnormal) *contactnormal = n;
- return false;
- }
-
- // Determine whether origin is on + or - side of plane (v1,v0,v2)
- n.cross(v0, v1, v2);
- ASSERT( !n.iszero() );
- // If the origin is on the - side of the plane, reverse the direction of the plane
- if(n.dot(v0) > 0)
- {
- swap(v1, v2);
- swap(v11, v21);
- swap(v12, v22);
- n.neg();
- }
-
- ///
- // Phase One: Identify a portal
-
- loopi(100)
- {
- // Obtain the support point in a direction perpendicular to the existing plane
- // Note: This point is guaranteed to lie off the plane
- vec v31 = p1.supportpoint(vec(n).neg());
- vec v32 = p2.supportpoint(n);
- vec v3 = vec(v32).sub(v31);
- if(v3.dot(n) <= 0)
- {
- if(contactnormal) *contactnormal = n;
- return false;
- }
-
- // If origin is outside (v1,v0,v3), then eliminate v2 and loop
- vec v3xv0;
- v3xv0.cross(v3, v0);
- if(v1.dot(v3xv0) < 0)
- {
- v2 = v3;
- v21 = v31;
- v22 = v32;
- n.cross(v0, v1, v3);
- continue;
- }
-
- // If origin is outside (v3,v0,v2), then eliminate v1 and loop
- if(v2.dot(v3xv0) > 0)
- {
- v1 = v3;
- v11 = v31;
- v12 = v32;
- n.cross(v0, v3, v2);
- continue;
- }
-
- bool hit = false;
-
- ///
- // Phase Two: Refine the portal
-
- // We are now inside of a wedge...
- for(int j = 0;; j++)
- {
- // Compute normal of the wedge face
- n.cross(v1, v2, v3);
-
- // Can this happen??? Can it be handled more cleanly?
- if(n.iszero())
- {
- ASSERT(0);
- return true;
- }
-
- n.normalize();
-
- // If the origin is inside the wedge, we have a hit
- if(n.dot(v1) >= 0 && !hit)
- {
- if(contactnormal) *contactnormal = n;
-
- // Compute the barycentric coordinates of the origin
- if(contactpoint1 || contactpoint2)
- {
- float b0 = v3.scalartriple(v1, v2),
- b1 = v0.scalartriple(v3, v2),
- b2 = v3.scalartriple(v0, v1),
- b3 = v0.scalartriple(v2, v1),
- sum = b0 + b1 + b2 + b3;
- if(sum <= 0)
- {
- b0 = 0;
- b1 = n.scalartriple(v2, v3);
- b2 = n.scalartriple(v3, v1);
- b3 = n.scalartriple(v1, v2);
- sum = b1 + b2 + b3;
- }
- if(contactpoint1)
- *contactpoint1 = (vec(v01).mul(b0).add(vec(v11).mul(b1)).add(vec(v21).mul(b2)).add(vec(v31).mul(b3))).mul(1.0f/sum);
- if(contactpoint2)
- *contactpoint2 = (vec(v02).mul(b0).add(vec(v12).mul(b1)).add(vec(v22).mul(b2)).add(vec(v32).mul(b3))).mul(1.0f/sum);
- }
-
- // HIT!!!
- hit = true;
- }
-
- // Find the support point in the direction of the wedge face
- vec v41 = p1.supportpoint(vec(n).neg());
- vec v42 = p2.supportpoint(n);
- vec v4 = vec(v42).sub(v41);
-
- // If the boundary is thin enough or the origin is outside the support plane for the newly discovered vertex, then we can terminate
- if(v4.dot(n) <= 0 || vec(v4).sub(v3).dot(n) <= boundarytolerance || j > 100)
- {
- if(contactnormal) *contactnormal = n;
- return hit;
- }
-
- // Test origin against the three planes that separate the new portal candidates: (v1,v4,v0) (v2,v4,v0) (v3,v4,v0)
- // Note: We're taking advantage of the triple product identities here as an optimization
- // (v1 % v4) * v0 == v1 * (v4 % v0) > 0 if origin inside (v1, v4, v0)
- // (v2 % v4) * v0 == v2 * (v4 % v0) > 0 if origin inside (v2, v4, v0)
- // (v3 % v4) * v0 == v3 * (v4 % v0) > 0 if origin inside (v3, v4, v0)
- vec v4xv0;
- v4xv0.cross(v4, v0);
- if(v1.dot(v4xv0) > 0) // Compute the tetrahedron dividing face d1 = (v4,v0,v1)
- {
- if(v2.dot(v4xv0) > 0) // Compute the tetrahedron dividing face d2 = (v4,v0,v2)
- {
- // Inside d1 & inside d2 ==> eliminate v1
- v1 = v4;
- v11 = v41;
- v12 = v42;
- }
- else
- {
- // Inside d1 & outside d2 ==> eliminate v3
- v3 = v4;
- v31 = v41;
- v32 = v42;
- }
- }
- else
- {
- if(v3.dot(v4xv0) > 0) // Compute the tetrahedron dividing face d3 = (v4,v0,v3)
- {
- // Outside d1 & inside d3 ==> eliminate v2
- v2 = v4;
- v21 = v41;
- v22 = v42;
- }
- else
- {
- // Outside d1 & outside d3 ==> eliminate v1
- v1 = v4;
- v11 = v41;
- v12 = v42;
- }
- }
- }
- }
- return false;
- }
+ struct CubePlanes
+ {
+ const clipplanes &p;
+
+ CubePlanes(const clipplanes &p) : p(p) {}
+
+ vec center() const { return p.o; }
+
+ vec supportpoint(const vec &n) const
+ {
+ int besti = 7;
+ float bestd = n.dot(p.v[7]);
+ loopi(7)
+ {
+ float d = n.dot(p.v[i]);
+ if(d > bestd) { besti = i; bestd = d; }
+ }
+ return p.v[besti];
+ }
+ };
+
+ struct SolidCube
+ {
+ vec o;
+ int size;
+
+ SolidCube(float x, float y, float z, int size) : o(x, y, z), size(size) {}
+ SolidCube(const vec &o, int size) : o(o), size(size) {}
+ SolidCube(const ivec &o, int size) : o(o), size(size) {}
+
+ vec center() const { return vec(o).add(size/2); }
+
+ vec supportpoint(const vec &n) const
+ {
+ vec p(o);
+ if(n.x > 0) p.x += size;
+ if(n.y > 0) p.y += size;
+ if(n.z > 0) p.z += size;
+ return p;
+ }
+ };
+
+ struct Ent
+ {
+ physent *ent;
+
+ Ent(physent *ent) : ent(ent) {}
+
+ vec center() const { return vec(ent->o.x, ent->o.y, ent->o.z + (ent->aboveeye - ent->eyeheight)/2); }
+ };
+
+ struct EntOBB : Ent
+ {
+ matrix3 orient;
+ float zmargin;
+
+ EntOBB(physent *ent, float zmargin = 0) : Ent(ent), zmargin(zmargin)
+ {
+ orient.setyaw(ent->yaw*RAD);
+ }
+
+ vec center() const { return vec(ent->o.x, ent->o.y, ent->o.z + (ent->aboveeye - ent->eyeheight - zmargin)/2); }
+
+ vec contactface(const vec &wn, const vec &wdir) const
+ {
+ vec n = orient.transform(wn).div(vec(ent->xradius, ent->yradius, (ent->aboveeye + ent->eyeheight + zmargin)/2)),
+ dir = orient.transform(wdir),
+ an(fabs(n.x), fabs(n.y), dir.z ? fabs(n.z) : 0),
+ fn(0, 0, 0);
+ if(an.x > an.y)
+ {
+ if(an.x > an.z) fn.x = n.x*dir.x < 0 ? (n.x > 0 ? 1 : -1) : 0;
+ else if(an.z > 0) fn.z = n.z*dir.z < 0 ? (n.z > 0 ? 1 : -1) : 0;
+ }
+ else if(an.y > an.z) fn.y = n.y*dir.y < 0 ? (n.y > 0 ? 1 : -1) : 0;
+ else if(an.z > 0) fn.z = n.z*dir.z < 0 ? (n.z > 0 ? 1 : -1) : 0;
+ return orient.transposedtransform(fn);
+ }
+
+ vec localsupportpoint(const vec &ln) const
+ {
+ return vec(ln.x > 0 ? ent->xradius : -ent->xradius,
+ ln.y > 0 ? ent->yradius : -ent->yradius,
+ ln.z > 0 ? ent->aboveeye : -ent->eyeheight - zmargin);
+ }
+
+ vec supportpoint(const vec &n) const
+ {
+ return orient.transposedtransform(localsupportpoint(orient.transform(n))).add(ent->o);
+ }
+
+ float supportcoordneg(float a, float b, float c) const
+ {
+ return localsupportpoint(vec(-a, -b, -c)).dot(vec(a, b, c));
+ }
+ float supportcoord(float a, float b, float c) const
+ {
+ return localsupportpoint(vec(a, b, c)).dot(vec(a, b, c));
+ }
+
+ float left() const { return supportcoordneg(orient.a.x, orient.b.x, orient.c.x) + ent->o.x; }
+ float right() const { return supportcoord(orient.a.x, orient.b.x, orient.c.x) + ent->o.x; }
+ float back() const { return supportcoordneg(orient.a.y, orient.b.y, orient.c.y) + ent->o.y; }
+ float front() const { return supportcoord(orient.a.y, orient.b.y, orient.c.y) + ent->o.y; }
+ float bottom() const { return ent->o.z - ent->eyeheight - zmargin; }
+ float top() const { return ent->o.z + ent->aboveeye; }
+ };
+
+ struct EntFuzzy : Ent
+ {
+ EntFuzzy(physent *ent) : Ent(ent) {}
+
+ float left() const { return ent->o.x - ent->radius; }
+ float right() const { return ent->o.x + ent->radius; }
+ float back() const { return ent->o.y - ent->radius; }
+ float front() const { return ent->o.y + ent->radius; }
+ float bottom() const { return ent->o.z - ent->eyeheight; }
+ float top() const { return ent->o.z + ent->aboveeye; }
+ };
+
+ struct EntCylinder : EntFuzzy
+ {
+ float zmargin;
+
+ EntCylinder(physent *ent, float zmargin = 0) : EntFuzzy(ent), zmargin(zmargin) {}
+
+ vec center() const { return vec(ent->o.x, ent->o.y, ent->o.z + (ent->aboveeye - ent->eyeheight - zmargin)/2); }
+
+ float bottom() const { return ent->o.z - ent->eyeheight - zmargin; }
+
+ vec contactface(const vec &n, const vec &dir) const
+ {
+ float dxy = n.dot2(n)/(ent->radius*ent->radius), dz = n.z*n.z*4/(ent->aboveeye + ent->eyeheight + zmargin);
+ vec fn(0, 0, 0);
+ if(dz > dxy && dir.z) fn.z = n.z*dir.z < 0 ? (n.z > 0 ? 1 : -1) : 0;
+ else if(n.dot2(dir) < 0)
+ {
+ fn.x = n.x;
+ fn.y = n.y;
+ fn.normalize();
+ }
+ return fn;
+ }
+
+ vec supportpoint(const vec &n) const
+ {
+ vec p(ent->o);
+ if(n.z > 0) p.z += ent->aboveeye;
+ else p.z -= ent->eyeheight + zmargin;
+ if(n.x || n.y)
+ {
+ float r = ent->radius / n.magnitude2();
+ p.x += n.x*r;
+ p.y += n.y*r;
+ }
+ return p;
+ }
+ };
+
+ struct EntCapsule : EntFuzzy
+ {
+ EntCapsule(physent *ent) : EntFuzzy(ent) {}
+
+ vec supportpoint(const vec &n) const
+ {
+ vec p(ent->o);
+ if(n.z > 0) p.z += ent->aboveeye - ent->radius;
+ else p.z -= ent->eyeheight - ent->radius;
+ p.add(vec(n).mul(ent->radius / n.magnitude()));
+ return p;
+ }
+ };
+
+ struct EntEllipsoid : EntFuzzy
+ {
+ EntEllipsoid(physent *ent) : EntFuzzy(ent) {}
+
+ vec supportpoint(const vec &dir) const
+ {
+ vec p(ent->o), n = vec(dir).normalize();
+ p.x += ent->radius*n.x;
+ p.y += ent->radius*n.y;
+ p.z += (ent->aboveeye + ent->eyeheight)/2*(1 + n.z) - ent->eyeheight;
+ return p;
+ }
+ };
+
+ struct Model
+ {
+ vec o, radius;
+ matrix3 orient;
+
+ Model(const vec &ent, const vec &center, const vec &radius, int yaw) : o(ent), radius(radius)
+ {
+ orient.setyaw(yaw*RAD);
+ o.add(orient.transposedtransform(center));
+ }
+
+ vec center() const { return o; }
+ };
+
+ struct ModelOBB : Model
+ {
+ ModelOBB(const vec &ent, const vec &center, const vec &radius, int yaw) :
+ Model(ent, center, radius, yaw)
+ {}
+
+ vec contactface(const vec &wn, const vec &wdir) const
+ {
+ vec n = orient.transform(wn).div(radius), dir = orient.transform(wdir),
+ an(fabs(n.x), fabs(n.y), dir.z ? fabs(n.z) : 0),
+ fn(0, 0, 0);
+ if(an.x > an.y)
+ {
+ if(an.x > an.z) fn.x = n.x*dir.x < 0 ? (n.x > 0 ? 1 : -1) : 0;
+ else if(an.z > 0) fn.z = n.z*dir.z < 0 ? (n.z > 0 ? 1 : -1) : 0;
+ }
+ else if(an.y > an.z) fn.y = n.y*dir.y < 0 ? (n.y > 0 ? 1 : -1) : 0;
+ else if(an.z > 0) fn.z = n.z*dir.z < 0 ? (n.z > 0 ? 1 : -1) : 0;
+ return orient.transposedtransform(fn);
+ }
+
+ vec supportpoint(const vec &n) const
+ {
+ vec ln = orient.transform(n), p(0, 0, 0);
+ if(ln.x > 0) p.x += radius.x;
+ else p.x -= radius.x;
+ if(ln.y > 0) p.y += radius.y;
+ else p.y -= radius.y;
+ if(ln.z > 0) p.z += radius.z;
+ else p.z -= radius.z;
+ return orient.transposedtransform(p).add(o);
+ }
+ };
+
+ struct ModelEllipse : Model
+ {
+ ModelEllipse(const vec &ent, const vec &center, const vec &radius, int yaw) :
+ Model(ent, center, radius, yaw)
+ {}
+
+ vec contactface(const vec &wn, const vec &wdir) const
+ {
+ vec n = orient.transform(wn).div(radius), dir = orient.transform(wdir);
+ float dxy = n.dot2(n), dz = n.z*n.z;
+ vec fn(0, 0, 0);
+ if(dz > dxy && dir.z) fn.z = n.z*dir.z < 0 ? (n.z > 0 ? 1 : -1) : 0;
+ else if(n.dot2(dir) < 0)
+ {
+ fn.x = n.x*radius.y;
+ fn.y = n.y*radius.x;
+ fn.normalize();
+ }
+ return orient.transposedtransform(fn);
+ }
+
+ vec supportpoint(const vec &n) const
+ {
+ vec ln = orient.transform(n), p(0, 0, 0);
+ if(ln.z > 0) p.z += radius.z;
+ else p.z -= radius.z;
+ if(ln.x || ln.y)
+ {
+ float r = ln.magnitude2();
+ p.x += ln.x*radius.x/r;
+ p.y += ln.y*radius.y/r;
+ }
+ return orient.transposedtransform(p).add(o);
+ }
+ };
+
+ const float boundarytolerance = 1e-3f;
+
+ template<class T, class U>
+ bool collide(const T &p1, const U &p2)
+ {
+ // v0 = center of Minkowski difference
+ vec v0 = p2.center().sub(p1.center());
+ if(v0.iszero()) return true; // v0 and origin overlap ==> hit
+
+ // v1 = support in direction of origin
+ vec n = vec(v0).neg();
+ vec v1 = p2.supportpoint(n).sub(p1.supportpoint(vec(n).neg()));
+ if(v1.dot(n) <= 0) return false; // origin outside v1 support plane ==> miss
+
+ // v2 = support perpendicular to plane containing origin, v0 and v1
+ n.cross(v1, v0);
+ if(n.iszero()) return true; // v0, v1 and origin colinear (and origin inside v1 support plane) == > hit
+ vec v2 = p2.supportpoint(n).sub(p1.supportpoint(vec(n).neg()));
+ if(v2.dot(n) <= 0) return false; // origin outside v2 support plane ==> miss
+
+ // v3 = support perpendicular to plane containing v0, v1 and v2
+ n.cross(v0, v1, v2);
+
+ // If the origin is on the - side of the plane, reverse the direction of the plane
+ if(n.dot(v0) > 0)
+ {
+ swap(v1, v2);
+ n.neg();
+ }
+
+ ///
+ // Phase One: Find a valid portal
+
+ loopi(100)
+ {
+ // Obtain the next support point
+ vec v3 = p2.supportpoint(n).sub(p1.supportpoint(vec(n).neg()));
+ if(v3.dot(n) <= 0) return false; // origin outside v3 support plane ==> miss
+
+ // If origin is outside (v1,v0,v3), then portal is invalid -- eliminate v2 and find new support outside face
+ vec v3xv0;
+ v3xv0.cross(v3, v0);
+ if(v1.dot(v3xv0) < 0)
+ {
+ v2 = v3;
+ n.cross(v0, v1, v3);
+ continue;
+ }
+
+ // If origin is outside (v3,v0,v2), then portal is invalid -- eliminate v1 and find new support outside face
+ if(v2.dot(v3xv0) > 0)
+ {
+ v1 = v3;
+ n.cross(v0, v3, v2);
+ continue;
+ }
+
+ ///
+ // Phase Two: Refine the portal
+
+ for(int j = 0;; j++)
+ {
+ // Compute outward facing normal of the portal
+ n.cross(v1, v2, v3);
+
+ // If the origin is inside the portal, we have a hit
+ if(n.dot(v1) >= 0) return true;
+
+ n.normalize();
+
+ // Find the support point in the direction of the portal's normal
+ vec v4 = p2.supportpoint(n).sub(p1.supportpoint(vec(n).neg()));
+
+ // If the origin is outside the support plane or the boundary is thin enough, we have a miss
+ if(v4.dot(n) <= 0 || vec(v4).sub(v3).dot(n) <= boundarytolerance || j > 100) return false;
+
+ // Test origin against the three planes that separate the new portal candidates: (v1,v4,v0) (v2,v4,v0) (v3,v4,v0)
+ // Note: We're taking advantage of the triple product identities here as an optimization
+ // (v1 % v4) * v0 == v1 * (v4 % v0) > 0 if origin inside (v1, v4, v0)
+ // (v2 % v4) * v0 == v2 * (v4 % v0) > 0 if origin inside (v2, v4, v0)
+ // (v3 % v4) * v0 == v3 * (v4 % v0) > 0 if origin inside (v3, v4, v0)
+ vec v4xv0;
+ v4xv0.cross(v4, v0);
+ if(v1.dot(v4xv0) > 0)
+ {
+ if(v2.dot(v4xv0) > 0) v1 = v4; // Inside v1 & inside v2 ==> eliminate v1
+ else v3 = v4; // Inside v1 & outside v2 ==> eliminate v3
+ }
+ else
+ {
+ if(v3.dot(v4xv0) > 0) v2 = v4; // Outside v1 & inside v3 ==> eliminate v2
+ else v1 = v4; // Outside v1 & outside v3 ==> eliminate v1
+ }
+ }
+ }
+ return false;
+ }
+
+ template<class T, class U>
+ bool collide(const T &p1, const U &p2, vec *contactnormal, vec *contactpoint1, vec *contactpoint2)
+ {
+ // v0 = center of Minkowski sum
+ vec v01 = p1.center();
+ vec v02 = p2.center();
+ vec v0 = vec(v02).sub(v01);
+
+ // Avoid case where centers overlap -- any direction is fine in this case
+ if(v0.iszero()) v0 = vec(0, 0, 1e-5f);
+
+ // v1 = support in direction of origin
+ vec n = vec(v0).neg();
+ vec v11 = p1.supportpoint(vec(n).neg());
+ vec v12 = p2.supportpoint(n);
+ vec v1 = vec(v12).sub(v11);
+ if(v1.dot(n) <= 0)
+ {
+ if(contactnormal) *contactnormal = n;
+ return false;
+ }
+
+ // v2 - support perpendicular to v1,v0
+ n.cross(v1, v0);
+ if(n.iszero())
+ {
+ n = vec(v1).sub(v0);
+ n.normalize();
+ if(contactnormal) *contactnormal = n;
+ if(contactpoint1) *contactpoint1 = v11;
+ if(contactpoint2) *contactpoint2 = v12;
+ return true;
+ }
+ vec v21 = p1.supportpoint(vec(n).neg());
+ vec v22 = p2.supportpoint(n);
+ vec v2 = vec(v22).sub(v21);
+ if(v2.dot(n) <= 0)
+ {
+ if(contactnormal) *contactnormal = n;
+ return false;
+ }
+
+ // Determine whether origin is on + or - side of plane (v1,v0,v2)
+ n.cross(v0, v1, v2);
+ ASSERT( !n.iszero() );
+ // If the origin is on the - side of the plane, reverse the direction of the plane
+ if(n.dot(v0) > 0)
+ {
+ swap(v1, v2);
+ swap(v11, v21);
+ swap(v12, v22);
+ n.neg();
+ }
+
+ ///
+ // Phase One: Identify a portal
+
+ loopi(100)
+ {
+ // Obtain the support point in a direction perpendicular to the existing plane
+ // Note: This point is guaranteed to lie off the plane
+ vec v31 = p1.supportpoint(vec(n).neg());
+ vec v32 = p2.supportpoint(n);
+ vec v3 = vec(v32).sub(v31);
+ if(v3.dot(n) <= 0)
+ {
+ if(contactnormal) *contactnormal = n;
+ return false;
+ }
+
+ // If origin is outside (v1,v0,v3), then eliminate v2 and loop
+ vec v3xv0;
+ v3xv0.cross(v3, v0);
+ if(v1.dot(v3xv0) < 0)
+ {
+ v2 = v3;
+ v21 = v31;
+ v22 = v32;
+ n.cross(v0, v1, v3);
+ continue;
+ }
+
+ // If origin is outside (v3,v0,v2), then eliminate v1 and loop
+ if(v2.dot(v3xv0) > 0)
+ {
+ v1 = v3;
+ v11 = v31;
+ v12 = v32;
+ n.cross(v0, v3, v2);
+ continue;
+ }
+
+ bool hit = false;
+
+ ///
+ // Phase Two: Refine the portal
+
+ // We are now inside of a wedge...
+ for(int j = 0;; j++)
+ {
+ // Compute normal of the wedge face
+ n.cross(v1, v2, v3);
+
+ // Can this happen??? Can it be handled more cleanly?
+ if(n.iszero())
+ {
+ ASSERT(0);
+ return true;
+ }
+
+ n.normalize();
+
+ // If the origin is inside the wedge, we have a hit
+ if(n.dot(v1) >= 0 && !hit)
+ {
+ if(contactnormal) *contactnormal = n;
+
+ // Compute the barycentric coordinates of the origin
+ if(contactpoint1 || contactpoint2)
+ {
+ float b0 = v3.scalartriple(v1, v2),
+ b1 = v0.scalartriple(v3, v2),
+ b2 = v3.scalartriple(v0, v1),
+ b3 = v0.scalartriple(v2, v1),
+ sum = b0 + b1 + b2 + b3;
+ if(sum <= 0)
+ {
+ b0 = 0;
+ b1 = n.scalartriple(v2, v3);
+ b2 = n.scalartriple(v3, v1);
+ b3 = n.scalartriple(v1, v2);
+ sum = b1 + b2 + b3;
+ }
+ if(contactpoint1)
+ *contactpoint1 = (vec(v01).mul(b0).add(vec(v11).mul(b1)).add(vec(v21).mul(b2)).add(vec(v31).mul(b3))).mul(1.0f/sum);
+ if(contactpoint2)
+ *contactpoint2 = (vec(v02).mul(b0).add(vec(v12).mul(b1)).add(vec(v22).mul(b2)).add(vec(v32).mul(b3))).mul(1.0f/sum);
+ }
+
+ // HIT!!!
+ hit = true;
+ }
+
+ // Find the support point in the direction of the wedge face
+ vec v41 = p1.supportpoint(vec(n).neg());
+ vec v42 = p2.supportpoint(n);
+ vec v4 = vec(v42).sub(v41);
+
+ // If the boundary is thin enough or the origin is outside the support plane for the newly discovered vertex, then we can terminate
+ if(v4.dot(n) <= 0 || vec(v4).sub(v3).dot(n) <= boundarytolerance || j > 100)
+ {
+ if(contactnormal) *contactnormal = n;
+ return hit;
+ }
+
+ // Test origin against the three planes that separate the new portal candidates: (v1,v4,v0) (v2,v4,v0) (v3,v4,v0)
+ // Note: We're taking advantage of the triple product identities here as an optimization
+ // (v1 % v4) * v0 == v1 * (v4 % v0) > 0 if origin inside (v1, v4, v0)
+ // (v2 % v4) * v0 == v2 * (v4 % v0) > 0 if origin inside (v2, v4, v0)
+ // (v3 % v4) * v0 == v3 * (v4 % v0) > 0 if origin inside (v3, v4, v0)
+ vec v4xv0;
+ v4xv0.cross(v4, v0);
+ if(v1.dot(v4xv0) > 0) // Compute the tetrahedron dividing face d1 = (v4,v0,v1)
+ {
+ if(v2.dot(v4xv0) > 0) // Compute the tetrahedron dividing face d2 = (v4,v0,v2)
+ {
+ // Inside d1 & inside d2 ==> eliminate v1
+ v1 = v4;
+ v11 = v41;
+ v12 = v42;
+ }
+ else
+ {
+ // Inside d1 & outside d2 ==> eliminate v3
+ v3 = v4;
+ v31 = v41;
+ v32 = v42;
+ }
+ }
+ else
+ {
+ if(v3.dot(v4xv0) > 0) // Compute the tetrahedron dividing face d3 = (v4,v0,v3)
+ {
+ // Outside d1 & inside d3 ==> eliminate v2
+ v2 = v4;
+ v21 = v41;
+ v22 = v42;
+ }
+ else
+ {
+ // Outside d1 & outside d3 ==> eliminate v1
+ v1 = v4;
+ v11 = v41;
+ v12 = v42;
+ }
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/src/engine/normal.cpp b/src/engine/normal.cpp
index d8641ab..dad00b0 100644
--- a/src/engine/normal.cpp
+++ b/src/engine/normal.cpp
@@ -2,27 +2,27 @@
struct normalgroup
{
- vec pos;
- int flat, normals, tnormals;
+ vec pos;
+ int flat, normals, tnormals;
- normalgroup() : flat(0), normals(-1), tnormals(-1) {}
- normalgroup(const vec &pos) : pos(pos), flat(0), normals(-1), tnormals(-1) {}
+ normalgroup() : flat(0), normals(-1), tnormals(-1) {}
+ normalgroup(const vec &pos) : pos(pos), flat(0), normals(-1), tnormals(-1) {}
};
-static inline bool htcmp(const vec &v, const normalgroup &n) { return v == n.pos; }
+static inline bool htcmp(const vec &v, const normalgroup &n) { return v == n.pos; }
struct normal
{
- int next;
- vec surface;
+ int next;
+ vec surface;
};
struct tnormal
{
- int next;
- float offset;
- int normals[2];
- normalgroup *groups[2];
+ int next;
+ float offset;
+ int normals[2];
+ normalgroup *groups[2];
};
hashset<normalgroup> normalgroups(1<<16);
@@ -36,91 +36,91 @@ static bool usetnormals = true;
static int addnormal(const vec &key, const vec &surface)
{
- normalgroup &g = normalgroups.access(key, key);
- normal &n = normals.add();
- n.next = g.normals;
- n.surface = surface;
- return g.normals = normals.length()-1;
+ normalgroup &g = normalgroups.access(key, key);
+ normal &n = normals.add();
+ n.next = g.normals;
+ n.surface = surface;
+ return g.normals = normals.length()-1;
}
static void addtnormal(const vec &key, float offset, int normal1, int normal2, normalgroup *group1, normalgroup *group2)
{
- normalgroup &g = normalgroups.access(key, key);
- tnormal &n = tnormals.add();
- n.next = g.tnormals;
- n.offset = offset;
- n.normals[0] = normal1;
- n.normals[1] = normal2;
- n.groups[0] = group1;
- n.groups[1] = group2;
- g.tnormals = tnormals.length()-1;
+ normalgroup &g = normalgroups.access(key, key);
+ tnormal &n = tnormals.add();
+ n.next = g.tnormals;
+ n.offset = offset;
+ n.normals[0] = normal1;
+ n.normals[1] = normal2;
+ n.groups[0] = group1;
+ n.groups[1] = group2;
+ g.tnormals = tnormals.length()-1;
}
static int addnormal(const vec &key, int axis)
{
- normalgroup &g = normalgroups.access(key, key);
- g.flat += 1<<(4*axis);
- return axis - 6;
+ normalgroup &g = normalgroups.access(key, key);
+ g.flat += 1<<(4*axis);
+ return axis - 6;
}
static inline void findnormal(const normalgroup &g, const vec &surface, vec &v)
{
- v = vec(0, 0, 0);
- int total = 0;
- if(surface.x >= lerpthreshold) { int n = (g.flat>>4)&0xF; v.x += n; total += n; }
- else if(surface.x <= -lerpthreshold) { int n = g.flat&0xF; v.x -= n; total += n; }
- if(surface.y >= lerpthreshold) { int n = (g.flat>>12)&0xF; v.y += n; total += n; }
- else if(surface.y <= -lerpthreshold) { int n = (g.flat>>8)&0xF; v.y -= n; total += n; }
- if(surface.z >= lerpthreshold) { int n = (g.flat>>20)&0xF; v.z += n; total += n; }
- else if(surface.z <= -lerpthreshold) { int n = (g.flat>>16)&0xF; v.z -= n; total += n; }
- for(int cur = g.normals; cur >= 0;)
- {
- normal &o = normals[cur];
- if(o.surface.dot(surface) >= lerpthreshold)
- {
- v.add(o.surface);
- total++;
- }
- cur = o.next;
- }
- if(total > 1) v.normalize();
- else if(!total) v = surface;
+ v = vec(0, 0, 0);
+ int total = 0;
+ if(surface.x >= lerpthreshold) { int n = (g.flat>>4)&0xF; v.x += n; total += n; }
+ else if(surface.x <= -lerpthreshold) { int n = g.flat&0xF; v.x -= n; total += n; }
+ if(surface.y >= lerpthreshold) { int n = (g.flat>>12)&0xF; v.y += n; total += n; }
+ else if(surface.y <= -lerpthreshold) { int n = (g.flat>>8)&0xF; v.y -= n; total += n; }
+ if(surface.z >= lerpthreshold) { int n = (g.flat>>20)&0xF; v.z += n; total += n; }
+ else if(surface.z <= -lerpthreshold) { int n = (g.flat>>16)&0xF; v.z -= n; total += n; }
+ for(int cur = g.normals; cur >= 0;)
+ {
+ normal &o = normals[cur];
+ if(o.surface.dot(surface) >= lerpthreshold)
+ {
+ v.add(o.surface);
+ total++;
+ }
+ cur = o.next;
+ }
+ if(total > 1) v.normalize();
+ else if(!total) v = surface;
}
static inline bool findtnormal(const normalgroup &g, const vec &surface, vec &v)
{
- float bestangle = lerpthreshold;
- tnormal *bestnorm = NULL;
- for(int cur = g.tnormals; cur >= 0;)
- {
- tnormal &o = tnormals[cur];
- static const vec flats[6] = { vec(-1, 0, 0), vec(1, 0, 0), vec(0, -1, 0), vec(0, 1, 0), vec(0, 0, -1), vec(0, 0, 1) };
- vec n1 = o.normals[0] < 0 ? flats[o.normals[0]+6] : normals[o.normals[0]].surface,
- n2 = o.normals[1] < 0 ? flats[o.normals[1]+6] : normals[o.normals[1]].surface,
- nt;
- nt.lerp(n1, n2, o.offset).normalize();
- float tangle = nt.dot(surface);
- if(tangle >= bestangle)
- {
- bestangle = tangle;
- bestnorm = &o;
- }
- cur = o.next;
- }
- if(!bestnorm) return false;
- vec n1, n2;
- findnormal(*bestnorm->groups[0], surface, n1);
- findnormal(*bestnorm->groups[1], surface, n2);
- v.lerp(n1, n2, bestnorm->offset).normalize();
- return true;
+ float bestangle = lerpthreshold;
+ tnormal *bestnorm = NULL;
+ for(int cur = g.tnormals; cur >= 0;)
+ {
+ tnormal &o = tnormals[cur];
+ static const vec flats[6] = { vec(-1, 0, 0), vec(1, 0, 0), vec(0, -1, 0), vec(0, 1, 0), vec(0, 0, -1), vec(0, 0, 1) };
+ vec n1 = o.normals[0] < 0 ? flats[o.normals[0]+6] : normals[o.normals[0]].surface,
+ n2 = o.normals[1] < 0 ? flats[o.normals[1]+6] : normals[o.normals[1]].surface,
+ nt;
+ nt.lerp(n1, n2, o.offset).normalize();
+ float tangle = nt.dot(surface);
+ if(tangle >= bestangle)
+ {
+ bestangle = tangle;
+ bestnorm = &o;
+ }
+ cur = o.next;
+ }
+ if(!bestnorm) return false;
+ vec n1, n2;
+ findnormal(*bestnorm->groups[0], surface, n1);
+ findnormal(*bestnorm->groups[1], surface, n2);
+ v.lerp(n1, n2, bestnorm->offset).normalize();
+ return true;
}
void findnormal(const vec &key, const vec &surface, vec &v)
{
- const normalgroup *g = normalgroups.access(key);
- if(!g) v = surface;
- else if(g->tnormals < 0 || !findtnormal(*g, surface, v))
- findnormal(*g, surface, v);
+ const normalgroup *g = normalgroups.access(key);
+ if(!g) v = surface;
+ else if(g->tnormals < 0 || !findtnormal(*g, surface, v))
+ findnormal(*g, surface, v);
}
VARR(lerpsubdiv, 0, 2, 4);
@@ -130,254 +130,254 @@ static uint progress = 0;
void show_addnormals_progress()
{
- float bar1 = float(progress) / float(allocnodes);
- renderprogress(bar1, "computing normals...");
+ float bar1 = float(progress) / float(allocnodes);
+ renderprogress(bar1, "computing normals...");
}
void addnormals(cube &c, const ivec &o, int size)
{
- CHECK_CALCLIGHT_PROGRESS(return, show_addnormals_progress);
-
- if(c.children)
- {
- progress++;
- size >>= 1;
- loopi(8) addnormals(c.children[i], ivec(i, o, size), size);
- return;
- }
- else if(isempty(c)) return;
-
- vec pos[MAXFACEVERTS];
- int norms[MAXFACEVERTS];
- int tj = usetnormals && c.ext ? c.ext->tjoints : -1, vis;
- loopi(6) if((vis = visibletris(c, i, o, size)))
- {
- CHECK_CALCLIGHT_PROGRESS(return, show_addnormals_progress);
- if(c.texture[i] == DEFAULT_SKY) continue;
-
- vec planes[2];
- int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0, convex = 0, numplanes = 0;
- if(numverts)
- {
- vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
- vec vo(ivec(o).mask(~0xFFF));
- loopj(numverts)
- {
- vertinfo &v = verts[j];
- pos[j] = vec(v.x, v.y, v.z).mul(1.0f/8).add(vo);
- }
- if(!(c.merged&(1<<i)) && !flataxisface(c, i)) convex = faceconvexity(verts, numverts, size);
- }
- else if(c.merged&(1<<i)) continue;
- else
- {
- ivec v[4];
- genfaceverts(c, i, v);
- if(!flataxisface(c, i)) convex = faceconvexity(v);
- int order = vis&4 || convex < 0 ? 1 : 0;
- vec vo(o);
- pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
- if(vis&1) pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
- pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
- if(vis&2) pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
- }
-
- if(!flataxisface(c, i))
- {
- planes[numplanes++].cross(pos[0], pos[1], pos[2]).normalize();
- if(convex) planes[numplanes++].cross(pos[0], pos[2], pos[3]).normalize();
- }
-
- if(!numplanes) loopk(numverts) norms[k] = addnormal(pos[k], i);
- else if(numplanes==1) loopk(numverts) norms[k] = addnormal(pos[k], planes[0]);
- else
- {
- vec avg = vec(planes[0]).add(planes[1]).normalize();
- norms[0] = addnormal(pos[0], avg);
- norms[1] = addnormal(pos[1], planes[0]);
- norms[2] = addnormal(pos[2], avg);
- for(int k = 3; k < numverts; k++) norms[k] = addnormal(pos[k], planes[1]);
- }
-
- while(tj >= 0 && tjoints[tj].edge < i*(MAXFACEVERTS+1)) tj = tjoints[tj].next;
- while(tj >= 0 && tjoints[tj].edge < (i+1)*(MAXFACEVERTS+1))
- {
- int edge = tjoints[tj].edge, e1 = edge%(MAXFACEVERTS+1), e2 = (e1+1)%numverts;
- const vec &v1 = pos[e1], &v2 = pos[e2];
- ivec d(vec(v2).sub(v1).mul(8));
- int axis = abs(d.x) > abs(d.y) ? (abs(d.x) > abs(d.z) ? 0 : 2) : (abs(d.y) > abs(d.z) ? 1 : 2);
- if(d[axis] < 0) d.neg();
- reduceslope(d);
- int origin = int(min(v1[axis], v2[axis])*8)&~0x7FFF,
- offset1 = (int(v1[axis]*8) - origin) / d[axis],
- offset2 = (int(v2[axis]*8) - origin) / d[axis];
- vec o = vec(v1).sub(vec(d).mul(offset1/8.0f)), n1, n2;
- float doffset = 1.0f / (offset2 - offset1);
-
- while(tj >= 0)
- {
- tjoint &t = tjoints[tj];
- if(t.edge != edge) break;
- float offset = (t.offset - offset1) * doffset;
- vec tpos = vec(d).mul(t.offset/8.0f).add(o);
- addtnormal(tpos, offset, norms[e1], norms[e2], normalgroups.access(v1), normalgroups.access(v2));
- tj = t.next;
- }
- }
- }
+ CHECK_CALCLIGHT_PROGRESS(return, show_addnormals_progress);
+
+ if(c.children)
+ {
+ progress++;
+ size >>= 1;
+ loopi(8) addnormals(c.children[i], ivec(i, o, size), size);
+ return;
+ }
+ else if(isempty(c)) return;
+
+ vec pos[MAXFACEVERTS];
+ int norms[MAXFACEVERTS];
+ int tj = usetnormals && c.ext ? c.ext->tjoints : -1, vis;
+ loopi(6) if((vis = visibletris(c, i, o, size)))
+ {
+ CHECK_CALCLIGHT_PROGRESS(return, show_addnormals_progress);
+ if(c.texture[i] == DEFAULT_SKY) continue;
+
+ vec planes[2];
+ int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0, convex = 0, numplanes = 0;
+ if(numverts)
+ {
+ vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
+ vec vo(ivec(o).mask(~0xFFF));
+ loopj(numverts)
+ {
+ vertinfo &v = verts[j];
+ pos[j] = vec(v.x, v.y, v.z).mul(1.0f/8).add(vo);
+ }
+ if(!(c.merged&(1<<i)) && !flataxisface(c, i)) convex = faceconvexity(verts, numverts, size);
+ }
+ else if(c.merged&(1<<i)) continue;
+ else
+ {
+ ivec v[4];
+ genfaceverts(c, i, v);
+ if(!flataxisface(c, i)) convex = faceconvexity(v);
+ int order = vis&4 || convex < 0 ? 1 : 0;
+ vec vo(o);
+ pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
+ if(vis&1) pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
+ pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
+ if(vis&2) pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
+ }
+
+ if(!flataxisface(c, i))
+ {
+ planes[numplanes++].cross(pos[0], pos[1], pos[2]).normalize();
+ if(convex) planes[numplanes++].cross(pos[0], pos[2], pos[3]).normalize();
+ }
+
+ if(!numplanes) loopk(numverts) norms[k] = addnormal(pos[k], i);
+ else if(numplanes==1) loopk(numverts) norms[k] = addnormal(pos[k], planes[0]);
+ else
+ {
+ vec avg = vec(planes[0]).add(planes[1]).normalize();
+ norms[0] = addnormal(pos[0], avg);
+ norms[1] = addnormal(pos[1], planes[0]);
+ norms[2] = addnormal(pos[2], avg);
+ for(int k = 3; k < numverts; k++) norms[k] = addnormal(pos[k], planes[1]);
+ }
+
+ while(tj >= 0 && tjoints[tj].edge < i*(MAXFACEVERTS+1)) tj = tjoints[tj].next;
+ while(tj >= 0 && tjoints[tj].edge < (i+1)*(MAXFACEVERTS+1))
+ {
+ int edge = tjoints[tj].edge, e1 = edge%(MAXFACEVERTS+1), e2 = (e1+1)%numverts;
+ const vec &v1 = pos[e1], &v2 = pos[e2];
+ ivec d(vec(v2).sub(v1).mul(8));
+ int axis = abs(d.x) > abs(d.y) ? (abs(d.x) > abs(d.z) ? 0 : 2) : (abs(d.y) > abs(d.z) ? 1 : 2);
+ if(d[axis] < 0) d.neg();
+ reduceslope(d);
+ int origin = int(min(v1[axis], v2[axis])*8)&~0x7FFF,
+ offset1 = (int(v1[axis]*8) - origin) / d[axis],
+ offset2 = (int(v2[axis]*8) - origin) / d[axis];
+ vec o = vec(v1).sub(vec(d).mul(offset1/8.0f)), n1, n2;
+ float doffset = 1.0f / (offset2 - offset1);
+
+ while(tj >= 0)
+ {
+ tjoint &t = tjoints[tj];
+ if(t.edge != edge) break;
+ float offset = (t.offset - offset1) * doffset;
+ vec tpos = vec(d).mul(t.offset/8.0f).add(o);
+ addtnormal(tpos, offset, norms[e1], norms[e2], normalgroups.access(v1), normalgroups.access(v2));
+ tj = t.next;
+ }
+ }
+ }
}
void calcnormals(bool lerptjoints)
{
- if(!lerpangle) return;
- usetnormals = lerptjoints;
- if(usetnormals) findtjoints();
- lerpthreshold = cos(lerpangle*RAD) - 1e-5f;
- progress = 1;
- loopi(8) addnormals(worldroot[i], ivec(i, ivec(0, 0, 0), worldsize/2), worldsize/2);
+ if(!lerpangle) return;
+ usetnormals = lerptjoints;
+ if(usetnormals) findtjoints();
+ lerpthreshold = cos(lerpangle*RAD) - 1e-5f;
+ progress = 1;
+ loopi(8) addnormals(worldroot[i], ivec(i, ivec(0, 0, 0), worldsize/2), worldsize/2);
}
void clearnormals()
{
- normalgroups.clear();
- normals.setsize(0);
- tnormals.setsize(0);
+ normalgroups.clear();
+ normals.setsize(0);
+ tnormals.setsize(0);
}
void calclerpverts(const vec2 *c, const vec *n, lerpvert *lv, int &numv)
{
- int i = 0;
- loopj(numv)
- {
- if(j)
- {
- if(c[j] == c[j-1] && n[j] == n[j-1]) continue;
- if(j == numv-1 && c[j] == c[0] && n[j] == n[0]) continue;
- }
- lv[i].normal = n[j];
- lv[i].tc = c[j];
- i++;
- }
- numv = i;
+ int i = 0;
+ loopj(numv)
+ {
+ if(j)
+ {
+ if(c[j] == c[j-1] && n[j] == n[j-1]) continue;
+ if(j == numv-1 && c[j] == c[0] && n[j] == n[0]) continue;
+ }
+ lv[i].normal = n[j];
+ lv[i].tc = c[j];
+ i++;
+ }
+ numv = i;
}
void setlerpstep(float v, lerpbounds &bounds)
{
- if(bounds.min->tc.y + 1 > bounds.max->tc.y)
- {
- bounds.nstep = vec(0, 0, 0);
- bounds.normal = bounds.min->normal;
- if(bounds.min->normal != bounds.max->normal)
- {
- bounds.normal.add(bounds.max->normal);
- bounds.normal.normalize();
- }
- bounds.ustep = 0;
- bounds.u = bounds.min->tc.x;
- return;
- }
-
- bounds.nstep = bounds.max->normal;
- bounds.nstep.sub(bounds.min->normal);
- bounds.nstep.div(bounds.max->tc.y-bounds.min->tc.y);
-
- bounds.normal = bounds.nstep;
- bounds.normal.mul(v - bounds.min->tc.y);
- bounds.normal.add(bounds.min->normal);
-
- bounds.ustep = (bounds.max->tc.x-bounds.min->tc.x) / (bounds.max->tc.y-bounds.min->tc.y);
- bounds.u = bounds.ustep * (v-bounds.min->tc.y) + bounds.min->tc.x;
+ if(bounds.min->tc.y + 1 > bounds.max->tc.y)
+ {
+ bounds.nstep = vec(0, 0, 0);
+ bounds.normal = bounds.min->normal;
+ if(bounds.min->normal != bounds.max->normal)
+ {
+ bounds.normal.add(bounds.max->normal);
+ bounds.normal.normalize();
+ }
+ bounds.ustep = 0;
+ bounds.u = bounds.min->tc.x;
+ return;
+ }
+
+ bounds.nstep = bounds.max->normal;
+ bounds.nstep.sub(bounds.min->normal);
+ bounds.nstep.div(bounds.max->tc.y-bounds.min->tc.y);
+
+ bounds.normal = bounds.nstep;
+ bounds.normal.mul(v - bounds.min->tc.y);
+ bounds.normal.add(bounds.min->normal);
+
+ bounds.ustep = (bounds.max->tc.x-bounds.min->tc.x) / (bounds.max->tc.y-bounds.min->tc.y);
+ bounds.u = bounds.ustep * (v-bounds.min->tc.y) + bounds.min->tc.x;
}
void initlerpbounds(float u, float v, const lerpvert *lv, int numv, lerpbounds &start, lerpbounds &end)
{
- const lerpvert *first = &lv[0], *second = NULL;
- loopi(numv-1)
- {
- if(lv[i+1].tc.y < first->tc.y) { second = first; first = &lv[i+1]; }
- else if(!second || lv[i+1].tc.y < second->tc.y) second = &lv[i+1];
- }
-
- if(int(first->tc.y) < int(second->tc.y)) { start.min = end.min = first; }
- else if(first->tc.x > second->tc.x) { start.min = second; end.min = first; }
- else { start.min = first; end.min = second; }
-
- if((lv[1].tc.x - lv->tc.x)*(lv[2].tc.y - lv->tc.y) > (lv[1].tc.y - lv->tc.y)*(lv[2].tc.x - lv->tc.x))
- {
- start.winding = end.winding = 1;
- start.max = (start.min == lv ? &lv[numv-1] : start.min-1);
- end.max = (end.min == &lv[numv-1] ? lv : end.min+1);
- }
- else
- {
- start.winding = end.winding = -1;
- start.max = (start.min == &lv[numv-1] ? lv : start.min+1);
- end.max = (end.min == lv ? &lv[numv-1] : end.min-1);
- }
-
- setlerpstep(v, start);
- setlerpstep(v, end);
+ const lerpvert *first = &lv[0], *second = NULL;
+ loopi(numv-1)
+ {
+ if(lv[i+1].tc.y < first->tc.y) { second = first; first = &lv[i+1]; }
+ else if(!second || lv[i+1].tc.y < second->tc.y) second = &lv[i+1];
+ }
+
+ if(int(first->tc.y) < int(second->tc.y)) { start.min = end.min = first; }
+ else if(first->tc.x > second->tc.x) { start.min = second; end.min = first; }
+ else { start.min = first; end.min = second; }
+
+ if((lv[1].tc.x - lv->tc.x)*(lv[2].tc.y - lv->tc.y) > (lv[1].tc.y - lv->tc.y)*(lv[2].tc.x - lv->tc.x))
+ {
+ start.winding = end.winding = 1;
+ start.max = (start.min == lv ? &lv[numv-1] : start.min-1);
+ end.max = (end.min == &lv[numv-1] ? lv : end.min+1);
+ }
+ else
+ {
+ start.winding = end.winding = -1;
+ start.max = (start.min == &lv[numv-1] ? lv : start.min+1);
+ end.max = (end.min == lv ? &lv[numv-1] : end.min-1);
+ }
+
+ setlerpstep(v, start);
+ setlerpstep(v, end);
}
void updatelerpbounds(float v, const lerpvert *lv, int numv, lerpbounds &start, lerpbounds &end)
{
- if(v >= start.max->tc.y)
- {
- const lerpvert *next = start.winding > 0 ?
- (start.max == lv ? &lv[numv-1] : start.max-1) :
- (start.max == &lv[numv-1] ? lv : start.max+1);
- if(next->tc.y > start.max->tc.y)
- {
- start.min = start.max;
- start.max = next;
- setlerpstep(v, start);
- }
- }
- if(v >= end.max->tc.y)
- {
- const lerpvert *next = end.winding > 0 ?
- (end.max == &lv[numv-1] ? lv : end.max+1) :
- (end.max == lv ? &lv[numv-1] : end.max-1);
- if(next->tc.y > end.max->tc.y)
- {
- end.min = end.max;
- end.max = next;
- setlerpstep(v, end);
- }
- }
+ if(v >= start.max->tc.y)
+ {
+ const lerpvert *next = start.winding > 0 ?
+ (start.max == lv ? &lv[numv-1] : start.max-1) :
+ (start.max == &lv[numv-1] ? lv : start.max+1);
+ if(next->tc.y > start.max->tc.y)
+ {
+ start.min = start.max;
+ start.max = next;
+ setlerpstep(v, start);
+ }
+ }
+ if(v >= end.max->tc.y)
+ {
+ const lerpvert *next = end.winding > 0 ?
+ (end.max == &lv[numv-1] ? lv : end.max+1) :
+ (end.max == lv ? &lv[numv-1] : end.max-1);
+ if(next->tc.y > end.max->tc.y)
+ {
+ end.min = end.max;
+ end.max = next;
+ setlerpstep(v, end);
+ }
+ }
}
void lerpnormal(float u, float v, const lerpvert *lv, int numv, lerpbounds &start, lerpbounds &end, vec &normal, vec &nstep)
-{
- updatelerpbounds(v, lv, numv, start, end);
-
- if(start.u + 1 > end.u)
- {
- nstep = vec(0, 0, 0);
- normal = start.normal;
- normal.add(end.normal);
- normal.normalize();
- }
- else
- {
- vec nstart(start.normal), nend(end.normal);
- nstart.normalize();
- nend.normalize();
-
- nstep = nend;
- nstep.sub(nstart);
- nstep.div(end.u-start.u);
-
- normal = nstep;
- normal.mul(u-start.u);
- normal.add(nstart);
- normal.normalize();
- }
-
- start.normal.add(start.nstep);
- start.u += start.ustep;
-
- end.normal.add(end.nstep);
- end.u += end.ustep;
+{
+ updatelerpbounds(v, lv, numv, start, end);
+
+ if(start.u + 1 > end.u)
+ {
+ nstep = vec(0, 0, 0);
+ normal = start.normal;
+ normal.add(end.normal);
+ normal.normalize();
+ }
+ else
+ {
+ vec nstart(start.normal), nend(end.normal);
+ nstart.normalize();
+ nend.normalize();
+
+ nstep = nend;
+ nstep.sub(nstart);
+ nstep.div(end.u-start.u);
+
+ normal = nstep;
+ normal.mul(u-start.u);
+ normal.add(nstart);
+ normal.normalize();
+ }
+
+ start.normal.add(start.nstep);
+ start.u += start.ustep;
+
+ end.normal.add(end.nstep);
+ end.u += end.ustep;
}
diff --git a/src/engine/octa.cpp b/src/engine/octa.cpp
index e4f0901..93077c5 100644
--- a/src/engine/octa.cpp
+++ b/src/engine/octa.cpp
@@ -7,254 +7,254 @@ int allocnodes = 0;
cubeext *growcubeext(cubeext *old, int maxverts)
{
- cubeext *ext = (cubeext *)new uchar[sizeof(cubeext) + maxverts*sizeof(vertinfo)];
- if(old)
- {
- ext->va = old->va;
- ext->ents = old->ents;
- ext->tjoints = old->tjoints;
- }
- else
- {
- ext->va = NULL;
- ext->ents = NULL;
- ext->tjoints = -1;
- }
- ext->maxverts = maxverts;
- return ext;
+ cubeext *ext = (cubeext *)new uchar[sizeof(cubeext) + maxverts*sizeof(vertinfo)];
+ if(old)
+ {
+ ext->va = old->va;
+ ext->ents = old->ents;
+ ext->tjoints = old->tjoints;
+ }
+ else
+ {
+ ext->va = NULL;
+ ext->ents = NULL;
+ ext->tjoints = -1;
+ }
+ ext->maxverts = maxverts;
+ return ext;
}
void setcubeext(cube &c, cubeext *ext)
{
- cubeext *old = c.ext;
- if(old == ext) return;
- c.ext = ext;
- if(old) delete[] (uchar *)old;
+ cubeext *old = c.ext;
+ if(old == ext) return;
+ c.ext = ext;
+ if(old) delete[] (uchar *)old;
}
-
+
cubeext *newcubeext(cube &c, int maxverts, bool init)
{
- if(c.ext && c.ext->maxverts >= maxverts) return c.ext;
- cubeext *ext = growcubeext(c.ext, maxverts);
- if(init)
- {
- if(c.ext)
- {
- memcpy(ext->surfaces, c.ext->surfaces, sizeof(ext->surfaces));
- memcpy(ext->verts(), c.ext->verts(), c.ext->maxverts*sizeof(vertinfo));
- }
- else memset(ext->surfaces, 0, sizeof(ext->surfaces));
- }
- setcubeext(c, ext);
- return ext;
+ if(c.ext && c.ext->maxverts >= maxverts) return c.ext;
+ cubeext *ext = growcubeext(c.ext, maxverts);
+ if(init)
+ {
+ if(c.ext)
+ {
+ memcpy(ext->surfaces, c.ext->surfaces, sizeof(ext->surfaces));
+ memcpy(ext->verts(), c.ext->verts(), c.ext->maxverts*sizeof(vertinfo));
+ }
+ else memset(ext->surfaces, 0, sizeof(ext->surfaces));
+ }
+ setcubeext(c, ext);
+ return ext;
}
cube *newcubes(uint face, int mat)
{
- cube *c = new cube[8];
- loopi(8)
- {
- c->children = NULL;
- c->ext = NULL;
- c->visible = 0;
- c->merged = 0;
- setfaces(*c, face);
- loopl(6) c->texture[l] = DEFAULT_GEOM;
- c->material = mat;
- c++;
- }
- allocnodes++;
- return c-8;
+ cube *c = new cube[8];
+ loopi(8)
+ {
+ c->children = NULL;
+ c->ext = NULL;
+ c->visible = 0;
+ c->merged = 0;
+ setfaces(*c, face);
+ loopl(6) c->texture[l] = DEFAULT_GEOM;
+ c->material = mat;
+ c++;
+ }
+ allocnodes++;
+ return c-8;
}
int familysize(const cube &c)
{
- int size = 1;
- if(c.children) loopi(8) size += familysize(c.children[i]);
- return size;
+ int size = 1;
+ if(c.children) loopi(8) size += familysize(c.children[i]);
+ return size;
}
void freeocta(cube *c)
{
- if(!c) return;
- loopi(8) discardchildren(c[i]);
- delete[] c;
- allocnodes--;
+ if(!c) return;
+ loopi(8) discardchildren(c[i]);
+ delete[] c;
+ allocnodes--;
}
void freecubeext(cube &c)
{
- if(c.ext)
- {
- delete[] (uchar *)c.ext;
- c.ext = NULL;
- }
+ if(c.ext)
+ {
+ delete[] (uchar *)c.ext;
+ c.ext = NULL;
+ }
}
void discardchildren(cube &c, bool fixtex, int depth)
{
- c.material = MAT_AIR;
- c.visible = 0;
- c.merged = 0;
- if(c.ext)
- {
- if(c.ext->va) destroyva(c.ext->va);
- c.ext->va = NULL;
- c.ext->tjoints = -1;
- freeoctaentities(c);
- freecubeext(c);
- }
- if(c.children)
- {
- uint filled = F_EMPTY;
- loopi(8)
- {
- discardchildren(c.children[i], fixtex, depth+1);
- filled |= c.children[i].faces[0];
- }
- if(fixtex)
- {
- loopi(6) c.texture[i] = getmippedtexture(c, i);
- if(depth > 0 && filled != F_EMPTY) c.faces[0] = F_SOLID;
- }
- DELETEA(c.children);
- allocnodes--;
- }
+ c.material = MAT_AIR;
+ c.visible = 0;
+ c.merged = 0;
+ if(c.ext)
+ {
+ if(c.ext->va) destroyva(c.ext->va);
+ c.ext->va = NULL;
+ c.ext->tjoints = -1;
+ freeoctaentities(c);
+ freecubeext(c);
+ }
+ if(c.children)
+ {
+ uint filled = F_EMPTY;
+ loopi(8)
+ {
+ discardchildren(c.children[i], fixtex, depth+1);
+ filled |= c.children[i].faces[0];
+ }
+ if(fixtex)
+ {
+ loopi(6) c.texture[i] = getmippedtexture(c, i);
+ if(depth > 0 && filled != F_EMPTY) c.faces[0] = F_SOLID;
+ }
+ DELETEA(c.children);
+ allocnodes--;
+ }
}
void getcubevector(cube &c, int d, int x, int y, int z, ivec &p)
{
- ivec v(d, x, y, z);
+ ivec v(d, x, y, z);
- loopi(3)
- p[i] = edgeget(cubeedge(c, i, v[R[i]], v[C[i]]), v[D[i]]);
+ loopi(3)
+ p[i] = edgeget(cubeedge(c, i, v[R[i]], v[C[i]]), v[D[i]]);
}
void setcubevector(cube &c, int d, int x, int y, int z, const ivec &p)
{
- ivec v(d, x, y, z);
+ ivec v(d, x, y, z);
- loopi(3)
- edgeset(cubeedge(c, i, v[R[i]], v[C[i]]), v[D[i]], p[i]);
+ loopi(3)
+ edgeset(cubeedge(c, i, v[R[i]], v[C[i]]), v[D[i]], p[i]);
}
static inline void getcubevector(cube &c, int i, ivec &p)
{
- p.x = edgeget(cubeedge(c, 0, (i>>R[0])&1, (i>>C[0])&1), (i>>D[0])&1);
- p.y = edgeget(cubeedge(c, 1, (i>>R[1])&1, (i>>C[1])&1), (i>>D[1])&1);
- p.z = edgeget(cubeedge(c, 2, (i>>R[2])&1, (i>>C[2])&1), (i>>D[2])&1);
+ p.x = edgeget(cubeedge(c, 0, (i>>R[0])&1, (i>>C[0])&1), (i>>D[0])&1);
+ p.y = edgeget(cubeedge(c, 1, (i>>R[1])&1, (i>>C[1])&1), (i>>D[1])&1);
+ p.z = edgeget(cubeedge(c, 2, (i>>R[2])&1, (i>>C[2])&1), (i>>D[2])&1);
}
static inline void setcubevector(cube &c, int i, const ivec &p)
{
- edgeset(cubeedge(c, 0, (i>>R[0])&1, (i>>C[0])&1), (i>>D[0])&1, p.x);
- edgeset(cubeedge(c, 1, (i>>R[1])&1, (i>>C[1])&1), (i>>D[1])&1, p.y);
- edgeset(cubeedge(c, 2, (i>>R[2])&1, (i>>C[2])&1), (i>>D[2])&1, p.z);
+ edgeset(cubeedge(c, 0, (i>>R[0])&1, (i>>C[0])&1), (i>>D[0])&1, p.x);
+ edgeset(cubeedge(c, 1, (i>>R[1])&1, (i>>C[1])&1), (i>>D[1])&1, p.y);
+ edgeset(cubeedge(c, 2, (i>>R[2])&1, (i>>C[2])&1), (i>>D[2])&1, p.z);
}
void optiface(uchar *p, cube &c)
{
- uint f = *(uint *)p;
- if(((f>>4)&0x0F0F0F0FU) == (f&0x0F0F0F0FU)) emptyfaces(c);
+ uint f = *(uint *)p;
+ if(((f>>4)&0x0F0F0F0FU) == (f&0x0F0F0F0FU)) emptyfaces(c);
}
void printcube()
{
- cube &c = lookupcube(lu); // assume this is cube being pointed at
- conoutf(CON_DEBUG, "= %p = (%d, %d, %d) @ %d", (void *)&c, lu.x, lu.y, lu.z, lusize);
- conoutf(CON_DEBUG, " x %.8x", c.faces[0]);
- conoutf(CON_DEBUG, " y %.8x", c.faces[1]);
- conoutf(CON_DEBUG, " z %.8x", c.faces[2]);
+ cube &c = lookupcube(lu); // assume this is cube being pointed at
+ conoutf(CON_DEBUG, "= %p = (%d, %d, %d) @ %d", (void *)&c, lu.x, lu.y, lu.z, lusize);
+ conoutf(CON_DEBUG, " x %.8x", c.faces[0]);
+ conoutf(CON_DEBUG, " y %.8x", c.faces[1]);
+ conoutf(CON_DEBUG, " z %.8x", c.faces[2]);
}
COMMAND(printcube, "");
bool isvalidcube(const cube &c)
{
- clipplanes p;
- genclipplanes(c, ivec(0, 0, 0), 256, p);
- loopi(8) // test that cube is convex
- {
- vec v = p.v[i];
- loopj(p.size) if(p.p[j].dist(v)>1e-3f) return false;
- }
- return true;
+ clipplanes p;
+ genclipplanes(c, ivec(0, 0, 0), 256, p);
+ loopi(8) // test that cube is convex
+ {
+ vec v = p.v[i];
+ loopj(p.size) if(p.p[j].dist(v)>1e-3f) return false;
+ }
+ return true;
}
void validatec(cube *c, int size)
{
- loopi(8)
- {
- if(c[i].children)
- {
- if(size<=1)
- {
- solidfaces(c[i]);
- discardchildren(c[i], true);
- }
- else validatec(c[i].children, size>>1);
- }
- else if(size > 0x1000)
- {
- subdividecube(c[i], true, false);
- validatec(c[i].children, size>>1);
- }
- else
- {
- loopj(3)
- {
- uint f = c[i].faces[j], e0 = f&0x0F0F0F0FU, e1 = (f>>4)&0x0F0F0F0FU;
- if(e0 == e1 || ((e1+0x07070707U)|(e1-e0))&0xF0F0F0F0U)
- {
- emptyfaces(c[i]);
- break;
- }
- }
- }
- }
+ loopi(8)
+ {
+ if(c[i].children)
+ {
+ if(size<=1)
+ {
+ solidfaces(c[i]);
+ discardchildren(c[i], true);
+ }
+ else validatec(c[i].children, size>>1);
+ }
+ else if(size > 0x1000)
+ {
+ subdividecube(c[i], true, false);
+ validatec(c[i].children, size>>1);
+ }
+ else
+ {
+ loopj(3)
+ {
+ uint f = c[i].faces[j], e0 = f&0x0F0F0F0FU, e1 = (f>>4)&0x0F0F0F0FU;
+ if(e0 == e1 || ((e1+0x07070707U)|(e1-e0))&0xF0F0F0F0U)
+ {
+ emptyfaces(c[i]);
+ break;
+ }
+ }
+ }
+ }
}
ivec lu;
int lusize;
cube &lookupcube(const ivec &to, int tsize, ivec &ro, int &rsize)
{
- int tx = clamp(to.x, 0, worldsize-1),
- ty = clamp(to.y, 0, worldsize-1),
- tz = clamp(to.z, 0, worldsize-1);
- int scale = worldscale-1, csize = abs(tsize);
- cube *c = &worldroot[octastep(tx, ty, tz, scale)];
- if(!(csize>>scale)) do
- {
- if(!c->children)
- {
- if(tsize > 0) do
- {
- subdividecube(*c);
- scale--;
- c = &c->children[octastep(tx, ty, tz, scale)];
- } while(!(csize>>scale));
- break;
- }
- scale--;
- c = &c->children[octastep(tx, ty, tz, scale)];
- } while(!(csize>>scale));
- ro = ivec(tx, ty, tz).mask(~0U<<scale);
- rsize = 1<<scale;
- return *c;
+ int tx = clamp(to.x, 0, worldsize-1),
+ ty = clamp(to.y, 0, worldsize-1),
+ tz = clamp(to.z, 0, worldsize-1);
+ int scale = worldscale-1, csize = abs(tsize);
+ cube *c = &worldroot[octastep(tx, ty, tz, scale)];
+ if(!(csize>>scale)) do
+ {
+ if(!c->children)
+ {
+ if(tsize > 0) do
+ {
+ subdividecube(*c);
+ scale--;
+ c = &c->children[octastep(tx, ty, tz, scale)];
+ } while(!(csize>>scale));
+ break;
+ }
+ scale--;
+ c = &c->children[octastep(tx, ty, tz, scale)];
+ } while(!(csize>>scale));
+ ro = ivec(tx, ty, tz).mask(~0U<<scale);
+ rsize = 1<<scale;
+ return *c;
}
int lookupmaterial(const vec &v)
{
- ivec o(v);
- if(!insideworld(o)) return MAT_AIR;
- int scale = worldscale-1;
- cube *c = &worldroot[octastep(o.x, o.y, o.z, scale)];
- while(c->children)
- {
- scale--;
- c = &c->children[octastep(o.x, o.y, o.z, scale)];
- }
- return c->material;
+ ivec o(v);
+ if(!insideworld(o)) return MAT_AIR;
+ int scale = worldscale-1;
+ cube *c = &worldroot[octastep(o.x, o.y, o.z, scale)];
+ while(c->children)
+ {
+ scale--;
+ c = &c->children[octastep(o.x, o.y, o.z, scale)];
+ }
+ return c->material;
}
const cube *neighbourstack[32];
@@ -262,198 +262,198 @@ int neighbourdepth = -1;
const cube &neighbourcube(const cube &c, int orient, const ivec &co, int size, ivec &ro, int &rsize)
{
- ivec n = co;
- int dim = dimension(orient);
- uint diff = n[dim];
- if(dimcoord(orient)) n[dim] += size; else n[dim] -= size;
- diff ^= n[dim];
- if(diff >= uint(worldsize)) { ro = n; rsize = size; return c; }
- int scale = worldscale;
- const cube *nc = worldroot;
- if(neighbourdepth >= 0)
- {
- scale -= neighbourdepth + 1;
- diff >>= scale;
- do { scale++; diff >>= 1; } while(diff);
- nc = neighbourstack[worldscale - scale];
- }
- scale--;
- nc = &nc[octastep(n.x, n.y, n.z, scale)];
- if(!(size>>scale) && nc->children) do
- {
- scale--;
- nc = &nc->children[octastep(n.x, n.y, n.z, scale)];
- } while(!(size>>scale) && nc->children);
- ro = n.mask(~0U<<scale);
- rsize = 1<<scale;
- return *nc;
+ ivec n = co;
+ int dim = dimension(orient);
+ uint diff = n[dim];
+ if(dimcoord(orient)) n[dim] += size; else n[dim] -= size;
+ diff ^= n[dim];
+ if(diff >= uint(worldsize)) { ro = n; rsize = size; return c; }
+ int scale = worldscale;
+ const cube *nc = worldroot;
+ if(neighbourdepth >= 0)
+ {
+ scale -= neighbourdepth + 1;
+ diff >>= scale;
+ do { scale++; diff >>= 1; } while(diff);
+ nc = neighbourstack[worldscale - scale];
+ }
+ scale--;
+ nc = &nc[octastep(n.x, n.y, n.z, scale)];
+ if(!(size>>scale) && nc->children) do
+ {
+ scale--;
+ nc = &nc->children[octastep(n.x, n.y, n.z, scale)];
+ } while(!(size>>scale) && nc->children);
+ ro = n.mask(~0U<<scale);
+ rsize = 1<<scale;
+ return *nc;
}
////////// (re)mip //////////
int getmippedtexture(const cube &p, int orient)
{
- cube *c = p.children;
- int d = dimension(orient), dc = dimcoord(orient), texs[4] = { -1, -1, -1, -1 }, numtexs = 0;
- loop(x, 2) loop(y, 2)
- {
- int n = octaindex(d, x, y, dc);
- if(isempty(c[n]))
- {
- n = oppositeocta(d, n);
- if(isempty(c[n]))
- continue;
- }
- int tex = c[n].texture[orient];
- if(tex > DEFAULT_SKY) loopi(numtexs) if(texs[i] == tex) return tex;
- texs[numtexs++] = tex;
- }
- loopirev(numtexs) if(!i || texs[i] > DEFAULT_SKY) return texs[i];
- return DEFAULT_GEOM;
+ cube *c = p.children;
+ int d = dimension(orient), dc = dimcoord(orient), texs[4] = { -1, -1, -1, -1 }, numtexs = 0;
+ loop(x, 2) loop(y, 2)
+ {
+ int n = octaindex(d, x, y, dc);
+ if(isempty(c[n]))
+ {
+ n = oppositeocta(d, n);
+ if(isempty(c[n]))
+ continue;
+ }
+ int tex = c[n].texture[orient];
+ if(tex > DEFAULT_SKY) loopi(numtexs) if(texs[i] == tex) return tex;
+ texs[numtexs++] = tex;
+ }
+ loopirev(numtexs) if(!i || texs[i] > DEFAULT_SKY) return texs[i];
+ return DEFAULT_GEOM;
}
void forcemip(cube &c, bool fixtex)
{
- cube *ch = c.children;
- emptyfaces(c);
+ cube *ch = c.children;
+ emptyfaces(c);
- loopi(8) loopj(8)
- {
- int n = i^(j==3 ? 4 : (j==4 ? 3 : j));
- if(!isempty(ch[n])) // breadth first search for cube near vert
- {
- ivec v;
- getcubevector(ch[n], i, v);
- // adjust vert to parent size
- setcubevector(c, i, ivec(n, v, 8).shr(1));
- break;
- }
- }
+ loopi(8) loopj(8)
+ {
+ int n = i^(j==3 ? 4 : (j==4 ? 3 : j));
+ if(!isempty(ch[n])) // breadth first search for cube near vert
+ {
+ ivec v;
+ getcubevector(ch[n], i, v);
+ // adjust vert to parent size
+ setcubevector(c, i, ivec(n, v, 8).shr(1));
+ break;
+ }
+ }
- if(fixtex) loopj(6)
- c.texture[j] = getmippedtexture(c, j);
+ if(fixtex) loopj(6)
+ c.texture[j] = getmippedtexture(c, j);
}
static int midedge(const ivec &a, const ivec &b, int xd, int yd, bool &perfect)
{
- int ax = a[xd], ay = a[yd], bx = b[xd], by = b[yd];
- if(ay==by) return ay;
- if(ax==bx) { perfect = false; return ay; }
- bool crossx = (ax<8 && bx>8) || (ax>8 && bx<8);
- bool crossy = (ay<8 && by>8) || (ay>8 && by<8);
- if(crossy && !crossx) { midedge(a,b,yd,xd,perfect); return 8; } // to test perfection
- if(ax<=8 && bx<=8) return ax>bx ? ay : by;
- if(ax>=8 && bx>=8) return ax<bx ? ay : by;
- int risex = (by-ay)*(8-ax)*256;
- int s = risex/(bx-ax);
- int y = s/256 + ay;
- if(((abs(s)&0xFF)!=0) || // ie: rounding error
- (crossy && y!=8) ||
- (y<0 || y>16)) perfect = false;
- return crossy ? 8 : min(max(y, 0), 16);
+ int ax = a[xd], ay = a[yd], bx = b[xd], by = b[yd];
+ if(ay==by) return ay;
+ if(ax==bx) { perfect = false; return ay; }
+ bool crossx = (ax<8 && bx>8) || (ax>8 && bx<8);
+ bool crossy = (ay<8 && by>8) || (ay>8 && by<8);
+ if(crossy && !crossx) { midedge(a,b,yd,xd,perfect); return 8; } // to test perfection
+ if(ax<=8 && bx<=8) return ax>bx ? ay : by;
+ if(ax>=8 && bx>=8) return ax<bx ? ay : by;
+ int risex = (by-ay)*(8-ax)*256;
+ int s = risex/(bx-ax);
+ int y = s/256 + ay;
+ if(((abs(s)&0xFF)!=0) || // ie: rounding error
+ (crossy && y!=8) ||
+ (y<0 || y>16)) perfect = false;
+ return crossy ? 8 : min(max(y, 0), 16);
}
static inline bool crosscenter(const ivec &a, const ivec &b, int xd, int yd)
{
- int ax = a[xd], ay = a[yd], bx = b[xd], by = b[yd];
- return (((ax <= 8 && bx <= 8) || (ax >= 8 && bx >= 8)) &&
- ((ay <= 8 && by <= 8) || (ay >= 8 && by >= 8))) ||
- (ax + bx == 16 && ay + by == 16);
+ int ax = a[xd], ay = a[yd], bx = b[xd], by = b[yd];
+ return (((ax <= 8 && bx <= 8) || (ax >= 8 && bx >= 8)) &&
+ ((ay <= 8 && by <= 8) || (ay >= 8 && by >= 8))) ||
+ (ax + bx == 16 && ay + by == 16);
}
bool subdividecube(cube &c, bool fullcheck, bool brighten)
{
- if(c.children) return true;
- if(c.ext) memset(c.ext->surfaces, 0, sizeof(c.ext->surfaces));
+ if(c.children) return true;
+ if(c.ext) memset(c.ext->surfaces, 0, sizeof(c.ext->surfaces));
if(isempty(c) || isentirelysolid(c))
- {
+ {
c.children = newcubes(isempty(c) ? F_EMPTY : F_SOLID, c.material);
- loopi(8)
- {
- loopl(6) c.children[i].texture[l] = c.texture[l];
- if(brighten && !isempty(c)) brightencube(c.children[i]);
- }
- return true;
- }
- cube *ch = c.children = newcubes(F_SOLID, c.material);
- bool perfect = true;
- ivec v[8];
- loopi(8)
- {
- getcubevector(c, i, v[i]);
- v[i].mul(2);
- }
-
- loopj(6)
- {
- int d = dimension(j), z = dimcoord(j);
- const ivec &v00 = v[octaindex(d, 0, 0, z)],
- &v10 = v[octaindex(d, 1, 0, z)],
- &v01 = v[octaindex(d, 0, 1, z)],
- &v11 = v[octaindex(d, 1, 1, z)];
- int e[3][3];
- // corners
- e[0][0] = v00[d];
- e[0][2] = v01[d];
- e[2][0] = v10[d];
- e[2][2] = v11[d];
- // edges
- e[0][1] = midedge(v00, v01, C[d], d, perfect);
- e[1][0] = midedge(v00, v10, R[d], d, perfect);
- e[1][2] = midedge(v11, v01, R[d], d, perfect);
- e[2][1] = midedge(v11, v10, C[d], d, perfect);
- // center
- bool p1 = perfect, p2 = perfect;
- int c1 = midedge(v00, v11, R[d], d, p1);
- int c2 = midedge(v01, v10, R[d], d, p2);
- if(z ? c1 > c2 : c1 < c2)
- {
- e[1][1] = c1;
- perfect = p1 && (c1 == c2 || crosscenter(v00, v11, C[d], R[d]));
- }
- else
- {
- e[1][1] = c2;
- perfect = p2 && (c1 == c2 || crosscenter(v01, v10, C[d], R[d]));
- }
-
- loopi(8)
- {
- ch[i].texture[j] = c.texture[j];
- int rd = (i>>R[d])&1, cd = (i>>C[d])&1, dd = (i>>D[d])&1;
- edgeset(cubeedge(ch[i], d, 0, 0), z, clamp(e[rd][cd] - dd*8, 0, 8));
- edgeset(cubeedge(ch[i], d, 1, 0), z, clamp(e[1+rd][cd] - dd*8, 0, 8));
- edgeset(cubeedge(ch[i], d, 0, 1), z, clamp(e[rd][1+cd] - dd*8, 0, 8));
- edgeset(cubeedge(ch[i], d, 1, 1), z, clamp(e[1+rd][1+cd] - dd*8, 0, 8));
- }
- }
-
- validatec(ch);
- if(fullcheck) loopi(8) if(!isvalidcube(ch[i])) // not so good...
- {
- emptyfaces(ch[i]);
- perfect=false;
- }
- if(brighten) loopi(8) if(!isempty(ch[i])) brightencube(ch[i]);
- return perfect;
+ loopi(8)
+ {
+ loopl(6) c.children[i].texture[l] = c.texture[l];
+ if(brighten && !isempty(c)) brightencube(c.children[i]);
+ }
+ return true;
+ }
+ cube *ch = c.children = newcubes(F_SOLID, c.material);
+ bool perfect = true;
+ ivec v[8];
+ loopi(8)
+ {
+ getcubevector(c, i, v[i]);
+ v[i].mul(2);
+ }
+
+ loopj(6)
+ {
+ int d = dimension(j), z = dimcoord(j);
+ const ivec &v00 = v[octaindex(d, 0, 0, z)],
+ &v10 = v[octaindex(d, 1, 0, z)],
+ &v01 = v[octaindex(d, 0, 1, z)],
+ &v11 = v[octaindex(d, 1, 1, z)];
+ int e[3][3];
+ // corners
+ e[0][0] = v00[d];
+ e[0][2] = v01[d];
+ e[2][0] = v10[d];
+ e[2][2] = v11[d];
+ // edges
+ e[0][1] = midedge(v00, v01, C[d], d, perfect);
+ e[1][0] = midedge(v00, v10, R[d], d, perfect);
+ e[1][2] = midedge(v11, v01, R[d], d, perfect);
+ e[2][1] = midedge(v11, v10, C[d], d, perfect);
+ // center
+ bool p1 = perfect, p2 = perfect;
+ int c1 = midedge(v00, v11, R[d], d, p1);
+ int c2 = midedge(v01, v10, R[d], d, p2);
+ if(z ? c1 > c2 : c1 < c2)
+ {
+ e[1][1] = c1;
+ perfect = p1 && (c1 == c2 || crosscenter(v00, v11, C[d], R[d]));
+ }
+ else
+ {
+ e[1][1] = c2;
+ perfect = p2 && (c1 == c2 || crosscenter(v01, v10, C[d], R[d]));
+ }
+
+ loopi(8)
+ {
+ ch[i].texture[j] = c.texture[j];
+ int rd = (i>>R[d])&1, cd = (i>>C[d])&1, dd = (i>>D[d])&1;
+ edgeset(cubeedge(ch[i], d, 0, 0), z, clamp(e[rd][cd] - dd*8, 0, 8));
+ edgeset(cubeedge(ch[i], d, 1, 0), z, clamp(e[1+rd][cd] - dd*8, 0, 8));
+ edgeset(cubeedge(ch[i], d, 0, 1), z, clamp(e[rd][1+cd] - dd*8, 0, 8));
+ edgeset(cubeedge(ch[i], d, 1, 1), z, clamp(e[1+rd][1+cd] - dd*8, 0, 8));
+ }
+ }
+
+ validatec(ch);
+ if(fullcheck) loopi(8) if(!isvalidcube(ch[i])) // not so good...
+ {
+ emptyfaces(ch[i]);
+ perfect=false;
+ }
+ if(brighten) loopi(8) if(!isempty(ch[i])) brightencube(ch[i]);
+ return perfect;
}
bool crushededge(uchar e, int dc) { return dc ? e==0 : e==0x88; }
int visibleorient(const cube &c, int orient)
{
- loopi(2)
- {
- int a = faceedgesidx[orient][i*2 + 0];
- int b = faceedgesidx[orient][i*2 + 1];
- loopj(2)
- {
- if(crushededge(c.edges[a],j) &&
- crushededge(c.edges[b],j) &&
- touchingface(c, orient)) return ((a>>2)<<1) + j;
- }
- }
- return orient;
+ loopi(2)
+ {
+ int a = faceedgesidx[orient][i*2 + 0];
+ int b = faceedgesidx[orient][i*2 + 1];
+ loopj(2)
+ {
+ if(crushededge(c.edges[a],j) &&
+ crushededge(c.edges[b],j) &&
+ touchingface(c, orient)) return ((a>>2)<<1) + j;
+ }
+ }
+ return orient;
}
VAR(mipvis, 0, 0, 1);
@@ -462,935 +462,935 @@ static int remipprogress = 0, remiptotal = 0;
bool remip(cube &c, const ivec &co, int size)
{
- cube *ch = c.children;
- if(!ch)
- {
- if(size<<1 <= 0x1000) return true;
- subdividecube(c);
- ch = c.children;
- }
- else if((remipprogress++&0xFFF)==1) renderprogress(float(remipprogress)/remiptotal, "remipping...");
-
- bool perfect = true;
- loopi(8)
- {
- ivec o(i, co, size);
- if(!remip(ch[i], o, size>>1)) perfect = false;
- }
-
- solidfaces(c); // so texmip is more consistent
- loopj(6)
- c.texture[j] = getmippedtexture(c, j); // parents get child texs regardless
-
- if(!perfect) return false;
- if(size<<1 > 0x1000) return false;
-
- ushort mat = MAT_AIR;
- loopi(8)
- {
- mat = ch[i].material;
- if((mat&MATF_CLIP) == MAT_NOCLIP || mat&MAT_ALPHA)
- {
- if(i > 0) return false;
- while(++i < 8) if(ch[i].material != mat) return false;
- break;
- }
- else if(!isentirelysolid(ch[i]))
- {
- while(++i < 8)
- {
- int omat = ch[i].material;
- if(isentirelysolid(ch[i]) ? (omat&MATF_CLIP) == MAT_NOCLIP || omat&MAT_ALPHA : mat != omat) return false;
- }
- break;
- }
- }
-
- cube n = c;
- n.ext = NULL;
- forcemip(n);
- n.children = NULL;
- if(!subdividecube(n, false, false))
- { freeocta(n.children); return false; }
-
- cube *nh = n.children;
- uchar vis[6] = {0, 0, 0, 0, 0, 0};
- loopi(8)
- {
- if(ch[i].faces[0] != nh[i].faces[0] ||
- ch[i].faces[1] != nh[i].faces[1] ||
- ch[i].faces[2] != nh[i].faces[2])
- { freeocta(nh); return false; }
-
- if(isempty(ch[i]) && isempty(nh[i])) continue;
-
- ivec o(i, co, size);
- loop(orient, 6)
- if(visibleface(ch[i], orient, o, size, MAT_AIR, (mat&MAT_ALPHA)^MAT_ALPHA, MAT_ALPHA))
- {
- if(ch[i].texture[orient] != n.texture[orient]) { freeocta(nh); return false; }
- vis[orient] |= 1<<i;
- }
- }
- if(mipvis) loop(orient, 6)
- {
- int mask = 0;
- loop(x, 2) loop(y, 2) mask |= 1<<octaindex(dimension(orient), x, y, dimcoord(orient));
- if(vis[orient]&mask && (vis[orient]&mask)!=mask) { freeocta(nh); return false; }
- }
-
- freeocta(nh);
- discardchildren(c);
- loopi(3) c.faces[i] = n.faces[i];
- c.material = mat;
- loopi(6) if(vis[i]) c.visible |= 1<<i;
- if(c.visible) c.visible |= 0x40;
- brightencube(c);
- return true;
+ cube *ch = c.children;
+ if(!ch)
+ {
+ if(size<<1 <= 0x1000) return true;
+ subdividecube(c);
+ ch = c.children;
+ }
+ else if((remipprogress++&0xFFF)==1) renderprogress(float(remipprogress)/remiptotal, "remipping...");
+
+ bool perfect = true;
+ loopi(8)
+ {
+ ivec o(i, co, size);
+ if(!remip(ch[i], o, size>>1)) perfect = false;
+ }
+
+ solidfaces(c); // so texmip is more consistent
+ loopj(6)
+ c.texture[j] = getmippedtexture(c, j); // parents get child texs regardless
+
+ if(!perfect) return false;
+ if(size<<1 > 0x1000) return false;
+
+ ushort mat = MAT_AIR;
+ loopi(8)
+ {
+ mat = ch[i].material;
+ if((mat&MATF_CLIP) == MAT_NOCLIP || mat&MAT_ALPHA)
+ {
+ if(i > 0) return false;
+ while(++i < 8) if(ch[i].material != mat) return false;
+ break;
+ }
+ else if(!isentirelysolid(ch[i]))
+ {
+ while(++i < 8)
+ {
+ int omat = ch[i].material;
+ if(isentirelysolid(ch[i]) ? (omat&MATF_CLIP) == MAT_NOCLIP || omat&MAT_ALPHA : mat != omat) return false;
+ }
+ break;
+ }
+ }
+
+ cube n = c;
+ n.ext = NULL;
+ forcemip(n);
+ n.children = NULL;
+ if(!subdividecube(n, false, false))
+ { freeocta(n.children); return false; }
+
+ cube *nh = n.children;
+ uchar vis[6] = {0, 0, 0, 0, 0, 0};
+ loopi(8)
+ {
+ if(ch[i].faces[0] != nh[i].faces[0] ||
+ ch[i].faces[1] != nh[i].faces[1] ||
+ ch[i].faces[2] != nh[i].faces[2])
+ { freeocta(nh); return false; }
+
+ if(isempty(ch[i]) && isempty(nh[i])) continue;
+
+ ivec o(i, co, size);
+ loop(orient, 6)
+ if(visibleface(ch[i], orient, o, size, MAT_AIR, (mat&MAT_ALPHA)^MAT_ALPHA, MAT_ALPHA))
+ {
+ if(ch[i].texture[orient] != n.texture[orient]) { freeocta(nh); return false; }
+ vis[orient] |= 1<<i;
+ }
+ }
+ if(mipvis) loop(orient, 6)
+ {
+ int mask = 0;
+ loop(x, 2) loop(y, 2) mask |= 1<<octaindex(dimension(orient), x, y, dimcoord(orient));
+ if(vis[orient]&mask && (vis[orient]&mask)!=mask) { freeocta(nh); return false; }
+ }
+
+ freeocta(nh);
+ discardchildren(c);
+ loopi(3) c.faces[i] = n.faces[i];
+ c.material = mat;
+ loopi(6) if(vis[i]) c.visible |= 1<<i;
+ if(c.visible) c.visible |= 0x40;
+ brightencube(c);
+ return true;
}
void mpremip(bool local)
{
- extern selinfo sel;
- if(local) game::edittrigger(sel, EDIT_REMIP);
- remipprogress = 1;
- remiptotal = allocnodes;
- loopi(8)
- {
- ivec o(i, ivec(0, 0, 0), worldsize>>1);
- remip(worldroot[i], o, worldsize>>2);
- }
- calcmerges();
- if(!local) allchanged();
+ extern selinfo sel;
+ if(local) game::edittrigger(sel, EDIT_REMIP);
+ remipprogress = 1;
+ remiptotal = allocnodes;
+ loopi(8)
+ {
+ ivec o(i, ivec(0, 0, 0), worldsize>>1);
+ remip(worldroot[i], o, worldsize>>2);
+ }
+ calcmerges();
+ if(!local) allchanged();
}
void remip_()
{
- mpremip(true);
- allchanged();
+ mpremip(true);
+ allchanged();
}
COMMANDN(remip, remip_, "");
static inline int edgeval(cube &c, const ivec &p, int dim, int coord)
{
- return edgeget(cubeedge(c, dim, p[R[dim]]>>3, p[C[dim]]>>3), coord);
+ return edgeget(cubeedge(c, dim, p[R[dim]]>>3, p[C[dim]]>>3), coord);
}
void genvertp(cube &c, ivec &p1, ivec &p2, ivec &p3, plane &pl, bool solid = false)
{
- int dim = 0;
- if(p1.y==p2.y && p2.y==p3.y) dim = 1;
- else if(p1.z==p2.z && p2.z==p3.z) dim = 2;
+ int dim = 0;
+ if(p1.y==p2.y && p2.y==p3.y) dim = 1;
+ else if(p1.z==p2.z && p2.z==p3.z) dim = 2;
- int coord = p1[dim];
- ivec v1(p1), v2(p2), v3(p3);
- v1[dim] = solid ? coord*8 : edgeval(c, p1, dim, coord);
- v2[dim] = solid ? coord*8 : edgeval(c, p2, dim, coord);
- v3[dim] = solid ? coord*8 : edgeval(c, p3, dim, coord);
+ int coord = p1[dim];
+ ivec v1(p1), v2(p2), v3(p3);
+ v1[dim] = solid ? coord*8 : edgeval(c, p1, dim, coord);
+ v2[dim] = solid ? coord*8 : edgeval(c, p2, dim, coord);
+ v3[dim] = solid ? coord*8 : edgeval(c, p3, dim, coord);
- pl.toplane(vec(v1), vec(v2), vec(v3));
+ pl.toplane(vec(v1), vec(v2), vec(v3));
}
static bool threeplaneintersect(plane &pl1, plane &pl2, plane &pl3, vec &dest)
{
- vec &t1 = dest, t2, t3, t4;
- t1.cross(pl1, pl2); t4 = t1; t1.mul(pl3.offset);
- t2.cross(pl3, pl1); t2.mul(pl2.offset);
- t3.cross(pl2, pl3); t3.mul(pl1.offset);
- t1.add(t2);
- t1.add(t3);
- t1.mul(-1);
- float d = t4.dot(pl3);
- if(d==0) return false;
- t1.div(d);
- return true;
+ vec &t1 = dest, t2, t3, t4;
+ t1.cross(pl1, pl2); t4 = t1; t1.mul(pl3.offset);
+ t2.cross(pl3, pl1); t2.mul(pl2.offset);
+ t3.cross(pl2, pl3); t3.mul(pl1.offset);
+ t1.add(t2);
+ t1.add(t3);
+ t1.mul(-1);
+ float d = t4.dot(pl3);
+ if(d==0) return false;
+ t1.div(d);
+ return true;
}
static void genedgespanvert(ivec &p, cube &c, vec &v)
{
- ivec p1(8-p.x, p.y, p.z);
- ivec p2(p.x, 8-p.y, p.z);
- ivec p3(p.x, p.y, 8-p.z);
+ ivec p1(8-p.x, p.y, p.z);
+ ivec p2(p.x, 8-p.y, p.z);
+ ivec p3(p.x, p.y, 8-p.z);
- plane plane1, plane2, plane3;
- genvertp(c, p, p1, p2, plane1);
- genvertp(c, p, p2, p3, plane2);
- genvertp(c, p, p3, p1, plane3);
- if(plane1==plane2) genvertp(c, p, p1, p2, plane1, true);
- if(plane1==plane3) genvertp(c, p, p1, p2, plane1, true);
- if(plane2==plane3) genvertp(c, p, p2, p3, plane2, true);
+ plane plane1, plane2, plane3;
+ genvertp(c, p, p1, p2, plane1);
+ genvertp(c, p, p2, p3, plane2);
+ genvertp(c, p, p3, p1, plane3);
+ if(plane1==plane2) genvertp(c, p, p1, p2, plane1, true);
+ if(plane1==plane3) genvertp(c, p, p1, p2, plane1, true);
+ if(plane2==plane3) genvertp(c, p, p2, p3, plane2, true);
- ASSERT(threeplaneintersect(plane1, plane2, plane3, v));
- //ASSERT(v.x>=0 && v.x<=8);
- //ASSERT(v.y>=0 && v.y<=8);
- //ASSERT(v.z>=0 && v.z<=8);
- v.x = max(0.0f, min(8.0f, v.x));
- v.y = max(0.0f, min(8.0f, v.y));
- v.z = max(0.0f, min(8.0f, v.z));
+ ASSERT(threeplaneintersect(plane1, plane2, plane3, v));
+ //ASSERT(v.x>=0 && v.x<=8);
+ //ASSERT(v.y>=0 && v.y<=8);
+ //ASSERT(v.z>=0 && v.z<=8);
+ v.x = max(0.0f, min(8.0f, v.x));
+ v.y = max(0.0f, min(8.0f, v.y));
+ v.z = max(0.0f, min(8.0f, v.z));
}
void edgespan2vectorcube(cube &c)
{
- if(isentirelysolid(c) || isempty(c)) return;
- cube o = c;
- loop(x, 2) loop(y, 2) loop(z, 2)
- {
- ivec p(8*x, 8*y, 8*z);
- vec v;
- genedgespanvert(p, o, v);
+ if(isentirelysolid(c) || isempty(c)) return;
+ cube o = c;
+ loop(x, 2) loop(y, 2) loop(z, 2)
+ {
+ ivec p(8*x, 8*y, 8*z);
+ vec v;
+ genedgespanvert(p, o, v);
- edgeset(cubeedge(c, 0, y, z), x, int(v.x+0.49f));
- edgeset(cubeedge(c, 1, z, x), y, int(v.y+0.49f));
- edgeset(cubeedge(c, 2, x, y), z, int(v.z+0.49f));
- }
+ edgeset(cubeedge(c, 0, y, z), x, int(v.x+0.49f));
+ edgeset(cubeedge(c, 1, z, x), y, int(v.y+0.49f));
+ edgeset(cubeedge(c, 2, x, y), z, int(v.z+0.49f));
+ }
}
const ivec cubecoords[8] = // verts of bounding cube
{
#define GENCUBEVERT(n, x, y, z) ivec(x, y, z),
- GENCUBEVERTS(0, 8, 0, 8, 0, 8)
-#undef GENCUBEVERT
+ GENCUBEVERTS(0, 8, 0, 8, 0, 8)
+#undef GENCUBEVERT
};
template<class T>
static inline void gencubevert(const cube &c, int i, T &v)
{
- switch(i)
- {
- default:
+ switch(i)
+ {
+ default:
#define GENCUBEVERT(n, x, y, z) \
- case n: \
- v = T(edgeget(cubeedge(c, 0, y, z), x), \
- edgeget(cubeedge(c, 1, z, x), y), \
- edgeget(cubeedge(c, 2, x, y), z)); \
- break;
- GENCUBEVERTS(0, 1, 0, 1, 0, 1)
+ case n: \
+ v = T(edgeget(cubeedge(c, 0, y, z), x), \
+ edgeget(cubeedge(c, 1, z, x), y), \
+ edgeget(cubeedge(c, 2, x, y), z)); \
+ break;
+ GENCUBEVERTS(0, 1, 0, 1, 0, 1)
#undef GENCUBEVERT
- }
+ }
}
void genfaceverts(const cube &c, int orient, ivec v[4])
{
- switch(orient)
- {
- default:
+ switch(orient)
+ {
+ default:
#define GENFACEORIENT(o, v0, v1, v2, v3) \
- case o: v0 v1 v2 v3 break;
+ case o: v0 v1 v2 v3 break;
#define GENFACEVERT(o, n, x,y,z, xv,yv,zv) \
- v[n] = ivec(edgeget(cubeedge(c, 0, y, z), x), \
- edgeget(cubeedge(c, 1, z, x), y), \
- edgeget(cubeedge(c, 2, x, y), z));
- GENFACEVERTS(0, 1, 0, 1, 0, 1, , , , , , )
- #undef GENFACEORIENT
- #undef GENFACEVERT
- }
+ v[n] = ivec(edgeget(cubeedge(c, 0, y, z), x), \
+ edgeget(cubeedge(c, 1, z, x), y), \
+ edgeget(cubeedge(c, 2, x, y), z));
+ GENFACEVERTS(0, 1, 0, 1, 0, 1, , , , , , )
+ #undef GENFACEORIENT
+ #undef GENFACEVERT
+ }
}
const ivec facecoords[6][4] =
{
#define GENFACEORIENT(o, v0, v1, v2, v3) \
- { v0, v1, v2, v3 },
+ { v0, v1, v2, v3 },
#define GENFACEVERT(o, n, x,y,z, xv,yv,zv) \
- ivec(x,y,z)
- GENFACEVERTS(0, 8, 0, 8, 0, 8, , , , , , )
+ ivec(x,y,z)
+ GENFACEVERTS(0, 8, 0, 8, 0, 8, , , , , , )
#undef GENFACEORIENT
#undef GENFACEVERT
};
const uchar fv[6][4] = // indexes for cubecoords, per each vert of a face orientation
{
- { 2, 1, 6, 5 },
- { 3, 4, 7, 0 },
- { 4, 5, 6, 7 },
- { 1, 2, 3, 0 },
- { 6, 1, 0, 7 },
- { 5, 4, 3, 2 },
+ { 2, 1, 6, 5 },
+ { 3, 4, 7, 0 },
+ { 4, 5, 6, 7 },
+ { 1, 2, 3, 0 },
+ { 6, 1, 0, 7 },
+ { 5, 4, 3, 2 },
};
const uchar fvmasks[64] = // mask of verts used given a mask of visible face orientations
{
- 0x00, 0x66, 0x99, 0xFF, 0xF0, 0xF6, 0xF9, 0xFF,
- 0x0F, 0x6F, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xC3, 0xE7, 0xDB, 0xFF, 0xF3, 0xF7, 0xFB, 0xFF,
- 0xCF, 0xEF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x3C, 0x7E, 0xBD, 0xFF, 0xFC, 0xFE, 0xFD, 0xFF,
- 0x3F, 0x7F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x66, 0x99, 0xFF, 0xF0, 0xF6, 0xF9, 0xFF,
+ 0x0F, 0x6F, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC3, 0xE7, 0xDB, 0xFF, 0xF3, 0xF7, 0xFB, 0xFF,
+ 0xCF, 0xEF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x3C, 0x7E, 0xBD, 0xFF, 0xFC, 0xFE, 0xFD, 0xFF,
+ 0x3F, 0x7F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};
const uchar faceedgesidx[6][4] = // ordered edges surrounding each orient
{//0..1 = row edges, 2..3 = column edges
- { 4, 5, 8, 10 },
- { 6, 7, 9, 11 },
- { 8, 9, 0, 2 },
- { 10, 11, 1, 3 },
- { 0, 1, 4, 6 },
- { 2, 3, 5, 7 },
+ { 4, 5, 8, 10 },
+ { 6, 7, 9, 11 },
+ { 8, 9, 0, 2 },
+ { 10, 11, 1, 3 },
+ { 0, 1, 4, 6 },
+ { 2, 3, 5, 7 },
};
bool flataxisface(const cube &c, int orient)
{
- uint face = c.faces[dimension(orient)];
- if(dimcoord(orient)) face >>= 4;
- return (face&0x0F0F0F0F) == 0x01010101*(face&0x0F);
+ uint face = c.faces[dimension(orient)];
+ if(dimcoord(orient)) face >>= 4;
+ return (face&0x0F0F0F0F) == 0x01010101*(face&0x0F);
}
bool collideface(const cube &c, int orient)
{
- if(flataxisface(c, orient))
- {
- uchar r1 = c.edges[faceedgesidx[orient][0]], r2 = c.edges[faceedgesidx[orient][1]];
- if(uchar((r1>>4)|(r2&0xF0)) == uchar((r1&0x0F)|(r2<<4))) return false;
- uchar c1 = c.edges[faceedgesidx[orient][2]], c2 = c.edges[faceedgesidx[orient][3]];
- if(uchar((c1>>4)|(c2&0xF0)) == uchar((c1&0x0F)|(c2<<4))) return false;
- }
- return true;
+ if(flataxisface(c, orient))
+ {
+ uchar r1 = c.edges[faceedgesidx[orient][0]], r2 = c.edges[faceedgesidx[orient][1]];
+ if(uchar((r1>>4)|(r2&0xF0)) == uchar((r1&0x0F)|(r2<<4))) return false;
+ uchar c1 = c.edges[faceedgesidx[orient][2]], c2 = c.edges[faceedgesidx[orient][3]];
+ if(uchar((c1>>4)|(c2&0xF0)) == uchar((c1&0x0F)|(c2<<4))) return false;
+ }
+ return true;
}
bool touchingface(const cube &c, int orient)
{
- uint face = c.faces[dimension(orient)];
- return dimcoord(orient) ? (face&0xF0F0F0F0)==0x80808080 : (face&0x0F0F0F0F)==0;
+ uint face = c.faces[dimension(orient)];
+ return dimcoord(orient) ? (face&0xF0F0F0F0)==0x80808080 : (face&0x0F0F0F0F)==0;
}
bool notouchingface(const cube &c, int orient)
{
- uint face = c.faces[dimension(orient)];
- return dimcoord(orient) ? (face&0x80808080)==0 : ((0x88888888-face)&0x08080808) == 0;
-}
+ uint face = c.faces[dimension(orient)];
+ return dimcoord(orient) ? (face&0x80808080)==0 : ((0x88888888-face)&0x08080808) == 0;
+}
int faceconvexity(const ivec v[4])
{
- ivec n;
- n.cross(ivec(v[1]).sub(v[0]), ivec(v[2]).sub(v[0]));
- return ivec(v[0]).sub(v[3]).dot(n);
- // 1 if convex, -1 if concave, 0 if flat
+ ivec n;
+ n.cross(ivec(v[1]).sub(v[0]), ivec(v[2]).sub(v[0]));
+ return ivec(v[0]).sub(v[3]).dot(n);
+ // 1 if convex, -1 if concave, 0 if flat
}
int faceconvexity(const vertinfo *verts, int numverts, int size)
{
- if(numverts < 4) return 0;
- ivec v0 = verts[0].getxyz(),
- e1 = verts[1].getxyz().sub(v0),
- e2 = verts[2].getxyz().sub(v0),
- n;
- if(size >= (8<<5))
- {
- if(size >= (8<<10)) n.cross(e1.shr(10), e2.shr(10));
- else n.cross(e1, e2).shr(10);
- }
- else n.cross(e1, e2);
- return verts[3].getxyz().sub(v0).dot(n);
+ if(numverts < 4) return 0;
+ ivec v0 = verts[0].getxyz(),
+ e1 = verts[1].getxyz().sub(v0),
+ e2 = verts[2].getxyz().sub(v0),
+ n;
+ if(size >= (8<<5))
+ {
+ if(size >= (8<<10)) n.cross(e1.shr(10), e2.shr(10));
+ else n.cross(e1, e2).shr(10);
+ }
+ else n.cross(e1, e2);
+ return verts[3].getxyz().sub(v0).dot(n);
}
int faceconvexity(const ivec v[4], int &vis)
{
- ivec e1, e2, e3, n;
- n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0]));
- int convex = (e3 = v[0]).sub(v[3]).dot(n);
- if(!convex)
- {
- if(ivec().cross(e3, e2).iszero()) { if(!n.iszero()) vis = 1; }
- else if(n.iszero()) { vis = 2; }
- return 0;
- }
- return convex;
-}
+ ivec e1, e2, e3, n;
+ n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0]));
+ int convex = (e3 = v[0]).sub(v[3]).dot(n);
+ if(!convex)
+ {
+ if(ivec().cross(e3, e2).iszero()) { if(!n.iszero()) vis = 1; }
+ else if(n.iszero()) { vis = 2; }
+ return 0;
+ }
+ return convex;
+}
int faceconvexity(const cube &c, int orient)
{
- if(flataxisface(c, orient)) return 0;
- ivec v[4];
- genfaceverts(c, orient, v);
- return faceconvexity(v);
+ if(flataxisface(c, orient)) return 0;
+ ivec v[4];
+ genfaceverts(c, orient, v);
+ return faceconvexity(v);
}
int faceorder(const cube &c, int orient) // gets above 'fv' so that each face is convex
{
- return faceconvexity(c, orient)<0 ? 1 : 0;
+ return faceconvexity(c, orient)<0 ? 1 : 0;
}
static inline void faceedges(const cube &c, int orient, uchar edges[4])
{
- loopk(4) edges[k] = c.edges[faceedgesidx[orient][k]];
+ loopk(4) edges[k] = c.edges[faceedgesidx[orient][k]];
}
uint faceedges(const cube &c, int orient)
{
- union { uchar edges[4]; uint face; } u;
- faceedges(c, orient, u.edges);
- return u.face;
+ union { uchar edges[4]; uint face; } u;
+ faceedges(c, orient, u.edges);
+ return u.face;
}
static inline int genfacevecs(const cube &cu, int orient, const ivec &pos, int size, bool solid, ivec2 *fvecs, const ivec *v = NULL)
{
- int i = 0;
- if(solid)
- {
- switch(orient)
- {
- #define GENFACEORIENT(orient, v0, v1, v2, v3) \
- case orient: \
- { \
- if(dimcoord(orient)) { v0 v1 v2 v3 } else { v3 v2 v1 v0 } \
- break; \
- }
- #define GENFACEVERT(orient, vert, xv,yv,zv, x,y,z) \
- { ivec2 &f = fvecs[i]; x ((xv)<<3); y ((yv)<<3); z ((zv)<<3); i++; }
- GENFACEVERTS(pos.x, pos.x+size, pos.y, pos.y+size, pos.z, pos.z+size, f.x = , f.x = , f.y = , f.y = , (void), (void))
- #undef GENFACEVERT
- }
- return 4;
- }
- ivec buf[4];
- if(!v) { genfaceverts(cu, orient, buf); v = buf; }
- ivec2 prev(INT_MAX, INT_MAX);
- switch(orient)
- {
- #define GENFACEVERT(orient, vert, sx,sy,sz, dx,dy,dz) \
- { \
- const ivec &e = v[vert]; \
- ivec ef; \
- ef.dx = e.sx; ef.dy = e.sy; ef.dz = e.sz; \
- if(ef.z == dimcoord(orient)*8) \
- { \
- ivec2 &f = fvecs[i]; \
- ivec pf; \
- pf.dx = pos.sx; pf.dy = pos.sy; pf.dz = pos.sz; \
- f = ivec2(ef.x*size + (pf.x<<3), ef.y*size + (pf.y<<3)); \
- if(f != prev) { prev = f; i++; } \
- } \
- }
- GENFACEVERTS(x, x, y, y, z, z, x, x, y, y, z, z)
- #undef GENFACEORIENT
- #undef GENFACEVERT
- }
- if(fvecs[0] == prev) i--;
- return i;
+ int i = 0;
+ if(solid)
+ {
+ switch(orient)
+ {
+ #define GENFACEORIENT(orient, v0, v1, v2, v3) \
+ case orient: \
+ { \
+ if(dimcoord(orient)) { v0 v1 v2 v3 } else { v3 v2 v1 v0 } \
+ break; \
+ }
+ #define GENFACEVERT(orient, vert, xv,yv,zv, x,y,z) \
+ { ivec2 &f = fvecs[i]; x ((xv)<<3); y ((yv)<<3); z ((zv)<<3); i++; }
+ GENFACEVERTS(pos.x, pos.x+size, pos.y, pos.y+size, pos.z, pos.z+size, f.x = , f.x = , f.y = , f.y = , (void), (void))
+ #undef GENFACEVERT
+ }
+ return 4;
+ }
+ ivec buf[4];
+ if(!v) { genfaceverts(cu, orient, buf); v = buf; }
+ ivec2 prev(INT_MAX, INT_MAX);
+ switch(orient)
+ {
+ #define GENFACEVERT(orient, vert, sx,sy,sz, dx,dy,dz) \
+ { \
+ const ivec &e = v[vert]; \
+ ivec ef; \
+ ef.dx = e.sx; ef.dy = e.sy; ef.dz = e.sz; \
+ if(ef.z == dimcoord(orient)*8) \
+ { \
+ ivec2 &f = fvecs[i]; \
+ ivec pf; \
+ pf.dx = pos.sx; pf.dy = pos.sy; pf.dz = pos.sz; \
+ f = ivec2(ef.x*size + (pf.x<<3), ef.y*size + (pf.y<<3)); \
+ if(f != prev) { prev = f; i++; } \
+ } \
+ }
+ GENFACEVERTS(x, x, y, y, z, z, x, x, y, y, z, z)
+ #undef GENFACEORIENT
+ #undef GENFACEVERT
+ }
+ if(fvecs[0] == prev) i--;
+ return i;
}
static inline int clipfacevecy(const ivec2 &o, const ivec2 &dir, int cx, int cy, int size, ivec2 &r)
{
- if(dir.x >= 0)
- {
- if(cx <= o.x || cx >= o.x+dir.x) return 0;
- }
- else if(cx <= o.x+dir.x || cx >= o.x) return 0;
+ if(dir.x >= 0)
+ {
+ if(cx <= o.x || cx >= o.x+dir.x) return 0;
+ }
+ else if(cx <= o.x+dir.x || cx >= o.x) return 0;
- int t = (o.y-cy) + (cx-o.x)*dir.y/dir.x;
- if(t <= 0 || t >= size) return 0;
+ int t = (o.y-cy) + (cx-o.x)*dir.y/dir.x;
+ if(t <= 0 || t >= size) return 0;
- r.x = cx;
- r.y = cy + t;
- return 1;
+ r.x = cx;
+ r.y = cy + t;
+ return 1;
}
static inline int clipfacevecx(const ivec2 &o, const ivec2 &dir, int cx, int cy, int size, ivec2 &r)
{
- if(dir.y >= 0)
- {
- if(cy <= o.y || cy >= o.y+dir.y) return 0;
- }
- else if(cy <= o.y+dir.y || cy >= o.y) return 0;
+ if(dir.y >= 0)
+ {
+ if(cy <= o.y || cy >= o.y+dir.y) return 0;
+ }
+ else if(cy <= o.y+dir.y || cy >= o.y) return 0;
- int t = (o.x-cx) + (cy-o.y)*dir.x/dir.y;
- if(t <= 0 || t >= size) return 0;
+ int t = (o.x-cx) + (cy-o.y)*dir.x/dir.y;
+ if(t <= 0 || t >= size) return 0;
- r.x = cx + t;
- r.y = cy;
- return 1;
+ r.x = cx + t;
+ r.y = cy;
+ return 1;
}
static inline int clipfacevec(const ivec2 &o, const ivec2 &dir, int cx, int cy, int size, ivec2 *rvecs)
{
- int r = 0;
+ int r = 0;
- if(o.x >= cx && o.x <= cx+size &&
- o.y >= cy && o.y <= cy+size &&
- ((o.x != cx && o.x != cx+size) || (o.y != cy && o.y != cy+size)))
- {
- rvecs[0].x = o.x;
- rvecs[0].y = o.y;
- r++;
- }
+ if(o.x >= cx && o.x <= cx+size &&
+ o.y >= cy && o.y <= cy+size &&
+ ((o.x != cx && o.x != cx+size) || (o.y != cy && o.y != cy+size)))
+ {
+ rvecs[0].x = o.x;
+ rvecs[0].y = o.y;
+ r++;
+ }
- r += clipfacevecx(o, dir, cx, cy, size, rvecs[r]);
- r += clipfacevecx(o, dir, cx, cy+size, size, rvecs[r]);
- r += clipfacevecy(o, dir, cx, cy, size, rvecs[r]);
- r += clipfacevecy(o, dir, cx+size, cy, size, rvecs[r]);
+ r += clipfacevecx(o, dir, cx, cy, size, rvecs[r]);
+ r += clipfacevecx(o, dir, cx, cy+size, size, rvecs[r]);
+ r += clipfacevecy(o, dir, cx, cy, size, rvecs[r]);
+ r += clipfacevecy(o, dir, cx+size, cy, size, rvecs[r]);
- ASSERT(r <= 2);
- return r;
+ ASSERT(r <= 2);
+ return r;
}
static inline bool insideface(const ivec2 *p, int nump, const ivec2 *o, int numo)
{
- int bounds = 0;
- ivec2 prev = o[numo-1];
- loopi(numo)
- {
- const ivec2 &cur = o[i];
- ivec2 dir(cur.x-prev.x, cur.y-prev.y);
- int offset = dir.x*prev.y - dir.y*prev.x;
- loopj(nump) if(dir.x*p[j].y - dir.y*p[j].x > offset) return false;
- bounds++;
- prev = cur;
- }
- return bounds>=3;
+ int bounds = 0;
+ ivec2 prev = o[numo-1];
+ loopi(numo)
+ {
+ const ivec2 &cur = o[i];
+ ivec2 dir(cur.x-prev.x, cur.y-prev.y);
+ int offset = dir.x*prev.y - dir.y*prev.x;
+ loopj(nump) if(dir.x*p[j].y - dir.y*p[j].x > offset) return false;
+ bounds++;
+ prev = cur;
+ }
+ return bounds>=3;
}
static inline int clipfacevecs(const ivec2 *o, int numo, int cx, int cy, int size, ivec2 *rvecs)
{
- cx <<= 3;
- cy <<= 3;
- size <<= 3;
+ cx <<= 3;
+ cy <<= 3;
+ size <<= 3;
- int r = 0;
- ivec2 prev = o[numo-1];
- loopi(numo)
- {
- const ivec2 &cur = o[i];
- r += clipfacevec(prev, ivec2(cur.x-prev.x, cur.y-prev.y), cx, cy, size, &rvecs[r]);
- prev = cur;
- }
- ivec2 corner[4] = {ivec2(cx, cy), ivec2(cx+size, cy), ivec2(cx+size, cy+size), ivec2(cx, cy+size)};
- loopi(4) if(insideface(&corner[i], 1, o, numo)) rvecs[r++] = corner[i];
- ASSERT(r <= 8);
- return r;
+ int r = 0;
+ ivec2 prev = o[numo-1];
+ loopi(numo)
+ {
+ const ivec2 &cur = o[i];
+ r += clipfacevec(prev, ivec2(cur.x-prev.x, cur.y-prev.y), cx, cy, size, &rvecs[r]);
+ prev = cur;
+ }
+ ivec2 corner[4] = {ivec2(cx, cy), ivec2(cx+size, cy), ivec2(cx+size, cy+size), ivec2(cx, cy+size)};
+ loopi(4) if(insideface(&corner[i], 1, o, numo)) rvecs[r++] = corner[i];
+ ASSERT(r <= 8);
+ return r;
}
bool collapsedface(const cube &c, int orient)
{
- int e0 = c.edges[faceedgesidx[orient][0]], e1 = c.edges[faceedgesidx[orient][1]],
- e2 = c.edges[faceedgesidx[orient][2]], e3 = c.edges[faceedgesidx[orient][3]],
- face = dimension(orient)*4,
- f0 = c.edges[face+0], f1 = c.edges[face+1],
- f2 = c.edges[face+2], f3 = c.edges[face+3];
- if(dimcoord(orient)) { f0 >>= 4; f1 >>= 4; f2 >>= 4; f3 >>= 4; }
- else { f0 &= 0xF; f1 &= 0xF; f2 &= 0xF; f3 &= 0xF; }
- ivec v0(e0&0xF, e2&0xF, f0),
- v1(e0>>4, e3&0xF, f1),
- v2(e1>>4, e3>>4, f3),
- v3(e1&0xF, e2>>4, f2);
- return ivec().cross(v1.sub(v0), v2.sub(v0)).iszero() &&
- ivec().cross(v2, v3.sub(v0)).iszero();
+ int e0 = c.edges[faceedgesidx[orient][0]], e1 = c.edges[faceedgesidx[orient][1]],
+ e2 = c.edges[faceedgesidx[orient][2]], e3 = c.edges[faceedgesidx[orient][3]],
+ face = dimension(orient)*4,
+ f0 = c.edges[face+0], f1 = c.edges[face+1],
+ f2 = c.edges[face+2], f3 = c.edges[face+3];
+ if(dimcoord(orient)) { f0 >>= 4; f1 >>= 4; f2 >>= 4; f3 >>= 4; }
+ else { f0 &= 0xF; f1 &= 0xF; f2 &= 0xF; f3 &= 0xF; }
+ ivec v0(e0&0xF, e2&0xF, f0),
+ v1(e0>>4, e3&0xF, f1),
+ v2(e1>>4, e3>>4, f3),
+ v3(e1&0xF, e2>>4, f2);
+ return ivec().cross(v1.sub(v0), v2.sub(v0)).iszero() &&
+ ivec().cross(v2, v3.sub(v0)).iszero();
}
static inline bool occludesface(const cube &c, int orient, const ivec &o, int size, const ivec &vo, int vsize, ushort vmat, ushort nmat, ushort matmask, const ivec2 *vf, int numv)
{
- int dim = dimension(orient);
- if(!c.children)
- {
- if(nmat != MAT_AIR && (c.material&matmask) == nmat)
- {
- ivec2 nf[8];
- return clipfacevecs(vf, numv, o[C[dim]], o[R[dim]], size, nf) < 3;
- }
- if(isentirelysolid(c)) return true;
- if(vmat != MAT_AIR && ((c.material&matmask) == vmat || (isliquid(vmat) && isclipped(c.material&MATF_VOLUME)))) return true;
- if(touchingface(c, orient) && faceedges(c, orient) == F_SOLID) return true;
- ivec2 cf[8];
- int numc = clipfacevecs(vf, numv, o[C[dim]], o[R[dim]], size, cf);
- if(numc < 3) return true;
- if(isempty(c) || notouchingface(c, orient)) return false;
- ivec2 of[4];
- int numo = genfacevecs(c, orient, o, size, false, of);
- return numo >= 3 && insideface(cf, numc, of, numo);
- }
-
- size >>= 1;
- int coord = dimcoord(orient);
- loopi(8) if(octacoord(dim, i) == coord)
- {
- if(!occludesface(c.children[i], orient, ivec(i, o, size), size, vo, vsize, vmat, nmat, matmask, vf, numv)) return false;
- }
-
- return true;
+ int dim = dimension(orient);
+ if(!c.children)
+ {
+ if(nmat != MAT_AIR && (c.material&matmask) == nmat)
+ {
+ ivec2 nf[8];
+ return clipfacevecs(vf, numv, o[C[dim]], o[R[dim]], size, nf) < 3;
+ }
+ if(isentirelysolid(c)) return true;
+ if(vmat != MAT_AIR && ((c.material&matmask) == vmat || (isliquid(vmat) && isclipped(c.material&MATF_VOLUME)))) return true;
+ if(touchingface(c, orient) && faceedges(c, orient) == F_SOLID) return true;
+ ivec2 cf[8];
+ int numc = clipfacevecs(vf, numv, o[C[dim]], o[R[dim]], size, cf);
+ if(numc < 3) return true;
+ if(isempty(c) || notouchingface(c, orient)) return false;
+ ivec2 of[4];
+ int numo = genfacevecs(c, orient, o, size, false, of);
+ return numo >= 3 && insideface(cf, numc, of, numo);
+ }
+
+ size >>= 1;
+ int coord = dimcoord(orient);
+ loopi(8) if(octacoord(dim, i) == coord)
+ {
+ if(!occludesface(c.children[i], orient, ivec(i, o, size), size, vo, vsize, vmat, nmat, matmask, vf, numv)) return false;
+ }
+
+ return true;
}
bool visibleface(const cube &c, int orient, const ivec &co, int size, ushort mat, ushort nmat, ushort matmask)
{
- if(mat != MAT_AIR)
- {
- if(faceedges(c, orient)==F_SOLID && touchingface(c, orient)) return false;
- }
- else
- {
- if(collapsedface(c, orient)) return false;
- if(!touchingface(c, orient)) return true;
- }
-
- ivec no;
- int nsize;
- const cube &o = neighbourcube(c, orient, co, size, no, nsize);
- if(&o==&c) return false;
-
- int opp = opposite(orient);
- if(nsize > size || (nsize == size && !o.children))
- {
- if(nmat != MAT_AIR && (o.material&matmask) == nmat) return true;
- if(isentirelysolid(o)) return false;
- if(mat != MAT_AIR && ((o.material&matmask) == mat || (isliquid(mat) && (o.material&MATF_VOLUME) == MAT_GLASS))) return false;
- if(isempty(o) || notouchingface(o, opp)) return true;
- if(touchingface(o, opp) && faceedges(o, opp) == F_SOLID) return false;
-
- ivec vo = ivec(co).mask(0xFFF);
- no.mask(0xFFF);
- ivec2 cf[4], of[4];
- int numc = genfacevecs(c, orient, vo, size, mat != MAT_AIR, cf),
- numo = genfacevecs(o, opp, no, nsize, false, of);
- return numo < 3 || !insideface(cf, numc, of, numo);
- }
-
-
- ivec vo = ivec(co).mask(0xFFF);
- no.mask(0xFFF);
- ivec2 cf[4];
- int numc = genfacevecs(c, orient, vo, size, mat != MAT_AIR, cf);
- return !occludesface(o, opp, no, nsize, vo, size, mat, nmat, matmask, cf, numc);
+ if(mat != MAT_AIR)
+ {
+ if(faceedges(c, orient)==F_SOLID && touchingface(c, orient)) return false;
+ }
+ else
+ {
+ if(collapsedface(c, orient)) return false;
+ if(!touchingface(c, orient)) return true;
+ }
+
+ ivec no;
+ int nsize;
+ const cube &o = neighbourcube(c, orient, co, size, no, nsize);
+ if(&o==&c) return false;
+
+ int opp = opposite(orient);
+ if(nsize > size || (nsize == size && !o.children))
+ {
+ if(nmat != MAT_AIR && (o.material&matmask) == nmat) return true;
+ if(isentirelysolid(o)) return false;
+ if(mat != MAT_AIR && ((o.material&matmask) == mat || (isliquid(mat) && (o.material&MATF_VOLUME) == MAT_GLASS))) return false;
+ if(isempty(o) || notouchingface(o, opp)) return true;
+ if(touchingface(o, opp) && faceedges(o, opp) == F_SOLID) return false;
+
+ ivec vo = ivec(co).mask(0xFFF);
+ no.mask(0xFFF);
+ ivec2 cf[4], of[4];
+ int numc = genfacevecs(c, orient, vo, size, mat != MAT_AIR, cf),
+ numo = genfacevecs(o, opp, no, nsize, false, of);
+ return numo < 3 || !insideface(cf, numc, of, numo);
+ }
+
+
+ ivec vo = ivec(co).mask(0xFFF);
+ no.mask(0xFFF);
+ ivec2 cf[4];
+ int numc = genfacevecs(c, orient, vo, size, mat != MAT_AIR, cf);
+ return !occludesface(o, opp, no, nsize, vo, size, mat, nmat, matmask, cf, numc);
}
int classifyface(const cube &c, int orient, const ivec &co, int size)
{
- if(collapsedface(c, orient)) return 0;
- int vismask = (c.material&MATF_CLIP) == MAT_NOCLIP ? 1 : 3;
- if(!touchingface(c, orient)) return vismask;
-
- ivec no;
- int nsize;
- const cube &o = neighbourcube(c, orient, co, size, no, nsize);
- if(&o==&c) return 0;
-
- int vis = 0, opp = opposite(orient);
- if(nsize > size || (nsize == size && !o.children))
- {
- if((~c.material & o.material) & MAT_ALPHA) vis |= 1;
- if((o.material&MATF_CLIP) == MAT_NOCLIP) vis |= vismask&2;
- if(vis == vismask || isentirelysolid(o)) return vis;
- if(isempty(o) || notouchingface(o, opp)) return vismask;
- if(touchingface(o, opp) && faceedges(o, opp) == F_SOLID) return vis;
-
- ivec vo = ivec(co).mask(0xFFF);
- no.mask(0xFFF);
- ivec2 cf[4], of[4];
- int numc = genfacevecs(c, orient, vo, size, false, cf),
- numo = genfacevecs(o, opp, no, nsize, false, of);
- if(numo < 3 || !insideface(cf, numc, of, numo)) return vismask;
- return vis;
- }
-
- ivec vo = ivec(co).mask(0xFFF);
- no.mask(0xFFF);
- ivec2 cf[4];
- int numc = genfacevecs(c, orient, vo, size, false, cf);
- if(!occludesface(o, opp, no, nsize, vo, size, MAT_AIR, (c.material&MAT_ALPHA)^MAT_ALPHA, MAT_ALPHA, cf, numc)) vis |= 1;
- if(vismask&2 && !occludesface(o, opp, no, nsize, vo, size, MAT_AIR, MAT_NOCLIP, MATF_CLIP, cf, numc)) vis |= 2;
- return vis;
+ if(collapsedface(c, orient)) return 0;
+ int vismask = (c.material&MATF_CLIP) == MAT_NOCLIP ? 1 : 3;
+ if(!touchingface(c, orient)) return vismask;
+
+ ivec no;
+ int nsize;
+ const cube &o = neighbourcube(c, orient, co, size, no, nsize);
+ if(&o==&c) return 0;
+
+ int vis = 0, opp = opposite(orient);
+ if(nsize > size || (nsize == size && !o.children))
+ {
+ if((~c.material & o.material) & MAT_ALPHA) vis |= 1;
+ if((o.material&MATF_CLIP) == MAT_NOCLIP) vis |= vismask&2;
+ if(vis == vismask || isentirelysolid(o)) return vis;
+ if(isempty(o) || notouchingface(o, opp)) return vismask;
+ if(touchingface(o, opp) && faceedges(o, opp) == F_SOLID) return vis;
+
+ ivec vo = ivec(co).mask(0xFFF);
+ no.mask(0xFFF);
+ ivec2 cf[4], of[4];
+ int numc = genfacevecs(c, orient, vo, size, false, cf),
+ numo = genfacevecs(o, opp, no, nsize, false, of);
+ if(numo < 3 || !insideface(cf, numc, of, numo)) return vismask;
+ return vis;
+ }
+
+ ivec vo = ivec(co).mask(0xFFF);
+ no.mask(0xFFF);
+ ivec2 cf[4];
+ int numc = genfacevecs(c, orient, vo, size, false, cf);
+ if(!occludesface(o, opp, no, nsize, vo, size, MAT_AIR, (c.material&MAT_ALPHA)^MAT_ALPHA, MAT_ALPHA, cf, numc)) vis |= 1;
+ if(vismask&2 && !occludesface(o, opp, no, nsize, vo, size, MAT_AIR, MAT_NOCLIP, MATF_CLIP, cf, numc)) vis |= 2;
+ return vis;
}
// more expensive version that checks both triangles of a face independently
int visibletris(const cube &c, int orient, const ivec &co, int size, ushort nmat, ushort matmask)
{
- int vis = 3, touching = 0xF;
- ivec v[4], e1, e2, e3, n;
- genfaceverts(c, orient, v);
- n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0]));
- int convex = (e3 = v[0]).sub(v[3]).dot(n);
- if(!convex)
- {
- if(ivec().cross(e3, e2).iszero() || v[1] == v[3]) { if(n.iszero()) return 0; vis = 1; touching = 0xF&~(1<<3); }
- else if(n.iszero()) { vis = 2; touching = 0xF&~(1<<1); }
- }
-
- int dim = dimension(orient), coord = dimcoord(orient);
- if(v[0][dim] != coord*8) touching &= ~(1<<0);
- if(v[1][dim] != coord*8) touching &= ~(1<<1);
- if(v[2][dim] != coord*8) touching &= ~(1<<2);
- if(v[3][dim] != coord*8) touching &= ~(1<<3);
- static const int notouchmasks[2][16] = // mask of triangles not touching
- { // order 0: flat or convex
- // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- { 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 1, 3, 0 },
- // order 1: concave
- { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 2, 0 },
- };
- int order = convex < 0 ? 1 : 0, notouch = notouchmasks[order][touching];
- if((vis&notouch)==vis) return vis;
-
- ivec no;
- int nsize;
- const cube &o = neighbourcube(c, orient, co, size, no, nsize);
- if(&o==&c) return 0;
-
- if((c.material&matmask) == nmat) nmat = MAT_AIR;
-
- ivec vo = ivec(co).mask(0xFFF);
- no.mask(0xFFF);
- ivec2 cf[4], of[4];
- int opp = opposite(orient), numo = 0, numc;
- if(nsize > size || (nsize == size && !o.children))
- {
- if(isempty(o) || notouchingface(o, opp)) return vis;
- if(nmat != MAT_AIR && (o.material&matmask) == nmat) return vis;
- if(isentirelysolid(o) || (touchingface(o, opp) && faceedges(o, opp) == F_SOLID)) return vis&notouch;
-
- numc = genfacevecs(c, orient, vo, size, false, cf, v);
- numo = genfacevecs(o, opp, no, nsize, false, of);
- if(numo < 3) return vis;
- if(insideface(cf, numc, of, numo)) return vis&notouch;
- }
- else
- {
- numc = genfacevecs(c, orient, vo, size, false, cf, v);
- if(occludesface(o, opp, no, nsize, vo, size, MAT_AIR, nmat, matmask, cf, numc)) return vis&notouch;
- }
- if(vis != 3 || notouch) return vis;
-
- static const int triverts[2][2][2][3] =
- { // order
- { // coord
- { { 1, 2, 3 }, { 0, 1, 3 } }, // verts
- { { 0, 1, 2 }, { 0, 2, 3 } }
- },
- { // coord
- { { 0, 1, 2 }, { 3, 0, 2 } }, // verts
- { { 1, 2, 3 }, { 1, 3, 0 } }
- }
- };
-
- do
- {
- loopi(2)
- {
- const int *verts = triverts[order][coord][i];
- ivec2 tf[3] = { cf[verts[0]], cf[verts[1]], cf[verts[2]] };
- if(numo > 0) { if(!insideface(tf, 3, of, numo)) continue; }
- else if(!occludesface(o, opp, no, nsize, vo, size, MAT_AIR, nmat, matmask, tf, 3)) continue;
- return vis & ~(1<<i);
- }
- vis |= 4;
- } while(++order <= 1);
-
- return 3;
+ int vis = 3, touching = 0xF;
+ ivec v[4], e1, e2, e3, n;
+ genfaceverts(c, orient, v);
+ n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0]));
+ int convex = (e3 = v[0]).sub(v[3]).dot(n);
+ if(!convex)
+ {
+ if(ivec().cross(e3, e2).iszero() || v[1] == v[3]) { if(n.iszero()) return 0; vis = 1; touching = 0xF&~(1<<3); }
+ else if(n.iszero()) { vis = 2; touching = 0xF&~(1<<1); }
+ }
+
+ int dim = dimension(orient), coord = dimcoord(orient);
+ if(v[0][dim] != coord*8) touching &= ~(1<<0);
+ if(v[1][dim] != coord*8) touching &= ~(1<<1);
+ if(v[2][dim] != coord*8) touching &= ~(1<<2);
+ if(v[3][dim] != coord*8) touching &= ~(1<<3);
+ static const int notouchmasks[2][16] = // mask of triangles not touching
+ { // order 0: flat or convex
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ { 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 1, 3, 0 },
+ // order 1: concave
+ { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 2, 0 },
+ };
+ int order = convex < 0 ? 1 : 0, notouch = notouchmasks[order][touching];
+ if((vis&notouch)==vis) return vis;
+
+ ivec no;
+ int nsize;
+ const cube &o = neighbourcube(c, orient, co, size, no, nsize);
+ if(&o==&c) return 0;
+
+ if((c.material&matmask) == nmat) nmat = MAT_AIR;
+
+ ivec vo = ivec(co).mask(0xFFF);
+ no.mask(0xFFF);
+ ivec2 cf[4], of[4];
+ int opp = opposite(orient), numo = 0, numc;
+ if(nsize > size || (nsize == size && !o.children))
+ {
+ if(isempty(o) || notouchingface(o, opp)) return vis;
+ if(nmat != MAT_AIR && (o.material&matmask) == nmat) return vis;
+ if(isentirelysolid(o) || (touchingface(o, opp) && faceedges(o, opp) == F_SOLID)) return vis&notouch;
+
+ numc = genfacevecs(c, orient, vo, size, false, cf, v);
+ numo = genfacevecs(o, opp, no, nsize, false, of);
+ if(numo < 3) return vis;
+ if(insideface(cf, numc, of, numo)) return vis&notouch;
+ }
+ else
+ {
+ numc = genfacevecs(c, orient, vo, size, false, cf, v);
+ if(occludesface(o, opp, no, nsize, vo, size, MAT_AIR, nmat, matmask, cf, numc)) return vis&notouch;
+ }
+ if(vis != 3 || notouch) return vis;
+
+ static const int triverts[2][2][2][3] =
+ { // order
+ { // coord
+ { { 1, 2, 3 }, { 0, 1, 3 } }, // verts
+ { { 0, 1, 2 }, { 0, 2, 3 } }
+ },
+ { // coord
+ { { 0, 1, 2 }, { 3, 0, 2 } }, // verts
+ { { 1, 2, 3 }, { 1, 3, 0 } }
+ }
+ };
+
+ do
+ {
+ loopi(2)
+ {
+ const int *verts = triverts[order][coord][i];
+ ivec2 tf[3] = { cf[verts[0]], cf[verts[1]], cf[verts[2]] };
+ if(numo > 0) { if(!insideface(tf, 3, of, numo)) continue; }
+ else if(!occludesface(o, opp, no, nsize, vo, size, MAT_AIR, nmat, matmask, tf, 3)) continue;
+ return vis & ~(1<<i);
+ }
+ vis |= 4;
+ } while(++order <= 1);
+
+ return 3;
}
void calcvert(const cube &c, const ivec &co, int size, ivec &v, int i, bool solid)
{
- if(solid) v = cubecoords[i]; else gencubevert(c, i, v);
- // avoid overflow
- if(size>=8) v.mul(size/8);
- else v.div(8/size);
- v.add(ivec(co).shl(3));
+ if(solid) v = cubecoords[i]; else gencubevert(c, i, v);
+ // avoid overflow
+ if(size>=8) v.mul(size/8);
+ else v.div(8/size);
+ v.add(ivec(co).shl(3));
}
void calcvert(const cube &c, const ivec &co, int size, vec &v, int i, bool solid)
{
- if(solid) v = vec(cubecoords[i]); else gencubevert(c, i, v);
- v.mul(size/8.0f).add(vec(co));
+ if(solid) v = vec(cubecoords[i]); else gencubevert(c, i, v);
+ v.mul(size/8.0f).add(vec(co));
}
int genclipplane(const cube &c, int orient, vec *v, plane *clip)
{
- int planes = 0, convex = faceconvexity(c, orient), order = convex < 0 ? 1 : 0;
- const vec &v0 = v[fv[orient][order]], &v1 = v[fv[orient][order+1]], &v2 = v[fv[orient][order+2]], &v3 = v[fv[orient][(order+3)&3]];
- if(v0==v2) return 0;
- if(v0!=v1 && v1!=v2) clip[planes++].toplane(v0, v1, v2);
- if(v0!=v3 && v2!=v3 && (!planes || convex)) clip[planes++].toplane(v0, v2, v3);
- return planes;
+ int planes = 0, convex = faceconvexity(c, orient), order = convex < 0 ? 1 : 0;
+ const vec &v0 = v[fv[orient][order]], &v1 = v[fv[orient][order+1]], &v2 = v[fv[orient][order+2]], &v3 = v[fv[orient][(order+3)&3]];
+ if(v0==v2) return 0;
+ if(v0!=v1 && v1!=v2) clip[planes++].toplane(v0, v1, v2);
+ if(v0!=v3 && v2!=v3 && (!planes || convex)) clip[planes++].toplane(v0, v2, v3);
+ return planes;
}
void genclipplanes(const cube &c, const ivec &co, int size, clipplanes &p, bool collide)
{
- // generate tight bounding box
- calcvert(c, co, size, p.v[0], 0);
- vec mx = p.v[0], mn = p.v[0];
- for(int i = 1; i < 8; i++)
- {
- calcvert(c, co, size, p.v[i], i);
- mx.max(p.v[i]);
- mn.min(p.v[i]);
- }
-
- p.r = mx.sub(mn).mul(0.5f);
- p.o = mn.add(p.r);
-
- p.size = 0;
- p.visible = 0;
- if(collide || (c.visible&0xC0) == 0x40)
- {
- loopi(6) if(c.visible&(1<<i))
- {
- int vis;
- if(flataxisface(c, i)) p.visible |= 1<<i;
- else if((vis = visibletris(c, i, co, size, MAT_NOCLIP, MATF_CLIP)))
- {
- int convex = faceconvexity(c, i), order = vis&4 || convex < 0 ? 1 : 0;
- const vec &v0 = p.v[fv[i][order]], &v1 = p.v[fv[i][order+1]], &v2 = p.v[fv[i][order+2]], &v3 = p.v[fv[i][(order+3)&3]];
- if(vis&1) { p.side[p.size] = i; p.p[p.size++].toplane(v0, v1, v2); }
- if(vis&2 && (!(vis&1) || convex)) { p.side[p.size] = i; p.p[p.size++].toplane(v0, v2, v3); }
- }
- }
- }
- else if(c.visible&0x80)
- {
- int vis;
- loopi(6) if((vis = visibletris(c, i, co, size)))
- {
- if(flataxisface(c, i)) p.visible |= 1<<i;
- else
- {
- int convex = faceconvexity(c, i), order = vis&4 || convex < 0 ? 1 : 0;
- const vec &v0 = p.v[fv[i][order]], &v1 = p.v[fv[i][order+1]], &v2 = p.v[fv[i][order+2]], &v3 = p.v[fv[i][(order+3)&3]];
- if(vis&1) { p.side[p.size] = i; p.p[p.size++].toplane(v0, v1, v2); }
- if(vis&2 && (!(vis&1) || convex)) { p.side[p.size] = i; p.p[p.size++].toplane(v0, v2, v3); }
- }
- }
- }
+ // generate tight bounding box
+ calcvert(c, co, size, p.v[0], 0);
+ vec mx = p.v[0], mn = p.v[0];
+ for(int i = 1; i < 8; i++)
+ {
+ calcvert(c, co, size, p.v[i], i);
+ mx.max(p.v[i]);
+ mn.min(p.v[i]);
+ }
+
+ p.r = mx.sub(mn).mul(0.5f);
+ p.o = mn.add(p.r);
+
+ p.size = 0;
+ p.visible = 0;
+ if(collide || (c.visible&0xC0) == 0x40)
+ {
+ loopi(6) if(c.visible&(1<<i))
+ {
+ int vis;
+ if(flataxisface(c, i)) p.visible |= 1<<i;
+ else if((vis = visibletris(c, i, co, size, MAT_NOCLIP, MATF_CLIP)))
+ {
+ int convex = faceconvexity(c, i), order = vis&4 || convex < 0 ? 1 : 0;
+ const vec &v0 = p.v[fv[i][order]], &v1 = p.v[fv[i][order+1]], &v2 = p.v[fv[i][order+2]], &v3 = p.v[fv[i][(order+3)&3]];
+ if(vis&1) { p.side[p.size] = i; p.p[p.size++].toplane(v0, v1, v2); }
+ if(vis&2 && (!(vis&1) || convex)) { p.side[p.size] = i; p.p[p.size++].toplane(v0, v2, v3); }
+ }
+ }
+ }
+ else if(c.visible&0x80)
+ {
+ int vis;
+ loopi(6) if((vis = visibletris(c, i, co, size)))
+ {
+ if(flataxisface(c, i)) p.visible |= 1<<i;
+ else
+ {
+ int convex = faceconvexity(c, i), order = vis&4 || convex < 0 ? 1 : 0;
+ const vec &v0 = p.v[fv[i][order]], &v1 = p.v[fv[i][order+1]], &v2 = p.v[fv[i][order+2]], &v3 = p.v[fv[i][(order+3)&3]];
+ if(vis&1) { p.side[p.size] = i; p.p[p.size++].toplane(v0, v1, v2); }
+ if(vis&2 && (!(vis&1) || convex)) { p.side[p.size] = i; p.p[p.size++].toplane(v0, v2, v3); }
+ }
+ }
+ }
}
static inline bool mergefacecmp(const facebounds &x, const facebounds &y)
{
- if(x.v2 < y.v2) return true;
- if(x.v2 > y.v2) return false;
- if(x.u1 < y.u1) return true;
- if(x.u1 > y.u1) return false;
- return false;
+ if(x.v2 < y.v2) return true;
+ if(x.v2 > y.v2) return false;
+ if(x.u1 < y.u1) return true;
+ if(x.u1 > y.u1) return false;
+ return false;
}
static int mergefacev(int orient, facebounds *m, int sz, facebounds &n)
{
- for(int i = sz-1; i >= 0; --i)
- {
- if(m[i].v2 < n.v1) break;
- if(m[i].v2 == n.v1 && m[i].u1 == n.u1 && m[i].u2 == n.u2)
- {
- n.v1 = m[i].v1;
- memmove(&m[i], &m[i+1], (sz - (i+1)) * sizeof(facebounds));
- return 1;
- }
- }
- return 0;
+ for(int i = sz-1; i >= 0; --i)
+ {
+ if(m[i].v2 < n.v1) break;
+ if(m[i].v2 == n.v1 && m[i].u1 == n.u1 && m[i].u2 == n.u2)
+ {
+ n.v1 = m[i].v1;
+ memmove(&m[i], &m[i+1], (sz - (i+1)) * sizeof(facebounds));
+ return 1;
+ }
+ }
+ return 0;
}
static int mergefaceu(int orient, facebounds &m, facebounds &n)
{
- if(m.v1 == n.v1 && m.v2 == n.v2 && m.u2 == n.u1)
- {
- n.u1 = m.u1;
- return 1;
- }
- return 0;
+ if(m.v1 == n.v1 && m.v2 == n.v2 && m.u2 == n.u1)
+ {
+ n.u1 = m.u1;
+ return 1;
+ }
+ return 0;
}
static int mergeface(int orient, facebounds *m, int sz, facebounds &n)
{
- for(bool merged = false; sz; merged = true)
- {
- int vmerged = mergefacev(orient, m, sz, n);
- sz -= vmerged;
- if(!vmerged && merged) break;
- if(!sz) break;
- int umerged = mergefaceu(orient, m[sz-1], n);
- sz -= umerged;
- if(!umerged) break;
- }
- m[sz++] = n;
- return sz;
+ for(bool merged = false; sz; merged = true)
+ {
+ int vmerged = mergefacev(orient, m, sz, n);
+ sz -= vmerged;
+ if(!vmerged && merged) break;
+ if(!sz) break;
+ int umerged = mergefaceu(orient, m[sz-1], n);
+ sz -= umerged;
+ if(!umerged) break;
+ }
+ m[sz++] = n;
+ return sz;
}
int mergefaces(int orient, facebounds *m, int sz)
{
- quicksort(m, sz, mergefacecmp);
+ quicksort(m, sz, mergefacecmp);
- int nsz = 0;
- loopi(sz) nsz = mergeface(orient, m, nsz, m[i]);
- return nsz;
+ int nsz = 0;
+ loopi(sz) nsz = mergeface(orient, m, nsz, m[i]);
+ return nsz;
}
struct cfkey
{
- uchar orient;
- ushort material, tex;
- ivec n;
- int offset;
+ uchar orient;
+ ushort material, tex;
+ ivec n;
+ int offset;
};
static inline bool htcmp(const cfkey &x, const cfkey &y)
{
- return x.orient == y.orient && x.tex == y.tex && x.n == y.n && x.offset == y.offset && x.material==y.material;
+ return x.orient == y.orient && x.tex == y.tex && x.n == y.n && x.offset == y.offset && x.material==y.material;
}
static inline uint hthash(const cfkey &k)
{
- return hthash(k.n)^k.offset^k.tex^k.orient^k.material;
+ return hthash(k.n)^k.offset^k.tex^k.orient^k.material;
}
void mincubeface(const cube &cu, int orient, const ivec &o, int size, const facebounds &orig, facebounds &cf, ushort nmat, ushort matmask)
{
- int dim = dimension(orient);
- if(cu.children)
- {
- size >>= 1;
- int coord = dimcoord(orient);
- loopi(8) if(octacoord(dim, i) == coord)
- mincubeface(cu.children[i], orient, ivec(i, o, size), size, orig, cf, nmat, matmask);
- return;
- }
- int c = C[dim], r = R[dim];
- ushort uco = (o[c]&0xFFF)<<3, vco = (o[r]&0xFFF)<<3;
- ushort uc1 = uco, vc1 = vco, uc2 = ushort(size<<3)+uco, vc2 = ushort(size<<3)+vco;
- uc1 = max(uc1, orig.u1);
- uc2 = min(uc2, orig.u2);
- vc1 = max(vc1, orig.v1);
- vc2 = min(vc2, orig.v2);
- if(!isempty(cu) && touchingface(cu, orient) && !(nmat!=MAT_AIR && (cu.material&matmask)==nmat))
- {
- uchar r1 = cu.edges[faceedgesidx[orient][0]], r2 = cu.edges[faceedgesidx[orient][1]],
- c1 = cu.edges[faceedgesidx[orient][2]], c2 = cu.edges[faceedgesidx[orient][3]];
- ushort u1 = max(c1&0xF, c2&0xF)*size+uco, u2 = min(c1>>4, c2>>4)*size+uco,
- v1 = max(r1&0xF, r2&0xF)*size+vco, v2 = min(r1>>4, r2>>4)*size+vco;
- u1 = max(u1, orig.u1);
- u2 = min(u2, orig.u2);
- v1 = max(v1, orig.v1);
- v2 = min(v2, orig.v2);
- if(v2-v1==vc2-vc1)
- {
- if(u2-u1==uc2-uc1) return;
- if(u1==uc1) uc1 = u2;
- if(u2==uc2) uc2 = u1;
- }
- else if(u2-u1==uc2-uc1)
- {
- if(v1==vc1) vc1 = v2;
- if(v2==vc2) vc2 = v1;
- }
- }
- if(uc1==uc2 || vc1==vc2) return;
- cf.u1 = min(cf.u1, uc1);
- cf.u2 = max(cf.u2, uc2);
- cf.v1 = min(cf.v1, vc1);
- cf.v2 = max(cf.v2, vc2);
+ int dim = dimension(orient);
+ if(cu.children)
+ {
+ size >>= 1;
+ int coord = dimcoord(orient);
+ loopi(8) if(octacoord(dim, i) == coord)
+ mincubeface(cu.children[i], orient, ivec(i, o, size), size, orig, cf, nmat, matmask);
+ return;
+ }
+ int c = C[dim], r = R[dim];
+ ushort uco = (o[c]&0xFFF)<<3, vco = (o[r]&0xFFF)<<3;
+ ushort uc1 = uco, vc1 = vco, uc2 = ushort(size<<3)+uco, vc2 = ushort(size<<3)+vco;
+ uc1 = max(uc1, orig.u1);
+ uc2 = min(uc2, orig.u2);
+ vc1 = max(vc1, orig.v1);
+ vc2 = min(vc2, orig.v2);
+ if(!isempty(cu) && touchingface(cu, orient) && !(nmat!=MAT_AIR && (cu.material&matmask)==nmat))
+ {
+ uchar r1 = cu.edges[faceedgesidx[orient][0]], r2 = cu.edges[faceedgesidx[orient][1]],
+ c1 = cu.edges[faceedgesidx[orient][2]], c2 = cu.edges[faceedgesidx[orient][3]];
+ ushort u1 = max(c1&0xF, c2&0xF)*size+uco, u2 = min(c1>>4, c2>>4)*size+uco,
+ v1 = max(r1&0xF, r2&0xF)*size+vco, v2 = min(r1>>4, r2>>4)*size+vco;
+ u1 = max(u1, orig.u1);
+ u2 = min(u2, orig.u2);
+ v1 = max(v1, orig.v1);
+ v2 = min(v2, orig.v2);
+ if(v2-v1==vc2-vc1)
+ {
+ if(u2-u1==uc2-uc1) return;
+ if(u1==uc1) uc1 = u2;
+ if(u2==uc2) uc2 = u1;
+ }
+ else if(u2-u1==uc2-uc1)
+ {
+ if(v1==vc1) vc1 = v2;
+ if(v2==vc2) vc2 = v1;
+ }
+ }
+ if(uc1==uc2 || vc1==vc2) return;
+ cf.u1 = min(cf.u1, uc1);
+ cf.u2 = max(cf.u2, uc2);
+ cf.v1 = min(cf.v1, vc1);
+ cf.v2 = max(cf.v2, vc2);
}
bool mincubeface(const cube &cu, int orient, const ivec &co, int size, facebounds &orig)
{
- ivec no;
- int nsize;
- const cube &nc = neighbourcube(cu, orient, co, size, no, nsize);
- facebounds mincf;
- mincf.u1 = orig.u2;
- mincf.u2 = orig.u1;
- mincf.v1 = orig.v2;
- mincf.v2 = orig.v1;
- mincubeface(nc, opposite(orient), no, nsize, orig, mincf, cu.material&MAT_ALPHA ? MAT_AIR : MAT_ALPHA, MAT_ALPHA);
- bool smaller = false;
- if(mincf.u1 > orig.u1) { orig.u1 = mincf.u1; smaller = true; }
- if(mincf.u2 < orig.u2) { orig.u2 = mincf.u2; smaller = true; }
- if(mincf.v1 > orig.v1) { orig.v1 = mincf.v1; smaller = true; }
- if(mincf.v2 < orig.v2) { orig.v2 = mincf.v2; smaller = true; }
- return smaller;
+ ivec no;
+ int nsize;
+ const cube &nc = neighbourcube(cu, orient, co, size, no, nsize);
+ facebounds mincf;
+ mincf.u1 = orig.u2;
+ mincf.u2 = orig.u1;
+ mincf.v1 = orig.v2;
+ mincf.v2 = orig.v1;
+ mincubeface(nc, opposite(orient), no, nsize, orig, mincf, cu.material&MAT_ALPHA ? MAT_AIR : MAT_ALPHA, MAT_ALPHA);
+ bool smaller = false;
+ if(mincf.u1 > orig.u1) { orig.u1 = mincf.u1; smaller = true; }
+ if(mincf.u2 < orig.u2) { orig.u2 = mincf.u2; smaller = true; }
+ if(mincf.v1 > orig.v1) { orig.v1 = mincf.v1; smaller = true; }
+ if(mincf.v2 < orig.v2) { orig.v2 = mincf.v2; smaller = true; }
+ return smaller;
}
VAR(maxmerge, 0, 6, 12);
@@ -1398,24 +1398,24 @@ VAR(minface, 0, 4, 12);
struct pvert
{
- ushort x, y;
+ ushort x, y;
- pvert() {}
- pvert(ushort x, ushort y) : x(x), y(y) {}
+ pvert() {}
+ pvert(ushort x, ushort y) : x(x), y(y) {}
- bool operator==(const pvert &o) const { return x == o.x && y == o.y; }
- bool operator!=(const pvert &o) const { return x != o.x || y != o.y; }
+ bool operator==(const pvert &o) const { return x == o.x && y == o.y; }
+ bool operator!=(const pvert &o) const { return x != o.x || y != o.y; }
};
struct pedge
{
- pvert from, to;
+ pvert from, to;
- pedge() {}
- pedge(const pvert &from, const pvert &to) : from(from), to(to) {}
+ pedge() {}
+ pedge(const pvert &from, const pvert &to) : from(from), to(to) {}
- bool operator==(const pedge &o) const { return from == o.from && to == o.to; }
- bool operator!=(const pedge &o) const { return from != o.from || to != o.to; }
+ bool operator==(const pedge &o) const { return from == o.from && to == o.to; }
+ bool operator!=(const pedge &o) const { return from != o.from || to != o.to; }
};
static inline uint hthash(const pedge &x) { return uint(x.from.x)^(uint(x.from.y)<<8); }
@@ -1423,458 +1423,458 @@ static inline bool htcmp(const pedge &x, const pedge &y) { return x == y; }
struct poly
{
- cube *c;
- int numverts;
- bool merged;
- pvert verts[MAXFACEVERTS];
+ cube *c;
+ int numverts;
+ bool merged;
+ pvert verts[MAXFACEVERTS];
};
bool clippoly(poly &p, const facebounds &b)
{
- pvert verts1[MAXFACEVERTS+4], verts2[MAXFACEVERTS+4];
- int numverts1 = 0, numverts2 = 0, px = p.verts[p.numverts-1].x, py = p.verts[p.numverts-1].y;
- loopi(p.numverts)
- {
- int x = p.verts[i].x, y = p.verts[i].y;
- if(x < b.u1)
- {
- if(px > b.u2) verts1[numverts1++] = pvert(b.u2, y + ((y - py)*(b.u2 - x))/(x - px));
- if(px > b.u1) verts1[numverts1++] = pvert(b.u1, y + ((y - py)*(b.u1 - x))/(x - px));
- }
- else if(x > b.u2)
- {
- if(px < b.u1) verts1[numverts1++] = pvert(b.u1, y + ((y - py)*(b.u1 - x))/(x - px));
- if(px < b.u2) verts1[numverts1++] = pvert(b.u2, y + ((y - py)*(b.u2 - x))/(x - px));
- }
- else
- {
- if(px < b.u1)
- {
- if(x > b.u1) verts1[numverts1++] = pvert(b.u1, y + ((y - py)*(b.u1 - x))/(x - px));
- }
- else if(px > b.u2 && x < b.u2) verts1[numverts1++] = pvert(b.u2, y + ((y - py)*(b.u2 - x))/(x - px));
- verts1[numverts1++] = pvert(x, y);
- }
- px = x;
- py = y;
- }
- if(numverts1 < 3) return false;
- px = verts1[numverts1-1].x;
- py = verts1[numverts1-1].y;
- loopi(numverts1)
- {
- int x = verts1[i].x, y = verts1[i].y;
- if(y < b.v1)
- {
- if(py > b.v2) verts2[numverts2++] = pvert(x + ((x - px)*(b.v2 - y))/(y - py), b.v2);
- if(py > b.v1) verts2[numverts2++] = pvert(x + ((x - px)*(b.v1 - y))/(y - py), b.v1);
- }
- else if(y > b.v2)
- {
- if(py < b.v1) verts2[numverts2++] = pvert(x + ((x - px)*(b.v1 - y))/(y - py), b.v1);
- if(py < b.v2) verts2[numverts2++] = pvert(x + ((x - px)*(b.v2 - y))/(y - py), b.v2);
- }
- else
- {
- if(py < b.v1)
- {
- if(y > b.v1) verts2[numverts2++] = pvert(x + ((x - px)*(b.v1 - y))/(y - py), b.v1);
- }
- else if(py > b.v2 && y < b.v2) verts2[numverts2++] = pvert(x + ((x - px)*(b.v2 - y))/(y - py), b.v2);
- verts2[numverts2++] = pvert(x, y);
- }
- px = x;
- py = y;
- }
- if(numverts2 < 3) return false;
- if(numverts2 > MAXFACEVERTS) return false;
- memcpy(p.verts, verts2, numverts2*sizeof(pvert));
- p.numverts = numverts2;
- return true;
-}
+ pvert verts1[MAXFACEVERTS+4], verts2[MAXFACEVERTS+4];
+ int numverts1 = 0, numverts2 = 0, px = p.verts[p.numverts-1].x, py = p.verts[p.numverts-1].y;
+ loopi(p.numverts)
+ {
+ int x = p.verts[i].x, y = p.verts[i].y;
+ if(x < b.u1)
+ {
+ if(px > b.u2) verts1[numverts1++] = pvert(b.u2, y + ((y - py)*(b.u2 - x))/(x - px));
+ if(px > b.u1) verts1[numverts1++] = pvert(b.u1, y + ((y - py)*(b.u1 - x))/(x - px));
+ }
+ else if(x > b.u2)
+ {
+ if(px < b.u1) verts1[numverts1++] = pvert(b.u1, y + ((y - py)*(b.u1 - x))/(x - px));
+ if(px < b.u2) verts1[numverts1++] = pvert(b.u2, y + ((y - py)*(b.u2 - x))/(x - px));
+ }
+ else
+ {
+ if(px < b.u1)
+ {
+ if(x > b.u1) verts1[numverts1++] = pvert(b.u1, y + ((y - py)*(b.u1 - x))/(x - px));
+ }
+ else if(px > b.u2 && x < b.u2) verts1[numverts1++] = pvert(b.u2, y + ((y - py)*(b.u2 - x))/(x - px));
+ verts1[numverts1++] = pvert(x, y);
+ }
+ px = x;
+ py = y;
+ }
+ if(numverts1 < 3) return false;
+ px = verts1[numverts1-1].x;
+ py = verts1[numverts1-1].y;
+ loopi(numverts1)
+ {
+ int x = verts1[i].x, y = verts1[i].y;
+ if(y < b.v1)
+ {
+ if(py > b.v2) verts2[numverts2++] = pvert(x + ((x - px)*(b.v2 - y))/(y - py), b.v2);
+ if(py > b.v1) verts2[numverts2++] = pvert(x + ((x - px)*(b.v1 - y))/(y - py), b.v1);
+ }
+ else if(y > b.v2)
+ {
+ if(py < b.v1) verts2[numverts2++] = pvert(x + ((x - px)*(b.v1 - y))/(y - py), b.v1);
+ if(py < b.v2) verts2[numverts2++] = pvert(x + ((x - px)*(b.v2 - y))/(y - py), b.v2);
+ }
+ else
+ {
+ if(py < b.v1)
+ {
+ if(y > b.v1) verts2[numverts2++] = pvert(x + ((x - px)*(b.v1 - y))/(y - py), b.v1);
+ }
+ else if(py > b.v2 && y < b.v2) verts2[numverts2++] = pvert(x + ((x - px)*(b.v2 - y))/(y - py), b.v2);
+ verts2[numverts2++] = pvert(x, y);
+ }
+ px = x;
+ py = y;
+ }
+ if(numverts2 < 3) return false;
+ if(numverts2 > MAXFACEVERTS) return false;
+ memcpy(p.verts, verts2, numverts2*sizeof(pvert));
+ p.numverts = numverts2;
+ return true;
+}
bool genpoly(cube &cu, int orient, const ivec &o, int size, int vis, ivec &n, int &offset, poly &p)
{
- int dim = dimension(orient), coord = dimcoord(orient);
- ivec v[4];
- genfaceverts(cu, orient, v);
- if(flataxisface(cu, orient))
- {
- n = ivec(0, 0, 0);
- n[dim] = coord ? 1 : -1;
- }
- else
- {
- if(faceconvexity(v)) return false;
- n.cross(ivec(v[1]).sub(v[0]), ivec(v[2]).sub(v[0]));
- if(n.iszero()) n.cross(ivec(v[2]).sub(v[0]), ivec(v[3]).sub(v[0]));
- reduceslope(n);
- }
-
- ivec po = ivec(o).mask(0xFFF).shl(3);
- loopk(4) v[k].mul(size).add(po);
- offset = -n.dot(v[3]);
-
- int r = R[dim], c = C[dim], order = vis&4 ? 1 : 0;
- p.numverts = 0;
- if(coord)
- {
- const ivec &v0 = v[order]; p.verts[p.numverts++] = pvert(v0[c], v0[r]);
- if(vis&1) { const ivec &v1 = v[order+1]; p.verts[p.numverts++] = pvert(v1[c], v1[r]); }
- const ivec &v2 = v[order+2]; p.verts[p.numverts++] = pvert(v2[c], v2[r]);
- if(vis&2) { const ivec &v3 = v[(order+3)&3]; p.verts[p.numverts++] = pvert(v3[c], v3[r]); }
- }
- else
- {
- if(vis&2) { const ivec &v3 = v[(order+3)&3]; p.verts[p.numverts++] = pvert(v3[c], v3[r]); }
- const ivec &v2 = v[order+2]; p.verts[p.numverts++] = pvert(v2[c], v2[r]);
- if(vis&1) { const ivec &v1 = v[order+1]; p.verts[p.numverts++] = pvert(v1[c], v1[r]); }
- const ivec &v0 = v[order]; p.verts[p.numverts++] = pvert(v0[c], v0[r]);
- }
-
- if(faceedges(cu, orient)!=F_SOLID)
- {
- int px = int(p.verts[p.numverts-2].x) - int(p.verts[p.numverts-3].x), py = int(p.verts[p.numverts-2].y) - int(p.verts[p.numverts-3].y),
- cx = int(p.verts[p.numverts-1].x) - int(p.verts[p.numverts-2].x), cy = int(p.verts[p.numverts-1].y) - int(p.verts[p.numverts-2].y),
- dir = px*cy - py*cx;
- if(dir > 0) return false;
- if(!dir) { if(p.numverts < 4) return false; p.verts[p.numverts-2] = p.verts[p.numverts-1]; p.numverts--; }
- px = cx; py = cy;
- cx = int(p.verts[0].x) - int(p.verts[p.numverts-1].x); cy = int(p.verts[0].y) - int(p.verts[p.numverts-1].y);
- dir = px*cy - py*cx;
- if(dir > 0) return false;
- if(!dir) { if(p.numverts < 4) return false; p.numverts--; }
- px = cx; py = cy;
- cx = int(p.verts[1].x) - int(p.verts[0].x); cy = int(p.verts[1].y) - int(p.verts[0].y);
- dir = px*cy - py*cx;
- if(dir > 0) return false;
- if(!dir) { if(p.numverts < 4) return false; p.verts[0] = p.verts[p.numverts-1]; p.numverts--; }
- px = cx; py = cy;
- cx = int(p.verts[2].x) - int(p.verts[1].x); cy = int(p.verts[2].y) - int(p.verts[1].y);
- dir = px*cy - py*cx;
- if(dir > 0) return false;
- if(!dir) { if(p.numverts < 4) return false; p.verts[1] = p.verts[2]; p.verts[2] = p.verts[3]; p.numverts--; }
- }
-
- p.c = &cu;
- p.merged = false;
-
- if(minface && size >= 1<<minface && touchingface(cu, orient))
- {
- facebounds b;
- b.u1 = b.u2 = p.verts[0].x;
- b.v1 = b.v2 = p.verts[0].y;
- for(int i = 1; i < p.numverts; i++)
- {
- const pvert &v = p.verts[i];
- b.u1 = min(b.u1, v.x);
- b.u2 = max(b.u2, v.x);
- b.v1 = min(b.v1, v.y);
- b.v2 = max(b.v2, v.y);
- }
- if(mincubeface(cu, orient, o, size, b) && clippoly(p, b))
- p.merged = true;
- }
-
- return true;
+ int dim = dimension(orient), coord = dimcoord(orient);
+ ivec v[4];
+ genfaceverts(cu, orient, v);
+ if(flataxisface(cu, orient))
+ {
+ n = ivec(0, 0, 0);
+ n[dim] = coord ? 1 : -1;
+ }
+ else
+ {
+ if(faceconvexity(v)) return false;
+ n.cross(ivec(v[1]).sub(v[0]), ivec(v[2]).sub(v[0]));
+ if(n.iszero()) n.cross(ivec(v[2]).sub(v[0]), ivec(v[3]).sub(v[0]));
+ reduceslope(n);
+ }
+
+ ivec po = ivec(o).mask(0xFFF).shl(3);
+ loopk(4) v[k].mul(size).add(po);
+ offset = -n.dot(v[3]);
+
+ int r = R[dim], c = C[dim], order = vis&4 ? 1 : 0;
+ p.numverts = 0;
+ if(coord)
+ {
+ const ivec &v0 = v[order]; p.verts[p.numverts++] = pvert(v0[c], v0[r]);
+ if(vis&1) { const ivec &v1 = v[order+1]; p.verts[p.numverts++] = pvert(v1[c], v1[r]); }
+ const ivec &v2 = v[order+2]; p.verts[p.numverts++] = pvert(v2[c], v2[r]);
+ if(vis&2) { const ivec &v3 = v[(order+3)&3]; p.verts[p.numverts++] = pvert(v3[c], v3[r]); }
+ }
+ else
+ {
+ if(vis&2) { const ivec &v3 = v[(order+3)&3]; p.verts[p.numverts++] = pvert(v3[c], v3[r]); }
+ const ivec &v2 = v[order+2]; p.verts[p.numverts++] = pvert(v2[c], v2[r]);
+ if(vis&1) { const ivec &v1 = v[order+1]; p.verts[p.numverts++] = pvert(v1[c], v1[r]); }
+ const ivec &v0 = v[order]; p.verts[p.numverts++] = pvert(v0[c], v0[r]);
+ }
+
+ if(faceedges(cu, orient)!=F_SOLID)
+ {
+ int px = int(p.verts[p.numverts-2].x) - int(p.verts[p.numverts-3].x), py = int(p.verts[p.numverts-2].y) - int(p.verts[p.numverts-3].y),
+ cx = int(p.verts[p.numverts-1].x) - int(p.verts[p.numverts-2].x), cy = int(p.verts[p.numverts-1].y) - int(p.verts[p.numverts-2].y),
+ dir = px*cy - py*cx;
+ if(dir > 0) return false;
+ if(!dir) { if(p.numverts < 4) return false; p.verts[p.numverts-2] = p.verts[p.numverts-1]; p.numverts--; }
+ px = cx; py = cy;
+ cx = int(p.verts[0].x) - int(p.verts[p.numverts-1].x); cy = int(p.verts[0].y) - int(p.verts[p.numverts-1].y);
+ dir = px*cy - py*cx;
+ if(dir > 0) return false;
+ if(!dir) { if(p.numverts < 4) return false; p.numverts--; }
+ px = cx; py = cy;
+ cx = int(p.verts[1].x) - int(p.verts[0].x); cy = int(p.verts[1].y) - int(p.verts[0].y);
+ dir = px*cy - py*cx;
+ if(dir > 0) return false;
+ if(!dir) { if(p.numverts < 4) return false; p.verts[0] = p.verts[p.numverts-1]; p.numverts--; }
+ px = cx; py = cy;
+ cx = int(p.verts[2].x) - int(p.verts[1].x); cy = int(p.verts[2].y) - int(p.verts[1].y);
+ dir = px*cy - py*cx;
+ if(dir > 0) return false;
+ if(!dir) { if(p.numverts < 4) return false; p.verts[1] = p.verts[2]; p.verts[2] = p.verts[3]; p.numverts--; }
+ }
+
+ p.c = &cu;
+ p.merged = false;
+
+ if(minface && size >= 1<<minface && touchingface(cu, orient))
+ {
+ facebounds b;
+ b.u1 = b.u2 = p.verts[0].x;
+ b.v1 = b.v2 = p.verts[0].y;
+ for(int i = 1; i < p.numverts; i++)
+ {
+ const pvert &v = p.verts[i];
+ b.u1 = min(b.u1, v.x);
+ b.u2 = max(b.u2, v.x);
+ b.v1 = min(b.v1, v.y);
+ b.v2 = max(b.v2, v.y);
+ }
+ if(mincubeface(cu, orient, o, size, b) && clippoly(p, b))
+ p.merged = true;
+ }
+
+ return true;
}
struct plink : pedge
{
- int polys[2];
+ int polys[2];
- plink() { clear(); }
- plink(const pedge &p) : pedge(p) { clear(); }
+ plink() { clear(); }
+ plink(const pedge &p) : pedge(p) { clear(); }
- void clear() { polys[0] = polys[1] = -1; }
+ void clear() { polys[0] = polys[1] = -1; }
};
bool mergepolys(int orient, hashset<plink> &links, vector<plink *> &queue, int owner, poly &p, poly &q, const pedge &e)
{
- int pe = -1, qe = -1;
- loopi(p.numverts) if(p.verts[i] == e.from) { pe = i; break; }
- loopi(q.numverts) if(q.verts[i] == e.to) { qe = i; break; }
- if(pe < 0 || qe < 0) return false;
- if(p.verts[(pe+1)%p.numverts] != e.to || q.verts[(qe+1)%q.numverts] != e.from) return false;
- /*
- * c----d
- * | |
- * F----T
- * | P |
- * b----a
- */
- pvert verts[2*MAXFACEVERTS];
- int numverts = 0, index = pe+2; // starts at A = T+1, ends at F = T+p.numverts
- loopi(p.numverts-1)
- {
- if(index >= p.numverts) index -= p.numverts;
- verts[numverts++] = p.verts[index++];
- }
- index = qe+2; // starts at C = T+2 = F+1, ends at T = T+q.numverts
- int px = int(verts[numverts-1].x) - int(verts[numverts-2].x), py = int(verts[numverts-1].y) - int(verts[numverts-2].y);
- loopi(q.numverts-1)
- {
- if(index >= q.numverts) index -= q.numverts;
- pvert &src = q.verts[index++];
- int cx = int(src.x) - int(verts[numverts-1].x), cy = int(src.y) - int(verts[numverts-1].y),
- dir = px*cy - py*cx;
- if(dir > 0) return false;
- if(!dir) numverts--;
- verts[numverts++] = src;
- px = cx;
- py = cy;
- }
- int cx = int(verts[0].x) - int(verts[numverts-1].x), cy = int(verts[0].y) - int(verts[numverts-1].y),
- dir = px*cy - py*cx;
- if(dir > 0) return false;
- if(!dir) numverts--;
-
- if(numverts > MAXFACEVERTS) return false;
-
- q.merged = true;
- q.numverts = 0;
-
- p.merged = true;
- p.numverts = numverts;
- memcpy(p.verts, verts, numverts*sizeof(pvert));
-
- int prev = p.numverts-1;
- loopj(p.numverts)
- {
- pedge e(p.verts[prev], p.verts[j]);
- int order = e.from.x > e.to.x || (e.from.x == e.to.x && e.from.y > e.to.y) ? 1 : 0;
- if(order) swap(e.from, e.to);
- plink &l = links.access(e, e);
- bool shouldqueue = l.polys[order] < 0 && l.polys[order^1] >= 0;
- l.polys[order] = owner;
- if(shouldqueue) queue.add(&l);
- prev = j;
- }
-
- return true;
+ int pe = -1, qe = -1;
+ loopi(p.numverts) if(p.verts[i] == e.from) { pe = i; break; }
+ loopi(q.numverts) if(q.verts[i] == e.to) { qe = i; break; }
+ if(pe < 0 || qe < 0) return false;
+ if(p.verts[(pe+1)%p.numverts] != e.to || q.verts[(qe+1)%q.numverts] != e.from) return false;
+ /*
+ * c----d
+ * | |
+ * F----T
+ * | P |
+ * b----a
+ */
+ pvert verts[2*MAXFACEVERTS];
+ int numverts = 0, index = pe+2; // starts at A = T+1, ends at F = T+p.numverts
+ loopi(p.numverts-1)
+ {
+ if(index >= p.numverts) index -= p.numverts;
+ verts[numverts++] = p.verts[index++];
+ }
+ index = qe+2; // starts at C = T+2 = F+1, ends at T = T+q.numverts
+ int px = int(verts[numverts-1].x) - int(verts[numverts-2].x), py = int(verts[numverts-1].y) - int(verts[numverts-2].y);
+ loopi(q.numverts-1)
+ {
+ if(index >= q.numverts) index -= q.numverts;
+ pvert &src = q.verts[index++];
+ int cx = int(src.x) - int(verts[numverts-1].x), cy = int(src.y) - int(verts[numverts-1].y),
+ dir = px*cy - py*cx;
+ if(dir > 0) return false;
+ if(!dir) numverts--;
+ verts[numverts++] = src;
+ px = cx;
+ py = cy;
+ }
+ int cx = int(verts[0].x) - int(verts[numverts-1].x), cy = int(verts[0].y) - int(verts[numverts-1].y),
+ dir = px*cy - py*cx;
+ if(dir > 0) return false;
+ if(!dir) numverts--;
+
+ if(numverts > MAXFACEVERTS) return false;
+
+ q.merged = true;
+ q.numverts = 0;
+
+ p.merged = true;
+ p.numverts = numverts;
+ memcpy(p.verts, verts, numverts*sizeof(pvert));
+
+ int prev = p.numverts-1;
+ loopj(p.numverts)
+ {
+ pedge e(p.verts[prev], p.verts[j]);
+ int order = e.from.x > e.to.x || (e.from.x == e.to.x && e.from.y > e.to.y) ? 1 : 0;
+ if(order) swap(e.from, e.to);
+ plink &l = links.access(e, e);
+ bool shouldqueue = l.polys[order] < 0 && l.polys[order^1] >= 0;
+ l.polys[order] = owner;
+ if(shouldqueue) queue.add(&l);
+ prev = j;
+ }
+
+ return true;
}
void addmerge(cube &cu, int orient, const ivec &co, const ivec &n, int offset, poly &p)
{
- cu.merged |= 1<<orient;
- if(!p.numverts)
- {
- if(cu.ext) cu.ext->surfaces[orient] = ambientsurface;
- return;
- }
- surfaceinfo surf = brightsurface;
- vertinfo verts[MAXFACEVERTS];
- surf.numverts |= p.numverts;
- int dim = dimension(orient), coord = dimcoord(orient), c = C[dim], r = R[dim];
- loopk(p.numverts)
- {
- pvert &src = p.verts[coord ? k : p.numverts-1-k];
- vertinfo &dst = verts[k];
- ivec v;
- v[c] = src.x;
- v[r] = src.y;
- v[dim] = -(offset + n[c]*src.x + n[r]*src.y)/n[dim];
- dst.set(v);
- }
- if(cu.ext)
- {
- const surfaceinfo &oldsurf = cu.ext->surfaces[orient];
- int numverts = oldsurf.numverts&MAXFACEVERTS;
- if(numverts == p.numverts)
- {
- ivec v0 = verts[0].getxyz();
- const vertinfo *oldverts = cu.ext->verts() + oldsurf.verts;
- loopj(numverts) if(v0 == oldverts[j].getxyz())
- {
- for(int k = 1; k < numverts; k++)
- {
- if(++j >= numverts) j = 0;
- if(verts[k].getxyz() != oldverts[j].getxyz()) goto nomatch;
- }
- return;
- }
- nomatch:;
- }
- }
- setsurface(cu, orient, surf, verts, p.numverts);
+ cu.merged |= 1<<orient;
+ if(!p.numverts)
+ {
+ if(cu.ext) cu.ext->surfaces[orient] = ambientsurface;
+ return;
+ }
+ surfaceinfo surf = brightsurface;
+ vertinfo verts[MAXFACEVERTS];
+ surf.numverts |= p.numverts;
+ int dim = dimension(orient), coord = dimcoord(orient), c = C[dim], r = R[dim];
+ loopk(p.numverts)
+ {
+ pvert &src = p.verts[coord ? k : p.numverts-1-k];
+ vertinfo &dst = verts[k];
+ ivec v;
+ v[c] = src.x;
+ v[r] = src.y;
+ v[dim] = -(offset + n[c]*src.x + n[r]*src.y)/n[dim];
+ dst.set(v);
+ }
+ if(cu.ext)
+ {
+ const surfaceinfo &oldsurf = cu.ext->surfaces[orient];
+ int numverts = oldsurf.numverts&MAXFACEVERTS;
+ if(numverts == p.numverts)
+ {
+ ivec v0 = verts[0].getxyz();
+ const vertinfo *oldverts = cu.ext->verts() + oldsurf.verts;
+ loopj(numverts) if(v0 == oldverts[j].getxyz())
+ {
+ for(int k = 1; k < numverts; k++)
+ {
+ if(++j >= numverts) j = 0;
+ if(verts[k].getxyz() != oldverts[j].getxyz()) goto nomatch;
+ }
+ return;
+ }
+ nomatch:;
+ }
+ }
+ setsurface(cu, orient, surf, verts, p.numverts);
}
static inline void clearmerge(cube &c, int orient)
{
- if(c.merged&(1<<orient))
- {
- c.merged &= ~(1<<orient);
- if(c.ext) c.ext->surfaces[orient] = brightsurface;
- }
+ if(c.merged&(1<<orient))
+ {
+ c.merged &= ~(1<<orient);
+ if(c.ext) c.ext->surfaces[orient] = brightsurface;
+ }
}
void addmerges(int orient, const ivec &co, const ivec &n, int offset, vector<poly> &polys)
{
- loopv(polys)
- {
- poly &p = polys[i];
- if(p.merged) addmerge(*p.c, orient, co, n, offset, p);
- else clearmerge(*p.c, orient);
- }
+ loopv(polys)
+ {
+ poly &p = polys[i];
+ if(p.merged) addmerge(*p.c, orient, co, n, offset, p);
+ else clearmerge(*p.c, orient);
+ }
}
void mergepolys(int orient, const ivec &co, const ivec &n, int offset, vector<poly> &polys)
{
- if(polys.length() <= 1) { addmerges(orient, co, n, offset, polys); return; }
- hashset<plink> links(polys.length() <= 32 ? 128 : 1024);
- vector<plink *> queue;
- loopv(polys)
- {
- poly &p = polys[i];
- int prev = p.numverts-1;
- loopj(p.numverts)
- {
- pedge e(p.verts[prev], p.verts[j]);
- int order = e.from.x > e.to.x || (e.from.x == e.to.x && e.from.y > e.to.y) ? 1 : 0;
- if(order) swap(e.from, e.to);
- plink &l = links.access(e, e);
- l.polys[order] = i;
- if(l.polys[0] >= 0 && l.polys[1] >= 0) queue.add(&l);
- prev = j;
- }
- }
- vector<plink *> nextqueue;
- while(queue.length())
- {
- loopv(queue)
- {
- plink &l = *queue[i];
- if(l.polys[0] >= 0 && l.polys[1] >= 0)
- mergepolys(orient, links, nextqueue, l.polys[0], polys[l.polys[0]], polys[l.polys[1]], l);
- }
- queue.setsize(0);
- queue.move(nextqueue);
- }
- addmerges(orient, co, n, offset, polys);
+ if(polys.length() <= 1) { addmerges(orient, co, n, offset, polys); return; }
+ hashset<plink> links(polys.length() <= 32 ? 128 : 1024);
+ vector<plink *> queue;
+ loopv(polys)
+ {
+ poly &p = polys[i];
+ int prev = p.numverts-1;
+ loopj(p.numverts)
+ {
+ pedge e(p.verts[prev], p.verts[j]);
+ int order = e.from.x > e.to.x || (e.from.x == e.to.x && e.from.y > e.to.y) ? 1 : 0;
+ if(order) swap(e.from, e.to);
+ plink &l = links.access(e, e);
+ l.polys[order] = i;
+ if(l.polys[0] >= 0 && l.polys[1] >= 0) queue.add(&l);
+ prev = j;
+ }
+ }
+ vector<plink *> nextqueue;
+ while(queue.length())
+ {
+ loopv(queue)
+ {
+ plink &l = *queue[i];
+ if(l.polys[0] >= 0 && l.polys[1] >= 0)
+ mergepolys(orient, links, nextqueue, l.polys[0], polys[l.polys[0]], polys[l.polys[1]], l);
+ }
+ queue.setsize(0);
+ queue.move(nextqueue);
+ }
+ addmerges(orient, co, n, offset, polys);
}
static int genmergeprogress = 0;
struct cfpolys
{
- vector<poly> polys;
+ vector<poly> polys;
};
static hashtable<cfkey, cfpolys> cpolys;
void genmerges(cube *c = worldroot, const ivec &o = ivec(0, 0, 0), int size = worldsize>>1)
{
- if((genmergeprogress++&0xFFF)==0) renderprogress(float(genmergeprogress)/allocnodes, "merging faces...");
- neighbourstack[++neighbourdepth] = c;
- loopi(8)
- {
- ivec co(i, o, size);
- int vis;
- if(c[i].children) genmerges(c[i].children, co, size>>1);
- else if(!isempty(c[i])) loopj(6) if((vis = visibletris(c[i], j, co, size)))
- {
- cfkey k;
- poly p;
- if(size < 1<<maxmerge && c != worldroot)
- {
- if(genpoly(c[i], j, co, size, vis, k.n, k.offset, p))
- {
- k.orient = j;
- k.tex = c[i].texture[j];
- k.material = c[i].material&MAT_ALPHA;
- cpolys[k].polys.add(p);
- continue;
- }
- }
- else if(minface && size >= 1<<minface && touchingface(c[i], j))
- {
- if(genpoly(c[i], j, co, size, vis, k.n, k.offset, p) && p.merged)
- {
- addmerge(c[i], j, co, k.n, k.offset, p);
- continue;
- }
- }
- clearmerge(c[i], j);
- }
- if((size == 1<<maxmerge || c == worldroot) && cpolys.numelems)
- {
- enumeratekt(cpolys, cfkey, key, cfpolys, val,
- {
- mergepolys(key.orient, co, key.n, key.offset, val.polys);
- });
- cpolys.clear();
- }
- }
- --neighbourdepth;
+ if((genmergeprogress++&0xFFF)==0) renderprogress(float(genmergeprogress)/allocnodes, "merging faces...");
+ neighbourstack[++neighbourdepth] = c;
+ loopi(8)
+ {
+ ivec co(i, o, size);
+ int vis;
+ if(c[i].children) genmerges(c[i].children, co, size>>1);
+ else if(!isempty(c[i])) loopj(6) if((vis = visibletris(c[i], j, co, size)))
+ {
+ cfkey k;
+ poly p;
+ if(size < 1<<maxmerge && c != worldroot)
+ {
+ if(genpoly(c[i], j, co, size, vis, k.n, k.offset, p))
+ {
+ k.orient = j;
+ k.tex = c[i].texture[j];
+ k.material = c[i].material&MAT_ALPHA;
+ cpolys[k].polys.add(p);
+ continue;
+ }
+ }
+ else if(minface && size >= 1<<minface && touchingface(c[i], j))
+ {
+ if(genpoly(c[i], j, co, size, vis, k.n, k.offset, p) && p.merged)
+ {
+ addmerge(c[i], j, co, k.n, k.offset, p);
+ continue;
+ }
+ }
+ clearmerge(c[i], j);
+ }
+ if((size == 1<<maxmerge || c == worldroot) && cpolys.numelems)
+ {
+ enumeratekt(cpolys, cfkey, key, cfpolys, val,
+ {
+ mergepolys(key.orient, co, key.n, key.offset, val.polys);
+ });
+ cpolys.clear();
+ }
+ }
+ --neighbourdepth;
}
int calcmergedsize(int orient, const ivec &co, int size, const vertinfo *verts, int numverts)
{
- ushort x1 = verts[0].x, y1 = verts[0].y, z1 = verts[0].z,
- x2 = x1, y2 = y1, z2 = z1;
- for(int i = 1; i < numverts; i++)
- {
- const vertinfo &v = verts[i];
- x1 = min(x1, v.x);
- x2 = max(x2, v.x);
- y1 = min(y1, v.y);
- y2 = max(y2, v.y);
- z1 = min(z1, v.z);
- z2 = max(z2, v.z);
- }
- int bits = 0;
- while(1<<bits < size) ++bits;
- bits += 3;
- ivec mo(co);
- mo.mask(0xFFF);
- mo.shl(3);
- while(bits<15)
- {
- mo.mask(~((1<<bits)-1));
- if(mo.x <= x1 && mo.x + (1<<bits) >= x2 &&
- mo.y <= y1 && mo.y + (1<<bits) >= y2 &&
- mo.z <= z1 && mo.z + (1<<bits) >= z2)
- break;
- bits++;
- }
- return bits-3;
+ ushort x1 = verts[0].x, y1 = verts[0].y, z1 = verts[0].z,
+ x2 = x1, y2 = y1, z2 = z1;
+ for(int i = 1; i < numverts; i++)
+ {
+ const vertinfo &v = verts[i];
+ x1 = min(x1, v.x);
+ x2 = max(x2, v.x);
+ y1 = min(y1, v.y);
+ y2 = max(y2, v.y);
+ z1 = min(z1, v.z);
+ z2 = max(z2, v.z);
+ }
+ int bits = 0;
+ while(1<<bits < size) ++bits;
+ bits += 3;
+ ivec mo(co);
+ mo.mask(0xFFF);
+ mo.shl(3);
+ while(bits<15)
+ {
+ mo.mask(~((1<<bits)-1));
+ if(mo.x <= x1 && mo.x + (1<<bits) >= x2 &&
+ mo.y <= y1 && mo.y + (1<<bits) >= y2 &&
+ mo.z <= z1 && mo.z + (1<<bits) >= z2)
+ break;
+ bits++;
+ }
+ return bits-3;
}
static void invalidatemerges(cube &c)
{
- if(c.merged)
- {
- brightencube(c);
- c.merged = 0;
- }
- if(c.ext)
- {
- if(c.ext->va)
- {
- if(!(c.ext->va->hasmerges&(MERGE_PART | MERGE_ORIGIN))) return;
- destroyva(c.ext->va);
- c.ext->va = NULL;
- }
- if(c.ext->tjoints >= 0) c.ext->tjoints = -1;
- }
- if(c.children) loopi(8) invalidatemerges(c.children[i]);
+ if(c.merged)
+ {
+ brightencube(c);
+ c.merged = 0;
+ }
+ if(c.ext)
+ {
+ if(c.ext->va)
+ {
+ if(!(c.ext->va->hasmerges&(MERGE_PART | MERGE_ORIGIN))) return;
+ destroyva(c.ext->va);
+ c.ext->va = NULL;
+ }
+ if(c.ext->tjoints >= 0) c.ext->tjoints = -1;
+ }
+ if(c.children) loopi(8) invalidatemerges(c.children[i]);
}
static int invalidatedmerges = 0;
void invalidatemerges(cube &c, const ivec &co, int size, bool msg)
{
- if(msg && invalidatedmerges!=totalmillis)
- {
- renderprogress(0, "invalidating merged surfaces...");
- invalidatedmerges = totalmillis;
- }
- invalidatemerges(c);
+ if(msg && invalidatedmerges!=totalmillis)
+ {
+ renderprogress(0, "invalidating merged surfaces...");
+ invalidatedmerges = totalmillis;
+ }
+ invalidatemerges(c);
}
void calcmerges()
{
- genmergeprogress = 0;
- genmerges();
+ genmergeprogress = 0;
+ genmerges();
}
diff --git a/src/engine/octa.h b/src/engine/octa.h
index b14c440..7312b7a 100644
--- a/src/engine/octa.h
+++ b/src/engine/octa.h
@@ -2,71 +2,71 @@
struct elementset
{
- ushort texture, lmid, envmap;
- uchar dim, layer;
- ushort length[2], minvert[2], maxvert[2];
+ ushort texture, lmid, envmap;
+ uchar dim, layer;
+ ushort length[2], minvert[2], maxvert[2];
};
enum
{
- EMID_NONE = 0,
- EMID_CUSTOM,
- EMID_SKY,
- EMID_RESERVED
+ EMID_NONE = 0,
+ EMID_CUSTOM,
+ EMID_SKY,
+ EMID_RESERVED
};
struct materialsurface
{
- ivec o;
- ushort csize, rsize;
- ushort material, skip;
- uchar orient, visible;
- union
- {
- short index;
- short depth;
- };
- union
- {
- entity *light;
- ushort envmap;
- uchar ends;
- };
+ ivec o;
+ ushort csize, rsize;
+ ushort material, skip;
+ uchar orient, visible;
+ union
+ {
+ short index;
+ short depth;
+ };
+ union
+ {
+ entity *light;
+ ushort envmap;
+ uchar ends;
+ };
};
struct vertinfo
{
- ushort x, y, z, u, v, norm;
+ ushort x, y, z, u, v, norm;
- void setxyz(ushort a, ushort b, ushort c) { x = a; y = b; z = c; }
- void setxyz(const ivec &v) { setxyz(v.x, v.y, v.z); }
- void set(ushort a, ushort b, ushort c, ushort s = 0, ushort t = 0, ushort n = 0) { setxyz(a, b, c); u = s; v = t; norm = n; }
- void set(const ivec &v, ushort s = 0, ushort t = 0, ushort n = 0) { set(v.x, v.y, v.z, s, t, n); }
- ivec getxyz() const { return ivec(x, y, z); }
+ void setxyz(ushort a, ushort b, ushort c) { x = a; y = b; z = c; }
+ void setxyz(const ivec &v) { setxyz(v.x, v.y, v.z); }
+ void set(ushort a, ushort b, ushort c, ushort s = 0, ushort t = 0, ushort n = 0) { setxyz(a, b, c); u = s; v = t; norm = n; }
+ void set(const ivec &v, ushort s = 0, ushort t = 0, ushort n = 0) { set(v.x, v.y, v.z, s, t, n); }
+ ivec getxyz() const { return ivec(x, y, z); }
};
enum
{
- LAYER_TOP = (1<<5),
- LAYER_BOTTOM = (1<<6),
- LAYER_DUP = (1<<7),
+ LAYER_TOP = (1<<5),
+ LAYER_BOTTOM = (1<<6),
+ LAYER_DUP = (1<<7),
- LAYER_BLEND = LAYER_TOP|LAYER_BOTTOM,
+ LAYER_BLEND = LAYER_TOP|LAYER_BOTTOM,
- MAXFACEVERTS = 15
+ MAXFACEVERTS = 15
};
enum { LMID_AMBIENT = 0, LMID_AMBIENT1, LMID_BRIGHT, LMID_BRIGHT1, LMID_DARK, LMID_DARK1, LMID_RESERVED };
struct surfaceinfo
{
- uchar lmid[2];
- uchar verts, numverts;
+ uchar lmid[2];
+ uchar verts, numverts;
- int totalverts() const { return numverts&LAYER_DUP ? (numverts&MAXFACEVERTS)*2 : numverts&MAXFACEVERTS; }
- bool used() const { return lmid[0] != LMID_AMBIENT || lmid[1] != LMID_AMBIENT || numverts&~LAYER_TOP; }
- void clear() { lmid[0] = LMID_AMBIENT; lmid[1] = LMID_AMBIENT; numverts = (numverts&MAXFACEVERTS) | LAYER_TOP; }
- void brighten() { lmid[0] = LMID_BRIGHT; lmid[1] = LMID_AMBIENT; numverts = (numverts&MAXFACEVERTS) | LAYER_TOP; }
+ int totalverts() const { return numverts&LAYER_DUP ? (numverts&MAXFACEVERTS)*2 : numverts&MAXFACEVERTS; }
+ bool used() const { return lmid[0] != LMID_AMBIENT || lmid[1] != LMID_AMBIENT || numverts&~LAYER_TOP; }
+ void clear() { lmid[0] = LMID_AMBIENT; lmid[1] = LMID_AMBIENT; numverts = (numverts&MAXFACEVERTS) | LAYER_TOP; }
+ void brighten() { lmid[0] = LMID_BRIGHT; lmid[1] = LMID_AMBIENT; numverts = (numverts&MAXFACEVERTS) | LAYER_TOP; }
};
static const surfaceinfo ambientsurface = {{LMID_AMBIENT, LMID_AMBIENT}, 0, LAYER_TOP};
@@ -75,167 +75,167 @@ static const surfaceinfo brightbottomsurface = {{LMID_AMBIENT, LMID_BRIGHT}, 0,
struct occludequery
{
- void *owner;
- GLuint id;
- int fragments;
+ void *owner;
+ GLuint id;
+ int fragments;
};
struct vtxarray;
struct octaentities
{
- vector<int> mapmodels;
- vector<int> other;
- occludequery *query;
- octaentities *next, *rnext;
- int distance;
- ivec o;
- int size;
- ivec bbmin, bbmax;
-
- octaentities(const ivec &o, int size) : query(0), o(o), size(size), bbmin(o), bbmax(o)
- {
- bbmin.add(size);
- }
+ vector<int> mapmodels;
+ vector<int> other;
+ occludequery *query;
+ octaentities *next, *rnext;
+ int distance;
+ ivec o;
+ int size;
+ ivec bbmin, bbmax;
+
+ octaentities(const ivec &o, int size) : query(0), o(o), size(size), bbmin(o), bbmax(o)
+ {
+ bbmin.add(size);
+ }
};
enum
{
- OCCLUDE_NOTHING = 0,
- OCCLUDE_GEOM,
- OCCLUDE_BB,
- OCCLUDE_PARENT
+ OCCLUDE_NOTHING = 0,
+ OCCLUDE_GEOM,
+ OCCLUDE_BB,
+ OCCLUDE_PARENT
};
enum
{
- MERGE_ORIGIN = 1<<0,
- MERGE_PART = 1<<1,
- MERGE_USE = 1<<2
+ MERGE_ORIGIN = 1<<0,
+ MERGE_PART = 1<<1,
+ MERGE_USE = 1<<2
};
struct vtxarray
{
- vtxarray *parent;
- vector<vtxarray *> children;
- vtxarray *next, *rnext; // linked list of visible VOBs
- vertex *vdata; // vertex data
- ushort voffset; // offset into vertex data
- ushort *edata, *skydata; // vertex indices
- GLuint vbuf, ebuf, skybuf; // VBOs
- ushort minvert, maxvert; // DRE info
- elementset *eslist; // List of element indices sets (range) per texture
- materialsurface *matbuf; // buffer of material surfaces
- int verts, tris, texs, blendtris, blends, alphabacktris, alphaback, alphafronttris, alphafront, alphatris, texmask, sky, explicitsky, skyfaces, skyclip, matsurfs, distance;
- double skyarea;
- ivec o;
- int size; // location and size of cube.
- ivec geommin, geommax; // BB of geom
- ivec shadowmapmin, shadowmapmax; // BB of shadowmapped surfaces
- ivec matmin, matmax; // BB of any materials
- ivec bbmin, bbmax; // BB of everything including children
- uchar curvfc, occluded;
- occludequery *query;
- vector<octaentities *> mapmodels;
- int hasmerges, mergelevel;
- uint dynlightmask;
- bool shadowed;
+ vtxarray *parent;
+ vector<vtxarray *> children;
+ vtxarray *next, *rnext; // linked list of visible VOBs
+ vertex *vdata; // vertex data
+ ushort voffset; // offset into vertex data
+ ushort *edata, *skydata; // vertex indices
+ GLuint vbuf, ebuf, skybuf; // VBOs
+ ushort minvert, maxvert; // DRE info
+ elementset *eslist; // List of element indices sets (range) per texture
+ materialsurface *matbuf; // buffer of material surfaces
+ int verts, tris, texs, blendtris, blends, alphabacktris, alphaback, alphafronttris, alphafront, alphatris, texmask, sky, explicitsky, skyfaces, skyclip, matsurfs, distance;
+ double skyarea;
+ ivec o;
+ int size; // location and size of cube.
+ ivec geommin, geommax; // BB of geom
+ ivec shadowmapmin, shadowmapmax; // BB of shadowmapped surfaces
+ ivec matmin, matmax; // BB of any materials
+ ivec bbmin, bbmax; // BB of everything including children
+ uchar curvfc, occluded;
+ occludequery *query;
+ vector<octaentities *> mapmodels;
+ int hasmerges, mergelevel;
+ uint dynlightmask;
+ bool shadowed;
};
struct cube;
struct clipplanes
{
- vec o, r, v[8];
- plane p[12];
- uchar side[12];
- uchar size, visible;
- const cube *owner;
- int version;
+ vec o, r, v[8];
+ plane p[12];
+ uchar side[12];
+ uchar size, visible;
+ const cube *owner;
+ int version;
};
struct facebounds
{
- ushort u1, u2, v1, v2;
+ ushort u1, u2, v1, v2;
- bool empty() const { return u1 >= u2 || v1 >= v2; }
+ bool empty() const { return u1 >= u2 || v1 >= v2; }
};
struct tjoint
{
- int next;
- ushort offset;
- uchar edge;
+ int next;
+ ushort offset;
+ uchar edge;
};
struct cubeext
{
- vtxarray *va; // vertex array for children, or NULL
- octaentities *ents; // map entities inside cube
- surfaceinfo surfaces[6]; // render info for each surface
- int tjoints; // linked list of t-joints
- uchar maxverts; // allocated space for verts
+ vtxarray *va; // vertex array for children, or NULL
+ octaentities *ents; // map entities inside cube
+ surfaceinfo surfaces[6]; // render info for each surface
+ int tjoints; // linked list of t-joints
+ uchar maxverts; // allocated space for verts
- vertinfo *verts() { return (vertinfo *)(this+1); }
+ vertinfo *verts() { return (vertinfo *)(this+1); }
};
struct cube
{
- cube *children; // points to 8 cube structures which are its children, or NULL. -Z first, then -Y, -X
- cubeext *ext; // extended info for the cube
- union
- {
- uchar edges[12]; // edges of the cube, each uchar is 2 4bit values denoting the range.
- // see documentation jpgs for more info.
- uint faces[3]; // 4 edges of each dimension together representing 2 perpendicular faces
- };
- ushort texture[6]; // one for each face. same order as orient.
- ushort material; // empty-space material
- uchar merged; // merged faces of the cube
- union
- {
- uchar escaped; // mask of which children have escaped merges
- uchar visible; // visibility info for faces
- };
+ cube *children; // points to 8 cube structures which are its children, or NULL. -Z first, then -Y, -X
+ cubeext *ext; // extended info for the cube
+ union
+ {
+ uchar edges[12]; // edges of the cube, each uchar is 2 4bit values denoting the range.
+ // see documentation jpgs for more info.
+ uint faces[3]; // 4 edges of each dimension together representing 2 perpendicular faces
+ };
+ ushort texture[6]; // one for each face. same order as orient.
+ ushort material; // empty-space material
+ uchar merged; // merged faces of the cube
+ union
+ {
+ uchar escaped; // mask of which children have escaped merges
+ uchar visible; // visibility info for faces
+ };
};
struct block3
{
- ivec o, s;
- int grid, orient;
- block3() {}
- block3(const selinfo &sel) : o(sel.o), s(sel.s), grid(sel.grid), orient(sel.orient) {}
- cube *c() { return (cube *)(this+1); }
- int size() const { return s.x*s.y*s.z; }
+ ivec o, s;
+ int grid, orient;
+ block3() {}
+ block3(const selinfo &sel) : o(sel.o), s(sel.s), grid(sel.grid), orient(sel.orient) {}
+ cube *c() { return (cube *)(this+1); }
+ int size() const { return s.x*s.y*s.z; }
};
struct editinfo
{
- block3 *copy;
- editinfo() : copy(NULL) {}
+ block3 *copy;
+ editinfo() : copy(NULL) {}
};
struct undoent { int i; entity e; };
struct undoblock // undo header, all data sits in payload
{
- undoblock *prev, *next;
- int size, timestamp, numents; // if numents is 0, is a cube undo record, otherwise an entity undo record
-
- block3 *block() { return (block3 *)(this + 1); }
- uchar *gridmap()
- {
- block3 *ub = block();
- return (uchar *)(ub->c() + ub->size());
- }
- undoent *ents() { return (undoent *)(this + 1); }
+ undoblock *prev, *next;
+ int size, timestamp, numents; // if numents is 0, is a cube undo record, otherwise an entity undo record
+
+ block3 *block() { return (block3 *)(this + 1); }
+ uchar *gridmap()
+ {
+ block3 *ub = block();
+ return (uchar *)(ub->c() + ub->size());
+ }
+ undoent *ents() { return (undoent *)(this + 1); }
};
-extern cube *worldroot; // the world data. only a ptr to 8 cubes (ie: like cube.children above)
+extern cube *worldroot; // the world data. only a ptr to 8 cubes (ie: like cube.children above)
extern int wtris, wverts, vtris, vverts, glde, gbatches, rplanes;
extern int allocnodes, allocva, selchildcount, selchildmat;
-const uint F_EMPTY = 0; // all edges in the range (0,0)
-const uint F_SOLID = 0x80808080; // all edges in the range (0,8)
+const uint F_EMPTY = 0; // all edges in the range (0,0)
+const uint F_SOLID = 0x80808080; // all edges in the range (0,8)
#define isempty(c) ((c).faces[0]==F_EMPTY)
#define isentirelysolid(c) ((c).faces[0]==F_SOLID && (c).faces[1]==F_SOLID && (c).faces[2]==F_SOLID)
@@ -249,23 +249,23 @@ const uint F_SOLID = 0x80808080; // all edges in the range (0,8)
#define cubeedge(c, d, x, y) ((c).edges[(((d)<<2)+((y)<<1)+(x))])
-#define octadim(d) (1<<(d)) // creates mask for bit of given dimension
-#define octacoord(d, i) (((i)&octadim(d))>>(d))
+#define octadim(d) (1<<(d)) // creates mask for bit of given dimension
+#define octacoord(d, i) (((i)&octadim(d))>>(d))
#define oppositeocta(d, i) ((i)^octadim(D[d]))
#define octaindex(d,x,y,z) (((z)<<D[d])+((y)<<C[d])+((x)<<R[d]))
#define octastep(x, y, z, scale) (((((z)>>(scale))&1)<<2) | ((((y)>>(scale))&1)<<1) | (((x)>>(scale))&1))
static inline uchar octaboxoverlap(const ivec &o, int size, const ivec &bbmin, const ivec &bbmax)
{
- uchar p = 0xFF; // bitmask of possible collisions with octants. 0 bit = 0 octant, etc
- ivec mid = ivec(o).add(size);
- if(mid.z <= bbmin.z) p &= 0xF0; // not in a -ve Z octant
- else if(mid.z >= bbmax.z) p &= 0x0F; // not in a +ve Z octant
- if(mid.y <= bbmin.y) p &= 0xCC; // not in a -ve Y octant
- else if(mid.y >= bbmax.y) p &= 0x33; // etc..
- if(mid.x <= bbmin.x) p &= 0xAA;
- else if(mid.x >= bbmax.x) p &= 0x55;
- return p;
+ uchar p = 0xFF; // bitmask of possible collisions with octants. 0 bit = 0 octant, etc
+ ivec mid = ivec(o).add(size);
+ if(mid.z <= bbmin.z) p &= 0xF0; // not in a -ve Z octant
+ else if(mid.z >= bbmax.z) p &= 0x0F; // not in a +ve Z octant
+ if(mid.y <= bbmin.y) p &= 0xCC; // not in a -ve Y octant
+ else if(mid.y >= bbmax.y) p &= 0x33; // etc..
+ if(mid.x <= bbmin.x) p &= 0xAA;
+ else if(mid.x >= bbmax.x) p &= 0x55;
+ return p;
}
#define loopoctabox(o, size, bbmin, bbmax) uchar possible = octaboxoverlap(o, size, bbmin, bbmax); loopi(8) if(possible&(1<<i))
@@ -273,12 +273,12 @@ static inline uchar octaboxoverlap(const ivec &o, int size, const ivec &bbmin, c
enum
{
- O_LEFT = 0,
- O_RIGHT,
- O_BACK,
- O_FRONT,
- O_BOTTOM,
- O_TOP
+ O_LEFT = 0,
+ O_RIGHT,
+ O_BACK,
+ O_FRONT,
+ O_BOTTOM,
+ O_TOP
};
#define dimension(orient) ((orient)>>1)
@@ -287,41 +287,41 @@ enum
enum
{
- VFC_FULL_VISIBLE = 0,
- VFC_PART_VISIBLE,
- VFC_FOGGED,
- VFC_NOT_VISIBLE,
- PVS_FULL_VISIBLE,
- PVS_PART_VISIBLE,
- PVS_FOGGED
+ VFC_FULL_VISIBLE = 0,
+ VFC_PART_VISIBLE,
+ VFC_FOGGED,
+ VFC_NOT_VISIBLE,
+ PVS_FULL_VISIBLE,
+ PVS_PART_VISIBLE,
+ PVS_FOGGED
};
#define GENCUBEVERTS(x0,x1, y0,y1, z0,z1) \
- GENCUBEVERT(0, x1, y1, z0) \
- GENCUBEVERT(1, x0, y1, z0) \
- GENCUBEVERT(2, x0, y1, z1) \
- GENCUBEVERT(3, x1, y1, z1) \
- GENCUBEVERT(4, x1, y0, z1) \
- GENCUBEVERT(5, x0, y0, z1) \
- GENCUBEVERT(6, x0, y0, z0) \
- GENCUBEVERT(7, x1, y0, z0)
+ GENCUBEVERT(0, x1, y1, z0) \
+ GENCUBEVERT(1, x0, y1, z0) \
+ GENCUBEVERT(2, x0, y1, z1) \
+ GENCUBEVERT(3, x1, y1, z1) \
+ GENCUBEVERT(4, x1, y0, z1) \
+ GENCUBEVERT(5, x0, y0, z1) \
+ GENCUBEVERT(6, x0, y0, z0) \
+ GENCUBEVERT(7, x1, y0, z0)
#define GENFACEVERTX(o,n, x,y,z, xv,yv,zv) GENFACEVERT(o,n, x,y,z, xv,yv,zv)
#define GENFACEVERTSX(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \
- GENFACEORIENT(0, GENFACEVERTX(0,0, x0,y1,z1, d0,r1,c1), GENFACEVERTX(0,1, x0,y1,z0, d0,r1,c0), GENFACEVERTX(0,2, x0,y0,z0, d0,r0,c0), GENFACEVERTX(0,3, x0,y0,z1, d0,r0,c1)) \
- GENFACEORIENT(1, GENFACEVERTX(1,0, x1,y1,z1, d1,r1,c1), GENFACEVERTX(1,1, x1,y0,z1, d1,r0,c1), GENFACEVERTX(1,2, x1,y0,z0, d1,r0,c0), GENFACEVERTX(1,3, x1,y1,z0, d1,r1,c0))
+ GENFACEORIENT(0, GENFACEVERTX(0,0, x0,y1,z1, d0,r1,c1), GENFACEVERTX(0,1, x0,y1,z0, d0,r1,c0), GENFACEVERTX(0,2, x0,y0,z0, d0,r0,c0), GENFACEVERTX(0,3, x0,y0,z1, d0,r0,c1)) \
+ GENFACEORIENT(1, GENFACEVERTX(1,0, x1,y1,z1, d1,r1,c1), GENFACEVERTX(1,1, x1,y0,z1, d1,r0,c1), GENFACEVERTX(1,2, x1,y0,z0, d1,r0,c0), GENFACEVERTX(1,3, x1,y1,z0, d1,r1,c0))
#define GENFACEVERTY(o,n, x,y,z, xv,yv,zv) GENFACEVERT(o,n, x,y,z, xv,yv,zv)
#define GENFACEVERTSY(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \
- GENFACEORIENT(2, GENFACEVERTY(2,0, x1,y0,z1, c1,d0,r1), GENFACEVERTY(2,1, x0,y0,z1, c0,d0,r1), GENFACEVERTY(2,2, x0,y0,z0, c0,d0,r0), GENFACEVERTY(2,3, x1,y0,z0, c1,d0,r0)) \
- GENFACEORIENT(3, GENFACEVERTY(3,0, x0,y1,z0, c0,d1,r0), GENFACEVERTY(3,1, x0,y1,z1, c0,d1,r1), GENFACEVERTY(3,2, x1,y1,z1, c1,d1,r1), GENFACEVERTY(3,3, x1,y1,z0, c1,d1,r0))
+ GENFACEORIENT(2, GENFACEVERTY(2,0, x1,y0,z1, c1,d0,r1), GENFACEVERTY(2,1, x0,y0,z1, c0,d0,r1), GENFACEVERTY(2,2, x0,y0,z0, c0,d0,r0), GENFACEVERTY(2,3, x1,y0,z0, c1,d0,r0)) \
+ GENFACEORIENT(3, GENFACEVERTY(3,0, x0,y1,z0, c0,d1,r0), GENFACEVERTY(3,1, x0,y1,z1, c0,d1,r1), GENFACEVERTY(3,2, x1,y1,z1, c1,d1,r1), GENFACEVERTY(3,3, x1,y1,z0, c1,d1,r0))
#define GENFACEVERTZ(o,n, x,y,z, xv,yv,zv) GENFACEVERT(o,n, x,y,z, xv,yv,zv)
#define GENFACEVERTSZ(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \
- GENFACEORIENT(4, GENFACEVERTZ(4,0, x0,y0,z0, r0,c0,d0), GENFACEVERTZ(4,1, x0,y1,z0, r0,c1,d0), GENFACEVERTZ(4,2, x1,y1,z0, r1,c1,d0), GENFACEVERTZ(4,3, x1,y0,z0, r1,c0,d0)) \
- GENFACEORIENT(5, GENFACEVERTZ(5,0, x0,y0,z1, r0,c0,d1), GENFACEVERTZ(5,1, x1,y0,z1, r1,c0,d1), GENFACEVERTZ(5,2, x1,y1,z1, r1,c1,d1), GENFACEVERTZ(5,3, x0,y1,z1, r0,c1,d1))
+ GENFACEORIENT(4, GENFACEVERTZ(4,0, x0,y0,z0, r0,c0,d0), GENFACEVERTZ(4,1, x0,y1,z0, r0,c1,d0), GENFACEVERTZ(4,2, x1,y1,z0, r1,c1,d0), GENFACEVERTZ(4,3, x1,y0,z0, r1,c0,d0)) \
+ GENFACEORIENT(5, GENFACEVERTZ(5,0, x0,y0,z1, r0,c0,d1), GENFACEVERTZ(5,1, x1,y0,z1, r1,c0,d1), GENFACEVERTZ(5,2, x1,y1,z1, r1,c1,d1), GENFACEVERTZ(5,3, x0,y1,z1, r0,c1,d1))
#define GENFACEVERTSXY(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \
- GENFACEVERTSX(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \
- GENFACEVERTSY(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1)
+ GENFACEVERTSX(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \
+ GENFACEVERTSY(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1)
#define GENFACEVERTS(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \
- GENFACEVERTSXY(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \
- GENFACEVERTSZ(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1)
+ GENFACEVERTSXY(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1) \
+ GENFACEVERTSZ(x0,x1, y0,y1, z0,z1, c0,c1, r0,r1, d0,d1)
diff --git a/src/engine/octaedit.cpp b/src/engine/octaedit.cpp
index af69db9..fa987d0 100644
--- a/src/engine/octaedit.cpp
+++ b/src/engine/octaedit.cpp
@@ -6,88 +6,88 @@ bool boxoutline = false;
void boxs(int orient, vec o, const vec &s, float size)
{
- int d = dimension(orient), dc = dimcoord(orient);
- float f = boxoutline ? (dc>0 ? 0.2f : -0.2f) : 0;
- o[D[d]] += dc * s[D[d]] + f;
-
- vec r(0, 0, 0), c(0, 0, 0);
- r[R[d]] = s[R[d]];
- c[C[d]] = s[C[d]];
-
- vec v1 = o, v2 = vec(o).add(r), v3 = vec(o).add(r).add(c), v4 = vec(o).add(c);
-
- r[R[d]] = 0.5f*size;
- c[C[d]] = 0.5f*size;
-
- gle::defvertex();
- gle::begin(GL_TRIANGLE_STRIP);
- gle::attrib(vec(v1).sub(r).sub(c));
- gle::attrib(vec(v1).add(r).add(c));
- gle::attrib(vec(v2).add(r).sub(c));
- gle::attrib(vec(v2).sub(r).add(c));
- gle::attrib(vec(v3).add(r).add(c));
- gle::attrib(vec(v3).sub(r).sub(c));
- gle::attrib(vec(v4).sub(r).add(c));
- gle::attrib(vec(v4).add(r).sub(c));
- gle::attrib(vec(v1).sub(r).sub(c));
- gle::attrib(vec(v1).add(r).add(c));
- xtraverts += gle::end();
+ int d = dimension(orient), dc = dimcoord(orient);
+ float f = boxoutline ? (dc>0 ? 0.2f : -0.2f) : 0;
+ o[D[d]] += dc * s[D[d]] + f;
+
+ vec r(0, 0, 0), c(0, 0, 0);
+ r[R[d]] = s[R[d]];
+ c[C[d]] = s[C[d]];
+
+ vec v1 = o, v2 = vec(o).add(r), v3 = vec(o).add(r).add(c), v4 = vec(o).add(c);
+
+ r[R[d]] = 0.5f*size;
+ c[C[d]] = 0.5f*size;
+
+ gle::defvertex();
+ gle::begin(GL_TRIANGLE_STRIP);
+ gle::attrib(vec(v1).sub(r).sub(c));
+ gle::attrib(vec(v1).add(r).add(c));
+ gle::attrib(vec(v2).add(r).sub(c));
+ gle::attrib(vec(v2).sub(r).add(c));
+ gle::attrib(vec(v3).add(r).add(c));
+ gle::attrib(vec(v3).sub(r).sub(c));
+ gle::attrib(vec(v4).sub(r).add(c));
+ gle::attrib(vec(v4).add(r).sub(c));
+ gle::attrib(vec(v1).sub(r).sub(c));
+ gle::attrib(vec(v1).add(r).add(c));
+ xtraverts += gle::end();
}
void boxs(int orient, vec o, const vec &s)
{
- int d = dimension(orient), dc = dimcoord(orient);
- float f = boxoutline ? (dc>0 ? 0.2f : -0.2f) : 0;
- o[D[d]] += dc * s[D[d]] + f;
+ int d = dimension(orient), dc = dimcoord(orient);
+ float f = boxoutline ? (dc>0 ? 0.2f : -0.2f) : 0;
+ o[D[d]] += dc * s[D[d]] + f;
- gle::defvertex();
- gle::begin(GL_LINE_LOOP);
+ gle::defvertex();
+ gle::begin(GL_LINE_LOOP);
- gle::attrib(o); o[R[d]] += s[R[d]];
- gle::attrib(o); o[C[d]] += s[C[d]];
- gle::attrib(o); o[R[d]] -= s[R[d]];
- gle::attrib(o);
+ gle::attrib(o); o[R[d]] += s[R[d]];
+ gle::attrib(o); o[C[d]] += s[C[d]];
+ gle::attrib(o); o[R[d]] -= s[R[d]];
+ gle::attrib(o);
- xtraverts += gle::end();
+ xtraverts += gle::end();
}
void boxs3D(const vec &o, vec s, int g)
{
- s.mul(g);
- loopi(6)
- boxs(i, o, s);
+ s.mul(g);
+ loopi(6)
+ boxs(i, o, s);
}
void boxsgrid(int orient, vec o, vec s, int g)
{
- int d = dimension(orient), dc = dimcoord(orient);
- float ox = o[R[d]],
- oy = o[C[d]],
- xs = s[R[d]],
- ys = s[C[d]],
- f = boxoutline ? (dc>0 ? 0.2f : -0.2f) : 0;
-
- o[D[d]] += dc * s[D[d]]*g + f;
-
- gle::defvertex();
- gle::begin(GL_LINES);
- loop(x, xs)
- {
- o[R[d]] += g;
- gle::attrib(o);
- o[C[d]] += ys*g;
- gle::attrib(o);
- o[C[d]] = oy;
- }
- loop(y, ys)
- {
- o[C[d]] += g;
- o[R[d]] = ox;
- gle::attrib(o);
- o[R[d]] += xs*g;
- gle::attrib(o);
- }
- xtraverts += gle::end();
+ int d = dimension(orient), dc = dimcoord(orient);
+ float ox = o[R[d]],
+ oy = o[C[d]],
+ xs = s[R[d]],
+ ys = s[C[d]],
+ f = boxoutline ? (dc>0 ? 0.2f : -0.2f) : 0;
+
+ o[D[d]] += dc * s[D[d]]*g + f;
+
+ gle::defvertex();
+ gle::begin(GL_LINES);
+ loop(x, xs)
+ {
+ o[R[d]] += g;
+ gle::attrib(o);
+ o[C[d]] += ys*g;
+ gle::attrib(o);
+ o[C[d]] = oy;
+ }
+ loop(y, ys)
+ {
+ o[C[d]] += g;
+ o[R[d]] = ox;
+ gle::attrib(o);
+ o[R[d]] += xs*g;
+ gle::attrib(o);
+ }
+ xtraverts += gle::end();
}
selinfo sel, lastsel, savedsel;
@@ -106,30 +106,30 @@ int horient = 0;
extern int entmoving;
VARF(dragging, 0, 0, 1,
- if(!dragging || cor[0]<0) return;
- lastcur = cur;
- lastcor = cor;
- sel.grid = gridsize;
- sel.orient = orient;
+ if(!dragging || cor[0]<0) return;
+ lastcur = cur;
+ lastcor = cor;
+ sel.grid = gridsize;
+ sel.orient = orient;
);
int moving = 0;
ICOMMAND(moving, "b", (int *n),
{
- if(*n >= 0)
- {
- if(!*n || (moving<=1 && !pointinsel(sel, vec(cur).add(1)))) moving = 0;
- else if(!moving) moving = 1;
- }
- intret(moving);
+ if(*n >= 0)
+ {
+ if(!*n || (moving<=1 && !pointinsel(sel, vec(cur).add(1)))) moving = 0;
+ else if(!moving) moving = 1;
+ }
+ intret(moving);
});
VARF(gridpower, 0, 3, 12,
{
- if(dragging) return;
- gridsize = 1<<gridpower;
- if(gridsize>=worldsize) gridsize = worldsize/2;
- cancelsel();
+ if(dragging) return;
+ gridsize = 1<<gridpower;
+ if(gridsize>=worldsize) gridsize = worldsize/2;
+ cancelsel();
});
VAR(passthroughsel, 0, 0, 1);
@@ -143,88 +143,88 @@ extern void hmapcancel();
void cubecancel()
{
- havesel = false;
- moving = dragging = hmapedit = passthroughsel = 0;
- forcenextundo();
- hmapcancel();
+ havesel = false;
+ moving = dragging = hmapedit = passthroughsel = 0;
+ forcenextundo();
+ hmapcancel();
}
void cancelsel()
{
- cubecancel();
- entcancel();
+ cubecancel();
+ entcancel();
}
void toggleedit(bool force)
{
- if(!force)
- {
- if(!isconnected()) return;
- if(player->state!=CS_ALIVE && player->state!=CS_DEAD && player->state!=CS_EDITING) return; // do not allow dead players to edit to avoid state confusion
- if(!game::allowedittoggle()) return; // not in most multiplayer modes
- }
- if(!(editmode = !editmode))
- {
- player->state = player->editstate;
- player->o.z -= player->eyeheight; // entinmap wants feet pos
- entinmap(player); // find spawn closest to current floating pos
- }
- else
- {
- game::resetgamestate();
- player->editstate = player->state;
- player->state = CS_EDITING;
- }
- cancelsel();
- stoppaintblendmap();
- keyrepeat(editmode);
- editing = entediting = editmode;
- extern int fullbright;
- if(fullbright) { initlights(); lightents(); }
- if(!force) game::edittoggled(editmode);
+ if(!force)
+ {
+ if(!isconnected()) return;
+ if(player->state!=CS_ALIVE && player->state!=CS_DEAD && player->state!=CS_EDITING) return; // do not allow dead players to edit to avoid state confusion
+ if(!game::allowedittoggle()) return; // not in most multiplayer modes
+ }
+ if(!(editmode = !editmode))
+ {
+ player->state = player->editstate;
+ player->o.z -= player->eyeheight; // entinmap wants feet pos
+ entinmap(player); // find spawn closest to current floating pos
+ }
+ else
+ {
+ game::resetgamestate();
+ player->editstate = player->state;
+ player->state = CS_EDITING;
+ }
+ cancelsel();
+ stoppaintblendmap();
+ keyrepeat(editmode);
+ editing = entediting = editmode;
+ extern int fullbright;
+ if(fullbright) { initlights(); lightents(); }
+ if(!force) game::edittoggled(editmode);
}
VARP(editinview, 0, 1, 1);
bool noedit(bool view, bool msg)
{
- if(!editmode) { if(msg) conoutf(CON_ERROR, "operation only allowed in edit mode"); return true; }
- if(view || haveselent()) return false;
- float r = 1.0f;
- vec o(sel.o), s(sel.s);
- s.mul(float(sel.grid) / 2.0f);
- o.add(s);
- r = float(max(s.x, max(s.y, s.z)));
- bool viewable = (isvisiblesphere(r, o) != VFC_NOT_VISIBLE);
- if(viewable || !editinview) return false;
- if(msg) conoutf(CON_ERROR, "selection not in view");
- return true;
+ if(!editmode) { if(msg) conoutf(CON_ERROR, "operation only allowed in edit mode"); return true; }
+ if(view || haveselent()) return false;
+ float r = 1.0f;
+ vec o(sel.o), s(sel.s);
+ s.mul(float(sel.grid) / 2.0f);
+ o.add(s);
+ r = float(max(s.x, max(s.y, s.z)));
+ bool viewable = (isvisiblesphere(r, o) != VFC_NOT_VISIBLE);
+ if(viewable || !editinview) return false;
+ if(msg) conoutf(CON_ERROR, "selection not in view");
+ return true;
}
void reorient()
{
- sel.cx = 0;
- sel.cy = 0;
- sel.cxs = sel.s[R[dimension(orient)]]*2;
- sel.cys = sel.s[C[dimension(orient)]]*2;
- sel.orient = orient;
+ sel.cx = 0;
+ sel.cy = 0;
+ sel.cxs = sel.s[R[dimension(orient)]]*2;
+ sel.cys = sel.s[C[dimension(orient)]]*2;
+ sel.orient = orient;
}
void selextend()
{
- if(noedit(true)) return;
- loopi(3)
- {
- if(cur[i]<sel.o[i])
- {
- sel.s[i] += (sel.o[i]-cur[i])/sel.grid;
- sel.o[i] = cur[i];
- }
- else if(cur[i]>=sel.o[i]+sel.s[i]*sel.grid)
- {
- sel.s[i] = (cur[i]-sel.o[i])/sel.grid+1;
- }
- }
+ if(noedit(true)) return;
+ loopi(3)
+ {
+ if(cur[i]<sel.o[i])
+ {
+ sel.s[i] += (sel.o[i]-cur[i])/sel.grid;
+ sel.o[i] = cur[i];
+ }
+ else if(cur[i]>=sel.o[i]+sel.s[i]*sel.grid)
+ {
+ sel.s[i] = (cur[i]-sel.o[i])/sel.grid+1;
+ }
+ }
}
ICOMMAND(edittoggle, "", (), toggleedit(false));
@@ -241,24 +241,24 @@ ICOMMAND(selswap, "", (), { if(noedit(true)) return; swap(sel, savedsel); });
ICOMMAND(getselpos, "", (),
{
- if(noedit(true)) return;
- defformatstring(pos, "%s %s %s", floatstr(sel.o.x), floatstr(sel.o.y), floatstr(sel.o.z));
- result(pos);
+ if(noedit(true)) return;
+ defformatstring(pos, "%s %s %s", floatstr(sel.o.x), floatstr(sel.o.y), floatstr(sel.o.z));
+ result(pos);
});
void setselpos(int *x, int *y, int *z)
{
- if(noedit(moving!=0)) return;
- havesel = true;
- sel.o = ivec(*x, *y, *z).mask(~(gridsize-1));
+ if(noedit(moving!=0)) return;
+ havesel = true;
+ sel.o = ivec(*x, *y, *z).mask(~(gridsize-1));
}
COMMAND(setselpos, "iii");
void movesel(int *dir, int *dim)
{
- if(noedit(moving!=0)) return;
- if(*dim < 0 || *dim > 2) return;
- sel.o[*dim] += *dir * sel.grid;
+ if(noedit(moving!=0)) return;
+ if(*dim < 0 || *dim > 2) return;
+ sel.o[*dim] += *dir * sel.grid;
}
COMMAND(movesel, "ii");
@@ -266,16 +266,16 @@ COMMAND(movesel, "ii");
cube &blockcube(int x, int y, int z, const block3 &b, int rgrid) // looks up a world cube, based on coordinates mapped by the block
{
- int dim = dimension(b.orient), dc = dimcoord(b.orient);
- ivec s(dim, x*b.grid, y*b.grid, dc*(b.s[dim]-1)*b.grid);
- s.add(b.o);
- if(dc) s[dim] -= z*b.grid; else s[dim] += z*b.grid;
- return lookupcube(s, rgrid);
+ int dim = dimension(b.orient), dc = dimcoord(b.orient);
+ ivec s(dim, x*b.grid, y*b.grid, dc*(b.s[dim]-1)*b.grid);
+ s.add(b.o);
+ if(dc) s[dim] -= z*b.grid; else s[dim] += z*b.grid;
+ return lookupcube(s, rgrid);
}
-#define loopxy(b) loop(y,(b).s[C[dimension((b).orient)]]) loop(x,(b).s[R[dimension((b).orient)]])
+#define loopxy(b) loop(y,(b).s[C[dimension((b).orient)]]) loop(x,(b).s[R[dimension((b).orient)]])
#define loopxyz(b, r, f) { loop(z,(b).s[D[dimension((b).orient)]]) loopxy((b)) { cube &c = blockcube(x,y,z,b,r); f; } }
-#define loopselxyz(f) { if(local) makeundo(); loopxyz(sel, sel.grid, f); changed(sel); }
+#define loopselxyz(f) { if(local) makeundo(); loopxyz(sel, sel.grid, f); changed(sel); }
#define selcube(x, y, z) blockcube(x, y, z, sel, sel.grid)
////////////// cursor ///////////////
@@ -286,61 +286,61 @@ ICOMMAND(havesel, "", (), intret(havesel ? selchildcount : 0));
void countselchild(cube *c, const ivec &cor, int size)
{
- ivec ss = ivec(sel.s).mul(sel.grid);
- loopoctaboxsize(cor, size, sel.o, ss)
- {
- ivec o(i, cor, size);
- if(c[i].children) countselchild(c[i].children, o, size/2);
- else
- {
- selchildcount++;
- if(c[i].material != MAT_AIR && selchildmat != MAT_AIR)
- {
- if(selchildmat < 0) selchildmat = c[i].material;
- else if(selchildmat != c[i].material) selchildmat = MAT_AIR;
- }
- }
- }
+ ivec ss = ivec(sel.s).mul(sel.grid);
+ loopoctaboxsize(cor, size, sel.o, ss)
+ {
+ ivec o(i, cor, size);
+ if(c[i].children) countselchild(c[i].children, o, size/2);
+ else
+ {
+ selchildcount++;
+ if(c[i].material != MAT_AIR && selchildmat != MAT_AIR)
+ {
+ if(selchildmat < 0) selchildmat = c[i].material;
+ else if(selchildmat != c[i].material) selchildmat = MAT_AIR;
+ }
+ }
+ }
}
void normalizelookupcube(const ivec &o)
{
- if(lusize>gridsize)
- {
- lu.x += (o.x-lu.x)/gridsize*gridsize;
- lu.y += (o.y-lu.y)/gridsize*gridsize;
- lu.z += (o.z-lu.z)/gridsize*gridsize;
- }
- else if(gridsize>lusize)
- {
- lu.x &= ~(gridsize-1);
- lu.y &= ~(gridsize-1);
- lu.z &= ~(gridsize-1);
- }
- lusize = gridsize;
+ if(lusize>gridsize)
+ {
+ lu.x += (o.x-lu.x)/gridsize*gridsize;
+ lu.y += (o.y-lu.y)/gridsize*gridsize;
+ lu.z += (o.z-lu.z)/gridsize*gridsize;
+ }
+ else if(gridsize>lusize)
+ {
+ lu.x &= ~(gridsize-1);
+ lu.y &= ~(gridsize-1);
+ lu.z &= ~(gridsize-1);
+ }
+ lusize = gridsize;
}
void updateselection()
{
- sel.o.x = min(lastcur.x, cur.x);
- sel.o.y = min(lastcur.y, cur.y);
- sel.o.z = min(lastcur.z, cur.z);
- sel.s.x = abs(lastcur.x-cur.x)/sel.grid+1;
- sel.s.y = abs(lastcur.y-cur.y)/sel.grid+1;
- sel.s.z = abs(lastcur.z-cur.z)/sel.grid+1;
+ sel.o.x = min(lastcur.x, cur.x);
+ sel.o.y = min(lastcur.y, cur.y);
+ sel.o.z = min(lastcur.z, cur.z);
+ sel.s.x = abs(lastcur.x-cur.x)/sel.grid+1;
+ sel.s.y = abs(lastcur.y-cur.y)/sel.grid+1;
+ sel.s.z = abs(lastcur.z-cur.z)/sel.grid+1;
}
bool editmoveplane(const vec &o, const vec &ray, int d, float off, vec &handle, vec &dest, bool first)
{
- plane pl(d, off);
- float dist = 0.0f;
- if(!pl.rayintersect(player->o, ray, dist))
- return false;
+ plane pl(d, off);
+ float dist = 0.0f;
+ if(!pl.rayintersect(player->o, ray, dist))
+ return false;
- dest = vec(ray).mul(dist).add(player->o);
- if(first) handle = vec(dest).sub(o);
- dest.sub(handle);
- return true;
+ dest = vec(ray).mul(dist).add(player->o);
+ if(first) handle = vec(dest).sub(o);
+ dest.sub(handle);
+ return true;
}
inline bool isheightmap(int orient, int d, bool empty, cube *c);
@@ -356,203 +356,203 @@ VARF(passthrough, 0, 0, 1, { passthroughsel = passthrough; entcancel(); });
void rendereditcursor()
{
- int d = dimension(sel.orient),
- od = dimension(orient),
- odc = dimcoord(orient);
-
- bool hidecursor = g3d_windowhit(true, false) || blendpaintmode, hovering = false;
- hmapsel = false;
-
- if(moving)
- {
- static vec dest, handle;
- if(editmoveplane(vec(sel.o), camdir, od, sel.o[D[od]]+odc*sel.grid*sel.s[D[od]], handle, dest, moving==1))
- {
- if(moving==1)
- {
- dest.add(handle);
- handle = vec(ivec(handle).mask(~(sel.grid-1)));
- dest.sub(handle);
- moving = 2;
- }
- ivec o = ivec(dest).mask(~(sel.grid-1));
- sel.o[R[od]] = o[R[od]];
- sel.o[C[od]] = o[C[od]];
- }
- }
- else
- if(entmoving)
- {
- entdrag(camdir);
- }
- else
- {
- ivec w;
- float sdist = 0, wdist = 0, t;
- int entorient = 0, ent = -1;
-
- wdist = rayent(player->o, camdir, 1e16f,
- (editmode && showmat ? RAY_EDITMAT : 0) // select cubes first
- | (!dragging && entediting && (!passthrough || !passthroughent) ? RAY_ENTS : 0)
- | RAY_SKIPFIRST
- | (passthroughcube || passthrough ? RAY_PASS : 0), gridsize, entorient, ent);
-
- if((havesel || dragging) && !passthroughsel && !hmapedit) // now try selecting the selection
- if(rayboxintersect(vec(sel.o), vec(sel.s).mul(sel.grid), player->o, camdir, sdist, orient))
- { // and choose the nearest of the two
- if(sdist < wdist)
- {
- wdist = sdist;
- ent = -1;
- }
- }
-
- if((hovering = hoveringonent(hidecursor ? -1 : ent, entorient)))
- {
- if(!havesel)
- {
- selchildcount = 0;
- selchildmat = -1;
- sel.s = ivec(0, 0, 0);
- }
- }
- else
- {
- vec w = vec(camdir).mul(wdist+0.05f).add(player->o);
- if(!insideworld(w))
- {
- loopi(3) wdist = min(wdist, ((camdir[i] > 0 ? worldsize : 0) - player->o[i]) / camdir[i]);
- w = vec(camdir).mul(wdist-0.05f).add(player->o);
- if(!insideworld(w))
- {
- wdist = 0;
- loopi(3) w[i] = clamp(player->o[i], 0.0f, float(worldsize));
- }
- }
- cube *c = &lookupcube(ivec(w));
- if(gridlookup && !dragging && !moving && !havesel && hmapedit!=1) gridsize = lusize;
- int mag = lusize / gridsize;
- normalizelookupcube(ivec(w));
- if(sdist == 0 || sdist > wdist) rayboxintersect(vec(lu), vec(gridsize), player->o, camdir, t=0, orient); // just getting orient
- cur = lu;
- cor = ivec(vec(w).mul(2).div(gridsize));
- od = dimension(orient);
- d = dimension(sel.orient);
-
- if(hmapedit==1 && dimcoord(horient) == (camdir[dimension(horient)]<0))
- {
- hmapsel = isheightmap(horient, dimension(horient), false, c);
- if(hmapsel)
- od = dimension(orient = horient);
- }
-
- if(dragging)
- {
- updateselection();
- sel.cx = min(cor[R[d]], lastcor[R[d]]);
- sel.cy = min(cor[C[d]], lastcor[C[d]]);
- sel.cxs = max(cor[R[d]], lastcor[R[d]]);
- sel.cys = max(cor[C[d]], lastcor[C[d]]);
-
- if(!selectcorners)
- {
- sel.cx &= ~1;
- sel.cy &= ~1;
- sel.cxs &= ~1;
- sel.cys &= ~1;
- sel.cxs -= sel.cx-2;
- sel.cys -= sel.cy-2;
- }
- else
- {
- sel.cxs -= sel.cx-1;
- sel.cys -= sel.cy-1;
- }
-
- sel.cx &= 1;
- sel.cy &= 1;
- havesel = true;
- }
- else if(!havesel)
- {
- sel.o = lu;
- sel.s.x = sel.s.y = sel.s.z = 1;
- sel.cx = sel.cy = 0;
- sel.cxs = sel.cys = 2;
- sel.grid = gridsize;
- sel.orient = orient;
- d = od;
- }
-
- sel.corner = (cor[R[d]]-(lu[R[d]]*2)/gridsize)+(cor[C[d]]-(lu[C[d]]*2)/gridsize)*2;
- selchildcount = 0;
- selchildmat = -1;
- countselchild(worldroot, ivec(0, 0, 0), worldsize/2);
- if(mag>=1 && selchildcount==1)
- {
- selchildmat = c->material;
- if(mag>1) selchildcount = -mag;
- }
- }
- }
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE);
-
- // cursors
-
- notextureshader->set();
-
- renderentselection(player->o, camdir, entmoving!=0);
-
- boxoutline = outline!=0;
-
- enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
-
- if(!moving && !hovering && !hidecursor)
- {
- if(hmapedit==1)
- gle::colorub(0, hmapsel ? 255 : 40, 0);
- else
- gle::colorub(120,120,120);
- boxs(orient, vec(lu), vec(lusize));
- }
-
- // selections
- if(havesel || moving)
- {
- d = dimension(sel.orient);
- gle::colorub(50,50,50); // grid
- boxsgrid(sel.orient, vec(sel.o), vec(sel.s), sel.grid);
- gle::colorub(200,0,0); // 0 reference
- boxs3D(vec(sel.o).sub(0.5f*min(gridsize*0.25f, 2.0f)), vec(min(gridsize*0.25f, 2.0f)), 1);
- gle::colorub(200,200,200);// 2D selection box
- vec co(sel.o.v), cs(sel.s.v);
- co[R[d]] += 0.5f*(sel.cx*gridsize);
- co[C[d]] += 0.5f*(sel.cy*gridsize);
- cs[R[d]] = 0.5f*(sel.cxs*gridsize);
- cs[C[d]] = 0.5f*(sel.cys*gridsize);
- cs[D[d]] *= gridsize;
- boxs(sel.orient, co, cs);
- if(hmapedit==1) // 3D selection box
- gle::colorub(0,120,0);
- else
- gle::colorub(0,0,120);
- boxs3D(vec(sel.o), vec(sel.s), sel.grid);
- }
-
- disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
-
- boxoutline = false;
-
- glDisable(GL_BLEND);
+ int d = dimension(sel.orient),
+ od = dimension(orient),
+ odc = dimcoord(orient);
+
+ bool hidecursor = g3d_windowhit(true, false) || blendpaintmode, hovering = false;
+ hmapsel = false;
+
+ if(moving)
+ {
+ static vec dest, handle;
+ if(editmoveplane(vec(sel.o), camdir, od, sel.o[D[od]]+odc*sel.grid*sel.s[D[od]], handle, dest, moving==1))
+ {
+ if(moving==1)
+ {
+ dest.add(handle);
+ handle = vec(ivec(handle).mask(~(sel.grid-1)));
+ dest.sub(handle);
+ moving = 2;
+ }
+ ivec o = ivec(dest).mask(~(sel.grid-1));
+ sel.o[R[od]] = o[R[od]];
+ sel.o[C[od]] = o[C[od]];
+ }
+ }
+ else
+ if(entmoving)
+ {
+ entdrag(camdir);
+ }
+ else
+ {
+ ivec w;
+ float sdist = 0, wdist = 0, t;
+ int entorient = 0, ent = -1;
+
+ wdist = rayent(player->o, camdir, 1e16f,
+ (editmode && showmat ? RAY_EDITMAT : 0) // select cubes first
+ | (!dragging && entediting && (!passthrough || !passthroughent) ? RAY_ENTS : 0)
+ | RAY_SKIPFIRST
+ | (passthroughcube || passthrough ? RAY_PASS : 0), gridsize, entorient, ent);
+
+ if((havesel || dragging) && !passthroughsel && !hmapedit) // now try selecting the selection
+ if(rayboxintersect(vec(sel.o), vec(sel.s).mul(sel.grid), player->o, camdir, sdist, orient))
+ { // and choose the nearest of the two
+ if(sdist < wdist)
+ {
+ wdist = sdist;
+ ent = -1;
+ }
+ }
+
+ if((hovering = hoveringonent(hidecursor ? -1 : ent, entorient)))
+ {
+ if(!havesel)
+ {
+ selchildcount = 0;
+ selchildmat = -1;
+ sel.s = ivec(0, 0, 0);
+ }
+ }
+ else
+ {
+ vec w = vec(camdir).mul(wdist+0.05f).add(player->o);
+ if(!insideworld(w))
+ {
+ loopi(3) wdist = min(wdist, ((camdir[i] > 0 ? worldsize : 0) - player->o[i]) / camdir[i]);
+ w = vec(camdir).mul(wdist-0.05f).add(player->o);
+ if(!insideworld(w))
+ {
+ wdist = 0;
+ loopi(3) w[i] = clamp(player->o[i], 0.0f, float(worldsize));
+ }
+ }
+ cube *c = &lookupcube(ivec(w));
+ if(gridlookup && !dragging && !moving && !havesel && hmapedit!=1) gridsize = lusize;
+ int mag = lusize / gridsize;
+ normalizelookupcube(ivec(w));
+ if(sdist == 0 || sdist > wdist) rayboxintersect(vec(lu), vec(gridsize), player->o, camdir, t=0, orient); // just getting orient
+ cur = lu;
+ cor = ivec(vec(w).mul(2).div(gridsize));
+ od = dimension(orient);
+ d = dimension(sel.orient);
+
+ if(hmapedit==1 && dimcoord(horient) == (camdir[dimension(horient)]<0))
+ {
+ hmapsel = isheightmap(horient, dimension(horient), false, c);
+ if(hmapsel)
+ od = dimension(orient = horient);
+ }
+
+ if(dragging)
+ {
+ updateselection();
+ sel.cx = min(cor[R[d]], lastcor[R[d]]);
+ sel.cy = min(cor[C[d]], lastcor[C[d]]);
+ sel.cxs = max(cor[R[d]], lastcor[R[d]]);
+ sel.cys = max(cor[C[d]], lastcor[C[d]]);
+
+ if(!selectcorners)
+ {
+ sel.cx &= ~1;
+ sel.cy &= ~1;
+ sel.cxs &= ~1;
+ sel.cys &= ~1;
+ sel.cxs -= sel.cx-2;
+ sel.cys -= sel.cy-2;
+ }
+ else
+ {
+ sel.cxs -= sel.cx-1;
+ sel.cys -= sel.cy-1;
+ }
+
+ sel.cx &= 1;
+ sel.cy &= 1;
+ havesel = true;
+ }
+ else if(!havesel)
+ {
+ sel.o = lu;
+ sel.s.x = sel.s.y = sel.s.z = 1;
+ sel.cx = sel.cy = 0;
+ sel.cxs = sel.cys = 2;
+ sel.grid = gridsize;
+ sel.orient = orient;
+ d = od;
+ }
+
+ sel.corner = (cor[R[d]]-(lu[R[d]]*2)/gridsize)+(cor[C[d]]-(lu[C[d]]*2)/gridsize)*2;
+ selchildcount = 0;
+ selchildmat = -1;
+ countselchild(worldroot, ivec(0, 0, 0), worldsize/2);
+ if(mag>=1 && selchildcount==1)
+ {
+ selchildmat = c->material;
+ if(mag>1) selchildcount = -mag;
+ }
+ }
+ }
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE);
+
+ // cursors
+
+ notextureshader->set();
+
+ renderentselection(player->o, camdir, entmoving!=0);
+
+ boxoutline = outline!=0;
+
+ enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
+
+ if(!moving && !hovering && !hidecursor)
+ {
+ if(hmapedit==1)
+ gle::colorub(0, hmapsel ? 255 : 40, 0);
+ else
+ gle::colorub(120,120,120);
+ boxs(orient, vec(lu), vec(lusize));
+ }
+
+ // selections
+ if(havesel || moving)
+ {
+ d = dimension(sel.orient);
+ gle::colorub(50,50,50); // grid
+ boxsgrid(sel.orient, vec(sel.o), vec(sel.s), sel.grid);
+ gle::colorub(200,0,0); // 0 reference
+ boxs3D(vec(sel.o).sub(0.5f*min(gridsize*0.25f, 2.0f)), vec(min(gridsize*0.25f, 2.0f)), 1);
+ gle::colorub(200,200,200);// 2D selection box
+ vec co(sel.o.v), cs(sel.s.v);
+ co[R[d]] += 0.5f*(sel.cx*gridsize);
+ co[C[d]] += 0.5f*(sel.cy*gridsize);
+ cs[R[d]] = 0.5f*(sel.cxs*gridsize);
+ cs[C[d]] = 0.5f*(sel.cys*gridsize);
+ cs[D[d]] *= gridsize;
+ boxs(sel.orient, co, cs);
+ if(hmapedit==1) // 3D selection box
+ gle::colorub(0,120,0);
+ else
+ gle::colorub(0,0,120);
+ boxs3D(vec(sel.o), vec(sel.s), sel.grid);
+ }
+
+ disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
+
+ boxoutline = false;
+
+ glDisable(GL_BLEND);
}
void tryedit()
{
- extern int hidehud;
- if(!editmode || hidehud || mainmenu) return;
- if(blendpaintmode) trypaintblendmap();
+ extern int hidehud;
+ if(!editmode || hidehud || mainmenu) return;
+ if(blendpaintmode) trypaintblendmap();
}
//////////// ready changes to vertex arrays ////////////
@@ -561,197 +561,196 @@ static bool haschanged = false;
void readychanges(const ivec &bbmin, const ivec &bbmax, cube *c, const ivec &cor, int size)
{
- loopoctabox(cor, size, bbmin, bbmax)
- {
- ivec o(i, cor, size);
- if(c[i].ext)
- {
- if(c[i].ext->va) // removes va s so that octarender will recreate
- {
- int hasmerges = c[i].ext->va->hasmerges;
- destroyva(c[i].ext->va);
- c[i].ext->va = NULL;
- if(hasmerges) invalidatemerges(c[i], o, size, true);
- }
- freeoctaentities(c[i]);
- c[i].ext->tjoints = -1;
- }
- if(c[i].children)
- {
- if(size<=1)
- {
- solidfaces(c[i]);
- discardchildren(c[i], true);
- brightencube(c[i]);
- }
- else readychanges(bbmin, bbmax, c[i].children, o, size/2);
- }
- else brightencube(c[i]);
- }
+ loopoctabox(cor, size, bbmin, bbmax)
+ {
+ ivec o(i, cor, size);
+ if(c[i].ext)
+ {
+ if(c[i].ext->va) // removes va s so that octarender will recreate
+ {
+ int hasmerges = c[i].ext->va->hasmerges;
+ destroyva(c[i].ext->va);
+ c[i].ext->va = NULL;
+ if(hasmerges) invalidatemerges(c[i], o, size, true);
+ }
+ freeoctaentities(c[i]);
+ c[i].ext->tjoints = -1;
+ }
+ if(c[i].children)
+ {
+ if(size<=1)
+ {
+ solidfaces(c[i]);
+ discardchildren(c[i], true);
+ brightencube(c[i]);
+ }
+ else readychanges(bbmin, bbmax, c[i].children, o, size/2);
+ }
+ else brightencube(c[i]);
+ }
}
void commitchanges(bool force)
{
- if(!force && !haschanged) return;
- haschanged = false;
+ if(!force && !haschanged) return;
+ haschanged = false;
- int oldlen = valist.length();
- resetclipplanes();
- entitiesinoctanodes();
- inbetweenframes = false;
- octarender();
- inbetweenframes = true;
- setupmaterials(oldlen);
- invalidatepostfx();
- updatevabbs();
+ int oldlen = valist.length();
+ resetclipplanes();
+ entitiesinoctanodes();
+ inbetweenframes = false;
+ octarender();
+ inbetweenframes = true;
+ setupmaterials(oldlen);
+ updatevabbs();
}
void changed(const block3 &sel, bool commit = true)
{
- if(sel.s.iszero()) return;
- readychanges(ivec(sel.o).sub(1), ivec(sel.s).mul(sel.grid).add(sel.o).add(1), worldroot, ivec(0, 0, 0), worldsize/2);
- haschanged = true;
+ if(sel.s.iszero()) return;
+ readychanges(ivec(sel.o).sub(1), ivec(sel.s).mul(sel.grid).add(sel.o).add(1), worldroot, ivec(0, 0, 0), worldsize/2);
+ haschanged = true;
- if(commit) commitchanges();
+ if(commit) commitchanges();
}
//////////// copy and undo /////////////
static inline void copycube(const cube &src, cube &dst)
{
- dst = src;
- dst.visible = 0;
- dst.merged = 0;
- dst.ext = NULL; // src cube is responsible for va destruction
- if(src.children)
- {
- dst.children = newcubes(F_EMPTY);
- loopi(8) copycube(src.children[i], dst.children[i]);
- }
+ dst = src;
+ dst.visible = 0;
+ dst.merged = 0;
+ dst.ext = NULL; // src cube is responsible for va destruction
+ if(src.children)
+ {
+ dst.children = newcubes(F_EMPTY);
+ loopi(8) copycube(src.children[i], dst.children[i]);
+ }
}
static inline void pastecube(const cube &src, cube &dst)
{
- discardchildren(dst);
- copycube(src, dst);
+ discardchildren(dst);
+ copycube(src, dst);
}
void blockcopy(const block3 &s, int rgrid, block3 *b)
{
- *b = s;
- cube *q = b->c();
- loopxyz(s, rgrid, copycube(c, *q++));
+ *b = s;
+ cube *q = b->c();
+ loopxyz(s, rgrid, copycube(c, *q++));
}
block3 *blockcopy(const block3 &s, int rgrid)
{
- int bsize = sizeof(block3)+sizeof(cube)*s.size();
- if(bsize <= 0 || bsize > (100<<20)) return NULL;
- block3 *b = (block3 *)new (false) uchar[bsize];
- if(b) blockcopy(s, rgrid, b);
- return b;
+ int bsize = sizeof(block3)+sizeof(cube)*s.size();
+ if(bsize <= 0 || bsize > (100<<20)) return NULL;
+ block3 *b = (block3 *)new (false) uchar[bsize];
+ if(b) blockcopy(s, rgrid, b);
+ return b;
}
void freeblock(block3 *b, bool alloced = true)
{
- cube *q = b->c();
- loopi(b->size()) discardchildren(*q++);
- if(alloced) delete[] b;
+ cube *q = b->c();
+ loopi(b->size()) discardchildren(*q++);
+ if(alloced) delete[] b;
}
-void selgridmap(selinfo &sel, uchar *g) // generates a map of the cube sizes at each grid point
+void selgridmap(selinfo &sel, uchar *g) // generates a map of the cube sizes at each grid point
{
- loopxyz(sel, -sel.grid, (*g++ = bitscan(lusize), (void)c));
+ loopxyz(sel, -sel.grid, (*g++ = bitscan(lusize), (void)c));
}
void freeundo(undoblock *u)
{
- if(!u->numents) freeblock(u->block(), false);
- delete[] (uchar *)u;
+ if(!u->numents) freeblock(u->block(), false);
+ delete[] (uchar *)u;
}
void pasteundoblock(block3 *b, uchar *g)
{
- cube *s = b->c();
- loopxyz(*b, 1<<min(int(*g++), worldscale-1), pastecube(*s++, c));
+ cube *s = b->c();
+ loopxyz(*b, 1<<min(int(*g++), worldscale-1), pastecube(*s++, c));
}
void pasteundo(undoblock *u)
{
- if(u->numents) pasteundoents(u);
- else pasteundoblock(u->block(), u->gridmap());
+ if(u->numents) pasteundoents(u);
+ else pasteundoblock(u->block(), u->gridmap());
}
static inline int undosize(undoblock *u)
{
- if(u->numents) return u->numents*sizeof(undoent);
- else
- {
- block3 *b = u->block();
- cube *q = b->c();
- int size = b->size(), total = size;
- loopj(size) total += familysize(*q++)*sizeof(cube);
- return total;
- }
+ if(u->numents) return u->numents*sizeof(undoent);
+ else
+ {
+ block3 *b = u->block();
+ cube *q = b->c();
+ int size = b->size(), total = size;
+ loopj(size) total += familysize(*q++)*sizeof(cube);
+ return total;
+ }
}
struct undolist
{
- undoblock *first, *last;
-
- undolist() : first(NULL), last(NULL) {}
-
- bool empty() { return !first; }
-
- void add(undoblock *u)
- {
- u->next = NULL;
- u->prev = last;
- if(!first) first = last = u;
- else
- {
- last->next = u;
- last = u;
- }
- }
-
- undoblock *popfirst()
- {
- undoblock *u = first;
- first = first->next;
- if(first) first->prev = NULL;
- else last = NULL;
- return u;
- }
-
- undoblock *poplast()
- {
- undoblock *u = last;
- last = last->prev;
- if(last) last->next = NULL;
- else first = NULL;
- return u;
- }
+ undoblock *first, *last;
+
+ undolist() : first(NULL), last(NULL) {}
+
+ bool empty() { return !first; }
+
+ void add(undoblock *u)
+ {
+ u->next = NULL;
+ u->prev = last;
+ if(!first) first = last = u;
+ else
+ {
+ last->next = u;
+ last = u;
+ }
+ }
+
+ undoblock *popfirst()
+ {
+ undoblock *u = first;
+ first = first->next;
+ if(first) first->prev = NULL;
+ else last = NULL;
+ return u;
+ }
+
+ undoblock *poplast()
+ {
+ undoblock *u = last;
+ last = last->prev;
+ if(last) last->next = NULL;
+ else first = NULL;
+ return u;
+ }
};
undolist undos, redos;
-VARP(undomegs, 0, 8, 100); // bounded by n megs
+VARP(undomegs, 0, 8, 100); // bounded by n megs
int totalundos = 0;
-void pruneundos(int maxremain) // bound memory
+void pruneundos(int maxremain) // bound memory
{
- while(totalundos > maxremain && !undos.empty())
- {
- undoblock *u = undos.popfirst();
- totalundos -= u->size;
- freeundo(u);
- }
- //conoutf(CON_DEBUG, "undo: %d of %d(%%%d)", totalundos, undomegs<<20, totalundos*100/(undomegs<<20));
- while(!redos.empty())
- {
- undoblock *u = redos.popfirst();
- totalundos -= u->size;
- freeundo(u);
- }
+ while(totalundos > maxremain && !undos.empty())
+ {
+ undoblock *u = undos.popfirst();
+ totalundos -= u->size;
+ freeundo(u);
+ }
+ //conoutf(CON_DEBUG, "undo: %d of %d(%%%d)", totalundos, undomegs<<20, totalundos*100/(undomegs<<20));
+ while(!redos.empty())
+ {
+ undoblock *u = redos.popfirst();
+ totalundos -= u->size;
+ freeundo(u);
+ }
}
void clearundos() { pruneundos(0); }
@@ -760,106 +759,106 @@ COMMAND(clearundos, "");
undoblock *newundocube(selinfo &s)
{
- int ssize = s.size(),
- selgridsize = ssize,
- blocksize = sizeof(block3)+ssize*sizeof(cube);
- if(blocksize <= 0 || blocksize > (undomegs<<20)) return NULL;
- undoblock *u = (undoblock *)new (false) uchar[sizeof(undoblock) + blocksize + selgridsize];
- if(!u) return NULL;
- u->numents = 0;
- block3 *b = u->block();
- blockcopy(s, -s.grid, b);
- uchar *g = u->gridmap();
- selgridmap(s, g);
- return u;
+ int ssize = s.size(),
+ selgridsize = ssize,
+ blocksize = sizeof(block3)+ssize*sizeof(cube);
+ if(blocksize <= 0 || blocksize > (undomegs<<20)) return NULL;
+ undoblock *u = (undoblock *)new (false) uchar[sizeof(undoblock) + blocksize + selgridsize];
+ if(!u) return NULL;
+ u->numents = 0;
+ block3 *b = u->block();
+ blockcopy(s, -s.grid, b);
+ uchar *g = u->gridmap();
+ selgridmap(s, g);
+ return u;
}
void addundo(undoblock *u)
{
- u->size = undosize(u);
- u->timestamp = totalmillis;
- undos.add(u);
- totalundos += u->size;
- pruneundos(undomegs<<20);
+ u->size = undosize(u);
+ u->timestamp = totalmillis;
+ undos.add(u);
+ totalundos += u->size;
+ pruneundos(undomegs<<20);
}
VARP(nompedit, 0, 1, 1);
void makeundo(selinfo &s)
{
- undoblock *u = newundocube(s);
- if(u) addundo(u);
+ undoblock *u = newundocube(s);
+ if(u) addundo(u);
}
-void makeundo() // stores state of selected cubes before editing
+void makeundo() // stores state of selected cubes before editing
{
- if(lastsel==sel || sel.s.iszero()) return;
- lastsel=sel;
- makeundo(sel);
+ if(lastsel==sel || sel.s.iszero()) return;
+ lastsel=sel;
+ makeundo(sel);
}
static inline int countblock(cube *c, int n = 8)
{
- int r = 0;
- loopi(n) if(c[i].children) r += countblock(c[i].children); else ++r;
- return r;
+ int r = 0;
+ loopi(n) if(c[i].children) r += countblock(c[i].children); else ++r;
+ return r;
}
static int countblock(block3 *b) { return countblock(b->c(), b->size()); }
void swapundo(undolist &a, undolist &b, int op)
{
- if(noedit()) return;
- if(a.empty()) { conoutf(CON_WARN, "nothing more to %s", op == EDIT_REDO ? "redo" : "undo"); return; }
- int ts = a.last->timestamp;
- if(multiplayer(false))
- {
- int n = 0, ops = 0;
- for(undoblock *u = a.last; u && ts==u->timestamp; u = u->prev)
- {
- ++ops;
- n += u->numents ? u->numents : countblock(u->block());
- if(ops > 10 || n > 2500)
- {
- conoutf(CON_WARN, "undo too big for multiplayer");
- if(nompedit) { multiplayer(); return; }
- op = -1;
- break;
- }
- }
- }
- selinfo l = sel;
- while(!a.empty() && ts==a.last->timestamp)
- {
- if(op >= 0) game::edittrigger(sel, op);
- undoblock *u = a.poplast(), *r;
- if(u->numents) r = copyundoents(u);
- else
- {
- block3 *ub = u->block();
- l.o = ub->o;
- l.s = ub->s;
- l.grid = ub->grid;
- l.orient = ub->orient;
- r = newundocube(l);
- }
- if(r)
- {
- r->size = u->size;
- r->timestamp = totalmillis;
- b.add(r);
- }
- pasteundo(u);
- if(!u->numents) changed(*u->block(), false);
- freeundo(u);
- }
- commitchanges();
- if(!hmapsel)
- {
- sel = l;
- reorient();
- }
- forcenextundo();
+ if(noedit()) return;
+ if(a.empty()) { conoutf(CON_WARN, "nothing more to %s", op == EDIT_REDO ? "redo" : "undo"); return; }
+ int ts = a.last->timestamp;
+ if(multiplayer(false))
+ {
+ int n = 0, ops = 0;
+ for(undoblock *u = a.last; u && ts==u->timestamp; u = u->prev)
+ {
+ ++ops;
+ n += u->numents ? u->numents : countblock(u->block());
+ if(ops > 10 || n > 2500)
+ {
+ conoutf(CON_WARN, "undo too big for multiplayer");
+ if(nompedit) { multiplayer(); return; }
+ op = -1;
+ break;
+ }
+ }
+ }
+ selinfo l = sel;
+ while(!a.empty() && ts==a.last->timestamp)
+ {
+ if(op >= 0) game::edittrigger(sel, op);
+ undoblock *u = a.poplast(), *r;
+ if(u->numents) r = copyundoents(u);
+ else
+ {
+ block3 *ub = u->block();
+ l.o = ub->o;
+ l.s = ub->s;
+ l.grid = ub->grid;
+ l.orient = ub->orient;
+ r = newundocube(l);
+ }
+ if(r)
+ {
+ r->size = u->size;
+ r->timestamp = totalmillis;
+ b.add(r);
+ }
+ pasteundo(u);
+ if(!u->numents) changed(*u->block(), false);
+ freeundo(u);
+ }
+ commitchanges();
+ if(!hmapsel)
+ {
+ sel = l;
+ reorient();
+ }
+ forcenextundo();
}
void editundo() { swapundo(undos, redos, EDIT_UNDO); }
@@ -874,388 +873,388 @@ editinfo *localedit = NULL;
template<class B>
static void packcube(cube &c, B &buf)
{
- if(c.children)
- {
- buf.put(0xFF);
- loopi(8) packcube(c.children[i], buf);
- }
- else
- {
- cube data = c;
- lilswap(data.texture, 6);
- buf.put(c.material&0xFF);
- buf.put(c.material>>8);
- buf.put(data.edges, sizeof(data.edges));
- buf.put((uchar *)data.texture, sizeof(data.texture));
- }
+ if(c.children)
+ {
+ buf.put(0xFF);
+ loopi(8) packcube(c.children[i], buf);
+ }
+ else
+ {
+ cube data = c;
+ lilswap(data.texture, 6);
+ buf.put(c.material&0xFF);
+ buf.put(c.material>>8);
+ buf.put(data.edges, sizeof(data.edges));
+ buf.put((uchar *)data.texture, sizeof(data.texture));
+ }
}
template<class B>
static bool packblock(block3 &b, B &buf)
{
- if(b.size() <= 0 || b.size() > (1<<20)) return false;
- block3 hdr = b;
- lilswap(hdr.o.v, 3);
- lilswap(hdr.s.v, 3);
- lilswap(&hdr.grid, 1);
- lilswap(&hdr.orient, 1);
- buf.put((const uchar *)&hdr, sizeof(hdr));
- cube *c = b.c();
- loopi(b.size()) packcube(c[i], buf);
- return true;
+ if(b.size() <= 0 || b.size() > (1<<20)) return false;
+ block3 hdr = b;
+ lilswap(hdr.o.v, 3);
+ lilswap(hdr.s.v, 3);
+ lilswap(&hdr.grid, 1);
+ lilswap(&hdr.orient, 1);
+ buf.put((const uchar *)&hdr, sizeof(hdr));
+ cube *c = b.c();
+ loopi(b.size()) packcube(c[i], buf);
+ return true;
}
struct vslothdr
{
- ushort index;
- ushort slot;
+ ushort index;
+ ushort slot;
};
static void packvslots(cube &c, vector<uchar> &buf, vector<ushort> &used)
{
- if(c.children)
- {
- loopi(8) packvslots(c.children[i], buf, used);
- }
- else loopi(6)
- {
- ushort index = c.texture[i];
- if(vslots.inrange(index) && vslots[index]->changed && used.find(index) < 0)
- {
- used.add(index);
- VSlot &vs = *vslots[index];
- vslothdr &hdr = *(vslothdr *)buf.pad(sizeof(vslothdr));
- hdr.index = index;
- hdr.slot = vs.slot->index;
- lilswap(&hdr.index, 2);
- packvslot(buf, vs);
- }
- }
+ if(c.children)
+ {
+ loopi(8) packvslots(c.children[i], buf, used);
+ }
+ else loopi(6)
+ {
+ ushort index = c.texture[i];
+ if(vslots.inrange(index) && vslots[index]->changed && used.find(index) < 0)
+ {
+ used.add(index);
+ VSlot &vs = *vslots[index];
+ vslothdr &hdr = *(vslothdr *)buf.pad(sizeof(vslothdr));
+ hdr.index = index;
+ hdr.slot = vs.slot->index;
+ lilswap(&hdr.index, 2);
+ packvslot(buf, vs);
+ }
+ }
}
static void packvslots(block3 &b, vector<uchar> &buf)
{
- vector<ushort> used;
- cube *c = b.c();
- loopi(b.size()) packvslots(c[i], buf, used);
- memset(buf.pad(sizeof(vslothdr)), 0, sizeof(vslothdr));
+ vector<ushort> used;
+ cube *c = b.c();
+ loopi(b.size()) packvslots(c[i], buf, used);
+ memset(buf.pad(sizeof(vslothdr)), 0, sizeof(vslothdr));
}
template<class B>
static void unpackcube(cube &c, B &buf)
{
- int mat = buf.get();
- if(mat == 0xFF)
- {
- c.children = newcubes(F_EMPTY);
- loopi(8) unpackcube(c.children[i], buf);
- }
- else
- {
- c.material = mat | (buf.get()<<8);
- buf.get(c.edges, sizeof(c.edges));
- buf.get((uchar *)c.texture, sizeof(c.texture));
- lilswap(c.texture, 6);
- }
+ int mat = buf.get();
+ if(mat == 0xFF)
+ {
+ c.children = newcubes(F_EMPTY);
+ loopi(8) unpackcube(c.children[i], buf);
+ }
+ else
+ {
+ c.material = mat | (buf.get()<<8);
+ buf.get(c.edges, sizeof(c.edges));
+ buf.get((uchar *)c.texture, sizeof(c.texture));
+ lilswap(c.texture, 6);
+ }
}
template<class B>
static bool unpackblock(block3 *&b, B &buf)
{
- if(b) { freeblock(b); b = NULL; }
- block3 hdr;
- if(buf.get((uchar *)&hdr, sizeof(hdr)) < int(sizeof(hdr))) return false;
- lilswap(hdr.o.v, 3);
- lilswap(hdr.s.v, 3);
- lilswap(&hdr.grid, 1);
- lilswap(&hdr.orient, 1);
- if(hdr.size() > (1<<20) || hdr.grid <= 0 || hdr.grid > (1<<12)) return false;
- b = (block3 *)new (false) uchar[sizeof(block3)+hdr.size()*sizeof(cube)];
- if(!b) return false;
- *b = hdr;
- cube *c = b->c();
- memset(c, 0, b->size()*sizeof(cube));
- loopi(b->size()) unpackcube(c[i], buf);
- return true;
+ if(b) { freeblock(b); b = NULL; }
+ block3 hdr;
+ if(buf.get((uchar *)&hdr, sizeof(hdr)) < int(sizeof(hdr))) return false;
+ lilswap(hdr.o.v, 3);
+ lilswap(hdr.s.v, 3);
+ lilswap(&hdr.grid, 1);
+ lilswap(&hdr.orient, 1);
+ if(hdr.size() > (1<<20) || hdr.grid <= 0 || hdr.grid > (1<<12)) return false;
+ b = (block3 *)new (false) uchar[sizeof(block3)+hdr.size()*sizeof(cube)];
+ if(!b) return false;
+ *b = hdr;
+ cube *c = b->c();
+ memset(c, 0, b->size()*sizeof(cube));
+ loopi(b->size()) unpackcube(c[i], buf);
+ return true;
}
struct vslotmap
{
- int index;
- VSlot *vslot;
+ int index;
+ VSlot *vslot;
- vslotmap() {}
- vslotmap(int index, VSlot *vslot) : index(index), vslot(vslot) {}
+ vslotmap() {}
+ vslotmap(int index, VSlot *vslot) : index(index), vslot(vslot) {}
};
static vector<vslotmap> unpackingvslots;
static void unpackvslots(cube &c, ucharbuf &buf)
{
- if(c.children)
- {
- loopi(8) unpackvslots(c.children[i], buf);
- }
- else loopi(6)
- {
- ushort tex = c.texture[i];
- loopvj(unpackingvslots) if(unpackingvslots[j].index == tex) { c.texture[i] = unpackingvslots[j].vslot->index; break; }
- }
+ if(c.children)
+ {
+ loopi(8) unpackvslots(c.children[i], buf);
+ }
+ else loopi(6)
+ {
+ ushort tex = c.texture[i];
+ loopvj(unpackingvslots) if(unpackingvslots[j].index == tex) { c.texture[i] = unpackingvslots[j].vslot->index; break; }
+ }
}
static void unpackvslots(block3 &b, ucharbuf &buf)
{
- while(buf.remaining() >= int(sizeof(vslothdr)))
- {
- vslothdr &hdr = *(vslothdr *)buf.pad(sizeof(vslothdr));
- lilswap(&hdr.index, 2);
- if(!hdr.index) break;
- VSlot &vs = *lookupslot(hdr.slot, false).variants;
- VSlot ds;
- if(!unpackvslot(buf, ds, false)) break;
- if(vs.index < 0 || vs.index == DEFAULT_SKY) continue;
- VSlot *edit = editvslot(vs, ds);
- unpackingvslots.add(vslotmap(hdr.index, edit ? edit : &vs));
- }
+ while(buf.remaining() >= int(sizeof(vslothdr)))
+ {
+ vslothdr &hdr = *(vslothdr *)buf.pad(sizeof(vslothdr));
+ lilswap(&hdr.index, 2);
+ if(!hdr.index) break;
+ VSlot &vs = *lookupslot(hdr.slot, false).variants;
+ VSlot ds;
+ if(!unpackvslot(buf, ds, false)) break;
+ if(vs.index < 0 || vs.index == DEFAULT_SKY) continue;
+ VSlot *edit = editvslot(vs, ds);
+ unpackingvslots.add(vslotmap(hdr.index, edit ? edit : &vs));
+ }
- cube *c = b.c();
- loopi(b.size()) unpackvslots(c[i], buf);
+ cube *c = b.c();
+ loopi(b.size()) unpackvslots(c[i], buf);
- unpackingvslots.setsize(0);
+ unpackingvslots.setsize(0);
}
static bool compresseditinfo(const uchar *inbuf, int inlen, uchar *&outbuf, int &outlen)
{
- uLongf len = compressBound(inlen);
- if(len > (1<<20)) return false;
- outbuf = new (false) uchar[len];
- if(!outbuf || compress2((Bytef *)outbuf, &len, (const Bytef *)inbuf, inlen, Z_BEST_COMPRESSION) != Z_OK || len > (1<<16))
- {
- delete[] outbuf;
- outbuf = NULL;
- return false;
- }
- outlen = len;
- return true;
+ uLongf len = compressBound(inlen);
+ if(len > (1<<20)) return false;
+ outbuf = new (false) uchar[len];
+ if(!outbuf || compress2((Bytef *)outbuf, &len, (const Bytef *)inbuf, inlen, Z_BEST_COMPRESSION) != Z_OK || len > (1<<16))
+ {
+ delete[] outbuf;
+ outbuf = NULL;
+ return false;
+ }
+ outlen = len;
+ return true;
}
static bool uncompresseditinfo(const uchar *inbuf, int inlen, uchar *&outbuf, int &outlen)
{
- if(compressBound(outlen) > (1<<20)) return false;
- uLongf len = outlen;
- outbuf = new (false) uchar[len];
- if(!outbuf || uncompress((Bytef *)outbuf, &len, (const Bytef *)inbuf, inlen) != Z_OK)
- {
- delete[] outbuf;
- outbuf = NULL;
- return false;
- }
- outlen = len;
- return true;
+ if(compressBound(outlen) > (1<<20)) return false;
+ uLongf len = outlen;
+ outbuf = new (false) uchar[len];
+ if(!outbuf || uncompress((Bytef *)outbuf, &len, (const Bytef *)inbuf, inlen) != Z_OK)
+ {
+ delete[] outbuf;
+ outbuf = NULL;
+ return false;
+ }
+ outlen = len;
+ return true;
}
bool packeditinfo(editinfo *e, int &inlen, uchar *&outbuf, int &outlen)
{
- vector<uchar> buf;
- if(!e || !e->copy || !packblock(*e->copy, buf)) return false;
- packvslots(*e->copy, buf);
- inlen = buf.length();
- return compresseditinfo(buf.getbuf(), buf.length(), outbuf, outlen);
+ vector<uchar> buf;
+ if(!e || !e->copy || !packblock(*e->copy, buf)) return false;
+ packvslots(*e->copy, buf);
+ inlen = buf.length();
+ return compresseditinfo(buf.getbuf(), buf.length(), outbuf, outlen);
}
bool unpackeditinfo(editinfo *&e, const uchar *inbuf, int inlen, int outlen)
{
- if(e && e->copy) { freeblock(e->copy); e->copy = NULL; }
- uchar *outbuf = NULL;
- if(!uncompresseditinfo(inbuf, inlen, outbuf, outlen)) return false;
- ucharbuf buf(outbuf, outlen);
- if(!e) e = editinfos.add(new editinfo);
- if(!unpackblock(e->copy, buf))
- {
- delete[] outbuf;
- return false;
- }
- unpackvslots(*e->copy, buf);
- delete[] outbuf;
- return true;
+ if(e && e->copy) { freeblock(e->copy); e->copy = NULL; }
+ uchar *outbuf = NULL;
+ if(!uncompresseditinfo(inbuf, inlen, outbuf, outlen)) return false;
+ ucharbuf buf(outbuf, outlen);
+ if(!e) e = editinfos.add(new editinfo);
+ if(!unpackblock(e->copy, buf))
+ {
+ delete[] outbuf;
+ return false;
+ }
+ unpackvslots(*e->copy, buf);
+ delete[] outbuf;
+ return true;
}
void freeeditinfo(editinfo *&e)
{
- if(!e) return;
- editinfos.removeobj(e);
- if(e->copy) freeblock(e->copy);
- delete e;
- e = NULL;
+ if(!e) return;
+ editinfos.removeobj(e);
+ if(e->copy) freeblock(e->copy);
+ delete e;
+ e = NULL;
}
bool packundo(undoblock *u, int &inlen, uchar *&outbuf, int &outlen)
{
- vector<uchar> buf;
- buf.reserve(512);
- *(ushort *)buf.pad(2) = lilswap(ushort(u->numents));
- if(u->numents)
- {
- undoent *ue = u->ents();
- loopi(u->numents)
- {
- *(ushort *)buf.pad(2) = lilswap(ushort(ue[i].i));
- entity &e = *(entity *)buf.pad(sizeof(entity));
- e = ue[i].e;
- lilswap(&e.o.x, 3);
- lilswap(&e.attr1, 5);
- }
- }
- else
- {
- block3 &b = *u->block();
- if(!packblock(b, buf)) return false;
- buf.put(u->gridmap(), b.size());
- packvslots(b, buf);
- }
- inlen = buf.length();
- return compresseditinfo(buf.getbuf(), buf.length(), outbuf, outlen);
+ vector<uchar> buf;
+ buf.reserve(512);
+ *(ushort *)buf.pad(2) = lilswap(ushort(u->numents));
+ if(u->numents)
+ {
+ undoent *ue = u->ents();
+ loopi(u->numents)
+ {
+ *(ushort *)buf.pad(2) = lilswap(ushort(ue[i].i));
+ entity &e = *(entity *)buf.pad(sizeof(entity));
+ e = ue[i].e;
+ lilswap(&e.o.x, 3);
+ lilswap(&e.attr1, 5);
+ }
+ }
+ else
+ {
+ block3 &b = *u->block();
+ if(!packblock(b, buf)) return false;
+ buf.put(u->gridmap(), b.size());
+ packvslots(b, buf);
+ }
+ inlen = buf.length();
+ return compresseditinfo(buf.getbuf(), buf.length(), outbuf, outlen);
}
bool unpackundo(const uchar *inbuf, int inlen, int outlen)
{
- uchar *outbuf = NULL;
- if(!uncompresseditinfo(inbuf, inlen, outbuf, outlen)) return false;
- ucharbuf buf(outbuf, outlen);
- if(buf.remaining() < 2)
- {
- delete[] outbuf;
- return false;
- }
- int numents = lilswap(*(const ushort *)buf.pad(2));
- if(numents)
- {
- if(buf.remaining() < numents*int(2 + sizeof(entity)))
- {
- delete[] outbuf;
- return false;
- }
- loopi(numents)
- {
- int idx = lilswap(*(const ushort *)buf.pad(2));
- entity &e = *(entity *)buf.pad(sizeof(entity));
- lilswap(&e.o.x, 3);
- lilswap(&e.attr1, 5);
- pasteundoent(idx, e);
- }
- }
- else
- {
- block3 *b = NULL;
- if(!unpackblock(b, buf) || b->grid >= worldsize || buf.remaining() < b->size())
- {
- freeblock(b);
- delete[] outbuf;
- return false;
- }
- uchar *g = buf.pad(b->size());
- unpackvslots(*b, buf);
- pasteundoblock(b, g);
- changed(*b, false);
- freeblock(b);
- }
- delete[] outbuf;
- commitchanges();
- return true;
+ uchar *outbuf = NULL;
+ if(!uncompresseditinfo(inbuf, inlen, outbuf, outlen)) return false;
+ ucharbuf buf(outbuf, outlen);
+ if(buf.remaining() < 2)
+ {
+ delete[] outbuf;
+ return false;
+ }
+ int numents = lilswap(*(const ushort *)buf.pad(2));
+ if(numents)
+ {
+ if(buf.remaining() < numents*int(2 + sizeof(entity)))
+ {
+ delete[] outbuf;
+ return false;
+ }
+ loopi(numents)
+ {
+ int idx = lilswap(*(const ushort *)buf.pad(2));
+ entity &e = *(entity *)buf.pad(sizeof(entity));
+ lilswap(&e.o.x, 3);
+ lilswap(&e.attr1, 5);
+ pasteundoent(idx, e);
+ }
+ }
+ else
+ {
+ block3 *b = NULL;
+ if(!unpackblock(b, buf) || b->grid >= worldsize || buf.remaining() < b->size())
+ {
+ freeblock(b);
+ delete[] outbuf;
+ return false;
+ }
+ uchar *g = buf.pad(b->size());
+ unpackvslots(*b, buf);
+ pasteundoblock(b, g);
+ changed(*b, false);
+ freeblock(b);
+ }
+ delete[] outbuf;
+ commitchanges();
+ return true;
}
bool packundo(int op, int &inlen, uchar *&outbuf, int &outlen)
{
- switch(op)
- {
- case EDIT_UNDO: return !undos.empty() && packundo(undos.last, inlen, outbuf, outlen);
- case EDIT_REDO: return !redos.empty() && packundo(redos.last, inlen, outbuf, outlen);
- default: return false;
- }
+ switch(op)
+ {
+ case EDIT_UNDO: return !undos.empty() && packundo(undos.last, inlen, outbuf, outlen);
+ case EDIT_REDO: return !redos.empty() && packundo(redos.last, inlen, outbuf, outlen);
+ default: return false;
+ }
}
struct prefabheader
{
- char magic[4];
- int version;
+ char magic[4];
+ int version;
};
struct prefab : editinfo
{
- char *name;
- GLuint ebo, vbo;
- int numtris, numverts;
+ char *name;
+ GLuint ebo, vbo;
+ int numtris, numverts;
- prefab() : name(NULL), ebo(0), vbo(0), numtris(0), numverts(0) {}
- ~prefab() { DELETEA(name); if(copy) freeblock(copy); }
+ prefab() : name(NULL), ebo(0), vbo(0), numtris(0), numverts(0) {}
+ ~prefab() { DELETEA(name); if(copy) freeblock(copy); }
- void cleanup()
- {
- if(ebo) { glDeleteBuffers_(1, &ebo); ebo = 0; }
- if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; }
- numtris = numverts = 0;
- }
+ void cleanup()
+ {
+ if(ebo) { glDeleteBuffers_(1, &ebo); ebo = 0; }
+ if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; }
+ numtris = numverts = 0;
+ }
};
static hashnameset<prefab> prefabs;
void cleanupprefabs()
{
- enumerate(prefabs, prefab, p, p.cleanup());
+ enumerate(prefabs, prefab, p, p.cleanup());
}
void delprefab(char *name)
{
- prefab *p = prefabs.access(name);
- if(p)
- {
- p->cleanup();
- prefabs.remove(name);
- conoutf("deleted prefab %s", name);
- }
+ prefab *p = prefabs.access(name);
+ if(p)
+ {
+ p->cleanup();
+ prefabs.remove(name);
+ conoutf("deleted prefab %s", name);
+ }
}
COMMAND(delprefab, "s");
void saveprefab(char *name)
{
- if(!name[0] || noedit(true) || (nompedit && multiplayer())) return;
- prefab *b = prefabs.access(name);
- if(!b)
- {
- b = &prefabs[name];
- b->name = newstring(name);
- }
- if(b->copy) freeblock(b->copy);
- protectsel(b->copy = blockcopy(block3(sel), sel.grid));
- changed(sel);
- defformatstring(filename, strpbrk(name, "/\\") ? "packages/%s.obr" : "packages/prefab/%s.obr", name);
- path(filename);
- stream *f = opengzfile(filename, "wb");
- if(!f) { conoutf(CON_ERROR, "could not write prefab to %s", filename); return; }
- prefabheader hdr;
- memcpy(hdr.magic, "OEBR", 4);
- hdr.version = 0;
- lilswap(&hdr.version, 1);
- f->write(&hdr, sizeof(hdr));
- streambuf<uchar> s(f);
- if(!packblock(*b->copy, s)) { delete f; conoutf(CON_ERROR, "could not pack prefab %s", filename); return; }
- delete f;
- conoutf("wrote prefab file %s", filename);
+ if(!name[0] || noedit(true) || (nompedit && multiplayer())) return;
+ prefab *b = prefabs.access(name);
+ if(!b)
+ {
+ b = &prefabs[name];
+ b->name = newstring(name);
+ }
+ if(b->copy) freeblock(b->copy);
+ protectsel(b->copy = blockcopy(block3(sel), sel.grid));
+ changed(sel);
+ defformatstring(filename, strpbrk(name, "/\\") ? "packages/%s.obr" : "packages/prefab/%s.obr", name);
+ path(filename);
+ stream *f = opengzfile(filename, "wb");
+ if(!f) { conoutf(CON_ERROR, "could not write prefab to %s", filename); return; }
+ prefabheader hdr;
+ memcpy(hdr.magic, "OEBR", 4);
+ hdr.version = 0;
+ lilswap(&hdr.version, 1);
+ f->write(&hdr, sizeof(hdr));
+ streambuf<uchar> s(f);
+ if(!packblock(*b->copy, s)) { delete f; conoutf(CON_ERROR, "could not pack prefab %s", filename); return; }
+ delete f;
+ conoutf("wrote prefab file %s", filename);
}
COMMAND(saveprefab, "s");
void pasteblock(block3 &b, selinfo &sel, bool local)
{
- sel.s = b.s;
- int o = sel.orient;
- sel.orient = b.orient;
- cube *s = b.c();
- loopselxyz(if(!isempty(*s) || s->children || s->material != MAT_AIR) pastecube(*s, c); s++); // 'transparent'. old opaque by 'delcube; paste'
- sel.orient = o;
+ sel.s = b.s;
+ int o = sel.orient;
+ sel.orient = b.orient;
+ cube *s = b.c();
+ loopselxyz(if(!isempty(*s) || s->children || s->material != MAT_AIR) pastecube(*s, c); s++); // 'transparent'. old opaque by 'delcube; paste'
+ sel.orient = o;
}
bool prefabloaded(const char *name)
{
- return prefabs.access(name) != NULL;
+ return prefabs.access(name) != NULL;
}
prefab *loadprefab(const char *name, bool msg = true)
@@ -1285,252 +1284,252 @@ prefab *loadprefab(const char *name, bool msg = true)
void pasteprefab(char *name)
{
- if(!name[0] || noedit() || (nompedit && multiplayer())) return;
- prefab *b = loadprefab(name, true);
- if(b) pasteblock(*b->copy, sel, true);
+ if(!name[0] || noedit() || (nompedit && multiplayer())) return;
+ prefab *b = loadprefab(name, true);
+ if(b) pasteblock(*b->copy, sel, true);
}
COMMAND(pasteprefab, "s");
struct prefabmesh
{
- struct vertex { vec pos; bvec4 norm; };
-
- static const int SIZE = 1<<9;
- int table[SIZE];
- vector<vertex> verts;
- vector<int> chain;
- vector<ushort> tris;
-
- prefabmesh() { memset(table, -1, sizeof(table)); }
-
- int addvert(const vertex &v)
- {
- uint h = hthash(v.pos)&(SIZE-1);
- for(int i = table[h]; i>=0; i = chain[i])
- {
- const vertex &c = verts[i];
- if(c.pos==v.pos && c.norm==v.norm) return i;
- }
- if(verts.length() >= USHRT_MAX) return -1;
- verts.add(v);
- chain.add(table[h]);
- return table[h] = verts.length()-1;
- }
-
- int addvert(const vec &pos, const bvec &norm)
- {
- vertex vtx;
- vtx.pos = pos;
- vtx.norm = norm;
- return addvert(vtx);
+ struct vertex { vec pos; bvec4 norm; };
+
+ static const int SIZE = 1<<9;
+ int table[SIZE];
+ vector<vertex> verts;
+ vector<int> chain;
+ vector<ushort> tris;
+
+ prefabmesh() { memset(table, -1, sizeof(table)); }
+
+ int addvert(const vertex &v)
+ {
+ uint h = hthash(v.pos)&(SIZE-1);
+ for(int i = table[h]; i>=0; i = chain[i])
+ {
+ const vertex &c = verts[i];
+ if(c.pos==v.pos && c.norm==v.norm) return i;
+ }
+ if(verts.length() >= USHRT_MAX) return -1;
+ verts.add(v);
+ chain.add(table[h]);
+ return table[h] = verts.length()-1;
+ }
+
+ int addvert(const vec &pos, const bvec &norm)
+ {
+ vertex vtx;
+ vtx.pos = pos;
+ vtx.norm = norm;
+ return addvert(vtx);
}
- void setup(prefab &p)
- {
- if(tris.empty()) return;
+ void setup(prefab &p)
+ {
+ if(tris.empty()) return;
- p.cleanup();
+ p.cleanup();
- loopv(verts) verts[i].norm.flip();
- if(!p.vbo) glGenBuffers_(1, &p.vbo);
- gle::bindvbo(p.vbo);
- glBufferData_(GL_ARRAY_BUFFER, verts.length()*sizeof(vertex), verts.getbuf(), GL_STATIC_DRAW);
- gle::clearvbo();
- p.numverts = verts.length();
+ loopv(verts) verts[i].norm.flip();
+ if(!p.vbo) glGenBuffers_(1, &p.vbo);
+ gle::bindvbo(p.vbo);
+ glBufferData_(GL_ARRAY_BUFFER, verts.length()*sizeof(vertex), verts.getbuf(), GL_STATIC_DRAW);
+ gle::clearvbo();
+ p.numverts = verts.length();
- if(!p.ebo) glGenBuffers_(1, &p.ebo);
- gle::bindebo(p.ebo);
- glBufferData_(GL_ELEMENT_ARRAY_BUFFER, tris.length()*sizeof(ushort), tris.getbuf(), GL_STATIC_DRAW);
- gle::clearebo();
- p.numtris = tris.length()/3;
- }
+ if(!p.ebo) glGenBuffers_(1, &p.ebo);
+ gle::bindebo(p.ebo);
+ glBufferData_(GL_ELEMENT_ARRAY_BUFFER, tris.length()*sizeof(ushort), tris.getbuf(), GL_STATIC_DRAW);
+ gle::clearebo();
+ p.numtris = tris.length()/3;
+ }
};
static void genprefabmesh(prefabmesh &r, cube &c, const ivec &co, int size)
{
- if(c.children)
- {
- neighbourstack[++neighbourdepth] = c.children;
- loopi(8)
- {
- ivec o(i, co, size/2);
- genprefabmesh(r, c.children[i], o, size/2);
- }
- --neighbourdepth;
- }
- else if(!isempty(c))
- {
- int vis;
- loopi(6) if((vis = visibletris(c, i, co, size)))
- {
- ivec v[4];
- genfaceverts(c, i, v);
- int convex = 0;
- if(!flataxisface(c, i)) convex = faceconvexity(v);
- int order = vis&4 || convex < 0 ? 1 : 0, numverts = 0;
- vec vo(co), pos[4], norm[4];
- pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
- if(vis&1) pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
- pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
- if(vis&2) pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
- guessnormals(pos, numverts, norm);
- int index[4];
- loopj(numverts) index[j] = r.addvert(pos[j], bvec(norm[j]));
- loopj(numverts-2) if(index[0]!=index[j+1] && index[j+1]!=index[j+2] && index[j+2]!=index[0])
- {
- r.tris.add(index[0]);
- r.tris.add(index[j+1]);
- r.tris.add(index[j+2]);
- }
- }
- }
+ if(c.children)
+ {
+ neighbourstack[++neighbourdepth] = c.children;
+ loopi(8)
+ {
+ ivec o(i, co, size/2);
+ genprefabmesh(r, c.children[i], o, size/2);
+ }
+ --neighbourdepth;
+ }
+ else if(!isempty(c))
+ {
+ int vis;
+ loopi(6) if((vis = visibletris(c, i, co, size)))
+ {
+ ivec v[4];
+ genfaceverts(c, i, v);
+ int convex = 0;
+ if(!flataxisface(c, i)) convex = faceconvexity(v);
+ int order = vis&4 || convex < 0 ? 1 : 0, numverts = 0;
+ vec vo(co), pos[4], norm[4];
+ pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
+ if(vis&1) pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
+ pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
+ if(vis&2) pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
+ guessnormals(pos, numverts, norm);
+ int index[4];
+ loopj(numverts) index[j] = r.addvert(pos[j], bvec(norm[j]));
+ loopj(numverts-2) if(index[0]!=index[j+1] && index[j+1]!=index[j+2] && index[j+2]!=index[0])
+ {
+ r.tris.add(index[0]);
+ r.tris.add(index[j+1]);
+ r.tris.add(index[j+2]);
+ }
+ }
+ }
}
void genprefabmesh(prefab &p)
{
- block3 b = *p.copy;
- b.o = ivec(0, 0, 0);
+ block3 b = *p.copy;
+ b.o = ivec(0, 0, 0);
- cube *oldworldroot = worldroot;
- int oldworldscale = worldscale, oldworldsize = worldsize;
+ cube *oldworldroot = worldroot;
+ int oldworldscale = worldscale, oldworldsize = worldsize;
- worldroot = newcubes();
- worldscale = 1;
- worldsize = 2;
- while(worldsize < max(max(b.s.x, b.s.y), b.s.z)*b.grid)
- {
- worldscale++;
- worldsize *= 2;
- }
+ worldroot = newcubes();
+ worldscale = 1;
+ worldsize = 2;
+ while(worldsize < max(max(b.s.x, b.s.y), b.s.z)*b.grid)
+ {
+ worldscale++;
+ worldsize *= 2;
+ }
- cube *s = p.copy->c();
- loopxyz(b, b.grid, if(!isempty(*s) || s->children) pastecube(*s, c); s++);
+ cube *s = p.copy->c();
+ loopxyz(b, b.grid, if(!isempty(*s) || s->children) pastecube(*s, c); s++);
- prefabmesh r;
- neighbourstack[++neighbourdepth] = worldroot;
- loopi(8) genprefabmesh(r, worldroot[i], ivec(i, ivec(0, 0, 0), worldsize/2), worldsize/2);
- --neighbourdepth;
- r.setup(p);
+ prefabmesh r;
+ neighbourstack[++neighbourdepth] = worldroot;
+ loopi(8) genprefabmesh(r, worldroot[i], ivec(i, ivec(0, 0, 0), worldsize/2), worldsize/2);
+ --neighbourdepth;
+ r.setup(p);
- freeocta(worldroot);
+ freeocta(worldroot);
- worldroot = oldworldroot;
- worldscale = oldworldscale;
- worldsize = oldworldsize;
+ worldroot = oldworldroot;
+ worldscale = oldworldscale;
+ worldsize = oldworldsize;
- useshaderbyname("prefab");
+ useshaderbyname("prefab");
}
extern int outlinecolour;
static void renderprefab(prefab &p, const vec &o, float yaw, float pitch, float roll, float size, const vec &color)
{
- if(!p.numtris)
- {
- genprefabmesh(p);
- if(!p.numtris) return;
- }
-
- block3 &b = *p.copy;
-
- matrix4 m;
- m.identity();
- m.settranslation(o);
- if(yaw) m.rotate_around_z(yaw*RAD);
- if(pitch) m.rotate_around_x(pitch*RAD);
- if(roll) m.rotate_around_y(-roll*RAD);
- matrix3 w(m);
- if(size > 0 && size != 1) m.scale(size);
- m.translate(vec(b.s).mul(-b.grid*0.5f));
-
- gle::bindvbo(p.vbo);
- gle::bindebo(p.ebo);
- gle::enablevertex();
- gle::enablenormal();
- prefabmesh::vertex *v = (prefabmesh::vertex *)0;
- gle::vertexpointer(sizeof(prefabmesh::vertex), v->pos.v);
- gle::normalpointer(sizeof(prefabmesh::vertex), v->norm.v, GL_BYTE);
-
- matrix4 pm;
- pm.mul(camprojmatrix, m);
- GLOBALPARAM(prefabmatrix, pm);
- GLOBALPARAM(prefabworld, w);
- SETSHADER(prefab);
- gle::color(color);
- glDrawRangeElements_(GL_TRIANGLES, 0, p.numverts-1, p.numtris*3, GL_UNSIGNED_SHORT, (ushort *)0);
-
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
-
- pm.mul(camprojmatrix, m);
- GLOBALPARAM(prefabmatrix, pm);
- SETSHADER(prefab);
- gle::color(vec::hexcolor(outlinecolour));
- glDrawRangeElements_(GL_TRIANGLES, 0, p.numverts-1, p.numtris*3, GL_UNSIGNED_SHORT, (ushort *)0);
-
- disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-
- gle::disablevertex();
- gle::disablenormal();
- gle::clearebo();
- gle::clearvbo();
+ if(!p.numtris)
+ {
+ genprefabmesh(p);
+ if(!p.numtris) return;
+ }
+
+ block3 &b = *p.copy;
+
+ matrix4 m;
+ m.identity();
+ m.settranslation(o);
+ if(yaw) m.rotate_around_z(yaw*RAD);
+ if(pitch) m.rotate_around_x(pitch*RAD);
+ if(roll) m.rotate_around_y(-roll*RAD);
+ matrix3 w(m);
+ if(size > 0 && size != 1) m.scale(size);
+ m.translate(vec(b.s).mul(-b.grid*0.5f));
+
+ gle::bindvbo(p.vbo);
+ gle::bindebo(p.ebo);
+ gle::enablevertex();
+ gle::enablenormal();
+ prefabmesh::vertex *v = (prefabmesh::vertex *)0;
+ gle::vertexpointer(sizeof(prefabmesh::vertex), v->pos.v);
+ gle::normalpointer(sizeof(prefabmesh::vertex), v->norm.v, GL_BYTE);
+
+ matrix4 pm;
+ pm.mul(camprojmatrix, m);
+ GLOBALPARAM(prefabmatrix, pm);
+ GLOBALPARAM(prefabworld, w);
+ SETSHADER(prefab);
+ gle::color(color);
+ glDrawRangeElements_(GL_TRIANGLES, 0, p.numverts-1, p.numtris*3, GL_UNSIGNED_SHORT, (ushort *)0);
+
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
+
+ pm.mul(camprojmatrix, m);
+ GLOBALPARAM(prefabmatrix, pm);
+ SETSHADER(prefab);
+ gle::color(vec::hexcolor(outlinecolour));
+ glDrawRangeElements_(GL_TRIANGLES, 0, p.numverts-1, p.numtris*3, GL_UNSIGNED_SHORT, (ushort *)0);
+
+ disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+
+ gle::disablevertex();
+ gle::disablenormal();
+ gle::clearebo();
+ gle::clearvbo();
}
void renderprefab(const char *name, const vec &o, float yaw, float pitch, float roll, float size, const vec &color)
{
- prefab *p = loadprefab(name, false);
- if(p) renderprefab(*p, o, yaw, pitch, roll, size, color);
+ prefab *p = loadprefab(name, false);
+ if(p) renderprefab(*p, o, yaw, pitch, roll, size, color);
}
void previewprefab(const char *name, const vec &color)
{
- prefab *p = loadprefab(name, false);
- if(p)
- {
- block3 &b = *p->copy;
- float yaw;
- vec o = calcmodelpreviewpos(vec(b.s).mul(b.grid*0.5f), yaw);
- renderprefab(*p, o, yaw, 0, 0, 1, color);
- }
+ prefab *p = loadprefab(name, false);
+ if(p)
+ {
+ block3 &b = *p->copy;
+ float yaw;
+ vec o = calcmodelpreviewpos(vec(b.s).mul(b.grid*0.5f), yaw);
+ renderprefab(*p, o, yaw, 0, 0, 1, color);
+ }
}
void mpcopy(editinfo *&e, selinfo &sel, bool local)
{
- if(local) game::edittrigger(sel, EDIT_COPY);
- if(e==NULL) e = editinfos.add(new editinfo);
- if(e->copy) freeblock(e->copy);
- e->copy = NULL;
- protectsel(e->copy = blockcopy(block3(sel), sel.grid));
- changed(sel);
+ if(local) game::edittrigger(sel, EDIT_COPY);
+ if(e==NULL) e = editinfos.add(new editinfo);
+ if(e->copy) freeblock(e->copy);
+ e->copy = NULL;
+ protectsel(e->copy = blockcopy(block3(sel), sel.grid));
+ changed(sel);
}
void mppaste(editinfo *&e, selinfo &sel, bool local)
{
- if(e==NULL) return;
- if(local) game::edittrigger(sel, EDIT_PASTE);
- if(e->copy) pasteblock(*e->copy, sel, local);
+ if(e==NULL) return;
+ if(local) game::edittrigger(sel, EDIT_PASTE);
+ if(e->copy) pasteblock(*e->copy, sel, local);
}
void copy()
{
- if(noedit(true)) return;
- mpcopy(localedit, sel, true);
+ if(noedit(true)) return;
+ mpcopy(localedit, sel, true);
}
void pastehilite()
{
- if(!localedit) return;
+ if(!localedit) return;
sel.s = localedit->copy->s;
- reorient();
- havesel = true;
+ reorient();
+ havesel = true;
}
void paste()
{
- if(noedit(true)) return;
- mppaste(localedit, sel, true);
+ if(noedit(true)) return;
+ mppaste(localedit, sel, true);
}
COMMAND(copy, "");
@@ -1542,31 +1541,31 @@ COMMANDN(redo, editredo, "");
static vector<int *> editingvslots;
struct vslotref
{
- vslotref(int &index) { editingvslots.add(&index); }
- ~vslotref() { editingvslots.pop(); }
+ vslotref(int &index) { editingvslots.add(&index); }
+ ~vslotref() { editingvslots.pop(); }
};
#define editingvslot(...) vslotref vslotrefs[] = { __VA_ARGS__ }; (void)vslotrefs;
void compacteditvslots()
{
- loopv(editingvslots) if(*editingvslots[i]) compactvslot(*editingvslots[i]);
- loopv(unpackingvslots) compactvslot(*unpackingvslots[i].vslot);
- loopv(editinfos)
- {
- editinfo *e = editinfos[i];
- compactvslots(e->copy->c(), e->copy->size());
- }
- for(undoblock *u = undos.first; u; u = u->next)
- if(!u->numents)
- compactvslots(u->block()->c(), u->block()->size());
- for(undoblock *u = redos.first; u; u = u->next)
- if(!u->numents)
- compactvslots(u->block()->c(), u->block()->size());
+ loopv(editingvslots) if(*editingvslots[i]) compactvslot(*editingvslots[i]);
+ loopv(unpackingvslots) compactvslot(*unpackingvslots[i].vslot);
+ loopv(editinfos)
+ {
+ editinfo *e = editinfos[i];
+ compactvslots(e->copy->c(), e->copy->size());
+ }
+ for(undoblock *u = undos.first; u; u = u->next)
+ if(!u->numents)
+ compactvslots(u->block()->c(), u->block()->size());
+ for(undoblock *u = redos.first; u; u = u->next)
+ if(!u->numents)
+ compactvslots(u->block()->c(), u->block()->size());
}
///////////// height maps ////////////////
-#define MAXBRUSH 64
+#define MAXBRUSH 64
#define MAXBRUSHC 63
#define MAXBRUSH2 32
int brush[MAXBRUSH][MAXBRUSH];
@@ -1578,23 +1577,23 @@ int brushmaxy = 0, brushminy = MAXBRUSH;
void clearbrush()
{
- memset(brush, 0, sizeof brush);
- brushmaxx = brushmaxy = 0;
- brushminx = brushminy = MAXBRUSH;
- paintbrush = false;
+ memset(brush, 0, sizeof brush);
+ brushmaxx = brushmaxy = 0;
+ brushminx = brushminy = MAXBRUSH;
+ paintbrush = false;
}
void brushvert(int *x, int *y, int *v)
{
- *x += MAXBRUSH2 - brushx + 1; // +1 for automatic padding
- *y += MAXBRUSH2 - brushy + 1;
- if(*x<0 || *y<0 || *x>=MAXBRUSH || *y>=MAXBRUSH) return;
- brush[*x][*y] = clamp(*v, 0, 8);
- paintbrush = paintbrush || (brush[*x][*y] > 0);
- brushmaxx = min(MAXBRUSH-1, max(brushmaxx, *x+1));
- brushmaxy = min(MAXBRUSH-1, max(brushmaxy, *y+1));
- brushminx = max(0, min(brushminx, *x-1));
- brushminy = max(0, min(brushminy, *y-1));
+ *x += MAXBRUSH2 - brushx + 1; // +1 for automatic padding
+ *y += MAXBRUSH2 - brushy + 1;
+ if(*x<0 || *y<0 || *x>=MAXBRUSH || *y>=MAXBRUSH) return;
+ brush[*x][*y] = clamp(*v, 0, 8);
+ paintbrush = paintbrush || (brush[*x][*y] > 0);
+ brushmaxx = min(MAXBRUSH-1, max(brushmaxx, *x+1));
+ brushmaxy = min(MAXBRUSH-1, max(brushmaxy, *y+1));
+ brushminx = max(0, min(brushminx, *x-1));
+ brushminy = max(0, min(brushminy, *y-1));
}
vector<int> htextures;
@@ -1604,317 +1603,317 @@ COMMAND(brushvert, "iii");
void hmapcancel() { htextures.setsize(0); }
COMMAND(hmapcancel, "");
ICOMMAND(hmapselect, "", (),
- int t = lookupcube(cur).texture[orient];
- int i = htextures.find(t);
- if(i<0)
- htextures.add(t);
- else
- htextures.remove(i);
+ int t = lookupcube(cur).texture[orient];
+ int i = htextures.find(t);
+ if(i<0)
+ htextures.add(t);
+ else
+ htextures.remove(i);
);
inline bool isheightmap(int o, int d, bool empty, cube *c)
{
- return havesel ||
- (empty && isempty(*c)) ||
- htextures.empty() ||
- htextures.find(c->texture[o]) >= 0;
+ return havesel ||
+ (empty && isempty(*c)) ||
+ htextures.empty() ||
+ htextures.find(c->texture[o]) >= 0;
}
namespace hmap
{
-# define PAINTED 1
-# define NOTHMAP 2
-# define MAPPED 16
- uchar flags[MAXBRUSH][MAXBRUSH];
- cube *cmap[MAXBRUSHC][MAXBRUSHC][4];
- int mapz[MAXBRUSHC][MAXBRUSHC];
- int map [MAXBRUSH][MAXBRUSH];
-
- selinfo changes;
- bool selecting;
- int d, dc, dr, dcr, biasup, br, hws, fg;
- int gx, gy, gz, mx, my, mz, nx, ny, nz, bmx, bmy, bnx, bny;
- uint fs;
- selinfo hundo;
-
- cube *getcube(ivec t, int f)
- {
- t[d] += dcr*f*gridsize;
- if(t[d] > nz || t[d] < mz) return NULL;
- cube *c = &lookupcube(t, gridsize);
- if(c->children) forcemip(*c, false);
- discardchildren(*c, true);
- if(!isheightmap(sel.orient, d, true, c)) return NULL;
- if (t.x < changes.o.x) changes.o.x = t.x;
- else if(t.x > changes.s.x) changes.s.x = t.x;
- if (t.y < changes.o.y) changes.o.y = t.y;
- else if(t.y > changes.s.y) changes.s.y = t.y;
- if (t.z < changes.o.z) changes.o.z = t.z;
- else if(t.z > changes.s.z) changes.s.z = t.z;
- return c;
- }
-
- uint getface(cube *c, int d)
- {
- return 0x0f0f0f0f & ((dc ? c->faces[d] : 0x88888888 - c->faces[d]) >> fs);
- }
-
- void pushside(cube &c, int d, int x, int y, int z)
- {
- ivec a;
- getcubevector(c, d, x, y, z, a);
- a[R[d]] = 8 - a[R[d]];
- setcubevector(c, d, x, y, z, a);
- }
-
- void addpoint(int x, int y, int z, int v)
- {
- if(!(flags[x][y] & MAPPED))
- map[x][y] = v + (z*8);
- flags[x][y] |= MAPPED;
- }
-
- void select(int x, int y, int z)
- {
- if((NOTHMAP & flags[x][y]) || (PAINTED & flags[x][y])) return;
- ivec t(d, x+gx, y+gy, dc ? z : hws-z);
- t.shl(gridpower);
-
- // selections may damage; must makeundo before
- hundo.o = t;
- hundo.o[D[d]] -= dcr*gridsize*2;
- makeundo(hundo);
-
- cube **c = cmap[x][y];
- loopk(4) c[k] = NULL;
- c[1] = getcube(t, 0);
- if(!c[1] || !isempty(*c[1]))
- { // try up
- c[2] = c[1];
- c[1] = getcube(t, 1);
- if(!c[1] || isempty(*c[1])) { c[0] = c[1]; c[1] = c[2]; c[2] = NULL; }
- else { z++; t[d]+=fg; }
- }
- else // drop down
- {
- z--;
- t[d]-= fg;
- c[0] = c[1];
- c[1] = getcube(t, 0);
- }
-
- if(!c[1] || isempty(*c[1])) { flags[x][y] |= NOTHMAP; return; }
-
- flags[x][y] |= PAINTED;
- mapz [x][y] = z;
-
- if(!c[0]) c[0] = getcube(t, 1);
- if(!c[2]) c[2] = getcube(t, -1);
- c[3] = getcube(t, -2);
- c[2] = !c[2] || isempty(*c[2]) ? NULL : c[2];
- c[3] = !c[3] || isempty(*c[3]) ? NULL : c[3];
-
- uint face = getface(c[1], d);
- if(face == 0x08080808 && (!c[0] || !isempty(*c[0]))) { flags[x][y] |= NOTHMAP; return; }
- if(c[1]->faces[R[d]] == F_SOLID) // was single
- face += 0x08080808;
- else // was pair
- face += c[2] ? getface(c[2], d) : 0x08080808;
- face += 0x08080808; // c[3]
- uchar *f = (uchar*)&face;
- addpoint(x, y, z, f[0]);
- addpoint(x+1, y, z, f[1]);
- addpoint(x, y+1, z, f[2]);
- addpoint(x+1, y+1, z, f[3]);
-
- if(selecting) // continue to adjacent cubes
- {
- if(x>bmx) select(x-1, y, z);
- if(x<bnx) select(x+1, y, z);
- if(y>bmy) select(x, y-1, z);
- if(y<bny) select(x, y+1, z);
- }
- }
-
- void ripple(int x, int y, int z, bool force)
- {
- if(force) select(x, y, z);
- if((NOTHMAP & flags[x][y]) || !(PAINTED & flags[x][y])) return;
-
- bool changed = false;
- int *o[4], best, par, q = 0;
- loopi(2) loopj(2) o[i+j*2] = &map[x+i][y+j];
- #define pullhmap(I, LT, GT, M, N, A) do { \
- best = I; \
- loopi(4) if(*o[i] LT best) best = *o[q = i] - M; \
- par = (best&(~7)) + N; \
- /* dual layer for extra smoothness */ \
- if(*o[q^3] GT par && !(*o[q^1] LT par || *o[q^2] LT par)) { \
- if(*o[q^3] GT par A 8 || *o[q^1] != par || *o[q^2] != par) { \
- *o[q^3] = (*o[q^3] GT par A 8 ? par A 8 : *o[q^3]); \
- *o[q^1] = *o[q^2] = par; \
- changed = true; \
- } \
- /* single layer */ \
- } else { \
- loopj(4) if(*o[j] GT par) { \
- *o[j] = par; \
- changed = true; \
- } \
- } \
- } while(0)
-
- if(biasup)
- pullhmap(0, >, <, 1, 0, -);
- else
- pullhmap(worldsize*8, <, >, 0, 8, +);
-
- cube **c = cmap[x][y];
- int e[2][2];
- int notempty = 0;
-
- loopk(4) if(c[k]) {
- loopi(2) loopj(2) {
- e[i][j] = min(8, map[x+i][y+j] - (mapz[x][y]+3-k)*8);
- notempty |= e[i][j] > 0;
- }
- if(notempty)
- {
- c[k]->texture[sel.orient] = c[1]->texture[sel.orient];
- solidfaces(*c[k]);
- loopi(2) loopj(2)
- {
- int f = e[i][j];
- if(f<0 || (f==0 && e[1-i][j]==0 && e[i][1-j]==0))
- {
- f=0;
- pushside(*c[k], d, i, j, 0);
- pushside(*c[k], d, i, j, 1);
- }
- edgeset(cubeedge(*c[k], d, i, j), dc, dc ? f : 8-f);
- }
- }
- else
- emptyfaces(*c[k]);
- }
-
- if(!changed) return;
- if(x>mx) ripple(x-1, y, mapz[x][y], true);
- if(x<nx) ripple(x+1, y, mapz[x][y], true);
- if(y>my) ripple(x, y-1, mapz[x][y], true);
- if(y<ny) ripple(x, y+1, mapz[x][y], true);
+# define PAINTED 1
+# define NOTHMAP 2
+# define MAPPED 16
+ uchar flags[MAXBRUSH][MAXBRUSH];
+ cube *cmap[MAXBRUSHC][MAXBRUSHC][4];
+ int mapz[MAXBRUSHC][MAXBRUSHC];
+ int map [MAXBRUSH][MAXBRUSH];
+
+ selinfo changes;
+ bool selecting;
+ int d, dc, dr, dcr, biasup, br, hws, fg;
+ int gx, gy, gz, mx, my, mz, nx, ny, nz, bmx, bmy, bnx, bny;
+ uint fs;
+ selinfo hundo;
+
+ cube *getcube(ivec t, int f)
+ {
+ t[d] += dcr*f*gridsize;
+ if(t[d] > nz || t[d] < mz) return NULL;
+ cube *c = &lookupcube(t, gridsize);
+ if(c->children) forcemip(*c, false);
+ discardchildren(*c, true);
+ if(!isheightmap(sel.orient, d, true, c)) return NULL;
+ if (t.x < changes.o.x) changes.o.x = t.x;
+ else if(t.x > changes.s.x) changes.s.x = t.x;
+ if (t.y < changes.o.y) changes.o.y = t.y;
+ else if(t.y > changes.s.y) changes.s.y = t.y;
+ if (t.z < changes.o.z) changes.o.z = t.z;
+ else if(t.z > changes.s.z) changes.s.z = t.z;
+ return c;
+ }
+
+ uint getface(cube *c, int d)
+ {
+ return 0x0f0f0f0f & ((dc ? c->faces[d] : 0x88888888 - c->faces[d]) >> fs);
+ }
+
+ void pushside(cube &c, int d, int x, int y, int z)
+ {
+ ivec a;
+ getcubevector(c, d, x, y, z, a);
+ a[R[d]] = 8 - a[R[d]];
+ setcubevector(c, d, x, y, z, a);
+ }
+
+ void addpoint(int x, int y, int z, int v)
+ {
+ if(!(flags[x][y] & MAPPED))
+ map[x][y] = v + (z*8);
+ flags[x][y] |= MAPPED;
+ }
+
+ void select(int x, int y, int z)
+ {
+ if((NOTHMAP & flags[x][y]) || (PAINTED & flags[x][y])) return;
+ ivec t(d, x+gx, y+gy, dc ? z : hws-z);
+ t.shl(gridpower);
+
+ // selections may damage; must makeundo before
+ hundo.o = t;
+ hundo.o[D[d]] -= dcr*gridsize*2;
+ makeundo(hundo);
+
+ cube **c = cmap[x][y];
+ loopk(4) c[k] = NULL;
+ c[1] = getcube(t, 0);
+ if(!c[1] || !isempty(*c[1]))
+ { // try up
+ c[2] = c[1];
+ c[1] = getcube(t, 1);
+ if(!c[1] || isempty(*c[1])) { c[0] = c[1]; c[1] = c[2]; c[2] = NULL; }
+ else { z++; t[d]+=fg; }
+ }
+ else // drop down
+ {
+ z--;
+ t[d]-= fg;
+ c[0] = c[1];
+ c[1] = getcube(t, 0);
+ }
+
+ if(!c[1] || isempty(*c[1])) { flags[x][y] |= NOTHMAP; return; }
+
+ flags[x][y] |= PAINTED;
+ mapz [x][y] = z;
+
+ if(!c[0]) c[0] = getcube(t, 1);
+ if(!c[2]) c[2] = getcube(t, -1);
+ c[3] = getcube(t, -2);
+ c[2] = !c[2] || isempty(*c[2]) ? NULL : c[2];
+ c[3] = !c[3] || isempty(*c[3]) ? NULL : c[3];
+
+ uint face = getface(c[1], d);
+ if(face == 0x08080808 && (!c[0] || !isempty(*c[0]))) { flags[x][y] |= NOTHMAP; return; }
+ if(c[1]->faces[R[d]] == F_SOLID) // was single
+ face += 0x08080808;
+ else // was pair
+ face += c[2] ? getface(c[2], d) : 0x08080808;
+ face += 0x08080808; // c[3]
+ uchar *f = (uchar*)&face;
+ addpoint(x, y, z, f[0]);
+ addpoint(x+1, y, z, f[1]);
+ addpoint(x, y+1, z, f[2]);
+ addpoint(x+1, y+1, z, f[3]);
+
+ if(selecting) // continue to adjacent cubes
+ {
+ if(x>bmx) select(x-1, y, z);
+ if(x<bnx) select(x+1, y, z);
+ if(y>bmy) select(x, y-1, z);
+ if(y<bny) select(x, y+1, z);
+ }
+ }
+
+ void ripple(int x, int y, int z, bool force)
+ {
+ if(force) select(x, y, z);
+ if((NOTHMAP & flags[x][y]) || !(PAINTED & flags[x][y])) return;
+
+ bool changed = false;
+ int *o[4], best, par, q = 0;
+ loopi(2) loopj(2) o[i+j*2] = &map[x+i][y+j];
+ #define pullhmap(I, LT, GT, M, N, A) do { \
+ best = I; \
+ loopi(4) if(*o[i] LT best) best = *o[q = i] - M; \
+ par = (best&(~7)) + N; \
+ /* dual layer for extra smoothness */ \
+ if(*o[q^3] GT par && !(*o[q^1] LT par || *o[q^2] LT par)) { \
+ if(*o[q^3] GT par A 8 || *o[q^1] != par || *o[q^2] != par) { \
+ *o[q^3] = (*o[q^3] GT par A 8 ? par A 8 : *o[q^3]); \
+ *o[q^1] = *o[q^2] = par; \
+ changed = true; \
+ } \
+ /* single layer */ \
+ } else { \
+ loopj(4) if(*o[j] GT par) { \
+ *o[j] = par; \
+ changed = true; \
+ } \
+ } \
+ } while(0)
+
+ if(biasup)
+ pullhmap(0, >, <, 1, 0, -);
+ else
+ pullhmap(worldsize*8, <, >, 0, 8, +);
+
+ cube **c = cmap[x][y];
+ int e[2][2];
+ int notempty = 0;
+
+ loopk(4) if(c[k]) {
+ loopi(2) loopj(2) {
+ e[i][j] = min(8, map[x+i][y+j] - (mapz[x][y]+3-k)*8);
+ notempty |= e[i][j] > 0;
+ }
+ if(notempty)
+ {
+ c[k]->texture[sel.orient] = c[1]->texture[sel.orient];
+ solidfaces(*c[k]);
+ loopi(2) loopj(2)
+ {
+ int f = e[i][j];
+ if(f<0 || (f==0 && e[1-i][j]==0 && e[i][1-j]==0))
+ {
+ f=0;
+ pushside(*c[k], d, i, j, 0);
+ pushside(*c[k], d, i, j, 1);
+ }
+ edgeset(cubeedge(*c[k], d, i, j), dc, dc ? f : 8-f);
+ }
+ }
+ else
+ emptyfaces(*c[k]);
+ }
+
+ if(!changed) return;
+ if(x>mx) ripple(x-1, y, mapz[x][y], true);
+ if(x<nx) ripple(x+1, y, mapz[x][y], true);
+ if(y>my) ripple(x, y-1, mapz[x][y], true);
+ if(y<ny) ripple(x, y+1, mapz[x][y], true);
#define DIAGONAL_RIPPLE(a,b,exp) if(exp) { \
- if(flags[x a][ y] & PAINTED) \
- ripple(x a, y b, mapz[x a][y], true); \
- else if(flags[x][y b] & PAINTED) \
- ripple(x a, y b, mapz[x][y b], true); \
- }
-
- DIAGONAL_RIPPLE(-1, -1, (x>mx && y>my)); // do diagonals because adjacents
- DIAGONAL_RIPPLE(-1, +1, (x>mx && y<ny)); // won't unless changed
- DIAGONAL_RIPPLE(+1, +1, (x<nx && y<ny));
- DIAGONAL_RIPPLE(+1, -1, (x<nx && y>my));
- }
+ if(flags[x a][ y] & PAINTED) \
+ ripple(x a, y b, mapz[x a][y], true); \
+ else if(flags[x][y b] & PAINTED) \
+ ripple(x a, y b, mapz[x][y b], true); \
+ }
+
+ DIAGONAL_RIPPLE(-1, -1, (x>mx && y>my)); // do diagonals because adjacents
+ DIAGONAL_RIPPLE(-1, +1, (x>mx && y<ny)); // won't unless changed
+ DIAGONAL_RIPPLE(+1, +1, (x<nx && y<ny));
+ DIAGONAL_RIPPLE(+1, -1, (x<nx && y>my));
+ }
#define loopbrush(i) for(int x=bmx; x<=bnx+i; x++) for(int y=bmy; y<=bny+i; y++)
- void paint()
- {
- loopbrush(1)
- map[x][y] -= dr * brush[x][y];
- }
-
- void smooth()
- {
- int sum, div;
- loopbrush(-2)
- {
- sum = 0;
- div = 9;
- loopi(3) loopj(3)
- if(flags[x+i][y+j] & MAPPED)
- sum += map[x+i][y+j];
- else div--;
- if(div)
- map[x+1][y+1] = sum / div;
- }
- }
-
- void rippleandset()
- {
- loopbrush(0)
- ripple(x, y, gz, false);
- }
-
- void run(int dir, int mode)
- {
- d = dimension(sel.orient);
- dc = dimcoord(sel.orient);
- dcr= dc ? 1 : -1;
- dr = dir>0 ? 1 : -1;
- br = dir>0 ? 0x08080808 : 0;
- // biasup = mode == dir<0;
- biasup = dir<0;
- bool paintme = paintbrush;
- int cx = (sel.corner&1 ? 0 : -1);
- int cy = (sel.corner&2 ? 0 : -1);
- hws= (worldsize>>gridpower);
- gx = (cur[R[d]] >> gridpower) + cx - MAXBRUSH2;
- gy = (cur[C[d]] >> gridpower) + cy - MAXBRUSH2;
- gz = (cur[D[d]] >> gridpower);
- fs = dc ? 4 : 0;
- fg = dc ? gridsize : -gridsize;
- mx = max(0, -gx); // ripple range
- my = max(0, -gy);
- nx = min(MAXBRUSH-1, hws-gx) - 1;
- ny = min(MAXBRUSH-1, hws-gy) - 1;
- if(havesel)
- { // selection range
- bmx = mx = max(mx, (sel.o[R[d]]>>gridpower)-gx);
- bmy = my = max(my, (sel.o[C[d]]>>gridpower)-gy);
- bnx = nx = min(nx, (sel.s[R[d]]+(sel.o[R[d]]>>gridpower))-gx-1);
- bny = ny = min(ny, (sel.s[C[d]]+(sel.o[C[d]]>>gridpower))-gy-1);
- }
- if(havesel && mode<0) // -ve means smooth selection
- paintme = false;
- else
- { // brush range
- bmx = max(mx, brushminx);
- bmy = max(my, brushminy);
- bnx = min(nx, brushmaxx-1);
- bny = min(ny, brushmaxy-1);
- }
- nz = worldsize-gridsize;
- mz = 0;
- hundo.s = ivec(d,1,1,5);
- hundo.orient = sel.orient;
- hundo.grid = gridsize;
- forcenextundo();
-
- changes.grid = gridsize;
- changes.s = changes.o = cur;
- memset(map, 0, sizeof map);
- memset(flags, 0, sizeof flags);
-
- selecting = true;
- select(clamp(MAXBRUSH2-cx, bmx, bnx),
- clamp(MAXBRUSH2-cy, bmy, bny),
- dc ? gz : hws - gz);
- selecting = false;
- if(paintme)
- paint();
- else
- smooth();
- rippleandset(); // pull up points to cubify, and set
- changes.s.sub(changes.o).shr(gridpower).add(1);
- changed(changes);
- }
+ void paint()
+ {
+ loopbrush(1)
+ map[x][y] -= dr * brush[x][y];
+ }
+
+ void smooth()
+ {
+ int sum, div;
+ loopbrush(-2)
+ {
+ sum = 0;
+ div = 9;
+ loopi(3) loopj(3)
+ if(flags[x+i][y+j] & MAPPED)
+ sum += map[x+i][y+j];
+ else div--;
+ if(div)
+ map[x+1][y+1] = sum / div;
+ }
+ }
+
+ void rippleandset()
+ {
+ loopbrush(0)
+ ripple(x, y, gz, false);
+ }
+
+ void run(int dir, int mode)
+ {
+ d = dimension(sel.orient);
+ dc = dimcoord(sel.orient);
+ dcr= dc ? 1 : -1;
+ dr = dir>0 ? 1 : -1;
+ br = dir>0 ? 0x08080808 : 0;
+ // biasup = mode == dir<0;
+ biasup = dir<0;
+ bool paintme = paintbrush;
+ int cx = (sel.corner&1 ? 0 : -1);
+ int cy = (sel.corner&2 ? 0 : -1);
+ hws= (worldsize>>gridpower);
+ gx = (cur[R[d]] >> gridpower) + cx - MAXBRUSH2;
+ gy = (cur[C[d]] >> gridpower) + cy - MAXBRUSH2;
+ gz = (cur[D[d]] >> gridpower);
+ fs = dc ? 4 : 0;
+ fg = dc ? gridsize : -gridsize;
+ mx = max(0, -gx); // ripple range
+ my = max(0, -gy);
+ nx = min(MAXBRUSH-1, hws-gx) - 1;
+ ny = min(MAXBRUSH-1, hws-gy) - 1;
+ if(havesel)
+ { // selection range
+ bmx = mx = max(mx, (sel.o[R[d]]>>gridpower)-gx);
+ bmy = my = max(my, (sel.o[C[d]]>>gridpower)-gy);
+ bnx = nx = min(nx, (sel.s[R[d]]+(sel.o[R[d]]>>gridpower))-gx-1);
+ bny = ny = min(ny, (sel.s[C[d]]+(sel.o[C[d]]>>gridpower))-gy-1);
+ }
+ if(havesel && mode<0) // -ve means smooth selection
+ paintme = false;
+ else
+ { // brush range
+ bmx = max(mx, brushminx);
+ bmy = max(my, brushminy);
+ bnx = min(nx, brushmaxx-1);
+ bny = min(ny, brushmaxy-1);
+ }
+ nz = worldsize-gridsize;
+ mz = 0;
+ hundo.s = ivec(d,1,1,5);
+ hundo.orient = sel.orient;
+ hundo.grid = gridsize;
+ forcenextundo();
+
+ changes.grid = gridsize;
+ changes.s = changes.o = cur;
+ memset(map, 0, sizeof map);
+ memset(flags, 0, sizeof flags);
+
+ selecting = true;
+ select(clamp(MAXBRUSH2-cx, bmx, bnx),
+ clamp(MAXBRUSH2-cy, bmy, bny),
+ dc ? gz : hws - gz);
+ selecting = false;
+ if(paintme)
+ paint();
+ else
+ smooth();
+ rippleandset(); // pull up points to cubify, and set
+ changes.s.sub(changes.o).shr(gridpower).add(1);
+ changed(changes);
+ }
}
void edithmap(int dir, int mode) {
- if((nompedit && multiplayer()) || !hmapsel) return;
- hmap::run(dir, mode);
+ if((nompedit && multiplayer()) || !hmapsel) return;
+ hmap::run(dir, mode);
}
///////////// main cube edit ////////////////
@@ -1923,151 +1922,151 @@ int bounded(int n) { return n<0 ? 0 : (n>8 ? 8 : n); }
void pushedge(uchar &edge, int dir, int dc)
{
- int ne = bounded(edgeget(edge, dc)+dir);
- edgeset(edge, dc, ne);
- int oe = edgeget(edge, 1-dc);
- if((dir<0 && dc && oe>ne) || (dir>0 && dc==0 && oe<ne)) edgeset(edge, 1-dc, ne);
+ int ne = bounded(edgeget(edge, dc)+dir);
+ edgeset(edge, dc, ne);
+ int oe = edgeget(edge, 1-dc);
+ if((dir<0 && dc && oe>ne) || (dir>0 && dc==0 && oe<ne)) edgeset(edge, 1-dc, ne);
}
void linkedpush(cube &c, int d, int x, int y, int dc, int dir)
{
- ivec v, p;
- getcubevector(c, d, x, y, dc, v);
+ ivec v, p;
+ getcubevector(c, d, x, y, dc, v);
- loopi(2) loopj(2)
- {
- getcubevector(c, d, i, j, dc, p);
- if(v==p)
- pushedge(cubeedge(c, d, i, j), dir, dc);
- }
+ loopi(2) loopj(2)
+ {
+ getcubevector(c, d, i, j, dc, p);
+ if(v==p)
+ pushedge(cubeedge(c, d, i, j), dir, dc);
+ }
}
static ushort getmaterial(cube &c)
{
- if(c.children)
- {
- ushort mat = getmaterial(c.children[7]);
- loopi(7) if(mat != getmaterial(c.children[i])) return MAT_AIR;
- return mat;
- }
- return c.material;
+ if(c.children)
+ {
+ ushort mat = getmaterial(c.children[7]);
+ loopi(7) if(mat != getmaterial(c.children[i])) return MAT_AIR;
+ return mat;
+ }
+ return c.material;
}
VAR(invalidcubeguard, 0, 1, 1);
void mpeditface(int dir, int mode, selinfo &sel, bool local)
{
- if(mode==1 && (sel.cx || sel.cy || sel.cxs&1 || sel.cys&1)) mode = 0;
- int d = dimension(sel.orient);
- int dc = dimcoord(sel.orient);
- int seldir = dc ? -dir : dir;
-
- if(local)
- game::edittrigger(sel, EDIT_FACE, dir, mode);
-
- if(mode==1)
- {
- int h = sel.o[d]+dc*sel.grid;
- if(((dir>0) == dc && h<=0) || ((dir<0) == dc && h>=worldsize)) return;
- if(dir<0) sel.o[d] += sel.grid * seldir;
- }
-
- if(dc) sel.o[d] += sel.us(d)-sel.grid;
- sel.s[d] = 1;
-
- loopselxyz(
- if(c.children) solidfaces(c);
- ushort mat = getmaterial(c);
- discardchildren(c, true);
- c.material = mat;
- if(mode==1) // fill command
- {
- if(dir<0)
- {
- solidfaces(c);
- cube &o = blockcube(x, y, 1, sel, -sel.grid);
- loopi(6)
- c.texture[i] = o.children ? (int) DEFAULT_GEOM : (int) o.texture[i];
- }
- else
- emptyfaces(c);
- }
- else
- {
- uint bak = c.faces[d];
- uchar *p = (uchar *)&c.faces[d];
-
- if(mode==2)
- linkedpush(c, d, sel.corner&1, sel.corner>>1, dc, seldir); // corner command
- else
- {
- loop(mx,2) loop(my,2) // pull/push edges command
- {
- if(x==0 && mx==0 && sel.cx) continue;
- if(y==0 && my==0 && sel.cy) continue;
- if(x==sel.s[R[d]]-1 && mx==1 && (sel.cx+sel.cxs)&1) continue;
- if(y==sel.s[C[d]]-1 && my==1 && (sel.cy+sel.cys)&1) continue;
- if(p[mx+my*2] != ((uchar *)&bak)[mx+my*2]) continue;
-
- linkedpush(c, d, mx, my, dc, seldir);
- }
- }
-
- optiface(p, c);
- if(invalidcubeguard==1 && !isvalidcube(c))
- {
- uint newbak = c.faces[d];
- uchar *m = (uchar *)&bak;
- uchar *n = (uchar *)&newbak;
- loopk(4) if(n[k] != m[k]) // tries to find partial edit that is valid
- {
- c.faces[d] = bak;
- c.edges[d*4+k] = n[k];
- if(isvalidcube(c))
- m[k] = n[k];
- }
- c.faces[d] = bak;
- }
- }
- );
- if (mode==1 && dir>0)
- sel.o[d] += sel.grid * seldir;
+ if(mode==1 && (sel.cx || sel.cy || sel.cxs&1 || sel.cys&1)) mode = 0;
+ int d = dimension(sel.orient);
+ int dc = dimcoord(sel.orient);
+ int seldir = dc ? -dir : dir;
+
+ if(local)
+ game::edittrigger(sel, EDIT_FACE, dir, mode);
+
+ if(mode==1)
+ {
+ int h = sel.o[d]+dc*sel.grid;
+ if(((dir>0) == dc && h<=0) || ((dir<0) == dc && h>=worldsize)) return;
+ if(dir<0) sel.o[d] += sel.grid * seldir;
+ }
+
+ if(dc) sel.o[d] += sel.us(d)-sel.grid;
+ sel.s[d] = 1;
+
+ loopselxyz(
+ if(c.children) solidfaces(c);
+ ushort mat = getmaterial(c);
+ discardchildren(c, true);
+ c.material = mat;
+ if(mode==1) // fill command
+ {
+ if(dir<0)
+ {
+ solidfaces(c);
+ cube &o = blockcube(x, y, 1, sel, -sel.grid);
+ loopi(6)
+ c.texture[i] = o.children ? (int) DEFAULT_GEOM : (int) o.texture[i];
+ }
+ else
+ emptyfaces(c);
+ }
+ else
+ {
+ uint bak = c.faces[d];
+ uchar *p = (uchar *)&c.faces[d];
+
+ if(mode==2)
+ linkedpush(c, d, sel.corner&1, sel.corner>>1, dc, seldir); // corner command
+ else
+ {
+ loop(mx,2) loop(my,2) // pull/push edges command
+ {
+ if(x==0 && mx==0 && sel.cx) continue;
+ if(y==0 && my==0 && sel.cy) continue;
+ if(x==sel.s[R[d]]-1 && mx==1 && (sel.cx+sel.cxs)&1) continue;
+ if(y==sel.s[C[d]]-1 && my==1 && (sel.cy+sel.cys)&1) continue;
+ if(p[mx+my*2] != ((uchar *)&bak)[mx+my*2]) continue;
+
+ linkedpush(c, d, mx, my, dc, seldir);
+ }
+ }
+
+ optiface(p, c);
+ if(invalidcubeguard==1 && !isvalidcube(c))
+ {
+ uint newbak = c.faces[d];
+ uchar *m = (uchar *)&bak;
+ uchar *n = (uchar *)&newbak;
+ loopk(4) if(n[k] != m[k]) // tries to find partial edit that is valid
+ {
+ c.faces[d] = bak;
+ c.edges[d*4+k] = n[k];
+ if(isvalidcube(c))
+ m[k] = n[k];
+ }
+ c.faces[d] = bak;
+ }
+ }
+ );
+ if (mode==1 && dir>0)
+ sel.o[d] += sel.grid * seldir;
}
void editface(int *dir, int *mode)
{
- if(noedit(moving!=0)) return;
- if(hmapedit!=1)
- mpeditface(*dir, *mode, sel, true);
- else
- edithmap(*dir, *mode);
+ if(noedit(moving!=0)) return;
+ if(hmapedit!=1)
+ mpeditface(*dir, *mode, sel, true);
+ else
+ edithmap(*dir, *mode);
}
VAR(selectionsurf, 0, 0, 1);
void pushsel(int *dir)
{
- if(noedit(moving!=0)) return;
- int d = dimension(orient);
- int s = dimcoord(orient) ? -*dir : *dir;
- sel.o[d] += s*sel.grid;
- if(selectionsurf==1)
- {
- player->o[d] += s*sel.grid;
- player->resetinterp();
- }
+ if(noedit(moving!=0)) return;
+ int d = dimension(orient);
+ int s = dimcoord(orient) ? -*dir : *dir;
+ sel.o[d] += s*sel.grid;
+ if(selectionsurf==1)
+ {
+ player->o[d] += s*sel.grid;
+ player->resetinterp();
+ }
}
void mpdelcube(selinfo &sel, bool local)
{
- if(local) game::edittrigger(sel, EDIT_DELCUBE);
- loopselxyz(discardchildren(c, true); emptyfaces(c));
+ if(local) game::edittrigger(sel, EDIT_DELCUBE);
+ loopselxyz(discardchildren(c, true); emptyfaces(c));
}
void delcube()
{
- if(noedit(true)) return;
- mpdelcube(sel, true);
+ if(noedit(true)) return;
+ mpdelcube(sel, true);
}
COMMAND(pushsel, "i");
@@ -2080,14 +2079,14 @@ int curtexindex = -1, lasttex = 0, lasttexmillis = -1;
int texpaneltimer = 0;
vector<ushort> texmru;
-void tofronttex() // maintain most recently used of the texture lists when applying texture
+void tofronttex() // maintain most recently used of the texture lists when applying texture
{
- int c = curtexindex;
- if(texmru.inrange(c))
- {
- texmru.insert(0, texmru.remove(c));
- curtexindex = -1;
- }
+ int c = curtexindex;
+ if(texmru.inrange(c))
+ {
+ texmru.insert(0, texmru.remove(c));
+ curtexindex = -1;
+ }
}
selinfo repsel;
@@ -2099,421 +2098,421 @@ VAR(usevdelta, 1, 0, 0);
static VSlot *remapvslot(int index, bool delta, const VSlot &ds)
{
- loopv(remappedvslots) if(remappedvslots[i].index == index) return remappedvslots[i].vslot;
- VSlot &vs = lookupvslot(index, false);
- if(vs.index < 0 || vs.index == DEFAULT_SKY) return NULL;
- VSlot *edit = NULL;
- if(delta)
- {
- VSlot ms;
- mergevslot(ms, vs, ds);
- edit = ms.changed ? editvslot(vs, ms) : vs.slot->variants;
- }
- else edit = ds.changed ? editvslot(vs, ds) : vs.slot->variants;
- if(!edit) edit = &vs;
- remappedvslots.add(vslotmap(vs.index, edit));
- return edit;
+ loopv(remappedvslots) if(remappedvslots[i].index == index) return remappedvslots[i].vslot;
+ VSlot &vs = lookupvslot(index, false);
+ if(vs.index < 0 || vs.index == DEFAULT_SKY) return NULL;
+ VSlot *edit = NULL;
+ if(delta)
+ {
+ VSlot ms;
+ mergevslot(ms, vs, ds);
+ edit = ms.changed ? editvslot(vs, ms) : vs.slot->variants;
+ }
+ else edit = ds.changed ? editvslot(vs, ds) : vs.slot->variants;
+ if(!edit) edit = &vs;
+ remappedvslots.add(vslotmap(vs.index, edit));
+ return edit;
}
static void remapvslots(cube &c, bool delta, const VSlot &ds, int orient, bool &findrep, VSlot *&findedit)
{
- if(c.children)
- {
- loopi(8) remapvslots(c.children[i], delta, ds, orient, findrep, findedit);
- return;
- }
- static VSlot ms;
- if(orient<0) loopi(6)
- {
- VSlot *edit = remapvslot(c.texture[i], delta, ds);
- if(edit)
- {
- c.texture[i] = edit->index;
- if(!findedit) findedit = edit;
- }
- }
- else
- {
- int i = visibleorient(c, orient);
- VSlot *edit = remapvslot(c.texture[i], delta, ds);
- if(edit)
- {
- if(findrep)
- {
- if(reptex < 0) reptex = c.texture[i];
- else if(reptex != c.texture[i]) findrep = false;
- }
- c.texture[i] = edit->index;
- if(!findedit) findedit = edit;
- }
- }
+ if(c.children)
+ {
+ loopi(8) remapvslots(c.children[i], delta, ds, orient, findrep, findedit);
+ return;
+ }
+ static VSlot ms;
+ if(orient<0) loopi(6)
+ {
+ VSlot *edit = remapvslot(c.texture[i], delta, ds);
+ if(edit)
+ {
+ c.texture[i] = edit->index;
+ if(!findedit) findedit = edit;
+ }
+ }
+ else
+ {
+ int i = visibleorient(c, orient);
+ VSlot *edit = remapvslot(c.texture[i], delta, ds);
+ if(edit)
+ {
+ if(findrep)
+ {
+ if(reptex < 0) reptex = c.texture[i];
+ else if(reptex != c.texture[i]) findrep = false;
+ }
+ c.texture[i] = edit->index;
+ if(!findedit) findedit = edit;
+ }
+ }
}
void edittexcube(cube &c, int tex, int orient, bool &findrep)
{
- if(orient<0) loopi(6) c.texture[i] = tex;
- else
- {
- int i = visibleorient(c, orient);
- if(findrep)
- {
- if(reptex < 0) reptex = c.texture[i];
- else if(reptex != c.texture[i]) findrep = false;
- }
- c.texture[i] = tex;
- }
- if(c.children) loopi(8) edittexcube(c.children[i], tex, orient, findrep);
+ if(orient<0) loopi(6) c.texture[i] = tex;
+ else
+ {
+ int i = visibleorient(c, orient);
+ if(findrep)
+ {
+ if(reptex < 0) reptex = c.texture[i];
+ else if(reptex != c.texture[i]) findrep = false;
+ }
+ c.texture[i] = tex;
+ }
+ if(c.children) loopi(8) edittexcube(c.children[i], tex, orient, findrep);
}
VAR(allfaces, 0, 0, 1);
void mpeditvslot(int delta, VSlot &ds, int allfaces, selinfo &sel, bool local)
{
- if(local)
- {
- game::edittrigger(sel, EDIT_VSLOT, delta, allfaces, 0, &ds);
- if(!(lastsel==sel)) tofronttex();
- if(allfaces || !(repsel == sel)) reptex = -1;
- repsel = sel;
- }
- bool findrep = local && !allfaces && reptex < 0;
- VSlot *findedit = NULL;
- loopselxyz(remapvslots(c, delta != 0, ds, allfaces ? -1 : sel.orient, findrep, findedit));
- remappedvslots.setsize(0);
- if(local && findedit)
- {
- lasttex = findedit->index;
- lasttexmillis = totalmillis;
- curtexindex = texmru.find(lasttex);
- if(curtexindex < 0)
- {
- curtexindex = texmru.length();
- texmru.add(lasttex);
- }
- }
+ if(local)
+ {
+ game::edittrigger(sel, EDIT_VSLOT, delta, allfaces, 0, &ds);
+ if(!(lastsel==sel)) tofronttex();
+ if(allfaces || !(repsel == sel)) reptex = -1;
+ repsel = sel;
+ }
+ bool findrep = local && !allfaces && reptex < 0;
+ VSlot *findedit = NULL;
+ loopselxyz(remapvslots(c, delta != 0, ds, allfaces ? -1 : sel.orient, findrep, findedit));
+ remappedvslots.setsize(0);
+ if(local && findedit)
+ {
+ lasttex = findedit->index;
+ lasttexmillis = totalmillis;
+ curtexindex = texmru.find(lasttex);
+ if(curtexindex < 0)
+ {
+ curtexindex = texmru.length();
+ texmru.add(lasttex);
+ }
+ }
}
bool mpeditvslot(int delta, int allfaces, selinfo &sel, ucharbuf &buf)
{
- VSlot ds;
- if(!unpackvslot(buf, ds, delta != 0)) return false;
- editingvslot(ds.layer);
- mpeditvslot(delta, ds, allfaces, sel, false);
- return true;
+ VSlot ds;
+ if(!unpackvslot(buf, ds, delta != 0)) return false;
+ editingvslot(ds.layer);
+ mpeditvslot(delta, ds, allfaces, sel, false);
+ return true;
}
void vdelta(char *body)
{
- if(noedit()) return;
- usevdelta++;
- execute(body);
- usevdelta--;
+ if(noedit()) return;
+ usevdelta++;
+ execute(body);
+ usevdelta--;
}
COMMAND(vdelta, "s");
void vrotate(int *n)
{
- if(noedit()) return;
- VSlot ds;
- ds.changed = 1<<VSLOT_ROTATION;
- ds.rotation = usevdelta ? *n : clamp(*n, 0, 7);
- mpeditvslot(usevdelta, ds, allfaces, sel, true);
+ if(noedit()) return;
+ VSlot ds;
+ ds.changed = 1<<VSLOT_ROTATION;
+ ds.rotation = usevdelta ? *n : clamp(*n, 0, 7);
+ mpeditvslot(usevdelta, ds, allfaces, sel, true);
}
COMMAND(vrotate, "i");
ICOMMAND(getvrotate, "i", (int *tex), intret(lookupvslot(*tex, false).rotation));
void voffset(int *x, int *y)
{
- if(noedit()) return;
- VSlot ds;
- ds.changed = 1<<VSLOT_OFFSET;
- ds.offset = usevdelta ? ivec2(*x, *y) : ivec2(*x, *y).max(0);
- mpeditvslot(usevdelta, ds, allfaces, sel, true);
+ if(noedit()) return;
+ VSlot ds;
+ ds.changed = 1<<VSLOT_OFFSET;
+ ds.offset = usevdelta ? ivec2(*x, *y) : ivec2(*x, *y).max(0);
+ mpeditvslot(usevdelta, ds, allfaces, sel, true);
}
COMMAND(voffset, "ii");
ICOMMAND(getvoffset, "i", (int *tex),
{
- VSlot &vslot = lookupvslot(*tex, false);
- defformatstring(str, "%d %d", vslot.offset.x, vslot.offset.y);
- result(str);
+ VSlot &vslot = lookupvslot(*tex, false);
+ defformatstring(str, "%d %d", vslot.offset.x, vslot.offset.y);
+ result(str);
});
void vscroll(float *s, float *t)
{
- if(noedit()) return;
- VSlot ds;
- ds.changed = 1<<VSLOT_SCROLL;
- ds.scroll = vec2(*s, *t).div(1000);
- mpeditvslot(usevdelta, ds, allfaces, sel, true);
+ if(noedit()) return;
+ VSlot ds;
+ ds.changed = 1<<VSLOT_SCROLL;
+ ds.scroll = vec2(*s, *t).div(1000);
+ mpeditvslot(usevdelta, ds, allfaces, sel, true);
}
COMMAND(vscroll, "ff");
ICOMMAND(getvscroll, "i", (int *tex),
{
- VSlot &vslot = lookupvslot(*tex, false);
- defformatstring(str, "%s %s", floatstr(vslot.scroll.x), floatstr(vslot.scroll.y));
- result(str);
+ VSlot &vslot = lookupvslot(*tex, false);
+ defformatstring(str, "%s %s", floatstr(vslot.scroll.x), floatstr(vslot.scroll.y));
+ result(str);
});
void vscale(float *scale)
{
- if(noedit()) return;
- VSlot ds;
- ds.changed = 1<<VSLOT_SCALE;
- ds.scale = *scale <= 0 ? 1 : (usevdelta ? *scale : clamp(*scale, 1/8.0f, 8.0f));
- mpeditvslot(usevdelta, ds, allfaces, sel, true);
+ if(noedit()) return;
+ VSlot ds;
+ ds.changed = 1<<VSLOT_SCALE;
+ ds.scale = *scale <= 0 ? 1 : (usevdelta ? *scale : clamp(*scale, 1/8.0f, 8.0f));
+ mpeditvslot(usevdelta, ds, allfaces, sel, true);
}
COMMAND(vscale, "f");
ICOMMAND(getvscale, "i", (int *tex), floatret(lookupvslot(*tex, false).scale));
void vlayer(int *n)
{
- if(noedit()) return;
- VSlot ds;
- ds.changed = 1<<VSLOT_LAYER;
- if(vslots.inrange(*n))
- {
- ds.layer = *n;
- if(vslots[ds.layer]->changed && nompedit && multiplayer()) return;
- }
- editingvslot(ds.layer);
- mpeditvslot(usevdelta, ds, allfaces, sel, true);
+ if(noedit()) return;
+ VSlot ds;
+ ds.changed = 1<<VSLOT_LAYER;
+ if(vslots.inrange(*n))
+ {
+ ds.layer = *n;
+ if(vslots[ds.layer]->changed && nompedit && multiplayer()) return;
+ }
+ editingvslot(ds.layer);
+ mpeditvslot(usevdelta, ds, allfaces, sel, true);
}
COMMAND(vlayer, "i");
ICOMMAND(getvlayer, "i", (int *tex), intret(lookupvslot(*tex, false).layer));
void valpha(float *front, float *back)
{
- if(noedit()) return;
- VSlot ds;
- ds.changed = 1<<VSLOT_ALPHA;
- ds.alphafront = clamp(*front, 0.0f, 1.0f);
- ds.alphaback = clamp(*back, 0.0f, 1.0f);
- mpeditvslot(usevdelta, ds, allfaces, sel, true);
+ if(noedit()) return;
+ VSlot ds;
+ ds.changed = 1<<VSLOT_ALPHA;
+ ds.alphafront = clamp(*front, 0.0f, 1.0f);
+ ds.alphaback = clamp(*back, 0.0f, 1.0f);
+ mpeditvslot(usevdelta, ds, allfaces, sel, true);
}
COMMAND(valpha, "ff");
ICOMMAND(getvalpha, "i", (int *tex),
{
- VSlot &vslot = lookupvslot(*tex, false);
- defformatstring(str, "%s %s", floatstr(vslot.alphafront), floatstr(vslot.alphaback));
- result(str);
+ VSlot &vslot = lookupvslot(*tex, false);
+ defformatstring(str, "%s %s", floatstr(vslot.alphafront), floatstr(vslot.alphaback));
+ result(str);
});
void vcolor(float *r, float *g, float *b)
{
- if(noedit()) return;
- VSlot ds;
- ds.changed = 1<<VSLOT_COLOR;
- ds.colorscale = vec(clamp(*r, 0.0f, 1.0f), clamp(*g, 0.0f, 1.0f), clamp(*b, 0.0f, 1.0f));
- mpeditvslot(usevdelta, ds, allfaces, sel, true);
+ if(noedit()) return;
+ VSlot ds;
+ ds.changed = 1<<VSLOT_COLOR;
+ ds.colorscale = vec(clamp(*r, 0.0f, 1.0f), clamp(*g, 0.0f, 1.0f), clamp(*b, 0.0f, 1.0f));
+ mpeditvslot(usevdelta, ds, allfaces, sel, true);
}
COMMAND(vcolor, "fff");
ICOMMAND(getvcolor, "i", (int *tex),
{
- VSlot &vslot = lookupvslot(*tex, false);
- defformatstring(str, "%s %s %s", floatstr(vslot.colorscale.r), floatstr(vslot.colorscale.g), floatstr(vslot.colorscale.b));
- result(str);
+ VSlot &vslot = lookupvslot(*tex, false);
+ defformatstring(str, "%s %s %s", floatstr(vslot.colorscale.r), floatstr(vslot.colorscale.g), floatstr(vslot.colorscale.b));
+ result(str);
});
void vreset()
{
- if(noedit()) return;
- VSlot ds;
- mpeditvslot(usevdelta, ds, allfaces, sel, true);
+ if(noedit()) return;
+ VSlot ds;
+ mpeditvslot(usevdelta, ds, allfaces, sel, true);
}
COMMAND(vreset, "");
void vshaderparam(const char *name, float *x, float *y, float *z, float *w)
{
- if(noedit()) return;
- VSlot ds;
- ds.changed = 1<<VSLOT_SHPARAM;
- if(name[0])
- {
- SlotShaderParam p = { getshaderparamname(name), -1, {*x, *y, *z, *w} };
- ds.params.add(p);
- }
- mpeditvslot(usevdelta, ds, allfaces, sel, true);
+ if(noedit()) return;
+ VSlot ds;
+ ds.changed = 1<<VSLOT_SHPARAM;
+ if(name[0])
+ {
+ SlotShaderParam p = { getshaderparamname(name), -1, {*x, *y, *z, *w} };
+ ds.params.add(p);
+ }
+ mpeditvslot(usevdelta, ds, allfaces, sel, true);
}
COMMAND(vshaderparam, "sffff");
ICOMMAND(getvshaderparam, "is", (int *tex, const char *name),
{
- VSlot &vslot = lookupvslot(*tex, false);
- loopv(vslot.params)
- {
- SlotShaderParam &p = vslot.params[i];
- if(!strcmp(p.name, name))
- {
- defformatstring(str, "%s %s %s %s", floatstr(p.val[0]), floatstr(p.val[1]), floatstr(p.val[2]), floatstr(p.val[3]));
- result(str);
- return;
- }
- }
+ VSlot &vslot = lookupvslot(*tex, false);
+ loopv(vslot.params)
+ {
+ SlotShaderParam &p = vslot.params[i];
+ if(!strcmp(p.name, name))
+ {
+ defformatstring(str, "%s %s %s %s", floatstr(p.val[0]), floatstr(p.val[1]), floatstr(p.val[2]), floatstr(p.val[3]));
+ result(str);
+ return;
+ }
+ }
});
ICOMMAND(getvshaderparamnames, "i", (int *tex),
{
- VSlot &vslot = lookupvslot(*tex, false);
- vector<char> str;
- loopv(vslot.params)
- {
- SlotShaderParam &p = vslot.params[i];
- if(i) str.put(' ');
- str.put(p.name, strlen(p.name));
- }
- str.add('\0');
- stringret(newstring(str.getbuf(), str.length()-1));
+ VSlot &vslot = lookupvslot(*tex, false);
+ vector<char> str;
+ loopv(vslot.params)
+ {
+ SlotShaderParam &p = vslot.params[i];
+ if(i) str.put(' ');
+ str.put(p.name, strlen(p.name));
+ }
+ str.add('\0');
+ stringret(newstring(str.getbuf(), str.length()-1));
});
void mpedittex(int tex, int allfaces, selinfo &sel, bool local)
{
- if(local)
- {
- game::edittrigger(sel, EDIT_TEX, tex, allfaces);
- if(allfaces || !(repsel == sel)) reptex = -1;
- repsel = sel;
- }
- bool findrep = local && !allfaces && reptex < 0;
- loopselxyz(edittexcube(c, tex, allfaces ? -1 : sel.orient, findrep));
+ if(local)
+ {
+ game::edittrigger(sel, EDIT_TEX, tex, allfaces);
+ if(allfaces || !(repsel == sel)) reptex = -1;
+ repsel = sel;
+ }
+ bool findrep = local && !allfaces && reptex < 0;
+ loopselxyz(edittexcube(c, tex, allfaces ? -1 : sel.orient, findrep));
}
static int unpacktex(int &tex, ucharbuf &buf, bool insert = true)
{
- if(tex < 0x10000) return true;
- VSlot ds;
- if(!unpackvslot(buf, ds, false)) return false;
- VSlot &vs = *lookupslot(tex & 0xFFFF, false).variants;
- if(vs.index < 0 || vs.index == DEFAULT_SKY) return false;
- VSlot *edit = insert ? editvslot(vs, ds) : findvslot(*vs.slot, vs, ds);
- if(!edit) return false;
- tex = edit->index;
- return true;
+ if(tex < 0x10000) return true;
+ VSlot ds;
+ if(!unpackvslot(buf, ds, false)) return false;
+ VSlot &vs = *lookupslot(tex & 0xFFFF, false).variants;
+ if(vs.index < 0 || vs.index == DEFAULT_SKY) return false;
+ VSlot *edit = insert ? editvslot(vs, ds) : findvslot(*vs.slot, vs, ds);
+ if(!edit) return false;
+ tex = edit->index;
+ return true;
}
int shouldpacktex(int index)
{
- if(vslots.inrange(index))
- {
- VSlot &vs = *vslots[index];
- if(vs.changed) return 0x10000 + vs.slot->index;
- }
- return 0;
+ if(vslots.inrange(index))
+ {
+ VSlot &vs = *vslots[index];
+ if(vs.changed) return 0x10000 + vs.slot->index;
+ }
+ return 0;
}
bool mpedittex(int tex, int allfaces, selinfo &sel, ucharbuf &buf)
{
- if(!unpacktex(tex, buf)) return false;
- mpedittex(tex, allfaces, sel, false);
- return true;
+ if(!unpacktex(tex, buf)) return false;
+ mpedittex(tex, allfaces, sel, false);
+ return true;
}
void filltexlist()
{
- if(texmru.length()!=vslots.length())
- {
- loopvrev(texmru) if(texmru[i]>=vslots.length())
- {
- if(curtexindex > i) curtexindex--;
- else if(curtexindex == i) curtexindex = -1;
- texmru.remove(i);
- }
- loopv(vslots) if(texmru.find(i)<0) texmru.add(i);
- }
+ if(texmru.length()!=vslots.length())
+ {
+ loopvrev(texmru) if(texmru[i]>=vslots.length())
+ {
+ if(curtexindex > i) curtexindex--;
+ else if(curtexindex == i) curtexindex = -1;
+ texmru.remove(i);
+ }
+ loopv(vslots) if(texmru.find(i)<0) texmru.add(i);
+ }
}
void compactmruvslots()
{
- remappedvslots.setsize(0);
- loopvrev(texmru)
- {
- if(vslots.inrange(texmru[i]))
- {
- VSlot &vs = *vslots[texmru[i]];
- if(vs.index >= 0)
- {
- texmru[i] = vs.index;
- continue;
- }
- }
- if(curtexindex > i) curtexindex--;
- else if(curtexindex == i) curtexindex = -1;
- texmru.remove(i);
- }
- if(vslots.inrange(lasttex))
- {
- VSlot &vs = *vslots[lasttex];
- lasttex = vs.index >= 0 ? vs.index : 0;
- }
- else lasttex = 0;
- reptex = vslots.inrange(reptex) ? vslots[reptex]->index : -1;
+ remappedvslots.setsize(0);
+ loopvrev(texmru)
+ {
+ if(vslots.inrange(texmru[i]))
+ {
+ VSlot &vs = *vslots[texmru[i]];
+ if(vs.index >= 0)
+ {
+ texmru[i] = vs.index;
+ continue;
+ }
+ }
+ if(curtexindex > i) curtexindex--;
+ else if(curtexindex == i) curtexindex = -1;
+ texmru.remove(i);
+ }
+ if(vslots.inrange(lasttex))
+ {
+ VSlot &vs = *vslots[lasttex];
+ lasttex = vs.index >= 0 ? vs.index : 0;
+ }
+ else lasttex = 0;
+ reptex = vslots.inrange(reptex) ? vslots[reptex]->index : -1;
}
void edittex(int i, bool save = true)
{
- lasttex = i;
- lasttexmillis = totalmillis;
- if(save)
- {
- loopvj(texmru) if(texmru[j]==lasttex) { curtexindex = j; break; }
- }
- mpedittex(i, allfaces, sel, true);
+ lasttex = i;
+ lasttexmillis = totalmillis;
+ if(save)
+ {
+ loopvj(texmru) if(texmru[j]==lasttex) { curtexindex = j; break; }
+ }
+ mpedittex(i, allfaces, sel, true);
}
void edittex_(int *dir)
{
- if(noedit()) return;
- filltexlist();
- if(texmru.empty()) return;
- texpaneltimer = 5000;
- if(!(lastsel==sel)) tofronttex();
- curtexindex = clamp(curtexindex<0 ? 0 : curtexindex+*dir, 0, texmru.length()-1);
- edittex(texmru[curtexindex], false);
+ if(noedit()) return;
+ filltexlist();
+ if(texmru.empty()) return;
+ texpaneltimer = 5000;
+ if(!(lastsel==sel)) tofronttex();
+ curtexindex = clamp(curtexindex<0 ? 0 : curtexindex+*dir, 0, texmru.length()-1);
+ edittex(texmru[curtexindex], false);
}
void gettex()
{
- if(noedit(true)) return;
- filltexlist();
- int tex = -1;
- loopxyz(sel, sel.grid, tex = c.texture[sel.orient]);
- loopv(texmru) if(texmru[i]==tex)
- {
- curtexindex = i;
- tofronttex();
- return;
- }
+ if(noedit(true)) return;
+ filltexlist();
+ int tex = -1;
+ loopxyz(sel, sel.grid, tex = c.texture[sel.orient]);
+ loopv(texmru) if(texmru[i]==tex)
+ {
+ curtexindex = i;
+ tofronttex();
+ return;
+ }
}
void getcurtex()
{
- if(noedit(true)) return;
- filltexlist();
- int index = curtexindex < 0 ? 0 : curtexindex;
- if(!texmru.inrange(index)) return;
- intret(texmru[index]);
+ if(noedit(true)) return;
+ filltexlist();
+ int index = curtexindex < 0 ? 0 : curtexindex;
+ if(!texmru.inrange(index)) return;
+ intret(texmru[index]);
}
void getseltex()
{
- if(noedit(true)) return;
- cube &c = lookupcube(sel.o, -sel.grid);
- if(c.children || isempty(c)) return;
- intret(c.texture[sel.orient]);
+ if(noedit(true)) return;
+ cube &c = lookupcube(sel.o, -sel.grid);
+ if(c.children || isempty(c)) return;
+ intret(c.texture[sel.orient]);
}
void gettexname(int *tex, int *subslot)
{
- if(noedit(true) || *tex<0) return;
- VSlot &vslot = lookupvslot(*tex, false);
- Slot &slot = *vslot.slot;
- if(!slot.sts.inrange(*subslot)) return;
- result(slot.sts[*subslot].name);
+ if(noedit(true) || *tex<0) return;
+ VSlot &vslot = lookupvslot(*tex, false);
+ Slot &slot = *vslot.slot;
+ if(!slot.sts.inrange(*subslot)) return;
+ result(slot.sts[*subslot].name);
}
void getslottex(int *idx)
{
- if(*idx < 0 || !slots.inrange(*idx)) { intret(-1); return; }
- Slot &slot = lookupslot(*idx, false);
- intret(slot.variants->index);
+ if(*idx < 0 || !slots.inrange(*idx)) { intret(-1); return; }
+ Slot &slot = lookupslot(*idx, false);
+ intret(slot.variants->index);
}
COMMANDN(edittex, edittex_, "i");
@@ -2529,38 +2528,38 @@ ICOMMAND(texloaded, "i", (int *tex), intret(slots.inrange(*tex) && slots[*tex]->
void replacetexcube(cube &c, int oldtex, int newtex)
{
- loopi(6) if(c.texture[i] == oldtex) c.texture[i] = newtex;
- if(c.children) loopi(8) replacetexcube(c.children[i], oldtex, newtex);
+ loopi(6) if(c.texture[i] == oldtex) c.texture[i] = newtex;
+ if(c.children) loopi(8) replacetexcube(c.children[i], oldtex, newtex);
}
void mpreplacetex(int oldtex, int newtex, bool insel, selinfo &sel, bool local)
{
- if(local) game::edittrigger(sel, EDIT_REPLACE, oldtex, newtex, insel ? 1 : 0);
- if(insel)
- {
- loopselxyz(replacetexcube(c, oldtex, newtex));
- }
- else
- {
- loopi(8) replacetexcube(worldroot[i], oldtex, newtex);
- }
- allchanged();
+ if(local) game::edittrigger(sel, EDIT_REPLACE, oldtex, newtex, insel ? 1 : 0);
+ if(insel)
+ {
+ loopselxyz(replacetexcube(c, oldtex, newtex));
+ }
+ else
+ {
+ loopi(8) replacetexcube(worldroot[i], oldtex, newtex);
+ }
+ allchanged();
}
bool mpreplacetex(int oldtex, int newtex, bool insel, selinfo &sel, ucharbuf &buf)
{
- if(!unpacktex(oldtex, buf, false)) return false;
- editingvslot(oldtex);
- if(!unpacktex(newtex, buf)) return false;
- mpreplacetex(oldtex, newtex, insel, sel, false);
- return true;
+ if(!unpacktex(oldtex, buf, false)) return false;
+ editingvslot(oldtex);
+ if(!unpacktex(newtex, buf)) return false;
+ mpreplacetex(oldtex, newtex, insel, sel, false);
+ return true;
}
void replace(bool insel)
{
- if(noedit()) return;
- if(reptex < 0) { conoutf(CON_ERROR, "can only replace after a texture edit"); return; }
- mpreplacetex(reptex, lasttex, insel, sel, true);
+ if(noedit()) return;
+ if(reptex < 0) { conoutf(CON_ERROR, "can only replace after a texture edit"); return; }
+ mpreplacetex(reptex, lasttex, insel, sel, true);
}
ICOMMAND(replace, "", (), replace(false));
@@ -2574,101 +2573,101 @@ uint mflip(uint face) { return (face&0xFF0000FF) | ((face&0x00FF0000)>>8) | ((fa
void flipcube(cube &c, int d)
{
- swap(c.texture[d*2], c.texture[d*2+1]);
- c.faces[D[d]] = dflip(c.faces[D[d]]);
- c.faces[C[d]] = cflip(c.faces[C[d]]);
- c.faces[R[d]] = rflip(c.faces[R[d]]);
- if(c.children)
- {
- loopi(8) if(i&octadim(d)) swap(c.children[i], c.children[i-octadim(d)]);
- loopi(8) flipcube(c.children[i], d);
- }
+ swap(c.texture[d*2], c.texture[d*2+1]);
+ c.faces[D[d]] = dflip(c.faces[D[d]]);
+ c.faces[C[d]] = cflip(c.faces[C[d]]);
+ c.faces[R[d]] = rflip(c.faces[R[d]]);
+ if(c.children)
+ {
+ loopi(8) if(i&octadim(d)) swap(c.children[i], c.children[i-octadim(d)]);
+ loopi(8) flipcube(c.children[i], d);
+ }
}
void rotatequad(cube &a, cube &b, cube &c, cube &d)
{
- cube t = a; a = b; b = c; c = d; d = t;
+ cube t = a; a = b; b = c; c = d; d = t;
}
void rotatecube(cube &c, int d) // rotates cube clockwise. see pics in cvs for help.
{
- c.faces[D[d]] = cflip (mflip(c.faces[D[d]]));
- c.faces[C[d]] = dflip (mflip(c.faces[C[d]]));
- c.faces[R[d]] = rflip (mflip(c.faces[R[d]]));
- swap(c.faces[R[d]], c.faces[C[d]]);
-
- swap(c.texture[2*R[d]], c.texture[2*C[d]+1]);
- swap(c.texture[2*C[d]], c.texture[2*R[d]+1]);
- swap(c.texture[2*C[d]], c.texture[2*C[d]+1]);
-
- if(c.children)
- {
- int row = octadim(R[d]);
- int col = octadim(C[d]);
- for(int i=0; i<=octadim(d); i+=octadim(d)) rotatequad
- (
- c.children[i+row],
- c.children[i],
- c.children[i+col],
- c.children[i+col+row]
- );
- loopi(8) rotatecube(c.children[i], d);
- }
+ c.faces[D[d]] = cflip (mflip(c.faces[D[d]]));
+ c.faces[C[d]] = dflip (mflip(c.faces[C[d]]));
+ c.faces[R[d]] = rflip (mflip(c.faces[R[d]]));
+ swap(c.faces[R[d]], c.faces[C[d]]);
+
+ swap(c.texture[2*R[d]], c.texture[2*C[d]+1]);
+ swap(c.texture[2*C[d]], c.texture[2*R[d]+1]);
+ swap(c.texture[2*C[d]], c.texture[2*C[d]+1]);
+
+ if(c.children)
+ {
+ int row = octadim(R[d]);
+ int col = octadim(C[d]);
+ for(int i=0; i<=octadim(d); i+=octadim(d)) rotatequad
+ (
+ c.children[i+row],
+ c.children[i],
+ c.children[i+col],
+ c.children[i+col+row]
+ );
+ loopi(8) rotatecube(c.children[i], d);
+ }
}
void mpflip(selinfo &sel, bool local)
{
- if(local)
- {
- game::edittrigger(sel, EDIT_FLIP);
- makeundo();
- }
- int zs = sel.s[dimension(sel.orient)];
- loopxy(sel)
- {
- loop(z,zs) flipcube(selcube(x, y, z), dimension(sel.orient));
- loop(z,zs/2)
- {
- cube &a = selcube(x, y, z);
- cube &b = selcube(x, y, zs-z-1);
- swap(a, b);
- }
- }
- changed(sel);
+ if(local)
+ {
+ game::edittrigger(sel, EDIT_FLIP);
+ makeundo();
+ }
+ int zs = sel.s[dimension(sel.orient)];
+ loopxy(sel)
+ {
+ loop(z,zs) flipcube(selcube(x, y, z), dimension(sel.orient));
+ loop(z,zs/2)
+ {
+ cube &a = selcube(x, y, z);
+ cube &b = selcube(x, y, zs-z-1);
+ swap(a, b);
+ }
+ }
+ changed(sel);
}
void flip()
{
- if(noedit()) return;
- mpflip(sel, true);
+ if(noedit()) return;
+ mpflip(sel, true);
}
void mprotate(int cw, selinfo &sel, bool local)
{
- if(local) game::edittrigger(sel, EDIT_ROTATE, cw);
- int d = dimension(sel.orient);
- if(!dimcoord(sel.orient)) cw = -cw;
- int m = sel.s[C[d]] < sel.s[R[d]] ? C[d] : R[d];
- int ss = sel.s[m] = max(sel.s[R[d]], sel.s[C[d]]);
- if(local) makeundo();
- loop(z,sel.s[D[d]]) loopi(cw>0 ? 1 : 3)
- {
- loopxy(sel) rotatecube(selcube(x,y,z), d);
- loop(y,ss/2) loop(x,ss-1-y*2) rotatequad
- (
- selcube(ss-1-y, x+y, z),
- selcube(x+y, y, z),
- selcube(y, ss-1-x-y, z),
- selcube(ss-1-x-y, ss-1-y, z)
- );
- }
- changed(sel);
+ if(local) game::edittrigger(sel, EDIT_ROTATE, cw);
+ int d = dimension(sel.orient);
+ if(!dimcoord(sel.orient)) cw = -cw;
+ int m = sel.s[C[d]] < sel.s[R[d]] ? C[d] : R[d];
+ int ss = sel.s[m] = max(sel.s[R[d]], sel.s[C[d]]);
+ if(local) makeundo();
+ loop(z,sel.s[D[d]]) loopi(cw>0 ? 1 : 3)
+ {
+ loopxy(sel) rotatecube(selcube(x,y,z), d);
+ loop(y,ss/2) loop(x,ss-1-y*2) rotatequad
+ (
+ selcube(ss-1-y, x+y, z),
+ selcube(x+y, y, z),
+ selcube(y, ss-1-x-y, z),
+ selcube(ss-1-x-y, ss-1-y, z)
+ );
+ }
+ changed(sel);
}
void rotate(int *cw)
{
- if(noedit()) return;
- mprotate(*cw, sel, true);
+ if(noedit()) return;
+ mprotate(*cw, sel, true);
}
COMMAND(flip, "");
@@ -2677,83 +2676,83 @@ COMMAND(rotate, "i");
enum { EDITMATF_EMPTY = 0x10000, EDITMATF_NOTEMPTY = 0x20000, EDITMATF_SOLID = 0x30000, EDITMATF_NOTSOLID = 0x40000 };
static const struct { const char *name; int filter; } editmatfilters[] =
{
- { "empty", EDITMATF_EMPTY },
- { "notempty", EDITMATF_NOTEMPTY },
- { "solid", EDITMATF_SOLID },
- { "notsolid", EDITMATF_NOTSOLID }
+ { "empty", EDITMATF_EMPTY },
+ { "notempty", EDITMATF_NOTEMPTY },
+ { "solid", EDITMATF_SOLID },
+ { "notsolid", EDITMATF_NOTSOLID }
};
void setmat(cube &c, ushort mat, ushort matmask, ushort filtermat, ushort filtermask, int filtergeom)
{
- if(c.children)
- loopi(8) setmat(c.children[i], mat, matmask, filtermat, filtermask, filtergeom);
- else if((c.material&filtermask) == filtermat)
- {
- switch(filtergeom)
- {
- case EDITMATF_EMPTY: if(isempty(c)) break; return;
- case EDITMATF_NOTEMPTY: if(!isempty(c)) break; return;
- case EDITMATF_SOLID: if(isentirelysolid(c)) break; return;
- case EDITMATF_NOTSOLID: if(!isentirelysolid(c)) break; return;
- }
- if(mat!=MAT_AIR)
- {
- c.material &= matmask;
- c.material |= mat;
- }
- else c.material = MAT_AIR;
- }
+ if(c.children)
+ loopi(8) setmat(c.children[i], mat, matmask, filtermat, filtermask, filtergeom);
+ else if((c.material&filtermask) == filtermat)
+ {
+ switch(filtergeom)
+ {
+ case EDITMATF_EMPTY: if(isempty(c)) break; return;
+ case EDITMATF_NOTEMPTY: if(!isempty(c)) break; return;
+ case EDITMATF_SOLID: if(isentirelysolid(c)) break; return;
+ case EDITMATF_NOTSOLID: if(!isentirelysolid(c)) break; return;
+ }
+ if(mat!=MAT_AIR)
+ {
+ c.material &= matmask;
+ c.material |= mat;
+ }
+ else c.material = MAT_AIR;
+ }
}
void mpeditmat(int matid, int filter, selinfo &sel, bool local)
{
- if(local) game::edittrigger(sel, EDIT_MAT, matid, filter);
-
- ushort filtermat = 0, filtermask = 0, matmask;
- int filtergeom = 0;
- if(filter >= 0)
- {
- filtermat = filter&0xFFFF;
- filtermask = filtermat&(MATF_VOLUME|MATF_INDEX) ? (int) MATF_VOLUME|MATF_INDEX : (filtermat&MATF_CLIP ? (int) MATF_CLIP : (int) filtermat);
- filtergeom = filter&~0xFFFF;
- }
- if(matid < 0)
- {
- matid = 0;
- matmask = filtermask;
- if(isclipped(filtermat&MATF_VOLUME)) matmask &= ~MATF_CLIP;
- if(isdeadly(filtermat&MATF_VOLUME)) matmask &= ~MAT_DEATH;
- }
- else
- {
- matmask = matid&(MATF_VOLUME|MATF_INDEX) ? 0 : (matid&MATF_CLIP ? ~MATF_CLIP : ~matid);
- if(isclipped(matid&MATF_VOLUME)) matid |= MAT_CLIP;
- if(isdeadly(matid&MATF_VOLUME)) matid |= MAT_DEATH;
- }
- loopselxyz(setmat(c, matid, matmask, filtermat, filtermask, filtergeom));
+ if(local) game::edittrigger(sel, EDIT_MAT, matid, filter);
+
+ ushort filtermat = 0, filtermask = 0, matmask;
+ int filtergeom = 0;
+ if(filter >= 0)
+ {
+ filtermat = filter&0xFFFF;
+ filtermask = filtermat&(MATF_VOLUME|MATF_INDEX) ? (int) MATF_VOLUME|MATF_INDEX : (filtermat&MATF_CLIP ? (int) MATF_CLIP : (int) filtermat);
+ filtergeom = filter&~0xFFFF;
+ }
+ if(matid < 0)
+ {
+ matid = 0;
+ matmask = filtermask;
+ if(isclipped(filtermat&MATF_VOLUME)) matmask &= ~MATF_CLIP;
+ if(isdeadly(filtermat&MATF_VOLUME)) matmask &= ~MAT_DEATH;
+ }
+ else
+ {
+ matmask = matid&(MATF_VOLUME|MATF_INDEX) ? 0 : (matid&MATF_CLIP ? ~MATF_CLIP : ~matid);
+ if(isclipped(matid&MATF_VOLUME)) matid |= MAT_CLIP;
+ if(isdeadly(matid&MATF_VOLUME)) matid |= MAT_DEATH;
+ }
+ loopselxyz(setmat(c, matid, matmask, filtermat, filtermask, filtergeom));
}
void editmat(char *name, char *filtername)
{
- if(noedit()) return;
- int filter = -1;
- if(filtername[0])
- {
- loopi(sizeof(editmatfilters)/sizeof(editmatfilters[0])) if(!strcmp(editmatfilters[i].name, filtername)) { filter = editmatfilters[i].filter; break; }
- if(filter < 0) filter = findmaterial(filtername);
- if(filter < 0)
- {
- conoutf(CON_ERROR, "unknown material \"%s\"", filtername);
- return;
- }
- }
- int id = -1;
- if(name[0] || filter < 0)
- {
- id = findmaterial(name);
- if(id<0) { conoutf(CON_ERROR, "unknown material \"%s\"", name); return; }
- }
- mpeditmat(id, filter, sel, true);
+ if(noedit()) return;
+ int filter = -1;
+ if(filtername[0])
+ {
+ loopi(sizeof(editmatfilters)/sizeof(editmatfilters[0])) if(!strcmp(editmatfilters[i].name, filtername)) { filter = editmatfilters[i].filter; break; }
+ if(filter < 0) filter = findmaterial(filtername);
+ if(filter < 0)
+ {
+ conoutf(CON_ERROR, "unknown material \"%s\"", filtername);
+ return;
+ }
+ }
+ int id = -1;
+ if(name[0] || filter < 0)
+ {
+ id = findmaterial(name);
+ if(id<0) { conoutf(CON_ERROR, "unknown material \"%s\"", name); return; }
+ }
+ mpeditmat(id, filter, sel, true);
}
COMMAND(editmat, "ss");
@@ -2773,106 +2772,106 @@ VAR(texguinum, 1, -1, 0);
struct texturegui : g3d_callback
{
- bool menuon;
- vec menupos;
- int menustart, menutab;
-
- texturegui() : menustart(-1) {}
-
- void gui(g3d_gui &g, bool firstpass)
- {
- int origtab = menutab, numtabs = max((slots.length() + texguiwidth*texguiheight - 1)/(texguiwidth*texguiheight), 1);
- if(!firstpass) texguinum = -1;
- g.start(menustart, 0.04f, &menutab);
- bool oldautotab = g.allowautotab(false);
- loopi(numtabs)
- {
- g.tab(!i ? "Textures" : NULL, 0xFFDD88);
- if(i+1 != origtab) continue; //don't load textures on non-visible tabs!
- Slot *rollover = NULL;
- loop(h, texguiheight)
- {
- g.pushlist();
- loop(w, texguiwidth)
- {
- extern VSlot dummyvslot;
- int ti = (i*texguiheight+h)*texguiwidth+w;
- if(ti<slots.length())
- {
- Slot &slot = lookupslot(ti, false);
- VSlot &vslot = *slot.variants;
- if(slot.sts.empty()) continue;
- else if(!slot.loaded && !slot.thumbnail)
- {
- if(totalmillis-lastthumbnail<texguitime)
- {
- g.texture(dummyvslot, texguiscale, false); //create an empty space
- continue;
- }
- loadthumbnail(slot);
- lastthumbnail = totalmillis;
- }
- int ret = g.texture(vslot, texguiscale, true);
- if(ret&G3D_ROLLOVER) { rollover = &slot; texguinum = ti; }
- if(ret&G3D_UP && (slot.loaded || slot.thumbnail!=notexture))
- {
- edittex(vslot.index);
- hudshader->set();
- }
- }
- else
- {
- g.texture(dummyvslot, texguiscale, false); //create an empty space
- }
- }
- g.poplist();
- }
- if(texguiname)
- {
- if(rollover)
- {
- defformatstring(name, "%d \f7:\fc %s", texguinum, rollover->sts[0].name);
- g.title(name, 0xFFDD88);
- }
- else g.space(1);
- }
- }
- g.allowautotab(oldautotab);
- g.end();
- }
-
- void showtextures(bool on)
- {
- if(on == menuon) return;
- if((menuon = on))
- {
- if(menustart <= lasttexmillis)
- menutab = 1+clamp(lookupvslot(lasttex, false).slot->index, 0, slots.length()-1)/(texguiwidth*texguiheight);
- menupos = menuinfrontofplayer();
- menustart = starttime();
- }
- else texguinum = -1;
- }
-
- void show()
- {
- if(!menuon) return;
- filltexlist();
- extern int usegui2d;
- if(!editmode || ((!texgui2d || !usegui2d) && camera1->o.dist(menupos) > menuautoclose)) { menuon = false; texguinum = -1; }
- else g3d_addgui(this, menupos, texgui2d ? GUI_2D : 0);
- }
+ bool menuon;
+ vec menupos;
+ int menustart, menutab;
+
+ texturegui() : menustart(-1) {}
+
+ void gui(g3d_gui &g, bool firstpass)
+ {
+ int origtab = menutab, numtabs = max((slots.length() + texguiwidth*texguiheight - 1)/(texguiwidth*texguiheight), 1);
+ if(!firstpass) texguinum = -1;
+ g.start(menustart, 0.04f, &menutab);
+ bool oldautotab = g.allowautotab(false);
+ loopi(numtabs)
+ {
+ g.tab(!i ? "Textures" : NULL, 0xFFDD88);
+ if(i+1 != origtab) continue; //don't load textures on non-visible tabs!
+ Slot *rollover = NULL;
+ loop(h, texguiheight)
+ {
+ g.pushlist();
+ loop(w, texguiwidth)
+ {
+ extern VSlot dummyvslot;
+ int ti = (i*texguiheight+h)*texguiwidth+w;
+ if(ti<slots.length())
+ {
+ Slot &slot = lookupslot(ti, false);
+ VSlot &vslot = *slot.variants;
+ if(slot.sts.empty()) continue;
+ else if(!slot.loaded && !slot.thumbnail)
+ {
+ if(totalmillis-lastthumbnail<texguitime)
+ {
+ g.texture(dummyvslot, texguiscale, false); //create an empty space
+ continue;
+ }
+ loadthumbnail(slot);
+ lastthumbnail = totalmillis;
+ }
+ int ret = g.texture(vslot, texguiscale, true);
+ if(ret&G3D_ROLLOVER) { rollover = &slot; texguinum = ti; }
+ if(ret&G3D_UP && (slot.loaded || slot.thumbnail!=notexture))
+ {
+ edittex(vslot.index);
+ hudshader->set();
+ }
+ }
+ else
+ {
+ g.texture(dummyvslot, texguiscale, false); //create an empty space
+ }
+ }
+ g.poplist();
+ }
+ if(texguiname)
+ {
+ if(rollover)
+ {
+ defformatstring(name, "%d \f7:\fc %s", texguinum, rollover->sts[0].name);
+ g.title(name, 0xFFDD88);
+ }
+ else g.space(1);
+ }
+ }
+ g.allowautotab(oldautotab);
+ g.end();
+ }
+
+ void showtextures(bool on)
+ {
+ if(on == menuon) return;
+ if((menuon = on))
+ {
+ if(menustart <= lasttexmillis)
+ menutab = 1+clamp(lookupvslot(lasttex, false).slot->index, 0, slots.length()-1)/(texguiwidth*texguiheight);
+ menupos = menuinfrontofplayer();
+ menustart = starttime();
+ }
+ else texguinum = -1;
+ }
+
+ void show()
+ {
+ if(!menuon) return;
+ filltexlist();
+ extern int usegui2d;
+ if(!editmode || ((!texgui2d || !usegui2d) && camera1->o.dist(menupos) > menuautoclose)) { menuon = false; texguinum = -1; }
+ else g3d_addgui(this, menupos, texgui2d ? GUI_2D : 0);
+ }
} gui;
void g3d_texturemenu()
{
- gui.show();
+ gui.show();
}
void showtexgui(int *n)
{
- if(!editmode) { conoutf(CON_ERROR, "operation only allowed in edit mode"); return; }
- gui.showtextures(*n==0 ? !gui.menuon : *n==1);
+ if(!editmode) { conoutf(CON_ERROR, "operation only allowed in edit mode"); return; }
+ gui.showtextures(*n==0 ? !gui.menuon : *n==1);
}
// 0/noargs = toggle, 1 = on, other = off - will autoclose if too far away or exit editmode
@@ -2880,97 +2879,97 @@ COMMAND(showtexgui, "i");
bool cleartexgui()
{
- if(!gui.menuon) return false;
- gui.showtextures(false);
- return true;
+ if(!gui.menuon) return false;
+ gui.showtextures(false);
+ return true;
}
ICOMMAND(cleartexgui, "", (), intret(cleartexgui() ? 1 : 0));
void rendertexturepanel(int w, int h)
{
- if((texpaneltimer -= curtime)>0 && editmode)
- {
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- pushhudmatrix();
- hudmatrix.scale(h/1800.0f, h/1800.0f, 1);
- flushhudmatrix(false);
- SETSHADER(hudrgb);
-
- int y = 50, gap = 10;
-
- gle::defvertex(2);
- gle::deftexcoord0();
-
- loopi(7)
- {
- int s = (i == 3 ? 285 : 220), ti = curtexindex+i-3;
- if(texmru.inrange(ti))
- {
- VSlot &vslot = lookupvslot(texmru[ti]), *layer = NULL;
- Slot &slot = *vslot.slot;
- Texture *tex = slot.sts.empty() ? notexture : slot.sts[0].t, *glowtex = NULL, *layertex = NULL;
- if(slot.texmask&(1<<TEX_GLOW))
- {
- loopvj(slot.sts) if(slot.sts[j].type==TEX_GLOW) { glowtex = slot.sts[j].t; break; }
- }
- if(vslot.layer)
- {
- layer = &lookupvslot(vslot.layer);
- layertex = layer->slot->sts.empty() ? notexture : layer->slot->sts[0].t;
- }
- float sx = min(1.0f, tex->xs/(float)tex->ys), sy = min(1.0f, tex->ys/(float)tex->xs);
- int x = w*1800/h-s-50, r = s;
- vec2 tc[4] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
- float xoff = vslot.offset.x, yoff = vslot.offset.y;
- if(vslot.rotation)
- {
- const texrotation &r = texrotations[vslot.rotation];
- if(r.swapxy) { swap(xoff, yoff); loopk(4) swap(tc[k].x, tc[k].y); }
- if(r.flipx) { xoff *= -1; loopk(4) tc[k].x *= -1; }
- if(r.flipy) { yoff *= -1; loopk(4) tc[k].y *= -1; }
- }
- loopk(4) { tc[k].x = tc[k].x/sx - xoff/tex->xs; tc[k].y = tc[k].y/sy - yoff/tex->ys; }
- glBindTexture(GL_TEXTURE_2D, tex->id);
- loopj(glowtex ? 3 : 2)
- {
- if(j < 2) gle::color(vec(vslot.colorscale).mul(j), texpaneltimer/1000.0f);
- else
- {
- glBindTexture(GL_TEXTURE_2D, glowtex->id);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- gle::color(vslot.glowcolor, texpaneltimer/1000.0f);
- }
- gle::begin(GL_TRIANGLE_STRIP);
- gle::attribf(x, y); gle::attrib(tc[0]);
- gle::attribf(x+r, y); gle::attrib(tc[1]);
- gle::attribf(x, y+r); gle::attrib(tc[3]);
- gle::attribf(x+r, y+r); gle::attrib(tc[2]);
- xtraverts += gle::end();
- if(j==1 && layertex)
- {
- gle::color(layer->colorscale, texpaneltimer/1000.0f);
- glBindTexture(GL_TEXTURE_2D, layertex->id);
- gle::begin(GL_TRIANGLE_STRIP);
- gle::attribf(x+r/2, y+r/2); gle::attrib(tc[0]);
- gle::attribf(x+r, y+r/2); gle::attrib(tc[1]);
- gle::attribf(x+r/2, y+r); gle::attrib(tc[3]);
- gle::attribf(x+r, y+r); gle::attrib(tc[2]);
- xtraverts += gle::end();
- }
- if(!j)
- {
- r -= 10;
- x += 5;
- y += 5;
- }
- else if(j == 2) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
- }
- y += s+gap;
- }
-
- pophudmatrix(true, false);
- hudshader->set();
- }
+ if((texpaneltimer -= curtime)>0 && editmode)
+ {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ pushhudmatrix();
+ hudmatrix.scale(h/1800.0f, h/1800.0f, 1);
+ flushhudmatrix(false);
+ SETSHADER(hudrgb);
+
+ int y = 50, gap = 10;
+
+ gle::defvertex(2);
+ gle::deftexcoord0();
+
+ loopi(7)
+ {
+ int s = (i == 3 ? 285 : 220), ti = curtexindex+i-3;
+ if(texmru.inrange(ti))
+ {
+ VSlot &vslot = lookupvslot(texmru[ti]), *layer = NULL;
+ Slot &slot = *vslot.slot;
+ Texture *tex = slot.sts.empty() ? notexture : slot.sts[0].t, *glowtex = NULL, *layertex = NULL;
+ if(slot.texmask&(1<<TEX_GLOW))
+ {
+ loopvj(slot.sts) if(slot.sts[j].type==TEX_GLOW) { glowtex = slot.sts[j].t; break; }
+ }
+ if(vslot.layer)
+ {
+ layer = &lookupvslot(vslot.layer);
+ layertex = layer->slot->sts.empty() ? notexture : layer->slot->sts[0].t;
+ }
+ float sx = min(1.0f, tex->xs/(float)tex->ys), sy = min(1.0f, tex->ys/(float)tex->xs);
+ int x = w*1800/h-s-50, r = s;
+ vec2 tc[4] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
+ float xoff = vslot.offset.x, yoff = vslot.offset.y;
+ if(vslot.rotation)
+ {
+ const texrotation &r = texrotations[vslot.rotation];
+ if(r.swapxy) { swap(xoff, yoff); loopk(4) swap(tc[k].x, tc[k].y); }
+ if(r.flipx) { xoff *= -1; loopk(4) tc[k].x *= -1; }
+ if(r.flipy) { yoff *= -1; loopk(4) tc[k].y *= -1; }
+ }
+ loopk(4) { tc[k].x = tc[k].x/sx - xoff/tex->xs; tc[k].y = tc[k].y/sy - yoff/tex->ys; }
+ glBindTexture(GL_TEXTURE_2D, tex->id);
+ loopj(glowtex ? 3 : 2)
+ {
+ if(j < 2) gle::color(vec(vslot.colorscale).mul(j), texpaneltimer/1000.0f);
+ else
+ {
+ glBindTexture(GL_TEXTURE_2D, glowtex->id);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ gle::color(vslot.glowcolor, texpaneltimer/1000.0f);
+ }
+ gle::begin(GL_TRIANGLE_STRIP);
+ gle::attribf(x, y); gle::attrib(tc[0]);
+ gle::attribf(x+r, y); gle::attrib(tc[1]);
+ gle::attribf(x, y+r); gle::attrib(tc[3]);
+ gle::attribf(x+r, y+r); gle::attrib(tc[2]);
+ xtraverts += gle::end();
+ if(j==1 && layertex)
+ {
+ gle::color(layer->colorscale, texpaneltimer/1000.0f);
+ glBindTexture(GL_TEXTURE_2D, layertex->id);
+ gle::begin(GL_TRIANGLE_STRIP);
+ gle::attribf(x+r/2, y+r/2); gle::attrib(tc[0]);
+ gle::attribf(x+r, y+r/2); gle::attrib(tc[1]);
+ gle::attribf(x+r/2, y+r); gle::attrib(tc[3]);
+ gle::attribf(x+r, y+r); gle::attrib(tc[2]);
+ xtraverts += gle::end();
+ }
+ if(!j)
+ {
+ r -= 10;
+ x += 5;
+ y += 5;
+ }
+ else if(j == 2) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+ y += s+gap;
+ }
+
+ pophudmatrix(true, false);
+ hudshader->set();
+ }
}
diff --git a/src/engine/octarender.cpp b/src/engine/octarender.cpp
index 217a49a..4605e1a 100644
--- a/src/engine/octarender.cpp
+++ b/src/engine/octarender.cpp
@@ -4,7 +4,7 @@
struct vboinfo
{
- int uses;
+ int uses;
};
hashtable<GLuint, vboinfo> vbos;
@@ -14,10 +14,10 @@ VARFN(vbosize, maxvbosize, 0, 1<<14, 1<<16, allchanged());
enum
{
- VBO_VBUF = 0,
- VBO_EBUF,
- VBO_SKYBUF,
- NUMVBO
+ VBO_VBUF = 0,
+ VBO_EBUF,
+ VBO_SKYBUF,
+ NUMVBO
};
static vector<uchar> vbodata[NUMVBO];
@@ -26,486 +26,486 @@ static int vbosize[NUMVBO];
void destroyvbo(GLuint vbo)
{
- vboinfo *exists = vbos.access(vbo);
- if(!exists) return;
- vboinfo &vbi = *exists;
- if(vbi.uses <= 0) return;
- vbi.uses--;
- if(!vbi.uses)
- {
- glDeleteBuffers_(1, &vbo);
- vbos.remove(vbo);
- }
+ vboinfo *exists = vbos.access(vbo);
+ if(!exists) return;
+ vboinfo &vbi = *exists;
+ if(vbi.uses <= 0) return;
+ vbi.uses--;
+ if(!vbi.uses)
+ {
+ glDeleteBuffers_(1, &vbo);
+ vbos.remove(vbo);
+ }
}
void genvbo(int type, void *buf, int len, vtxarray **vas, int numva)
{
- gle::disable();
-
- GLuint vbo;
- glGenBuffers_(1, &vbo);
- GLenum target = type==VBO_VBUF ? GL_ARRAY_BUFFER : GL_ELEMENT_ARRAY_BUFFER;
- glBindBuffer_(target, vbo);
- glBufferData_(target, len, buf, GL_STATIC_DRAW);
- glBindBuffer_(target, 0);
-
- vboinfo &vbi = vbos[vbo];
- vbi.uses = numva;
-
- if(printvbo) conoutf(CON_DEBUG, "vbo %d: type %d, size %d, %d uses", vbo, type, len, numva);
-
- loopi(numva)
- {
- vtxarray *va = vas[i];
- switch(type)
- {
- case VBO_VBUF:
- va->vbuf = vbo;
- break;
- case VBO_EBUF:
- va->ebuf = vbo;
- break;
- case VBO_SKYBUF:
- va->skybuf = vbo;
- break;
- }
- }
+ gle::disable();
+
+ GLuint vbo;
+ glGenBuffers_(1, &vbo);
+ GLenum target = type==VBO_VBUF ? GL_ARRAY_BUFFER : GL_ELEMENT_ARRAY_BUFFER;
+ glBindBuffer_(target, vbo);
+ glBufferData_(target, len, buf, GL_STATIC_DRAW);
+ glBindBuffer_(target, 0);
+
+ vboinfo &vbi = vbos[vbo];
+ vbi.uses = numva;
+
+ if(printvbo) conoutf(CON_DEBUG, "vbo %d: type %d, size %d, %d uses", vbo, type, len, numva);
+
+ loopi(numva)
+ {
+ vtxarray *va = vas[i];
+ switch(type)
+ {
+ case VBO_VBUF:
+ va->vbuf = vbo;
+ break;
+ case VBO_EBUF:
+ va->ebuf = vbo;
+ break;
+ case VBO_SKYBUF:
+ va->skybuf = vbo;
+ break;
+ }
+ }
}
bool readva(vtxarray *va, ushort *&edata, vertex *&vdata)
{
- if(!va->vbuf || !va->ebuf) return false;
+ if(!va->vbuf || !va->ebuf) return false;
- edata = new ushort[3*va->tris];
- vdata = new vertex[va->verts];
+ edata = new ushort[3*va->tris];
+ vdata = new vertex[va->verts];
- gle::bindebo(va->ebuf);
- glGetBufferSubData_(GL_ELEMENT_ARRAY_BUFFER, (size_t)va->edata, 3*va->tris*sizeof(ushort), edata);
- gle::clearebo();
+ gle::bindebo(va->ebuf);
+ glGetBufferSubData_(GL_ELEMENT_ARRAY_BUFFER, (size_t)va->edata, 3*va->tris*sizeof(ushort), edata);
+ gle::clearebo();
- gle::bindvbo(va->vbuf);
- glGetBufferSubData_(GL_ARRAY_BUFFER, va->voffset*sizeof(vertex), va->verts*sizeof(vertex), vdata);
- gle::clearvbo();
- return true;
+ gle::bindvbo(va->vbuf);
+ glGetBufferSubData_(GL_ARRAY_BUFFER, va->voffset*sizeof(vertex), va->verts*sizeof(vertex), vdata);
+ gle::clearvbo();
+ return true;
}
void flushvbo(int type = -1)
{
- if(type < 0)
- {
- loopi(NUMVBO) flushvbo(i);
- return;
- }
-
- vector<uchar> &data = vbodata[type];
- if(data.empty()) return;
- vector<vtxarray *> &vas = vbovas[type];
- genvbo(type, data.getbuf(), data.length(), vas.getbuf(), vas.length());
- data.setsize(0);
- vas.setsize(0);
- vbosize[type] = 0;
+ if(type < 0)
+ {
+ loopi(NUMVBO) flushvbo(i);
+ return;
+ }
+
+ vector<uchar> &data = vbodata[type];
+ if(data.empty()) return;
+ vector<vtxarray *> &vas = vbovas[type];
+ genvbo(type, data.getbuf(), data.length(), vas.getbuf(), vas.length());
+ data.setsize(0);
+ vas.setsize(0);
+ vbosize[type] = 0;
}
uchar *addvbo(vtxarray *va, int type, int numelems, int elemsize)
{
- vbosize[type] += numelems;
+ vbosize[type] += numelems;
- vector<uchar> &data = vbodata[type];
- vector<vtxarray *> &vas = vbovas[type];
+ vector<uchar> &data = vbodata[type];
+ vector<vtxarray *> &vas = vbovas[type];
- vas.add(va);
+ vas.add(va);
- int len = numelems*elemsize;
- uchar *buf = data.reserve(len).buf;
- data.advance(len);
- return buf;
+ int len = numelems*elemsize;
+ uchar *buf = data.reserve(len).buf;
+ data.advance(len);
+ return buf;
}
struct verthash
{
- static const int SIZE = 1<<13;
- int table[SIZE];
- vector<vertex> verts;
- vector<int> chain;
-
- verthash() { clearverts(); }
-
- void clearverts()
- {
- memset(table, -1, sizeof(table));
- chain.setsize(0);
- verts.setsize(0);
- }
-
- int addvert(const vertex &v)
- {
- uint h = hthash(v.pos)&(SIZE-1);
- for(int i = table[h]; i>=0; i = chain[i])
- {
- const vertex &c = verts[i];
- if(c.pos==v.pos && c.tc==v.tc && c.norm==v.norm && c.tangent==v.tangent && (v.lm.iszero() || c.lm==v.lm))
- return i;
- }
- if(verts.length() >= USHRT_MAX) return -1;
- verts.add(v);
- chain.add(table[h]);
- return table[h] = verts.length()-1;
- }
-
- int addvert(const vec &pos, const vec2 &tc = vec2(0, 0), const svec2 &lm = svec2(0, 0), const bvec &norm = bvec(128, 128, 128), const bvec4 &tangent = bvec4(128, 128, 128, 128))
- {
- vertex vtx;
- vtx.pos = pos;
- vtx.tc = tc;
- vtx.lm = lm;
- vtx.norm = norm;
- vtx.tangent = tangent;
- return addvert(vtx);
- }
+ static const int SIZE = 1<<13;
+ int table[SIZE];
+ vector<vertex> verts;
+ vector<int> chain;
+
+ verthash() { clearverts(); }
+
+ void clearverts()
+ {
+ memset(table, -1, sizeof(table));
+ chain.setsize(0);
+ verts.setsize(0);
+ }
+
+ int addvert(const vertex &v)
+ {
+ uint h = hthash(v.pos)&(SIZE-1);
+ for(int i = table[h]; i>=0; i = chain[i])
+ {
+ const vertex &c = verts[i];
+ if(c.pos==v.pos && c.tc==v.tc && c.norm==v.norm && c.tangent==v.tangent && (v.lm.iszero() || c.lm==v.lm))
+ return i;
+ }
+ if(verts.length() >= USHRT_MAX) return -1;
+ verts.add(v);
+ chain.add(table[h]);
+ return table[h] = verts.length()-1;
+ }
+
+ int addvert(const vec &pos, const vec2 &tc = vec2(0, 0), const svec2 &lm = svec2(0, 0), const bvec &norm = bvec(128, 128, 128), const bvec4 &tangent = bvec4(128, 128, 128, 128))
+ {
+ vertex vtx;
+ vtx.pos = pos;
+ vtx.tc = tc;
+ vtx.lm = lm;
+ vtx.norm = norm;
+ vtx.tangent = tangent;
+ return addvert(vtx);
+ }
};
enum
{
- NO_ALPHA = 0,
- ALPHA_BACK,
- ALPHA_FRONT
+ NO_ALPHA = 0,
+ ALPHA_BACK,
+ ALPHA_FRONT
};
struct sortkey
{
- ushort tex, lmid, envmap;
- uchar dim, layer, alpha;
+ ushort tex, lmid, envmap;
+ uchar dim, layer, alpha;
- sortkey() {}
- sortkey(ushort tex, ushort lmid, uchar dim, uchar layer = LAYER_TOP, ushort envmap = EMID_NONE, uchar alpha = NO_ALPHA)
- : tex(tex), lmid(lmid), envmap(envmap), dim(dim), layer(layer), alpha(alpha)
- {}
+ sortkey() {}
+ sortkey(ushort tex, ushort lmid, uchar dim, uchar layer = LAYER_TOP, ushort envmap = EMID_NONE, uchar alpha = NO_ALPHA)
+ : tex(tex), lmid(lmid), envmap(envmap), dim(dim), layer(layer), alpha(alpha)
+ {}
- bool operator==(const sortkey &o) const { return tex==o.tex && lmid==o.lmid && envmap==o.envmap && dim==o.dim && layer==o.layer && alpha==o.alpha; }
+ bool operator==(const sortkey &o) const { return tex==o.tex && lmid==o.lmid && envmap==o.envmap && dim==o.dim && layer==o.layer && alpha==o.alpha; }
};
struct sortval
{
- int unlit;
- vector<ushort> tris[2];
+ int unlit;
+ vector<ushort> tris[2];
- sortval() : unlit(0) {}
+ sortval() : unlit(0) {}
};
static inline bool htcmp(const sortkey &x, const sortkey &y)
{
- return x == y;
+ return x == y;
}
static inline uint hthash(const sortkey &k)
{
- return k.tex + k.lmid*9741;
+ return k.tex + k.lmid*9741;
}
struct vacollect : verthash
{
- ivec origin;
- int size;
- hashtable<sortkey, sortval> indices;
- vector<sortkey> texs;
- vector<materialsurface> matsurfs;
- vector<octaentities *> mapmodels;
- vector<ushort> skyindices, explicitskyindices;
- vector<facebounds> skyfaces[6];
- int worldtris, skytris, skymask, skyclip, skyarea;
-
- void clear()
- {
- clearverts();
- worldtris = skytris = 0;
- skymask = 0;
- skyclip = INT_MAX;
- skyarea = 0;
- indices.clear();
- skyindices.setsize(0);
- explicitskyindices.setsize(0);
- matsurfs.setsize(0);
- mapmodels.setsize(0);
- texs.setsize(0);
- loopi(6) skyfaces[i].setsize(0);
- }
-
- void remapunlit(vector<sortkey> &remap)
- {
- uint lastlmid[8] = { LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT },
- firstlmid[8] = { LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT };
- int firstlit[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
- loopv(texs)
- {
- sortkey &k = texs[i];
- if(k.lmid>=LMID_RESERVED)
- {
- LightMapTexture &lmtex = lightmaptexs[k.lmid];
- int type = lmtex.type&LM_TYPE;
- if(k.layer==LAYER_BLEND) type += 2;
- else if(k.alpha) type += 4 + 2*(k.alpha-1);
- lastlmid[type] = lmtex.unlitx>=0 ? (int) k.lmid : (int) LMID_AMBIENT;
- if(firstlmid[type]==LMID_AMBIENT && lastlmid[type]!=LMID_AMBIENT)
- {
- firstlit[type] = i;
- firstlmid[type] = lastlmid[type];
- }
- }
- else if(k.lmid==LMID_AMBIENT)
- {
- Shader *s = lookupvslot(k.tex, false).slot->shader;
- int type = s->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE;
- if(k.layer==LAYER_BLEND) type += 2;
- else if(k.alpha) type += 4 + 2*(k.alpha-1);
- if(lastlmid[type]!=LMID_AMBIENT)
- {
- sortval &t = indices[k];
- if(t.unlit<=0) t.unlit = lastlmid[type];
- }
- }
- }
- loopj(2)
- {
- int offset = 2*j;
- if(firstlmid[offset]==LMID_AMBIENT && firstlmid[offset+1]==LMID_AMBIENT) continue;
- loopi(max(firstlit[offset], firstlit[offset+1]))
- {
- sortkey &k = texs[i];
- if((j ? k.layer!=LAYER_BLEND : k.layer==LAYER_BLEND) || k.alpha) continue;
- if(k.lmid!=LMID_AMBIENT) continue;
- Shader *s = lookupvslot(k.tex, false).slot->shader;
- int type = offset + (s->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE);
- if(firstlmid[type]==LMID_AMBIENT) continue;
- indices[k].unlit = firstlmid[type];
- }
- }
- loopj(2)
- {
- int offset = 4 + 2*j;
- if(firstlmid[offset]==LMID_AMBIENT && firstlmid[offset+1]==LMID_AMBIENT) continue;
- loopi(max(firstlit[offset], firstlit[offset+1]))
- {
- sortkey &k = texs[i];
- if(k.alpha != j+1) continue;
- if(k.lmid!=LMID_AMBIENT) continue;
- Shader *s = lookupvslot(k.tex, false).slot->shader;
- int type = offset + (s->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE);
- if(firstlmid[type]==LMID_AMBIENT) continue;
- indices[k].unlit = firstlmid[type];
- }
- }
- loopv(remap)
- {
- sortkey &k = remap[i];
- sortval &t = indices[k];
- if(t.unlit<=0) continue;
- LightMapTexture &lm = lightmaptexs[t.unlit];
- svec2 lmtc(short(ceil((lm.unlitx + 0.5f) * SHRT_MAX/lm.w)),
- short(ceil((lm.unlity + 0.5f) * SHRT_MAX/lm.h)));
- loopl(2) loopvj(t.tris[l])
- {
- vertex &vtx = verts[t.tris[l][j]];
- if(vtx.lm.iszero()) vtx.lm = lmtc;
- else if(vtx.lm != lmtc)
- {
- vertex vtx2 = vtx;
- vtx2.lm = lmtc;
- t.tris[l][j] = addvert(vtx2);
- }
- }
- sortval *dst = indices.access(sortkey(k.tex, t.unlit, k.dim, k.layer, k.envmap, k.alpha));
- if(dst) loopl(2) loopvj(t.tris[l]) dst->tris[l].add(t.tris[l][j]);
- }
- }
-
- void optimize()
- {
- vector<sortkey> remap;
- enumeratekt(indices, sortkey, k, sortval, t,
- loopl(2) if(t.tris[l].length() && t.unlit<=0)
- {
- if(k.lmid>=LMID_RESERVED && lightmaptexs[k.lmid].unlitx>=0)
- {
- sortkey ukey(k.tex, LMID_AMBIENT, k.dim, k.layer, k.envmap, k.alpha);
- sortval *uval = indices.access(ukey);
- if(uval && uval->unlit<=0)
- {
- if(uval->unlit<0) texs.removeobj(ukey);
- else remap.add(ukey);
- uval->unlit = k.lmid;
- }
- }
- else if(k.lmid==LMID_AMBIENT)
- {
- remap.add(k);
- t.unlit = -1;
- }
- texs.add(k);
- break;
- }
- );
- texs.sort(texsort);
-
- remapunlit(remap);
-
- matsurfs.shrink(optimizematsurfs(matsurfs.getbuf(), matsurfs.length()));
- }
-
- static inline bool texsort(const sortkey &x, const sortkey &y)
- {
- if(x.alpha < y.alpha) return true;
- if(x.alpha > y.alpha) return false;
- if(x.layer < y.layer) return true;
- if(x.layer > y.layer) return false;
- if(x.tex == y.tex)
- {
- if(x.lmid < y.lmid) return true;
- if(x.lmid > y.lmid) return false;
- if(x.envmap < y.envmap) return true;
- if(x.envmap > y.envmap) return false;
- if(x.dim < y.dim) return true;
- if(x.dim > y.dim) return false;
- return false;
- }
- VSlot &xs = lookupvslot(x.tex, false), &ys = lookupvslot(y.tex, false);
- if(xs.slot->shader < ys.slot->shader) return true;
- if(xs.slot->shader > ys.slot->shader) return false;
- if(xs.slot->params.length() < ys.slot->params.length()) return true;
- if(xs.slot->params.length() > ys.slot->params.length()) return false;
- if(x.tex < y.tex) return true;
- else return false;
- }
+ ivec origin;
+ int size;
+ hashtable<sortkey, sortval> indices;
+ vector<sortkey> texs;
+ vector<materialsurface> matsurfs;
+ vector<octaentities *> mapmodels;
+ vector<ushort> skyindices, explicitskyindices;
+ vector<facebounds> skyfaces[6];
+ int worldtris, skytris, skymask, skyclip, skyarea;
+
+ void clear()
+ {
+ clearverts();
+ worldtris = skytris = 0;
+ skymask = 0;
+ skyclip = INT_MAX;
+ skyarea = 0;
+ indices.clear();
+ skyindices.setsize(0);
+ explicitskyindices.setsize(0);
+ matsurfs.setsize(0);
+ mapmodels.setsize(0);
+ texs.setsize(0);
+ loopi(6) skyfaces[i].setsize(0);
+ }
+
+ void remapunlit(vector<sortkey> &remap)
+ {
+ uint lastlmid[8] = { LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT },
+ firstlmid[8] = { LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT };
+ int firstlit[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+ loopv(texs)
+ {
+ sortkey &k = texs[i];
+ if(k.lmid>=LMID_RESERVED)
+ {
+ LightMapTexture &lmtex = lightmaptexs[k.lmid];
+ int type = lmtex.type&LM_TYPE;
+ if(k.layer==LAYER_BLEND) type += 2;
+ else if(k.alpha) type += 4 + 2*(k.alpha-1);
+ lastlmid[type] = lmtex.unlitx>=0 ? (int) k.lmid : (int) LMID_AMBIENT;
+ if(firstlmid[type]==LMID_AMBIENT && lastlmid[type]!=LMID_AMBIENT)
+ {
+ firstlit[type] = i;
+ firstlmid[type] = lastlmid[type];
+ }
+ }
+ else if(k.lmid==LMID_AMBIENT)
+ {
+ Shader *s = lookupvslot(k.tex, false).slot->shader;
+ int type = s->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE;
+ if(k.layer==LAYER_BLEND) type += 2;
+ else if(k.alpha) type += 4 + 2*(k.alpha-1);
+ if(lastlmid[type]!=LMID_AMBIENT)
+ {
+ sortval &t = indices[k];
+ if(t.unlit<=0) t.unlit = lastlmid[type];
+ }
+ }
+ }
+ loopj(2)
+ {
+ int offset = 2*j;
+ if(firstlmid[offset]==LMID_AMBIENT && firstlmid[offset+1]==LMID_AMBIENT) continue;
+ loopi(max(firstlit[offset], firstlit[offset+1]))
+ {
+ sortkey &k = texs[i];
+ if((j ? k.layer!=LAYER_BLEND : k.layer==LAYER_BLEND) || k.alpha) continue;
+ if(k.lmid!=LMID_AMBIENT) continue;
+ Shader *s = lookupvslot(k.tex, false).slot->shader;
+ int type = offset + (s->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE);
+ if(firstlmid[type]==LMID_AMBIENT) continue;
+ indices[k].unlit = firstlmid[type];
+ }
+ }
+ loopj(2)
+ {
+ int offset = 4 + 2*j;
+ if(firstlmid[offset]==LMID_AMBIENT && firstlmid[offset+1]==LMID_AMBIENT) continue;
+ loopi(max(firstlit[offset], firstlit[offset+1]))
+ {
+ sortkey &k = texs[i];
+ if(k.alpha != j+1) continue;
+ if(k.lmid!=LMID_AMBIENT) continue;
+ Shader *s = lookupvslot(k.tex, false).slot->shader;
+ int type = offset + (s->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE);
+ if(firstlmid[type]==LMID_AMBIENT) continue;
+ indices[k].unlit = firstlmid[type];
+ }
+ }
+ loopv(remap)
+ {
+ sortkey &k = remap[i];
+ sortval &t = indices[k];
+ if(t.unlit<=0) continue;
+ LightMapTexture &lm = lightmaptexs[t.unlit];
+ svec2 lmtc(short(ceil((lm.unlitx + 0.5f) * SHRT_MAX/lm.w)),
+ short(ceil((lm.unlity + 0.5f) * SHRT_MAX/lm.h)));
+ loopl(2) loopvj(t.tris[l])
+ {
+ vertex &vtx = verts[t.tris[l][j]];
+ if(vtx.lm.iszero()) vtx.lm = lmtc;
+ else if(vtx.lm != lmtc)
+ {
+ vertex vtx2 = vtx;
+ vtx2.lm = lmtc;
+ t.tris[l][j] = addvert(vtx2);
+ }
+ }
+ sortval *dst = indices.access(sortkey(k.tex, t.unlit, k.dim, k.layer, k.envmap, k.alpha));
+ if(dst) loopl(2) loopvj(t.tris[l]) dst->tris[l].add(t.tris[l][j]);
+ }
+ }
+
+ void optimize()
+ {
+ vector<sortkey> remap;
+ enumeratekt(indices, sortkey, k, sortval, t,
+ loopl(2) if(t.tris[l].length() && t.unlit<=0)
+ {
+ if(k.lmid>=LMID_RESERVED && lightmaptexs[k.lmid].unlitx>=0)
+ {
+ sortkey ukey(k.tex, LMID_AMBIENT, k.dim, k.layer, k.envmap, k.alpha);
+ sortval *uval = indices.access(ukey);
+ if(uval && uval->unlit<=0)
+ {
+ if(uval->unlit<0) texs.removeobj(ukey);
+ else remap.add(ukey);
+ uval->unlit = k.lmid;
+ }
+ }
+ else if(k.lmid==LMID_AMBIENT)
+ {
+ remap.add(k);
+ t.unlit = -1;
+ }
+ texs.add(k);
+ break;
+ }
+ );
+ texs.sort(texsort);
+
+ remapunlit(remap);
+
+ matsurfs.shrink(optimizematsurfs(matsurfs.getbuf(), matsurfs.length()));
+ }
+
+ static inline bool texsort(const sortkey &x, const sortkey &y)
+ {
+ if(x.alpha < y.alpha) return true;
+ if(x.alpha > y.alpha) return false;
+ if(x.layer < y.layer) return true;
+ if(x.layer > y.layer) return false;
+ if(x.tex == y.tex)
+ {
+ if(x.lmid < y.lmid) return true;
+ if(x.lmid > y.lmid) return false;
+ if(x.envmap < y.envmap) return true;
+ if(x.envmap > y.envmap) return false;
+ if(x.dim < y.dim) return true;
+ if(x.dim > y.dim) return false;
+ return false;
+ }
+ VSlot &xs = lookupvslot(x.tex, false), &ys = lookupvslot(y.tex, false);
+ if(xs.slot->shader < ys.slot->shader) return true;
+ if(xs.slot->shader > ys.slot->shader) return false;
+ if(xs.slot->params.length() < ys.slot->params.length()) return true;
+ if(xs.slot->params.length() > ys.slot->params.length()) return false;
+ if(x.tex < y.tex) return true;
+ else return false;
+ }
#define GENVERTS(type, ptr, body) do \
- { \
- type *f = (type *)ptr; \
- loopv(verts) \
- { \
- const vertex &v = verts[i]; \
- body; \
- f++; \
- } \
- } while(0)
-
- void genverts(void *buf)
- {
- GENVERTS(vertex, buf, { *f = v; f->norm.flip(); f->tangent.flip(); });
- }
-
- void setupdata(vtxarray *va)
- {
- va->verts = verts.length();
- va->tris = worldtris/3;
- va->vbuf = 0;
- va->vdata = 0;
- va->minvert = 0;
- va->maxvert = va->verts-1;
- va->voffset = 0;
- if(va->verts)
- {
- if(vbosize[VBO_VBUF] + verts.length() > maxvbosize ||
- vbosize[VBO_EBUF] + worldtris > USHRT_MAX ||
- vbosize[VBO_SKYBUF] + skytris > USHRT_MAX)
- flushvbo();
-
- va->voffset = vbosize[VBO_VBUF];
- uchar *vdata = addvbo(va, VBO_VBUF, va->verts, sizeof(vertex));
- genverts(vdata);
- va->minvert += va->voffset;
- va->maxvert += va->voffset;
- }
-
- va->matbuf = NULL;
- va->matsurfs = matsurfs.length();
- if(va->matsurfs)
- {
- va->matbuf = new materialsurface[matsurfs.length()];
- memcpy(va->matbuf, matsurfs.getbuf(), matsurfs.length()*sizeof(materialsurface));
- }
-
- va->skybuf = 0;
- va->skydata = 0;
- va->sky = skyindices.length();
- va->explicitsky = explicitskyindices.length();
- if(va->sky + va->explicitsky)
- {
- va->skydata += vbosize[VBO_SKYBUF];
- ushort *skydata = (ushort *)addvbo(va, VBO_SKYBUF, va->sky+va->explicitsky, sizeof(ushort));
- memcpy(skydata, skyindices.getbuf(), va->sky*sizeof(ushort));
- memcpy(skydata+va->sky, explicitskyindices.getbuf(), va->explicitsky*sizeof(ushort));
- if(va->voffset) loopi(va->sky+va->explicitsky) skydata[i] += va->voffset;
- }
-
- va->eslist = NULL;
- va->texs = texs.length();
- va->blendtris = 0;
- va->blends = 0;
- va->alphabacktris = 0;
- va->alphaback = 0;
- va->alphafronttris = 0;
- va->alphafront = 0;
- va->ebuf = 0;
- va->edata = 0;
- va->texmask = 0;
- if(va->texs)
- {
- va->eslist = new elementset[va->texs];
- va->edata += vbosize[VBO_EBUF];
- ushort *edata = (ushort *)addvbo(va, VBO_EBUF, worldtris, sizeof(ushort)), *curbuf = edata;
- loopv(texs)
- {
- const sortkey &k = texs[i];
- const sortval &t = indices[k];
- elementset &e = va->eslist[i];
- e.texture = k.tex;
- e.lmid = t.unlit>0 ? t.unlit : k.lmid;
- e.dim = k.dim;
- e.layer = k.layer;
- e.envmap = k.envmap;
- ushort *startbuf = curbuf;
- loopl(2)
- {
- e.minvert[l] = USHRT_MAX;
- e.maxvert[l] = 0;
-
- if(t.tris[l].length())
- {
- memcpy(curbuf, t.tris[l].getbuf(), t.tris[l].length() * sizeof(ushort));
-
- loopvj(t.tris[l])
- {
- curbuf[j] += va->voffset;
- e.minvert[l] = min(e.minvert[l], curbuf[j]);
- e.maxvert[l] = max(e.maxvert[l], curbuf[j]);
- }
-
- curbuf += t.tris[l].length();
- }
- e.length[l] = curbuf-startbuf;
- }
- if(k.layer==LAYER_BLEND) { va->texs--; va->tris -= e.length[1]/3; va->blends++; va->blendtris += e.length[1]/3; }
- else if(k.alpha==ALPHA_BACK) { va->texs--; va->tris -= e.length[1]/3; va->alphaback++; va->alphabacktris += e.length[1]/3; }
- else if(k.alpha==ALPHA_FRONT) { va->texs--; va->tris -= e.length[1]/3; va->alphafront++; va->alphafronttris += e.length[1]/3; }
-
- Slot &slot = *lookupvslot(k.tex, false).slot;
- loopvj(slot.sts) va->texmask |= 1<<slot.sts[j].type;
- if(slot.shader->type&SHADER_ENVMAP) va->texmask |= 1<<TEX_ENVMAP;
- }
- }
-
- va->alphatris = va->alphabacktris + va->alphafronttris;
-
- if(mapmodels.length()) va->mapmodels.put(mapmodels.getbuf(), mapmodels.length());
- }
-
- bool emptyva()
- {
- return verts.empty() && matsurfs.empty() && skyindices.empty() && explicitskyindices.empty() && mapmodels.empty();
- }
+ { \
+ type *f = (type *)ptr; \
+ loopv(verts) \
+ { \
+ const vertex &v = verts[i]; \
+ body; \
+ f++; \
+ } \
+ } while(0)
+
+ void genverts(void *buf)
+ {
+ GENVERTS(vertex, buf, { *f = v; f->norm.flip(); f->tangent.flip(); });
+ }
+
+ void setupdata(vtxarray *va)
+ {
+ va->verts = verts.length();
+ va->tris = worldtris/3;
+ va->vbuf = 0;
+ va->vdata = 0;
+ va->minvert = 0;
+ va->maxvert = va->verts-1;
+ va->voffset = 0;
+ if(va->verts)
+ {
+ if(vbosize[VBO_VBUF] + verts.length() > maxvbosize ||
+ vbosize[VBO_EBUF] + worldtris > USHRT_MAX ||
+ vbosize[VBO_SKYBUF] + skytris > USHRT_MAX)
+ flushvbo();
+
+ va->voffset = vbosize[VBO_VBUF];
+ uchar *vdata = addvbo(va, VBO_VBUF, va->verts, sizeof(vertex));
+ genverts(vdata);
+ va->minvert += va->voffset;
+ va->maxvert += va->voffset;
+ }
+
+ va->matbuf = NULL;
+ va->matsurfs = matsurfs.length();
+ if(va->matsurfs)
+ {
+ va->matbuf = new materialsurface[matsurfs.length()];
+ memcpy(va->matbuf, matsurfs.getbuf(), matsurfs.length()*sizeof(materialsurface));
+ }
+
+ va->skybuf = 0;
+ va->skydata = 0;
+ va->sky = skyindices.length();
+ va->explicitsky = explicitskyindices.length();
+ if(va->sky + va->explicitsky)
+ {
+ va->skydata += vbosize[VBO_SKYBUF];
+ ushort *skydata = (ushort *)addvbo(va, VBO_SKYBUF, va->sky+va->explicitsky, sizeof(ushort));
+ memcpy(skydata, skyindices.getbuf(), va->sky*sizeof(ushort));
+ memcpy(skydata+va->sky, explicitskyindices.getbuf(), va->explicitsky*sizeof(ushort));
+ if(va->voffset) loopi(va->sky+va->explicitsky) skydata[i] += va->voffset;
+ }
+
+ va->eslist = NULL;
+ va->texs = texs.length();
+ va->blendtris = 0;
+ va->blends = 0;
+ va->alphabacktris = 0;
+ va->alphaback = 0;
+ va->alphafronttris = 0;
+ va->alphafront = 0;
+ va->ebuf = 0;
+ va->edata = 0;
+ va->texmask = 0;
+ if(va->texs)
+ {
+ va->eslist = new elementset[va->texs];
+ va->edata += vbosize[VBO_EBUF];
+ ushort *edata = (ushort *)addvbo(va, VBO_EBUF, worldtris, sizeof(ushort)), *curbuf = edata;
+ loopv(texs)
+ {
+ const sortkey &k = texs[i];
+ const sortval &t = indices[k];
+ elementset &e = va->eslist[i];
+ e.texture = k.tex;
+ e.lmid = t.unlit>0 ? t.unlit : k.lmid;
+ e.dim = k.dim;
+ e.layer = k.layer;
+ e.envmap = k.envmap;
+ ushort *startbuf = curbuf;
+ loopl(2)
+ {
+ e.minvert[l] = USHRT_MAX;
+ e.maxvert[l] = 0;
+
+ if(t.tris[l].length())
+ {
+ memcpy(curbuf, t.tris[l].getbuf(), t.tris[l].length() * sizeof(ushort));
+
+ loopvj(t.tris[l])
+ {
+ curbuf[j] += va->voffset;
+ e.minvert[l] = min(e.minvert[l], curbuf[j]);
+ e.maxvert[l] = max(e.maxvert[l], curbuf[j]);
+ }
+
+ curbuf += t.tris[l].length();
+ }
+ e.length[l] = curbuf-startbuf;
+ }
+ if(k.layer==LAYER_BLEND) { va->texs--; va->tris -= e.length[1]/3; va->blends++; va->blendtris += e.length[1]/3; }
+ else if(k.alpha==ALPHA_BACK) { va->texs--; va->tris -= e.length[1]/3; va->alphaback++; va->alphabacktris += e.length[1]/3; }
+ else if(k.alpha==ALPHA_FRONT) { va->texs--; va->tris -= e.length[1]/3; va->alphafront++; va->alphafronttris += e.length[1]/3; }
+
+ Slot &slot = *lookupvslot(k.tex, false).slot;
+ loopvj(slot.sts) va->texmask |= 1<<slot.sts[j].type;
+ if(slot.shader->type&SHADER_ENVMAP) va->texmask |= 1<<TEX_ENVMAP;
+ }
+ }
+
+ va->alphatris = va->alphabacktris + va->alphafronttris;
+
+ if(mapmodels.length()) va->mapmodels.put(mapmodels.getbuf(), mapmodels.length());
+ }
+
+ bool emptyva()
+ {
+ return verts.empty() && matsurfs.empty() && skyindices.empty() && explicitskyindices.empty() && mapmodels.empty();
+ }
} vc;
int recalcprogress = 0;
-#define progress(s) if((recalcprogress++&0xFFF)==0) renderprogress(recalcprogress/(float)allocnodes, s);
+#define progress(s) if((recalcprogress++&0xFFF)==0) renderprogress(recalcprogress/(float)allocnodes, s);
vector<tjoint> tjoints;
@@ -513,323 +513,323 @@ vec shadowmapmin, shadowmapmax;
int calcshadowmask(vec *pos, int numpos)
{
- extern vec shadowdir;
- int mask = 0, used = 1;
- vec pe = vec(pos[1]).sub(pos[0]);
- loopk(numpos-2)
- {
- vec e = vec(pos[k+2]).sub(pos[0]);
- if(vec().cross(pe, e).dot(shadowdir)>0)
- {
- mask |= 1<<k;
- used |= 6<<k;
- }
- pe = e;
- }
- if(!mask) return 0;
- loopk(numpos) if(used&(1<<k))
- {
- const vec &v = pos[k];
- shadowmapmin.min(v);
- shadowmapmax.max(v);
- }
- return mask;
+ extern vec shadowdir;
+ int mask = 0, used = 1;
+ vec pe = vec(pos[1]).sub(pos[0]);
+ loopk(numpos-2)
+ {
+ vec e = vec(pos[k+2]).sub(pos[0]);
+ if(vec().cross(pe, e).dot(shadowdir)>0)
+ {
+ mask |= 1<<k;
+ used |= 6<<k;
+ }
+ pe = e;
+ }
+ if(!mask) return 0;
+ loopk(numpos) if(used&(1<<k))
+ {
+ const vec &v = pos[k];
+ shadowmapmin.min(v);
+ shadowmapmax.max(v);
+ }
+ return mask;
}
VARFP(filltjoints, 0, 1, 1, allchanged());
void reduceslope(ivec &n)
{
- int mindim = -1, minval = 64;
- loopi(3) if(n[i])
- {
- int val = abs(n[i]);
- if(mindim < 0 || val < minval)
- {
- mindim = i;
- minval = val;
- }
- }
- if(!(n[R[mindim]]%minval) && !(n[C[mindim]]%minval)) n.div(minval);
- while(!((n.x|n.y|n.z)&1)) n.shr(1);
+ int mindim = -1, minval = 64;
+ loopi(3) if(n[i])
+ {
+ int val = abs(n[i]);
+ if(mindim < 0 || val < minval)
+ {
+ mindim = i;
+ minval = val;
+ }
+ }
+ if(!(n[R[mindim]]%minval) && !(n[C[mindim]]%minval)) n.div(minval);
+ while(!((n.x|n.y|n.z)&1)) n.shr(1);
}
// [rotation][dimension]
extern const vec orientation_tangent[8][3] =
{
- { vec(0, 1, 0), vec( 1, 0, 0), vec( 1, 0, 0) },
- { vec(0, 0, -1), vec( 0, 0, -1), vec( 0, 1, 0) },
- { vec(0, -1, 0), vec(-1, 0, 0), vec(-1, 0, 0) },
- { vec(0, 0, 1), vec( 0, 0, 1), vec( 0, -1, 0) },
- { vec(0, -1, 0), vec(-1, 0, 0), vec(-1, 0, 0) },
- { vec(0, 1, 0), vec( 1, 0, 0), vec( 1, 0, 0) },
- { vec(0, 0, -1), vec( 0, 0, -1), vec( 0, 1, 0) },
- { vec(0, 0, 1), vec( 0, 0, 1), vec( 0, -1, 0) },
+ { vec(0, 1, 0), vec( 1, 0, 0), vec( 1, 0, 0) },
+ { vec(0, 0, -1), vec( 0, 0, -1), vec( 0, 1, 0) },
+ { vec(0, -1, 0), vec(-1, 0, 0), vec(-1, 0, 0) },
+ { vec(0, 0, 1), vec( 0, 0, 1), vec( 0, -1, 0) },
+ { vec(0, -1, 0), vec(-1, 0, 0), vec(-1, 0, 0) },
+ { vec(0, 1, 0), vec( 1, 0, 0), vec( 1, 0, 0) },
+ { vec(0, 0, -1), vec( 0, 0, -1), vec( 0, 1, 0) },
+ { vec(0, 0, 1), vec( 0, 0, 1), vec( 0, -1, 0) },
};
extern const vec orientation_bitangent[8][3] =
{
- { vec(0, 0, -1), vec( 0, 0, -1), vec( 0, 1, 0) },
- { vec(0, -1, 0), vec(-1, 0, 0), vec(-1, 0, 0) },
- { vec(0, 0, 1), vec( 0, 0, 1), vec( 0, -1, 0) },
- { vec(0, 1, 0), vec( 1, 0, 0), vec( 1, 0, 0) },
- { vec(0, 0, -1), vec( 0, 0, -1), vec( 0, 1, 0) },
- { vec(0, 0, 1), vec( 0, 0, 1), vec( 0, -1, 0) },
- { vec(0, 1, 0), vec( 1, 0, 0), vec( 1, 0, 0) },
- { vec(0, -1, 0), vec(-1, 0, 0), vec(-1, 0, 0) },
+ { vec(0, 0, -1), vec( 0, 0, -1), vec( 0, 1, 0) },
+ { vec(0, -1, 0), vec(-1, 0, 0), vec(-1, 0, 0) },
+ { vec(0, 0, 1), vec( 0, 0, 1), vec( 0, -1, 0) },
+ { vec(0, 1, 0), vec( 1, 0, 0), vec( 1, 0, 0) },
+ { vec(0, 0, -1), vec( 0, 0, -1), vec( 0, 1, 0) },
+ { vec(0, 0, 1), vec( 0, 0, 1), vec( 0, -1, 0) },
+ { vec(0, 1, 0), vec( 1, 0, 0), vec( 1, 0, 0) },
+ { vec(0, -1, 0), vec(-1, 0, 0), vec(-1, 0, 0) },
};
void addtris(const sortkey &key, int orient, vertex *verts, int *index, int numverts, int convex, int shadowmask, int tj)
{
- int &total = key.tex==DEFAULT_SKY ? vc.skytris : vc.worldtris;
- int edge = orient*(MAXFACEVERTS+1);
- loopi(numverts-2) if(index[0]!=index[i+1] && index[i+1]!=index[i+2] && index[i+2]!=index[0])
- {
- vector<ushort> &idxs = key.tex==DEFAULT_SKY ? vc.explicitskyindices : vc.indices[key].tris[(shadowmask>>i)&1];
- int left = index[0], mid = index[i+1], right = index[i+2], start = left, i0 = left, i1 = -1;
- loopk(4)
- {
- int i2 = -1, ctj = -1, cedge = -1;
- switch(k)
- {
- case 1: i1 = i2 = mid; cedge = edge+i+1; break;
- case 2: if(i1 != mid || i0 == left) { i0 = i1; i1 = right; } i2 = right; if(i+1 == numverts-2) cedge = edge+i+2; break;
- case 3: if(i0 == start) { i0 = i1; i1 = left; } i2 = left; // fall-through
- default: if(!i) cedge = edge; break;
- }
- if(i1 != i2)
- {
- if(total + 3 > USHRT_MAX) return;
- total += 3;
- idxs.add(i0);
- idxs.add(i1);
- idxs.add(i2);
- i1 = i2;
- }
- if(cedge >= 0)
- {
- for(ctj = tj;;)
- {
- if(ctj < 0) break;
- if(tjoints[ctj].edge < cedge) { ctj = tjoints[ctj].next; continue; }
- if(tjoints[ctj].edge != cedge) ctj = -1;
- break;
- }
- }
- if(ctj >= 0)
- {
- int e1 = cedge%(MAXFACEVERTS+1), e2 = (e1+1)%numverts;
- vertex &v1 = verts[e1], &v2 = verts[e2];
- ivec d(vec(v2.pos).sub(v1.pos).mul(8));
- int axis = abs(d.x) > abs(d.y) ? (abs(d.x) > abs(d.z) ? 0 : 2) : (abs(d.y) > abs(d.z) ? 1 : 2);
- if(d[axis] < 0) d.neg();
- reduceslope(d);
- int origin = int(min(v1.pos[axis], v2.pos[axis])*8)&~0x7FFF,
- offset1 = (int(v1.pos[axis]*8) - origin) / d[axis],
- offset2 = (int(v2.pos[axis]*8) - origin) / d[axis];
- vec o = vec(v1.pos).sub(vec(d).mul(offset1/8.0f));
- float doffset = 1.0f / (offset2 - offset1);
-
- if(i1 < 0) for(;;)
- {
- tjoint &t = tjoints[ctj];
- if(t.next < 0 || tjoints[t.next].edge != cedge) break;
- ctj = t.next;
- }
- while(ctj >= 0)
- {
- tjoint &t = tjoints[ctj];
- if(t.edge != cedge) break;
- float offset = (t.offset - offset1) * doffset;
- vertex vt;
- vt.pos = vec(d).mul(t.offset/8.0f).add(o);
- vt.tc.lerp(v1.tc, v2.tc, offset);
- vt.lm.x = short(v1.lm.x + (v2.lm.x-v1.lm.x)*offset),
- vt.lm.y = short(v1.lm.y + (v2.lm.y-v1.lm.y)*offset);
- vt.norm.lerp(v1.norm, v2.norm, offset);
- vt.tangent.lerp(v1.tangent, v2.tangent, offset);
- int i2 = vc.addvert(vt);
- if(i2 < 0) return;
- if(i1 >= 0)
- {
- if(total + 3 > USHRT_MAX) return;
- total += 3;
- idxs.add(i0);
- idxs.add(i1);
- idxs.add(i2);
- i1 = i2;
- }
- else start = i0 = i2;
- ctj = t.next;
- }
- }
- }
- }
+ int &total = key.tex==DEFAULT_SKY ? vc.skytris : vc.worldtris;
+ int edge = orient*(MAXFACEVERTS+1);
+ loopi(numverts-2) if(index[0]!=index[i+1] && index[i+1]!=index[i+2] && index[i+2]!=index[0])
+ {
+ vector<ushort> &idxs = key.tex==DEFAULT_SKY ? vc.explicitskyindices : vc.indices[key].tris[(shadowmask>>i)&1];
+ int left = index[0], mid = index[i+1], right = index[i+2], start = left, i0 = left, i1 = -1;
+ loopk(4)
+ {
+ int i2 = -1, ctj = -1, cedge = -1;
+ switch(k)
+ {
+ case 1: i1 = i2 = mid; cedge = edge+i+1; break;
+ case 2: if(i1 != mid || i0 == left) { i0 = i1; i1 = right; } i2 = right; if(i+1 == numverts-2) cedge = edge+i+2; break;
+ case 3: if(i0 == start) { i0 = i1; i1 = left; } i2 = left; // fall-through
+ default: if(!i) cedge = edge; break;
+ }
+ if(i1 != i2)
+ {
+ if(total + 3 > USHRT_MAX) return;
+ total += 3;
+ idxs.add(i0);
+ idxs.add(i1);
+ idxs.add(i2);
+ i1 = i2;
+ }
+ if(cedge >= 0)
+ {
+ for(ctj = tj;;)
+ {
+ if(ctj < 0) break;
+ if(tjoints[ctj].edge < cedge) { ctj = tjoints[ctj].next; continue; }
+ if(tjoints[ctj].edge != cedge) ctj = -1;
+ break;
+ }
+ }
+ if(ctj >= 0)
+ {
+ int e1 = cedge%(MAXFACEVERTS+1), e2 = (e1+1)%numverts;
+ vertex &v1 = verts[e1], &v2 = verts[e2];
+ ivec d(vec(v2.pos).sub(v1.pos).mul(8));
+ int axis = abs(d.x) > abs(d.y) ? (abs(d.x) > abs(d.z) ? 0 : 2) : (abs(d.y) > abs(d.z) ? 1 : 2);
+ if(d[axis] < 0) d.neg();
+ reduceslope(d);
+ int origin = int(min(v1.pos[axis], v2.pos[axis])*8)&~0x7FFF,
+ offset1 = (int(v1.pos[axis]*8) - origin) / d[axis],
+ offset2 = (int(v2.pos[axis]*8) - origin) / d[axis];
+ vec o = vec(v1.pos).sub(vec(d).mul(offset1/8.0f));
+ float doffset = 1.0f / (offset2 - offset1);
+
+ if(i1 < 0) for(;;)
+ {
+ tjoint &t = tjoints[ctj];
+ if(t.next < 0 || tjoints[t.next].edge != cedge) break;
+ ctj = t.next;
+ }
+ while(ctj >= 0)
+ {
+ tjoint &t = tjoints[ctj];
+ if(t.edge != cedge) break;
+ float offset = (t.offset - offset1) * doffset;
+ vertex vt;
+ vt.pos = vec(d).mul(t.offset/8.0f).add(o);
+ vt.tc.lerp(v1.tc, v2.tc, offset);
+ vt.lm.x = short(v1.lm.x + (v2.lm.x-v1.lm.x)*offset),
+ vt.lm.y = short(v1.lm.y + (v2.lm.y-v1.lm.y)*offset);
+ vt.norm.lerp(v1.norm, v2.norm, offset);
+ vt.tangent.lerp(v1.tangent, v2.tangent, offset);
+ int i2 = vc.addvert(vt);
+ if(i2 < 0) return;
+ if(i1 >= 0)
+ {
+ if(total + 3 > USHRT_MAX) return;
+ total += 3;
+ idxs.add(i0);
+ idxs.add(i1);
+ idxs.add(i2);
+ i1 = i2;
+ }
+ else start = i0 = i2;
+ ctj = t.next;
+ }
+ }
+ }
+ }
}
static inline void calctexgen(VSlot &vslot, int dim, vec4 &sgen, vec4 &tgen)
{
- Texture *tex = vslot.slot->sts.empty() ? notexture : vslot.slot->sts[0].t;
- const texrotation &r = texrotations[vslot.rotation];
- float k = TEX_SCALE/vslot.scale,
- xs = r.flipx ? -tex->xs : tex->xs,
- ys = r.flipy ? -tex->ys : tex->ys,
- sk = k/xs, tk = k/ys,
- soff = -(r.swapxy ? vslot.offset.y : vslot.offset.x)/xs,
- toff = -(r.swapxy ? vslot.offset.x : vslot.offset.y)/ys;
- static const int si[] = { 1, 0, 0 }, ti[] = { 2, 2, 1 };
- int sdim = si[dim], tdim = ti[dim];
- sgen = vec4(0, 0, 0, soff);
- tgen = vec4(0, 0, 0, toff);
- if(r.swapxy)
- {
- sgen[tdim] = (dim <= 1 ? -sk : sk);
- tgen[sdim] = tk;
- }
- else
- {
- sgen[sdim] = sk;
- tgen[tdim] = (dim <= 1 ? -tk : tk);
- }
+ Texture *tex = vslot.slot->sts.empty() ? notexture : vslot.slot->sts[0].t;
+ const texrotation &r = texrotations[vslot.rotation];
+ float k = TEX_SCALE/vslot.scale,
+ xs = r.flipx ? -tex->xs : tex->xs,
+ ys = r.flipy ? -tex->ys : tex->ys,
+ sk = k/xs, tk = k/ys,
+ soff = -(r.swapxy ? vslot.offset.y : vslot.offset.x)/xs,
+ toff = -(r.swapxy ? vslot.offset.x : vslot.offset.y)/ys;
+ static const int si[] = { 1, 0, 0 }, ti[] = { 2, 2, 1 };
+ int sdim = si[dim], tdim = ti[dim];
+ sgen = vec4(0, 0, 0, soff);
+ tgen = vec4(0, 0, 0, toff);
+ if(r.swapxy)
+ {
+ sgen[tdim] = (dim <= 1 ? -sk : sk);
+ tgen[sdim] = tk;
+ }
+ else
+ {
+ sgen[sdim] = sk;
+ tgen[tdim] = (dim <= 1 ? -tk : tk);
+ }
}
ushort encodenormal(const vec &n)
{
- if(n.iszero()) return 0;
- int yaw = int(-atan2(n.x, n.y)/RAD), pitch = int(asin(n.z)/RAD);
- return ushort(clamp(pitch + 90, 0, 180)*360 + (yaw < 0 ? yaw%360 + 360 : yaw%360) + 1);
+ if(n.iszero()) return 0;
+ int yaw = int(-atan2(n.x, n.y)/RAD), pitch = int(asin(n.z)/RAD);
+ return ushort(clamp(pitch + 90, 0, 180)*360 + (yaw < 0 ? yaw%360 + 360 : yaw%360) + 1);
}
vec decodenormal(ushort norm)
{
- if(!norm) return vec(0, 0, 1);
- norm--;
- const vec2 &yaw = sincos360[norm%360], &pitch = sincos360[norm/360+270];
- return vec(-yaw.y*pitch.x, yaw.x*pitch.x, pitch.y);
+ if(!norm) return vec(0, 0, 1);
+ norm--;
+ const vec2 &yaw = sincos360[norm%360], &pitch = sincos360[norm/360+270];
+ return vec(-yaw.y*pitch.x, yaw.x*pitch.x, pitch.y);
}
void guessnormals(const vec *pos, int numverts, vec *normals)
{
- vec n1, n2;
- n1.cross(pos[0], pos[1], pos[2]);
- if(numverts != 4)
- {
- n1.normalize();
- loopk(numverts) normals[k] = n1;
- return;
- }
- n2.cross(pos[0], pos[2], pos[3]);
- if(n1.iszero())
- {
- n2.normalize();
- loopk(4) normals[k] = n2;
- return;
- }
- else n1.normalize();
- if(n2.iszero())
- {
- loopk(4) normals[k] = n1;
- return;
- }
- else n2.normalize();
- vec avg = vec(n1).add(n2).normalize();
- normals[0] = avg;
- normals[1] = n1;
- normals[2] = avg;
- normals[3] = n2;
+ vec n1, n2;
+ n1.cross(pos[0], pos[1], pos[2]);
+ if(numverts != 4)
+ {
+ n1.normalize();
+ loopk(numverts) normals[k] = n1;
+ return;
+ }
+ n2.cross(pos[0], pos[2], pos[3]);
+ if(n1.iszero())
+ {
+ n2.normalize();
+ loopk(4) normals[k] = n2;
+ return;
+ }
+ else n1.normalize();
+ if(n2.iszero())
+ {
+ loopk(4) normals[k] = n1;
+ return;
+ }
+ else n2.normalize();
+ vec avg = vec(n1).add(n2).normalize();
+ normals[0] = avg;
+ normals[1] = n1;
+ normals[2] = avg;
+ normals[3] = n2;
}
void addcubeverts(VSlot &vslot, int orient, int size, vec *pos, int convex, ushort texture, ushort lmid, vertinfo *vinfo, int numverts, int tj = -1, ushort envmap = EMID_NONE, int grassy = 0, bool alpha = false, int layer = LAYER_TOP)
{
- (void) grassy;
- int dim = dimension(orient);
- int shadowmask = texture==DEFAULT_SKY || alpha ? 0 : calcshadowmask(pos, numverts);
-
- LightMap *lm = NULL;
- LightMapTexture *lmtex = NULL;
- if(lightmaps.inrange(lmid-LMID_RESERVED))
- {
- lm = &lightmaps[lmid-LMID_RESERVED];
- if((lm->type&LM_TYPE)==LM_DIFFUSE ||
- ((lm->type&LM_TYPE)==LM_BUMPMAP0 &&
- lightmaps.inrange(lmid+1-LMID_RESERVED) &&
- (lightmaps[lmid+1-LMID_RESERVED].type&LM_TYPE)==LM_BUMPMAP1))
- lmtex = &lightmaptexs[lm->tex];
- else lm = NULL;
- }
-
- vec4 sgen, tgen;
- calctexgen(vslot, dim, sgen, tgen);
- vertex verts[MAXFACEVERTS];
- int index[MAXFACEVERTS];
- loopk(numverts)
- {
- vertex &v = verts[k];
- v.pos = pos[k];
- v.tc = vec2(sgen.dot(v.pos), tgen.dot(v.pos));
- if(lmtex)
- {
- v.lm = svec2(short(ceil((lm->offsetx + vinfo[k].u*(float(LM_PACKW)/float(USHRT_MAX+1)) + 0.5f) * float(SHRT_MAX)/lmtex->w)),
- short(ceil((lm->offsety + vinfo[k].v*(float(LM_PACKH)/float(USHRT_MAX+1)) + 0.5f) * float(SHRT_MAX)/lmtex->h)));
- }
- else v.lm = svec2(0, 0);
- if(vinfo && vinfo[k].norm)
- {
- vec n = decodenormal(vinfo[k].norm), t = orientation_tangent[vslot.rotation][dim];
- t.project(n).normalize();
- v.norm = bvec(n);
- v.tangent = bvec4(bvec(t), orientation_bitangent[vslot.rotation][dim].scalartriple(n, t) < 0 ? 0 : 255);
- }
- else
- {
- v.norm = vinfo && vinfo[k].norm && envmap != EMID_NONE ? bvec(decodenormal(vinfo[k].norm)) : bvec(128, 128, 255);
- v.tangent = bvec4(255, 128, 128, 255);
- }
- index[k] = vc.addvert(v);
- if(index[k] < 0) return;
- }
-
- if(texture == DEFAULT_SKY)
- {
- loopk(numverts) vc.skyclip = min(vc.skyclip, int(pos[k].z*8)>>3);
- vc.skymask |= 0x3F&~(1<<orient);
- }
-
- if(lmid >= LMID_RESERVED) lmid = lm ? lm->tex : LMID_AMBIENT;
-
- sortkey key(texture, lmid, !vslot.scroll.iszero() ? dim : 3, layer == LAYER_BLEND ? LAYER_BLEND : LAYER_TOP, envmap, alpha ? (vslot.alphaback ? ALPHA_BACK : (vslot.alphafront ? ALPHA_FRONT : NO_ALPHA)) : NO_ALPHA);
- addtris(key, orient, verts, index, numverts, convex, shadowmask, tj);
+ (void) grassy;
+ int dim = dimension(orient);
+ int shadowmask = texture==DEFAULT_SKY || alpha ? 0 : calcshadowmask(pos, numverts);
+
+ LightMap *lm = NULL;
+ LightMapTexture *lmtex = NULL;
+ if(lightmaps.inrange(lmid-LMID_RESERVED))
+ {
+ lm = &lightmaps[lmid-LMID_RESERVED];
+ if((lm->type&LM_TYPE)==LM_DIFFUSE ||
+ ((lm->type&LM_TYPE)==LM_BUMPMAP0 &&
+ lightmaps.inrange(lmid+1-LMID_RESERVED) &&
+ (lightmaps[lmid+1-LMID_RESERVED].type&LM_TYPE)==LM_BUMPMAP1))
+ lmtex = &lightmaptexs[lm->tex];
+ else lm = NULL;
+ }
+
+ vec4 sgen, tgen;
+ calctexgen(vslot, dim, sgen, tgen);
+ vertex verts[MAXFACEVERTS];
+ int index[MAXFACEVERTS];
+ loopk(numverts)
+ {
+ vertex &v = verts[k];
+ v.pos = pos[k];
+ v.tc = vec2(sgen.dot(v.pos), tgen.dot(v.pos));
+ if(lmtex)
+ {
+ v.lm = svec2(short(ceil((lm->offsetx + vinfo[k].u*(float(LM_PACKW)/float(USHRT_MAX+1)) + 0.5f) * float(SHRT_MAX)/lmtex->w)),
+ short(ceil((lm->offsety + vinfo[k].v*(float(LM_PACKH)/float(USHRT_MAX+1)) + 0.5f) * float(SHRT_MAX)/lmtex->h)));
+ }
+ else v.lm = svec2(0, 0);
+ if(vinfo && vinfo[k].norm)
+ {
+ vec n = decodenormal(vinfo[k].norm), t = orientation_tangent[vslot.rotation][dim];
+ t.project(n).normalize();
+ v.norm = bvec(n);
+ v.tangent = bvec4(bvec(t), orientation_bitangent[vslot.rotation][dim].scalartriple(n, t) < 0 ? 0 : 255);
+ }
+ else
+ {
+ v.norm = vinfo && vinfo[k].norm && envmap != EMID_NONE ? bvec(decodenormal(vinfo[k].norm)) : bvec(128, 128, 255);
+ v.tangent = bvec4(255, 128, 128, 255);
+ }
+ index[k] = vc.addvert(v);
+ if(index[k] < 0) return;
+ }
+
+ if(texture == DEFAULT_SKY)
+ {
+ loopk(numverts) vc.skyclip = min(vc.skyclip, int(pos[k].z*8)>>3);
+ vc.skymask |= 0x3F&~(1<<orient);
+ }
+
+ if(lmid >= LMID_RESERVED) lmid = lm ? lm->tex : LMID_AMBIENT;
+
+ sortkey key(texture, lmid, !vslot.scroll.iszero() ? dim : 3, layer == LAYER_BLEND ? LAYER_BLEND : LAYER_TOP, envmap, alpha ? (vslot.alphaback ? ALPHA_BACK : (vslot.alphafront ? ALPHA_FRONT : NO_ALPHA)) : NO_ALPHA);
+ addtris(key, orient, verts, index, numverts, convex, shadowmask, tj);
}
struct edgegroup
{
- ivec slope, origin;
- int axis;
+ ivec slope, origin;
+ int axis;
};
static uint hthash(const edgegroup &g)
{
- return g.slope.x^(g.slope.y<<2)^(g.slope.z<<4)^g.origin.x^g.origin.y^g.origin.z;
+ return g.slope.x^(g.slope.y<<2)^(g.slope.z<<4)^g.origin.x^g.origin.y^g.origin.z;
}
static bool htcmp(const edgegroup &x, const edgegroup &y)
{
- return x.slope==y.slope && x.origin==y.origin;
+ return x.slope==y.slope && x.origin==y.origin;
}
enum
{
- CE_START = 1<<0,
- CE_END = 1<<1,
- CE_FLIP = 1<<2,
- CE_DUP = 1<<3
+ CE_START = 1<<0,
+ CE_END = 1<<1,
+ CE_FLIP = 1<<2,
+ CE_DUP = 1<<3
};
struct cubeedge
{
- cube *c;
- int next, offset;
- ushort size;
- uchar index, flags;
+ cube *c;
+ int next, offset;
+ ushort size;
+ uchar index, flags;
};
vector<cubeedge> cubeedges;
@@ -837,273 +837,273 @@ hashtable<edgegroup, int> edgegroups(1<<13);
void gencubeedges(cube &c, const ivec &co, int size)
{
- ivec pos[MAXFACEVERTS];
- int vis;
- loopi(6) if((vis = visibletris(c, i, co, size)))
- {
- int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0;
- if(numverts)
- {
- vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
- ivec vo = ivec(co).mask(~0xFFF).shl(3);
- loopj(numverts)
- {
- vertinfo &v = verts[j];
- pos[j] = ivec(v.x, v.y, v.z).add(vo);
- }
- }
- else if(c.merged&(1<<i)) continue;
- else
- {
- ivec v[4];
- genfaceverts(c, i, v);
- int order = vis&4 || (!flataxisface(c, i) && faceconvexity(v) < 0) ? 1 : 0;
- ivec vo = ivec(co).shl(3);
- pos[numverts++] = v[order].mul(size).add(vo);
- if(vis&1) pos[numverts++] = v[order+1].mul(size).add(vo);
- pos[numverts++] = v[order+2].mul(size).add(vo);
- if(vis&2) pos[numverts++] = v[(order+3)&3].mul(size).add(vo);
- }
- loopj(numverts)
- {
- int e1 = j, e2 = j+1 < numverts ? j+1 : 0;
- ivec d = pos[e2];
- d.sub(pos[e1]);
- if(d.iszero()) continue;
- int axis = abs(d.x) > abs(d.y) ? (abs(d.x) > abs(d.z) ? 0 : 2) : (abs(d.y) > abs(d.z) ? 1 : 2);
- if(d[axis] < 0)
- {
- d.neg();
- swap(e1, e2);
- }
- reduceslope(d);
-
- int t1 = pos[e1][axis]/d[axis],
- t2 = pos[e2][axis]/d[axis];
- edgegroup g;
- g.origin = ivec(pos[e1]).sub(ivec(d).mul(t1));
- g.slope = d;
- g.axis = axis;
- cubeedge ce;
- ce.c = &c;
- ce.offset = t1;
- ce.size = t2 - t1;
- ce.index = i*(MAXFACEVERTS+1)+j;
- ce.flags = CE_START | CE_END | (e1!=j ? CE_FLIP : 0);
- ce.next = -1;
-
- bool insert = true;
- int *exists = edgegroups.access(g);
- if(exists)
- {
- int prev = -1, cur = *exists;
- while(cur >= 0)
- {
- cubeedge &p = cubeedges[cur];
- if(ce.offset <= p.offset+p.size)
- {
- if(ce.offset < p.offset) break;
- if(p.flags&CE_DUP ?
- ce.offset+ce.size <= p.offset+p.size :
- ce.offset==p.offset && ce.size==p.size)
- {
- p.flags |= CE_DUP;
- insert = false;
- break;
- }
- if(ce.offset == p.offset+p.size) ce.flags &= ~CE_START;
- }
- prev = cur;
- cur = p.next;
- }
- if(insert)
- {
- ce.next = cur;
- while(cur >= 0)
- {
- cubeedge &p = cubeedges[cur];
- if(ce.offset+ce.size==p.offset) { ce.flags &= ~CE_END; break; }
- cur = p.next;
- }
- if(prev>=0) cubeedges[prev].next = cubeedges.length();
- else *exists = cubeedges.length();
- }
- }
- else edgegroups[g] = cubeedges.length();
-
- if(insert) cubeedges.add(ce);
- }
- }
+ ivec pos[MAXFACEVERTS];
+ int vis;
+ loopi(6) if((vis = visibletris(c, i, co, size)))
+ {
+ int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0;
+ if(numverts)
+ {
+ vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
+ ivec vo = ivec(co).mask(~0xFFF).shl(3);
+ loopj(numverts)
+ {
+ vertinfo &v = verts[j];
+ pos[j] = ivec(v.x, v.y, v.z).add(vo);
+ }
+ }
+ else if(c.merged&(1<<i)) continue;
+ else
+ {
+ ivec v[4];
+ genfaceverts(c, i, v);
+ int order = vis&4 || (!flataxisface(c, i) && faceconvexity(v) < 0) ? 1 : 0;
+ ivec vo = ivec(co).shl(3);
+ pos[numverts++] = v[order].mul(size).add(vo);
+ if(vis&1) pos[numverts++] = v[order+1].mul(size).add(vo);
+ pos[numverts++] = v[order+2].mul(size).add(vo);
+ if(vis&2) pos[numverts++] = v[(order+3)&3].mul(size).add(vo);
+ }
+ loopj(numverts)
+ {
+ int e1 = j, e2 = j+1 < numverts ? j+1 : 0;
+ ivec d = pos[e2];
+ d.sub(pos[e1]);
+ if(d.iszero()) continue;
+ int axis = abs(d.x) > abs(d.y) ? (abs(d.x) > abs(d.z) ? 0 : 2) : (abs(d.y) > abs(d.z) ? 1 : 2);
+ if(d[axis] < 0)
+ {
+ d.neg();
+ swap(e1, e2);
+ }
+ reduceslope(d);
+
+ int t1 = pos[e1][axis]/d[axis],
+ t2 = pos[e2][axis]/d[axis];
+ edgegroup g;
+ g.origin = ivec(pos[e1]).sub(ivec(d).mul(t1));
+ g.slope = d;
+ g.axis = axis;
+ cubeedge ce;
+ ce.c = &c;
+ ce.offset = t1;
+ ce.size = t2 - t1;
+ ce.index = i*(MAXFACEVERTS+1)+j;
+ ce.flags = CE_START | CE_END | (e1!=j ? CE_FLIP : 0);
+ ce.next = -1;
+
+ bool insert = true;
+ int *exists = edgegroups.access(g);
+ if(exists)
+ {
+ int prev = -1, cur = *exists;
+ while(cur >= 0)
+ {
+ cubeedge &p = cubeedges[cur];
+ if(ce.offset <= p.offset+p.size)
+ {
+ if(ce.offset < p.offset) break;
+ if(p.flags&CE_DUP ?
+ ce.offset+ce.size <= p.offset+p.size :
+ ce.offset==p.offset && ce.size==p.size)
+ {
+ p.flags |= CE_DUP;
+ insert = false;
+ break;
+ }
+ if(ce.offset == p.offset+p.size) ce.flags &= ~CE_START;
+ }
+ prev = cur;
+ cur = p.next;
+ }
+ if(insert)
+ {
+ ce.next = cur;
+ while(cur >= 0)
+ {
+ cubeedge &p = cubeedges[cur];
+ if(ce.offset+ce.size==p.offset) { ce.flags &= ~CE_END; break; }
+ cur = p.next;
+ }
+ if(prev>=0) cubeedges[prev].next = cubeedges.length();
+ else *exists = cubeedges.length();
+ }
+ }
+ else edgegroups[g] = cubeedges.length();
+
+ if(insert) cubeedges.add(ce);
+ }
+ }
}
void gencubeedges(cube *c = worldroot, const ivec &co = ivec(0, 0, 0), int size = worldsize>>1)
{
- progress("fixing t-joints...");
- neighbourstack[++neighbourdepth] = c;
- loopi(8)
- {
- ivec o(i, co, size);
- if(c[i].ext) c[i].ext->tjoints = -1;
- if(c[i].children) gencubeedges(c[i].children, o, size>>1);
- else if(!isempty(c[i])) gencubeedges(c[i], o, size);
- }
- --neighbourdepth;
+ progress("fixing t-joints...");
+ neighbourstack[++neighbourdepth] = c;
+ loopi(8)
+ {
+ ivec o(i, co, size);
+ if(c[i].ext) c[i].ext->tjoints = -1;
+ if(c[i].children) gencubeedges(c[i].children, o, size>>1);
+ else if(!isempty(c[i])) gencubeedges(c[i], o, size);
+ }
+ --neighbourdepth;
}
void gencubeverts(cube &c, const ivec &co, int size, int csi)
{
- if(!(c.visible&0xC0)) return;
-
- int vismask = ~c.merged & 0x3F;
- if(!(c.visible&0x80)) vismask &= c.visible;
- if(!vismask) return;
-
- int tj = filltjoints && c.ext ? c.ext->tjoints : -1, vis;
- loopi(6) if(vismask&(1<<i) && (vis = visibletris(c, i, co, size)))
- {
- vec pos[MAXFACEVERTS];
- vertinfo *verts = NULL;
- int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0, convex = 0;
- if(numverts)
- {
- verts = c.ext->verts() + c.ext->surfaces[i].verts;
- vec vo(ivec(co).mask(~0xFFF));
- loopj(numverts) pos[j] = vec(verts[j].getxyz()).mul(1.0f/8).add(vo);
- if(!flataxisface(c, i)) convex = faceconvexity(verts, numverts, size);
- }
- else
- {
- ivec v[4];
- genfaceverts(c, i, v);
- if(!flataxisface(c, i)) convex = faceconvexity(v);
- int order = vis&4 || convex < 0 ? 1 : 0;
- vec vo(co);
- pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
- if(vis&1) pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
- pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
- if(vis&2) pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
- }
-
- VSlot &vslot = lookupvslot(c.texture[i], true),
- *layer = vslot.layer && !(c.material&MAT_ALPHA) ? &lookupvslot(vslot.layer, true) : NULL;
- ushort envmap = vslot.slot->shader->type&SHADER_ENVMAP ? (int) (vslot.slot->texmask&(1<<TEX_ENVMAP) ? (int) EMID_CUSTOM : (int) closestenvmap(i, co, size)) : (int) EMID_NONE,
- envmap2 = layer && layer->slot->shader->type&SHADER_ENVMAP ? (int) (layer->slot->texmask&(1<<TEX_ENVMAP) ? (int) EMID_CUSTOM : (int) closestenvmap(i, co, size)) : (int) EMID_NONE;
- while(tj >= 0 && tjoints[tj].edge < i*(MAXFACEVERTS+1)) tj = tjoints[tj].next;
- int hastj = tj >= 0 && tjoints[tj].edge < (i+1)*(MAXFACEVERTS+1) ? tj : -1;
- if(!c.ext)
- addcubeverts(vslot, i, size, pos, convex, c.texture[i], LMID_AMBIENT, NULL, numverts, hastj, envmap, 0, (c.material&MAT_ALPHA)!=0);
- else
- {
- const surfaceinfo &surf = c.ext->surfaces[i];
- if(!surf.numverts || surf.numverts&LAYER_TOP)
- addcubeverts(vslot, i, size, pos, convex, c.texture[i], surf.lmid[0], verts, numverts, hastj, envmap, 0, (c.material&MAT_ALPHA)!=0, LAYER_TOP|(surf.numverts&LAYER_BLEND));
- if(surf.numverts&LAYER_BOTTOM)
- addcubeverts(layer ? *layer : vslot, i, size, pos, convex, vslot.layer, surf.lmid[1], surf.numverts&LAYER_DUP ? verts + numverts : verts, numverts, hastj, envmap2);
- }
- }
+ if(!(c.visible&0xC0)) return;
+
+ int vismask = ~c.merged & 0x3F;
+ if(!(c.visible&0x80)) vismask &= c.visible;
+ if(!vismask) return;
+
+ int tj = filltjoints && c.ext ? c.ext->tjoints : -1, vis;
+ loopi(6) if(vismask&(1<<i) && (vis = visibletris(c, i, co, size)))
+ {
+ vec pos[MAXFACEVERTS];
+ vertinfo *verts = NULL;
+ int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0, convex = 0;
+ if(numverts)
+ {
+ verts = c.ext->verts() + c.ext->surfaces[i].verts;
+ vec vo(ivec(co).mask(~0xFFF));
+ loopj(numverts) pos[j] = vec(verts[j].getxyz()).mul(1.0f/8).add(vo);
+ if(!flataxisface(c, i)) convex = faceconvexity(verts, numverts, size);
+ }
+ else
+ {
+ ivec v[4];
+ genfaceverts(c, i, v);
+ if(!flataxisface(c, i)) convex = faceconvexity(v);
+ int order = vis&4 || convex < 0 ? 1 : 0;
+ vec vo(co);
+ pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
+ if(vis&1) pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
+ pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
+ if(vis&2) pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
+ }
+
+ VSlot &vslot = lookupvslot(c.texture[i], true),
+ *layer = vslot.layer && !(c.material&MAT_ALPHA) ? &lookupvslot(vslot.layer, true) : NULL;
+ ushort envmap = vslot.slot->shader->type&SHADER_ENVMAP ? (int) (vslot.slot->texmask&(1<<TEX_ENVMAP) ? (int) EMID_CUSTOM : (int) closestenvmap(i, co, size)) : (int) EMID_NONE,
+ envmap2 = layer && layer->slot->shader->type&SHADER_ENVMAP ? (int) (layer->slot->texmask&(1<<TEX_ENVMAP) ? (int) EMID_CUSTOM : (int) closestenvmap(i, co, size)) : (int) EMID_NONE;
+ while(tj >= 0 && tjoints[tj].edge < i*(MAXFACEVERTS+1)) tj = tjoints[tj].next;
+ int hastj = tj >= 0 && tjoints[tj].edge < (i+1)*(MAXFACEVERTS+1) ? tj : -1;
+ if(!c.ext)
+ addcubeverts(vslot, i, size, pos, convex, c.texture[i], LMID_AMBIENT, NULL, numverts, hastj, envmap, 0, (c.material&MAT_ALPHA)!=0);
+ else
+ {
+ const surfaceinfo &surf = c.ext->surfaces[i];
+ if(!surf.numverts || surf.numverts&LAYER_TOP)
+ addcubeverts(vslot, i, size, pos, convex, c.texture[i], surf.lmid[0], verts, numverts, hastj, envmap, 0, (c.material&MAT_ALPHA)!=0, LAYER_TOP|(surf.numverts&LAYER_BLEND));
+ if(surf.numverts&LAYER_BOTTOM)
+ addcubeverts(layer ? *layer : vslot, i, size, pos, convex, vslot.layer, surf.lmid[1], surf.numverts&LAYER_DUP ? verts + numverts : verts, numverts, hastj, envmap2);
+ }
+ }
}
static inline bool skyoccluded(cube &c, int orient)
{
- return touchingface(c, orient) && faceedges(c, orient) == F_SOLID;
+ return touchingface(c, orient) && faceedges(c, orient) == F_SOLID;
}
static int dummyskyfaces[6];
static inline int hasskyfaces(cube &c, const ivec &co, int size, int faces[6] = dummyskyfaces)
{
- int numfaces = 0;
- if(isempty(c) || c.material&MAT_ALPHA)
- {
- if(co.x == 0) faces[numfaces++] = O_LEFT;
- if(co.x + size == worldsize) faces[numfaces++] = O_RIGHT;
- if(co.y == 0) faces[numfaces++] = O_BACK;
- if(co.y + size == worldsize) faces[numfaces++] = O_FRONT;
- if(co.z == 0) faces[numfaces++] = O_BOTTOM;
- if(co.z + size == worldsize) faces[numfaces++] = O_TOP;
- }
- else if(!isentirelysolid(c))
- {
- if(co.x == 0 && !skyoccluded(c, O_LEFT)) faces[numfaces++] = O_LEFT;
- if(co.x + size == worldsize && !skyoccluded(c, O_RIGHT)) faces[numfaces++] = O_RIGHT;
- if(co.y == 0 && !skyoccluded(c, O_BACK)) faces[numfaces++] = O_BACK;
- if(co.y + size == worldsize && !skyoccluded(c, O_FRONT)) faces[numfaces++] = O_FRONT;
- if(co.z == 0 && !skyoccluded(c, O_BOTTOM)) faces[numfaces++] = O_BOTTOM;
- if(co.z + size == worldsize && !skyoccluded(c, O_TOP)) faces[numfaces++] = O_TOP;
- }
- return numfaces;
+ int numfaces = 0;
+ if(isempty(c) || c.material&MAT_ALPHA)
+ {
+ if(co.x == 0) faces[numfaces++] = O_LEFT;
+ if(co.x + size == worldsize) faces[numfaces++] = O_RIGHT;
+ if(co.y == 0) faces[numfaces++] = O_BACK;
+ if(co.y + size == worldsize) faces[numfaces++] = O_FRONT;
+ if(co.z == 0) faces[numfaces++] = O_BOTTOM;
+ if(co.z + size == worldsize) faces[numfaces++] = O_TOP;
+ }
+ else if(!isentirelysolid(c))
+ {
+ if(co.x == 0 && !skyoccluded(c, O_LEFT)) faces[numfaces++] = O_LEFT;
+ if(co.x + size == worldsize && !skyoccluded(c, O_RIGHT)) faces[numfaces++] = O_RIGHT;
+ if(co.y == 0 && !skyoccluded(c, O_BACK)) faces[numfaces++] = O_BACK;
+ if(co.y + size == worldsize && !skyoccluded(c, O_FRONT)) faces[numfaces++] = O_FRONT;
+ if(co.z == 0 && !skyoccluded(c, O_BOTTOM)) faces[numfaces++] = O_BOTTOM;
+ if(co.z + size == worldsize && !skyoccluded(c, O_TOP)) faces[numfaces++] = O_TOP;
+ }
+ return numfaces;
}
void minskyface(cube &cu, int orient, const ivec &co, int size, facebounds &orig)
{
- facebounds mincf;
- mincf.u1 = orig.u2;
- mincf.u2 = orig.u1;
- mincf.v1 = orig.v2;
- mincf.v2 = orig.v1;
- mincubeface(cu, orient, co, size, orig, mincf, MAT_ALPHA, MAT_ALPHA);
- orig.u1 = max(mincf.u1, orig.u1);
- orig.u2 = min(mincf.u2, orig.u2);
- orig.v1 = max(mincf.v1, orig.v1);
- orig.v2 = min(mincf.v2, orig.v2);
+ facebounds mincf;
+ mincf.u1 = orig.u2;
+ mincf.u2 = orig.u1;
+ mincf.v1 = orig.v2;
+ mincf.v2 = orig.v1;
+ mincubeface(cu, orient, co, size, orig, mincf, MAT_ALPHA, MAT_ALPHA);
+ orig.u1 = max(mincf.u1, orig.u1);
+ orig.u2 = min(mincf.u2, orig.u2);
+ orig.v1 = max(mincf.v1, orig.v1);
+ orig.v2 = min(mincf.v2, orig.v2);
}
void genskyfaces(cube &c, const ivec &o, int size)
{
- int faces[6], numfaces = hasskyfaces(c, o, size, faces);
- if(!numfaces) return;
-
- loopi(numfaces)
- {
- int orient = faces[i], dim = dimension(orient);
- facebounds m;
- m.u1 = (o[C[dim]]&0xFFF)<<3;
- m.u2 = m.u1 + (size<<3);
- m.v1 = (o[R[dim]]&0xFFF)<<3;
- m.v2 = m.v1 + (size<<3);
- minskyface(c, orient, o, size, m);
- if(m.u1 >= m.u2 || m.v1 >= m.v2) continue;
- vc.skyarea += (int(m.u2-m.u1)*int(m.v2-m.v1) + (1<<(2*3))-1)>>(2*3);
- vc.skyfaces[orient].add(m);
- }
+ int faces[6], numfaces = hasskyfaces(c, o, size, faces);
+ if(!numfaces) return;
+
+ loopi(numfaces)
+ {
+ int orient = faces[i], dim = dimension(orient);
+ facebounds m;
+ m.u1 = (o[C[dim]]&0xFFF)<<3;
+ m.u2 = m.u1 + (size<<3);
+ m.v1 = (o[R[dim]]&0xFFF)<<3;
+ m.v2 = m.v1 + (size<<3);
+ minskyface(c, orient, o, size, m);
+ if(m.u1 >= m.u2 || m.v1 >= m.v2) continue;
+ vc.skyarea += (int(m.u2-m.u1)*int(m.v2-m.v1) + (1<<(2*3))-1)>>(2*3);
+ vc.skyfaces[orient].add(m);
+ }
}
void addskyverts(const ivec &o, int size)
{
- loopi(6)
- {
- int dim = dimension(i), c = C[dim], r = R[dim];
- vector<facebounds> &sf = vc.skyfaces[i];
- if(sf.empty()) continue;
- vc.skymask |= 0x3F&~(1<<opposite(i));
- sf.setsize(mergefaces(i, sf.getbuf(), sf.length()));
- loopvj(sf)
- {
- facebounds &m = sf[j];
- int index[4];
- loopk(4)
- {
- const ivec &coords = facecoords[opposite(i)][k];
- vec v;
- v[dim] = o[dim];
- if(coords[dim]) v[dim] += size;
- v[c] = (o[c]&~0xFFF) + (coords[c] ? m.u2 : m.u1)/8.0f;
- v[r] = (o[r]&~0xFFF) + (coords[r] ? m.v2 : m.v1)/8.0f;
- index[k] = vc.addvert(v);
- if(index[k] < 0) goto nextskyface;
- vc.skyclip = min(vc.skyclip, int(v.z*8)>>3);
- }
- if(vc.skytris + 6 > USHRT_MAX) break;
- vc.skytris += 6;
- vc.skyindices.add(index[0]);
- vc.skyindices.add(index[1]);
- vc.skyindices.add(index[2]);
-
- vc.skyindices.add(index[0]);
- vc.skyindices.add(index[2]);
- vc.skyindices.add(index[3]);
- nextskyface:;
- }
- }
+ loopi(6)
+ {
+ int dim = dimension(i), c = C[dim], r = R[dim];
+ vector<facebounds> &sf = vc.skyfaces[i];
+ if(sf.empty()) continue;
+ vc.skymask |= 0x3F&~(1<<opposite(i));
+ sf.setsize(mergefaces(i, sf.getbuf(), sf.length()));
+ loopvj(sf)
+ {
+ facebounds &m = sf[j];
+ int index[4];
+ loopk(4)
+ {
+ const ivec &coords = facecoords[opposite(i)][k];
+ vec v;
+ v[dim] = o[dim];
+ if(coords[dim]) v[dim] += size;
+ v[c] = (o[c]&~0xFFF) + (coords[c] ? m.u2 : m.u1)/8.0f;
+ v[r] = (o[r]&~0xFFF) + (coords[r] ? m.v2 : m.v1)/8.0f;
+ index[k] = vc.addvert(v);
+ if(index[k] < 0) goto nextskyface;
+ vc.skyclip = min(vc.skyclip, int(v.z*8)>>3);
+ }
+ if(vc.skytris + 6 > USHRT_MAX) break;
+ vc.skytris += 6;
+ vc.skyindices.add(index[0]);
+ vc.skyindices.add(index[1]);
+ vc.skyindices.add(index[2]);
+
+ vc.skyindices.add(index[0]);
+ vc.skyindices.add(index[2]);
+ vc.skyindices.add(index[3]);
+ nextskyface:;
+ }
+ }
}
////////// Vertex Arrays //////////////
@@ -1114,125 +1114,125 @@ vector<vtxarray *> valist, varoot;
vtxarray *newva(const ivec &co, int size)
{
- vc.optimize();
-
- vtxarray *va = new vtxarray;
- va->parent = NULL;
- va->o = co;
- va->size = size;
- va->skyarea = vc.skyarea;
- va->skyfaces = vc.skymask;
- va->skyclip = vc.skyclip < INT_MAX ? vc.skyclip : INT_MAX;
- va->curvfc = VFC_NOT_VISIBLE;
- va->occluded = OCCLUDE_NOTHING;
- va->query = NULL;
- va->bbmin = ivec(-1, -1, -1);
- va->bbmax = ivec(-1, -1, -1);
- va->hasmerges = 0;
- va->mergelevel = -1;
-
- vc.setupdata(va);
-
- wverts += va->verts;
- wtris += va->tris + va->blends + va->alphatris;
- allocva++;
- valist.add(va);
-
- return va;
+ vc.optimize();
+
+ vtxarray *va = new vtxarray;
+ va->parent = NULL;
+ va->o = co;
+ va->size = size;
+ va->skyarea = vc.skyarea;
+ va->skyfaces = vc.skymask;
+ va->skyclip = vc.skyclip < INT_MAX ? vc.skyclip : INT_MAX;
+ va->curvfc = VFC_NOT_VISIBLE;
+ va->occluded = OCCLUDE_NOTHING;
+ va->query = NULL;
+ va->bbmin = ivec(-1, -1, -1);
+ va->bbmax = ivec(-1, -1, -1);
+ va->hasmerges = 0;
+ va->mergelevel = -1;
+
+ vc.setupdata(va);
+
+ wverts += va->verts;
+ wtris += va->tris + va->blends + va->alphatris;
+ allocva++;
+ valist.add(va);
+
+ return va;
}
void destroyva(vtxarray *va, bool reparent)
{
- wverts -= va->verts;
- wtris -= va->tris + va->blends + va->alphatris;
- allocva--;
- valist.removeobj(va);
- if(!va->parent) varoot.removeobj(va);
- if(reparent)
- {
- if(va->parent) va->parent->children.removeobj(va);
- loopv(va->children)
- {
- vtxarray *child = va->children[i];
- child->parent = va->parent;
- if(child->parent) child->parent->children.add(child);
- }
- }
- if(va->vbuf) destroyvbo(va->vbuf);
- if(va->ebuf) destroyvbo(va->ebuf);
- if(va->skybuf) destroyvbo(va->skybuf);
- if(va->eslist) delete[] va->eslist;
- if(va->matbuf) delete[] va->matbuf;
- delete va;
+ wverts -= va->verts;
+ wtris -= va->tris + va->blends + va->alphatris;
+ allocva--;
+ valist.removeobj(va);
+ if(!va->parent) varoot.removeobj(va);
+ if(reparent)
+ {
+ if(va->parent) va->parent->children.removeobj(va);
+ loopv(va->children)
+ {
+ vtxarray *child = va->children[i];
+ child->parent = va->parent;
+ if(child->parent) child->parent->children.add(child);
+ }
+ }
+ if(va->vbuf) destroyvbo(va->vbuf);
+ if(va->ebuf) destroyvbo(va->ebuf);
+ if(va->skybuf) destroyvbo(va->skybuf);
+ if(va->eslist) delete[] va->eslist;
+ if(va->matbuf) delete[] va->matbuf;
+ delete va;
}
void clearvas(cube *c)
{
- loopi(8)
- {
- if(c[i].ext)
- {
- if(c[i].ext->va) destroyva(c[i].ext->va, false);
- c[i].ext->va = NULL;
- c[i].ext->tjoints = -1;
- }
- if(c[i].children) clearvas(c[i].children);
- }
+ loopi(8)
+ {
+ if(c[i].ext)
+ {
+ if(c[i].ext->va) destroyva(c[i].ext->va, false);
+ c[i].ext->va = NULL;
+ c[i].ext->tjoints = -1;
+ }
+ if(c[i].children) clearvas(c[i].children);
+ }
}
void updatevabb(vtxarray *va, bool force)
{
- if(!force && va->bbmin.x >= 0) return;
-
- va->bbmin = va->geommin;
- va->bbmax = va->geommax;
- va->bbmin.min(va->matmin);
- va->bbmax.max(va->matmax);
- loopv(va->children)
- {
- vtxarray *child = va->children[i];
- updatevabb(child, force);
- va->bbmin.min(child->bbmin);
- va->bbmax.max(child->bbmax);
- }
- loopv(va->mapmodels)
- {
- octaentities *oe = va->mapmodels[i];
- va->bbmin.min(oe->bbmin);
- va->bbmax.max(oe->bbmax);
- }
- va->bbmin.max(va->o);
- va->bbmax.min(ivec(va->o).add(va->size));
-
- if(va->skyfaces)
- {
- va->skyfaces |= 0x80;
- if(va->sky) loop(dim, 3) if(va->skyfaces&(3<<(2*dim)))
- {
- int r = R[dim], c = C[dim];
- if((va->skyfaces&(1<<(2*dim)) && va->o[dim] < va->bbmin[dim]) ||
- (va->skyfaces&(2<<(2*dim)) && va->o[dim]+va->size > va->bbmax[dim]) ||
- va->o[r] < va->bbmin[r] || va->o[r]+va->size > va->bbmax[r] ||
- va->o[c] < va->bbmin[c] || va->o[c]+va->size > va->bbmax[c])
- {
- va->skyfaces &= ~0x80;
- break;
- }
- }
- }
+ if(!force && va->bbmin.x >= 0) return;
+
+ va->bbmin = va->geommin;
+ va->bbmax = va->geommax;
+ va->bbmin.min(va->matmin);
+ va->bbmax.max(va->matmax);
+ loopv(va->children)
+ {
+ vtxarray *child = va->children[i];
+ updatevabb(child, force);
+ va->bbmin.min(child->bbmin);
+ va->bbmax.max(child->bbmax);
+ }
+ loopv(va->mapmodels)
+ {
+ octaentities *oe = va->mapmodels[i];
+ va->bbmin.min(oe->bbmin);
+ va->bbmax.max(oe->bbmax);
+ }
+ va->bbmin.max(va->o);
+ va->bbmax.min(ivec(va->o).add(va->size));
+
+ if(va->skyfaces)
+ {
+ va->skyfaces |= 0x80;
+ if(va->sky) loop(dim, 3) if(va->skyfaces&(3<<(2*dim)))
+ {
+ int r = R[dim], c = C[dim];
+ if((va->skyfaces&(1<<(2*dim)) && va->o[dim] < va->bbmin[dim]) ||
+ (va->skyfaces&(2<<(2*dim)) && va->o[dim]+va->size > va->bbmax[dim]) ||
+ va->o[r] < va->bbmin[r] || va->o[r]+va->size > va->bbmax[r] ||
+ va->o[c] < va->bbmin[c] || va->o[c]+va->size > va->bbmax[c])
+ {
+ va->skyfaces &= ~0x80;
+ break;
+ }
+ }
+ }
}
void updatevabbs(bool force)
{
- loopv(varoot) updatevabb(varoot[i], force);
+ loopv(varoot) updatevabb(varoot[i], force);
}
struct mergedface
{
- uchar orient, lmid, numverts;
- ushort mat, tex, envmap;
- vertinfo *verts;
- int tjoints;
+ uchar orient, lmid, numverts;
+ ushort mat, tex, envmap;
+ vertinfo *verts;
+ int tjoints;
};
#define MAXMERGELEVEL 12
@@ -1241,263 +1241,263 @@ static vector<mergedface> vamerges[MAXMERGELEVEL+1];
int genmergedfaces(cube &c, const ivec &co, int size, int minlevel = -1)
{
- if(!c.ext || isempty(c)) return -1;
- int tj = c.ext->tjoints, maxlevel = -1;
- loopi(6) if(c.merged&(1<<i))
- {
- surfaceinfo &surf = c.ext->surfaces[i];
- int numverts = surf.numverts&MAXFACEVERTS;
- if(!numverts)
- {
- if(minlevel < 0) vahasmerges |= MERGE_PART;
- continue;
- }
- mergedface mf;
- mf.orient = i;
- mf.mat = c.material;
- mf.tex = c.texture[i];
- mf.envmap = EMID_NONE;
- mf.lmid = surf.lmid[0];
- mf.numverts = surf.numverts;
- mf.verts = c.ext->verts() + surf.verts;
- mf.tjoints = -1;
- int level = calcmergedsize(i, co, size, mf.verts, mf.numverts&MAXFACEVERTS);
- if(level > minlevel)
- {
- maxlevel = max(maxlevel, level);
-
- while(tj >= 0 && tjoints[tj].edge < i*(MAXFACEVERTS+1)) tj = tjoints[tj].next;
- if(tj >= 0 && tjoints[tj].edge < (i+1)*(MAXFACEVERTS+1)) mf.tjoints = tj;
-
- VSlot &vslot = lookupvslot(mf.tex, true),
- *layer = vslot.layer && !(c.material&MAT_ALPHA) ? &lookupvslot(vslot.layer, true) : NULL;
- if(vslot.slot->shader->type&SHADER_ENVMAP)
- mf.envmap = vslot.slot->texmask&(1<<TEX_ENVMAP) ? (int) EMID_CUSTOM : (int) closestenvmap(i, co, size);
- ushort envmap2 = layer && layer->slot->shader->type&SHADER_ENVMAP ? (int) (layer->slot->texmask&(1<<TEX_ENVMAP) ? (int) EMID_CUSTOM : (int) closestenvmap(i, co, size)) : (int) EMID_NONE;
-
- if(surf.numverts&LAYER_TOP) vamerges[level].add(mf);
- if(surf.numverts&LAYER_BOTTOM)
- {
- mf.tex = vslot.layer;
- mf.envmap = envmap2;
- mf.lmid = surf.lmid[1];
- mf.numverts &= ~LAYER_TOP;
- if(surf.numverts&LAYER_DUP) mf.verts += numverts;
- vamerges[level].add(mf);
- }
- }
- }
- if(maxlevel >= 0)
- {
- vamergemax = max(vamergemax, maxlevel);
- vahasmerges |= MERGE_ORIGIN;
- }
- return maxlevel;
+ if(!c.ext || isempty(c)) return -1;
+ int tj = c.ext->tjoints, maxlevel = -1;
+ loopi(6) if(c.merged&(1<<i))
+ {
+ surfaceinfo &surf = c.ext->surfaces[i];
+ int numverts = surf.numverts&MAXFACEVERTS;
+ if(!numverts)
+ {
+ if(minlevel < 0) vahasmerges |= MERGE_PART;
+ continue;
+ }
+ mergedface mf;
+ mf.orient = i;
+ mf.mat = c.material;
+ mf.tex = c.texture[i];
+ mf.envmap = EMID_NONE;
+ mf.lmid = surf.lmid[0];
+ mf.numverts = surf.numverts;
+ mf.verts = c.ext->verts() + surf.verts;
+ mf.tjoints = -1;
+ int level = calcmergedsize(i, co, size, mf.verts, mf.numverts&MAXFACEVERTS);
+ if(level > minlevel)
+ {
+ maxlevel = max(maxlevel, level);
+
+ while(tj >= 0 && tjoints[tj].edge < i*(MAXFACEVERTS+1)) tj = tjoints[tj].next;
+ if(tj >= 0 && tjoints[tj].edge < (i+1)*(MAXFACEVERTS+1)) mf.tjoints = tj;
+
+ VSlot &vslot = lookupvslot(mf.tex, true),
+ *layer = vslot.layer && !(c.material&MAT_ALPHA) ? &lookupvslot(vslot.layer, true) : NULL;
+ if(vslot.slot->shader->type&SHADER_ENVMAP)
+ mf.envmap = vslot.slot->texmask&(1<<TEX_ENVMAP) ? (int) EMID_CUSTOM : (int) closestenvmap(i, co, size);
+ ushort envmap2 = layer && layer->slot->shader->type&SHADER_ENVMAP ? (int) (layer->slot->texmask&(1<<TEX_ENVMAP) ? (int) EMID_CUSTOM : (int) closestenvmap(i, co, size)) : (int) EMID_NONE;
+
+ if(surf.numverts&LAYER_TOP) vamerges[level].add(mf);
+ if(surf.numverts&LAYER_BOTTOM)
+ {
+ mf.tex = vslot.layer;
+ mf.envmap = envmap2;
+ mf.lmid = surf.lmid[1];
+ mf.numverts &= ~LAYER_TOP;
+ if(surf.numverts&LAYER_DUP) mf.verts += numverts;
+ vamerges[level].add(mf);
+ }
+ }
+ }
+ if(maxlevel >= 0)
+ {
+ vamergemax = max(vamergemax, maxlevel);
+ vahasmerges |= MERGE_ORIGIN;
+ }
+ return maxlevel;
}
int findmergedfaces(cube &c, const ivec &co, int size, int csi, int minlevel)
{
- if(c.ext && c.ext->va && !(c.ext->va->hasmerges&MERGE_ORIGIN)) return c.ext->va->mergelevel;
- else if(c.children)
- {
- int maxlevel = -1;
- loopi(8)
- {
- ivec o(i, co, size/2);
- int level = findmergedfaces(c.children[i], o, size/2, csi-1, minlevel);
- maxlevel = max(maxlevel, level);
- }
- return maxlevel;
- }
- else if(c.ext && c.merged) return genmergedfaces(c, co, size, minlevel);
- else return -1;
+ if(c.ext && c.ext->va && !(c.ext->va->hasmerges&MERGE_ORIGIN)) return c.ext->va->mergelevel;
+ else if(c.children)
+ {
+ int maxlevel = -1;
+ loopi(8)
+ {
+ ivec o(i, co, size/2);
+ int level = findmergedfaces(c.children[i], o, size/2, csi-1, minlevel);
+ maxlevel = max(maxlevel, level);
+ }
+ return maxlevel;
+ }
+ else if(c.ext && c.merged) return genmergedfaces(c, co, size, minlevel);
+ else return -1;
}
void addmergedverts(int level, const ivec &o)
{
- vector<mergedface> &mfl = vamerges[level];
- if(mfl.empty()) return;
- vec vo(ivec(o).mask(~0xFFF));
- vec pos[MAXFACEVERTS];
- loopv(mfl)
- {
- mergedface &mf = mfl[i];
- int numverts = mf.numverts&MAXFACEVERTS;
- loopi(numverts)
- {
- vertinfo &v = mf.verts[i];
- pos[i] = vec(v.x, v.y, v.z).mul(1.0f/8).add(vo);
- }
- VSlot &vslot = lookupvslot(mf.tex, true);
- addcubeverts(vslot, mf.orient, 1<<level, pos, 0, mf.tex, mf.lmid, mf.verts, numverts, mf.tjoints, mf.envmap, 0, (mf.mat&MAT_ALPHA)!=0, mf.numverts&LAYER_BLEND);
- vahasmerges |= MERGE_USE;
- }
- mfl.setsize(0);
+ vector<mergedface> &mfl = vamerges[level];
+ if(mfl.empty()) return;
+ vec vo(ivec(o).mask(~0xFFF));
+ vec pos[MAXFACEVERTS];
+ loopv(mfl)
+ {
+ mergedface &mf = mfl[i];
+ int numverts = mf.numverts&MAXFACEVERTS;
+ loopi(numverts)
+ {
+ vertinfo &v = mf.verts[i];
+ pos[i] = vec(v.x, v.y, v.z).mul(1.0f/8).add(vo);
+ }
+ VSlot &vslot = lookupvslot(mf.tex, true);
+ addcubeverts(vslot, mf.orient, 1<<level, pos, 0, mf.tex, mf.lmid, mf.verts, numverts, mf.tjoints, mf.envmap, 0, (mf.mat&MAT_ALPHA)!=0, mf.numverts&LAYER_BLEND);
+ vahasmerges |= MERGE_USE;
+ }
+ mfl.setsize(0);
}
void rendercube(cube &c, const ivec &co, int size, int csi, int &maxlevel) // creates vertices and indices ready to be put into a va
{
- //if(size<=16) return;
- if(c.ext && c.ext->va)
- {
- maxlevel = max(maxlevel, c.ext->va->mergelevel);
- return; // don't re-render
- }
-
- if(c.children)
- {
- neighbourstack[++neighbourdepth] = c.children;
- c.escaped = 0;
- loopi(8)
- {
- ivec o(i, co, size/2);
- int level = -1;
- rendercube(c.children[i], o, size/2, csi-1, level);
- if(level >= csi)
- c.escaped |= 1<<i;
- maxlevel = max(maxlevel, level);
- }
- --neighbourdepth;
-
- if(csi <= MAXMERGELEVEL && vamerges[csi].length()) addmergedverts(csi, co);
-
- if(c.ext)
- {
- if(c.ext->ents && c.ext->ents->mapmodels.length()) vc.mapmodels.add(c.ext->ents);
- }
- return;
- }
-
- genskyfaces(c, co, size);
-
- if(!isempty(c))
- {
- gencubeverts(c, co, size, csi);
- if(c.merged) maxlevel = max(maxlevel, genmergedfaces(c, co, size));
- }
- if(c.material != MAT_AIR) genmatsurfs(c, co, size, vc.matsurfs);
-
- if(c.ext)
- {
- if(c.ext->ents && c.ext->ents->mapmodels.length()) vc.mapmodels.add(c.ext->ents);
- }
-
- if(csi <= MAXMERGELEVEL && vamerges[csi].length()) addmergedverts(csi, co);
+ //if(size<=16) return;
+ if(c.ext && c.ext->va)
+ {
+ maxlevel = max(maxlevel, c.ext->va->mergelevel);
+ return; // don't re-render
+ }
+
+ if(c.children)
+ {
+ neighbourstack[++neighbourdepth] = c.children;
+ c.escaped = 0;
+ loopi(8)
+ {
+ ivec o(i, co, size/2);
+ int level = -1;
+ rendercube(c.children[i], o, size/2, csi-1, level);
+ if(level >= csi)
+ c.escaped |= 1<<i;
+ maxlevel = max(maxlevel, level);
+ }
+ --neighbourdepth;
+
+ if(csi <= MAXMERGELEVEL && vamerges[csi].length()) addmergedverts(csi, co);
+
+ if(c.ext)
+ {
+ if(c.ext->ents && c.ext->ents->mapmodels.length()) vc.mapmodels.add(c.ext->ents);
+ }
+ return;
+ }
+
+ genskyfaces(c, co, size);
+
+ if(!isempty(c))
+ {
+ gencubeverts(c, co, size, csi);
+ if(c.merged) maxlevel = max(maxlevel, genmergedfaces(c, co, size));
+ }
+ if(c.material != MAT_AIR) genmatsurfs(c, co, size, vc.matsurfs);
+
+ if(c.ext)
+ {
+ if(c.ext->ents && c.ext->ents->mapmodels.length()) vc.mapmodels.add(c.ext->ents);
+ }
+
+ if(csi <= MAXMERGELEVEL && vamerges[csi].length()) addmergedverts(csi, co);
}
void calcgeombb(const ivec &co, int size, ivec &bbmin, ivec &bbmax)
{
- vec vmin(co), vmax = vmin;
- vmin.add(size);
-
- loopv(vc.verts)
- {
- const vec &v = vc.verts[i].pos;
- vmin.min(v);
- vmax.max(v);
- }
-
- bbmin = ivec(vmin.mul(8)).shr(3);
- bbmax = ivec(vmax.mul(8)).add(7).shr(3);
+ vec vmin(co), vmax = vmin;
+ vmin.add(size);
+
+ loopv(vc.verts)
+ {
+ const vec &v = vc.verts[i].pos;
+ vmin.min(v);
+ vmax.max(v);
+ }
+
+ bbmin = ivec(vmin.mul(8)).shr(3);
+ bbmax = ivec(vmax.mul(8)).add(7).shr(3);
}
void calcmatbb(const ivec &co, int size, ivec &bbmin, ivec &bbmax)
{
- bbmax = co;
- (bbmin = bbmax).add(size);
- loopv(vc.matsurfs)
- {
- materialsurface &m = vc.matsurfs[i];
- switch(m.material&MATF_VOLUME)
- {
- case MAT_WATER:
- case MAT_GLASS:
- case MAT_LAVA:
- break;
-
- default:
- continue;
- }
-
- int dim = dimension(m.orient),
- r = R[dim],
- c = C[dim];
- bbmin[dim] = min(bbmin[dim], m.o[dim]);
- bbmax[dim] = max(bbmax[dim], m.o[dim]);
-
- bbmin[r] = min(bbmin[r], m.o[r]);
- bbmax[r] = max(bbmax[r], m.o[r] + m.rsize);
-
- bbmin[c] = min(bbmin[c], m.o[c]);
- bbmax[c] = max(bbmax[c], m.o[c] + m.csize);
- }
+ bbmax = co;
+ (bbmin = bbmax).add(size);
+ loopv(vc.matsurfs)
+ {
+ materialsurface &m = vc.matsurfs[i];
+ switch(m.material&MATF_VOLUME)
+ {
+ case MAT_WATER:
+ case MAT_GLASS:
+ case MAT_LAVA:
+ break;
+
+ default:
+ continue;
+ }
+
+ int dim = dimension(m.orient),
+ r = R[dim],
+ c = C[dim];
+ bbmin[dim] = min(bbmin[dim], m.o[dim]);
+ bbmax[dim] = max(bbmax[dim], m.o[dim]);
+
+ bbmin[r] = min(bbmin[r], m.o[r]);
+ bbmax[r] = max(bbmax[r], m.o[r] + m.rsize);
+
+ bbmin[c] = min(bbmin[c], m.o[c]);
+ bbmax[c] = max(bbmax[c], m.o[c] + m.csize);
+ }
}
void setva(cube &c, const ivec &co, int size, int csi)
{
- ASSERT(size <= 0x1000);
+ ASSERT(size <= 0x1000);
- int vamergeoffset[MAXMERGELEVEL+1];
- loopi(MAXMERGELEVEL+1) vamergeoffset[i] = vamerges[i].length();
+ int vamergeoffset[MAXMERGELEVEL+1];
+ loopi(MAXMERGELEVEL+1) vamergeoffset[i] = vamerges[i].length();
- vc.origin = co;
- vc.size = size;
+ vc.origin = co;
+ vc.size = size;
- shadowmapmin = vec(co).add(size);
- shadowmapmax = vec(co);
+ shadowmapmin = vec(co).add(size);
+ shadowmapmax = vec(co);
- int maxlevel = -1;
- rendercube(c, co, size, csi, maxlevel);
+ int maxlevel = -1;
+ rendercube(c, co, size, csi, maxlevel);
- ivec bbmin, bbmax;
+ ivec bbmin, bbmax;
- calcgeombb(co, size, bbmin, bbmax);
+ calcgeombb(co, size, bbmin, bbmax);
- addskyverts(co, size);
+ addskyverts(co, size);
- if(size == min(0x1000, worldsize/2) || !vc.emptyva())
- {
- vtxarray *va = newva(co, size);
- ext(c).va = va;
- va->geommin = bbmin;
- va->geommax = bbmax;
- calcmatbb(co, size, va->matmin, va->matmax);
- va->shadowmapmin = ivec(shadowmapmin.mul(8)).shr(3);
- va->shadowmapmax = ivec(shadowmapmax.mul(8)).add(7).shr(3);
- va->hasmerges = vahasmerges;
- va->mergelevel = vamergemax;
- }
- else
- {
- loopi(MAXMERGELEVEL+1) vamerges[i].setsize(vamergeoffset[i]);
- }
+ if(size == min(0x1000, worldsize/2) || !vc.emptyva())
+ {
+ vtxarray *va = newva(co, size);
+ ext(c).va = va;
+ va->geommin = bbmin;
+ va->geommax = bbmax;
+ calcmatbb(co, size, va->matmin, va->matmax);
+ va->shadowmapmin = ivec(shadowmapmin.mul(8)).shr(3);
+ va->shadowmapmax = ivec(shadowmapmax.mul(8)).add(7).shr(3);
+ va->hasmerges = vahasmerges;
+ va->mergelevel = vamergemax;
+ }
+ else
+ {
+ loopi(MAXMERGELEVEL+1) vamerges[i].setsize(vamergeoffset[i]);
+ }
- vc.clear();
+ vc.clear();
}
static inline int setcubevisibility(cube &c, const ivec &co, int size)
{
- int numvis = 0, vismask = 0, collidemask = 0, checkmask = 0;
- loopi(6)
- {
- int facemask = classifyface(c, i, co, size);
- if(facemask&1)
- {
- vismask |= 1<<i;
- if(c.merged&(1<<i))
- {
- if(c.ext && c.ext->surfaces[i].numverts&MAXFACEVERTS) numvis++;
- }
- else
- {
- numvis++;
- if(c.texture[i] != DEFAULT_SKY && !(c.ext && c.ext->surfaces[i].numverts&MAXFACEVERTS)) checkmask |= 1<<i;
- }
- }
- if(facemask&2 && collideface(c, i)) collidemask |= 1<<i;
- }
- c.visible = collidemask | (vismask ? (vismask != collidemask ? (checkmask ? 0x80|0x40 : 0x80) : 0x40) : 0);
- return numvis;
+ int numvis = 0, vismask = 0, collidemask = 0, checkmask = 0;
+ loopi(6)
+ {
+ int facemask = classifyface(c, i, co, size);
+ if(facemask&1)
+ {
+ vismask |= 1<<i;
+ if(c.merged&(1<<i))
+ {
+ if(c.ext && c.ext->surfaces[i].numverts&MAXFACEVERTS) numvis++;
+ }
+ else
+ {
+ numvis++;
+ if(c.texture[i] != DEFAULT_SKY && !(c.ext && c.ext->surfaces[i].numverts&MAXFACEVERTS)) checkmask |= 1<<i;
+ }
+ }
+ if(facemask&2 && collideface(c, i)) collidemask |= 1<<i;
+ }
+ c.visible = collidemask | (vismask ? (vismask != collidemask ? (checkmask ? 0x80|0x40 : 0x80) : 0x40) : 0);
+ return numvis;
}
VARF(vafacemax, 64, 384, 256*256, allchanged());
@@ -1506,207 +1506,206 @@ VARF(vacubesize, 32, 128, 0x1000, allchanged());
int updateva(cube *c, const ivec &co, int size, int csi)
{
- progress("recalculating geometry...");
- int ccount = 0, cmergemax = vamergemax, chasmerges = vahasmerges;
- neighbourstack[++neighbourdepth] = c;
- loopi(8) // counting number of semi-solid/solid children cubes
- {
- int count = 0, childpos = varoot.length();
- ivec o(i, co, size);
- vamergemax = 0;
- vahasmerges = 0;
- if(c[i].ext && c[i].ext->va)
- {
- varoot.add(c[i].ext->va);
- if(c[i].ext->va->hasmerges&MERGE_ORIGIN) findmergedfaces(c[i], o, size, csi, csi);
- }
- else
- {
- if(c[i].children) count += updateva(c[i].children, o, size/2, csi-1);
- else
- {
- if(!isempty(c[i])) count += setcubevisibility(c[i], o, size);
- count += hasskyfaces(c[i], o, size);
- }
- int tcount = count + (csi <= MAXMERGELEVEL ? vamerges[csi].length() : 0);
- if(tcount > vafacemax || (tcount >= vafacemin && size >= vacubesize) || size == min(0x1000, worldsize/2))
- {
- loadprogress = clamp(recalcprogress/float(allocnodes), 0.0f, 1.0f);
- setva(c[i], o, size, csi);
- if(c[i].ext && c[i].ext->va)
- {
- while(varoot.length() > childpos)
- {
- vtxarray *child = varoot.pop();
- c[i].ext->va->children.add(child);
- child->parent = c[i].ext->va;
- }
- varoot.add(c[i].ext->va);
- if(vamergemax > size)
- {
- cmergemax = max(cmergemax, vamergemax);
- chasmerges |= vahasmerges&~MERGE_USE;
- }
- continue;
- }
- else count = 0;
- }
- }
- if(csi+1 <= MAXMERGELEVEL && vamerges[csi].length()) vamerges[csi+1].move(vamerges[csi]);
- cmergemax = max(cmergemax, vamergemax);
- chasmerges |= vahasmerges;
- ccount += count;
- }
- --neighbourdepth;
- vamergemax = cmergemax;
- vahasmerges = chasmerges;
-
- return ccount;
+ progress("recalculating geometry...");
+ int ccount = 0, cmergemax = vamergemax, chasmerges = vahasmerges;
+ neighbourstack[++neighbourdepth] = c;
+ loopi(8) // counting number of semi-solid/solid children cubes
+ {
+ int count = 0, childpos = varoot.length();
+ ivec o(i, co, size);
+ vamergemax = 0;
+ vahasmerges = 0;
+ if(c[i].ext && c[i].ext->va)
+ {
+ varoot.add(c[i].ext->va);
+ if(c[i].ext->va->hasmerges&MERGE_ORIGIN) findmergedfaces(c[i], o, size, csi, csi);
+ }
+ else
+ {
+ if(c[i].children) count += updateva(c[i].children, o, size/2, csi-1);
+ else
+ {
+ if(!isempty(c[i])) count += setcubevisibility(c[i], o, size);
+ count += hasskyfaces(c[i], o, size);
+ }
+ int tcount = count + (csi <= MAXMERGELEVEL ? vamerges[csi].length() : 0);
+ if(tcount > vafacemax || (tcount >= vafacemin && size >= vacubesize) || size == min(0x1000, worldsize/2))
+ {
+ loadprogress = clamp(recalcprogress/float(allocnodes), 0.0f, 1.0f);
+ setva(c[i], o, size, csi);
+ if(c[i].ext && c[i].ext->va)
+ {
+ while(varoot.length() > childpos)
+ {
+ vtxarray *child = varoot.pop();
+ c[i].ext->va->children.add(child);
+ child->parent = c[i].ext->va;
+ }
+ varoot.add(c[i].ext->va);
+ if(vamergemax > size)
+ {
+ cmergemax = max(cmergemax, vamergemax);
+ chasmerges |= vahasmerges&~MERGE_USE;
+ }
+ continue;
+ }
+ else count = 0;
+ }
+ }
+ if(csi+1 <= MAXMERGELEVEL && vamerges[csi].length()) vamerges[csi+1].move(vamerges[csi]);
+ cmergemax = max(cmergemax, vamergemax);
+ chasmerges |= vahasmerges;
+ ccount += count;
+ }
+ --neighbourdepth;
+ vamergemax = cmergemax;
+ vahasmerges = chasmerges;
+
+ return ccount;
}
void addtjoint(const edgegroup &g, const cubeedge &e, int offset)
{
- int vcoord = (g.slope[g.axis]*offset + g.origin[g.axis]) & 0x7FFF;
- tjoint &tj = tjoints.add();
- tj.offset = vcoord / g.slope[g.axis];
- tj.edge = e.index;
-
- int prev = -1, cur = ext(*e.c).tjoints;
- while(cur >= 0)
- {
- tjoint &o = tjoints[cur];
- if(tj.edge < o.edge || (tj.edge==o.edge && (e.flags&CE_FLIP ? tj.offset > o.offset : tj.offset < o.offset))) break;
- prev = cur;
- cur = o.next;
- }
-
- tj.next = cur;
- if(prev < 0) e.c->ext->tjoints = tjoints.length()-1;
- else tjoints[prev].next = tjoints.length()-1;
+ int vcoord = (g.slope[g.axis]*offset + g.origin[g.axis]) & 0x7FFF;
+ tjoint &tj = tjoints.add();
+ tj.offset = vcoord / g.slope[g.axis];
+ tj.edge = e.index;
+
+ int prev = -1, cur = ext(*e.c).tjoints;
+ while(cur >= 0)
+ {
+ tjoint &o = tjoints[cur];
+ if(tj.edge < o.edge || (tj.edge==o.edge && (e.flags&CE_FLIP ? tj.offset > o.offset : tj.offset < o.offset))) break;
+ prev = cur;
+ cur = o.next;
+ }
+
+ tj.next = cur;
+ if(prev < 0) e.c->ext->tjoints = tjoints.length()-1;
+ else tjoints[prev].next = tjoints.length()-1;
}
void findtjoints(int cur, const edgegroup &g)
{
- int active = -1;
- while(cur >= 0)
- {
- cubeedge &e = cubeedges[cur];
- int prevactive = -1, curactive = active;
- while(curactive >= 0)
- {
- cubeedge &a = cubeedges[curactive];
- if(a.offset+a.size <= e.offset)
- {
- if(prevactive >= 0) cubeedges[prevactive].next = a.next;
- else active = a.next;
- }
- else
- {
- prevactive = curactive;
- if(!(a.flags&CE_DUP))
- {
- if(e.flags&CE_START && e.offset > a.offset && e.offset < a.offset+a.size)
- addtjoint(g, a, e.offset);
- if(e.flags&CE_END && e.offset+e.size > a.offset && e.offset+e.size < a.offset+a.size)
- addtjoint(g, a, e.offset+e.size);
- }
- if(!(e.flags&CE_DUP))
- {
- if(a.flags&CE_START && a.offset > e.offset && a.offset < e.offset+e.size)
- addtjoint(g, e, a.offset);
- if(a.flags&CE_END && a.offset+a.size > e.offset && a.offset+a.size < e.offset+e.size)
- addtjoint(g, e, a.offset+a.size);
- }
- }
- curactive = a.next;
- }
- int next = e.next;
- e.next = active;
- active = cur;
- cur = next;
- }
+ int active = -1;
+ while(cur >= 0)
+ {
+ cubeedge &e = cubeedges[cur];
+ int prevactive = -1, curactive = active;
+ while(curactive >= 0)
+ {
+ cubeedge &a = cubeedges[curactive];
+ if(a.offset+a.size <= e.offset)
+ {
+ if(prevactive >= 0) cubeedges[prevactive].next = a.next;
+ else active = a.next;
+ }
+ else
+ {
+ prevactive = curactive;
+ if(!(a.flags&CE_DUP))
+ {
+ if(e.flags&CE_START && e.offset > a.offset && e.offset < a.offset+a.size)
+ addtjoint(g, a, e.offset);
+ if(e.flags&CE_END && e.offset+e.size > a.offset && e.offset+e.size < a.offset+a.size)
+ addtjoint(g, a, e.offset+e.size);
+ }
+ if(!(e.flags&CE_DUP))
+ {
+ if(a.flags&CE_START && a.offset > e.offset && a.offset < e.offset+e.size)
+ addtjoint(g, e, a.offset);
+ if(a.flags&CE_END && a.offset+a.size > e.offset && a.offset+a.size < e.offset+e.size)
+ addtjoint(g, e, a.offset+a.size);
+ }
+ }
+ curactive = a.next;
+ }
+ int next = e.next;
+ e.next = active;
+ active = cur;
+ cur = next;
+ }
}
void findtjoints()
{
- recalcprogress = 0;
- gencubeedges();
- tjoints.setsize(0);
- enumeratekt(edgegroups, edgegroup, g, int, e, findtjoints(e, g));
- cubeedges.setsize(0);
- edgegroups.clear();
+ recalcprogress = 0;
+ gencubeedges();
+ tjoints.setsize(0);
+ enumeratekt(edgegroups, edgegroup, g, int, e, findtjoints(e, g));
+ cubeedges.setsize(0);
+ edgegroups.clear();
}
-void octarender() // creates va s for all leaf cubes that don't already have them
+void octarender() // creates va s for all leaf cubes that don't already have them
{
- int csi = 0;
- while(1<<csi < worldsize) csi++;
-
- recalcprogress = 0;
- varoot.setsize(0);
- updateva(worldroot, ivec(0, 0, 0), worldsize/2, csi-1);
- loadprogress = 0;
- flushvbo();
-
- explicitsky = 0;
- skyarea = 0;
- loopv(valist)
- {
- vtxarray *va = valist[i];
- explicitsky += va->explicitsky;
- skyarea += va->skyarea;
- }
-
- visibleva = NULL;
+ int csi = 0;
+ while(1<<csi < worldsize) csi++;
+
+ recalcprogress = 0;
+ varoot.setsize(0);
+ updateva(worldroot, ivec(0, 0, 0), worldsize/2, csi-1);
+ loadprogress = 0;
+ flushvbo();
+
+ explicitsky = 0;
+ skyarea = 0;
+ loopv(valist)
+ {
+ vtxarray *va = valist[i];
+ explicitsky += va->explicitsky;
+ skyarea += va->skyarea;
+ }
+
+ visibleva = NULL;
}
void precachetextures()
{
- vector<int> texs;
- loopv(valist)
- {
- vtxarray *va = valist[i];
- loopj(va->texs + va->blends) if(texs.find(va->eslist[j].texture) < 0) texs.add(va->eslist[j].texture);
- }
- loopv(texs)
- {
- loadprogress = float(i+1)/texs.length();
- lookupvslot(texs[i]);
- }
- loadprogress = 0;
+ vector<int> texs;
+ loopv(valist)
+ {
+ vtxarray *va = valist[i];
+ loopj(va->texs + va->blends) if(texs.find(va->eslist[j].texture) < 0) texs.add(va->eslist[j].texture);
+ }
+ loopv(texs)
+ {
+ loadprogress = float(i+1)/texs.length();
+ lookupvslot(texs[i]);
+ }
+ loadprogress = 0;
}
void allchanged(bool load)
{
- renderprogress(0, "clearing vertex arrays...");
- clearvas(worldroot);
- resetqueries();
- resetclipplanes();
- if(load)
- {
- setupsky();
- initenvmaps();
- }
- guessshadowdir();
- entitiesinoctanodes();
- tjoints.setsize(0);
- if(filltjoints) findtjoints();
- octarender();
- if(load) precachetextures();
- setupmaterials();
- invalidatepostfx();
- updatevabbs(true);
- lightents();
- if(load)
- {
- seedparticles();
- drawtextures();
- }
+ renderprogress(0, "clearing vertex arrays...");
+ clearvas(worldroot);
+ resetqueries();
+ resetclipplanes();
+ if(load)
+ {
+ setupsky();
+ initenvmaps();
+ }
+ guessshadowdir();
+ entitiesinoctanodes();
+ tjoints.setsize(0);
+ if(filltjoints) findtjoints();
+ octarender();
+ if(load) precachetextures();
+ setupmaterials();
+ updatevabbs(true);
+ lightents();
+ if(load)
+ {
+ seedparticles();
+ drawtextures();
+ }
}
void recalc()
{
- allchanged(true);
+ allchanged(true);
}
COMMAND(recalc, "");
diff --git a/src/engine/physics.cpp b/src/engine/physics.cpp
index 4c23739..19eee8b 100644
--- a/src/engine/physics.cpp
+++ b/src/engine/physics.cpp
@@ -12,88 +12,88 @@ static int clipcacheversion = -2;
static inline clipplanes &getclipplanes(const cube &c, const ivec &o, int size, bool collide = true, int offset = 0)
{
- clipplanes &p = clipcache[int(&c - worldroot)&(MAXCLIPPLANES-1)];
- if(p.owner != &c || p.version != clipcacheversion+offset)
- {
- p.owner = &c;
- p.version = clipcacheversion+offset;
- genclipplanes(c, o, size, p, collide);
- }
- return p;
+ clipplanes &p = clipcache[int(&c - worldroot)&(MAXCLIPPLANES-1)];
+ if(p.owner != &c || p.version != clipcacheversion+offset)
+ {
+ p.owner = &c;
+ p.version = clipcacheversion+offset;
+ genclipplanes(c, o, size, p, collide);
+ }
+ return p;
}
void resetclipplanes()
{
- clipcacheversion += 2;
- if(!clipcacheversion)
- {
- memclear(clipcache);
- clipcacheversion = 2;
- }
+ clipcacheversion += 2;
+ if(!clipcacheversion)
+ {
+ memclear(clipcache);
+ clipcacheversion = 2;
+ }
}
///////////////////////// ray - cube collision ///////////////////////////////////////////////
#define INTERSECTPLANES(setentry, exit) \
- float enterdist = -1e16f, exitdist = 1e16f; \
- loopi(p.size) \
- { \
- float pdist = p.p[i].dist(v), facing = ray.dot(p.p[i]); \
- if(facing < 0) \
- { \
- pdist /= -facing; \
- if(pdist > enterdist) \
- { \
- if(pdist > exitdist) exit; \
- enterdist = pdist; \
- setentry; \
- } \
- } \
- else if(facing > 0) \
- { \
- pdist /= -facing; \
- if(pdist < exitdist) \
- { \
- if(pdist < enterdist) exit; \
- exitdist = pdist; \
- } \
- } \
- else if(pdist > 0) exit; \
- }
+ float enterdist = -1e16f, exitdist = 1e16f; \
+ loopi(p.size) \
+ { \
+ float pdist = p.p[i].dist(v), facing = ray.dot(p.p[i]); \
+ if(facing < 0) \
+ { \
+ pdist /= -facing; \
+ if(pdist > enterdist) \
+ { \
+ if(pdist > exitdist) exit; \
+ enterdist = pdist; \
+ setentry; \
+ } \
+ } \
+ else if(facing > 0) \
+ { \
+ pdist /= -facing; \
+ if(pdist < exitdist) \
+ { \
+ if(pdist < enterdist) exit; \
+ exitdist = pdist; \
+ } \
+ } \
+ else if(pdist > 0) exit; \
+ }
#define INTERSECTBOX(setentry, exit) \
- loop(i, 3) \
- { \
- if(ray[i]) \
- { \
- float prad = fabs(p.r[i] * invray[i]), pdist = (p.o[i] - v[i]) * invray[i], pmin = pdist - prad, pmax = pdist + prad; \
- if(pmin > enterdist) \
- { \
- if(pmin > exitdist) exit; \
- enterdist = pmin; \
- setentry; \
- } \
- if(pmax < exitdist) \
- { \
- if(pmax < enterdist) exit; \
- exitdist = pmax; \
- } \
- } \
- else if(v[i] < p.o[i]-p.r[i] || v[i] > p.o[i]+p.r[i]) exit; \
- }
+ loop(i, 3) \
+ { \
+ if(ray[i]) \
+ { \
+ float prad = fabs(p.r[i] * invray[i]), pdist = (p.o[i] - v[i]) * invray[i], pmin = pdist - prad, pmax = pdist + prad; \
+ if(pmin > enterdist) \
+ { \
+ if(pmin > exitdist) exit; \
+ enterdist = pmin; \
+ setentry; \
+ } \
+ if(pmax < exitdist) \
+ { \
+ if(pmax < enterdist) exit; \
+ exitdist = pmax; \
+ } \
+ } \
+ else if(v[i] < p.o[i]-p.r[i] || v[i] > p.o[i]+p.r[i]) exit; \
+ }
vec hitsurface;
static inline bool raycubeintersect(const clipplanes &p, const cube &c, const vec &v, const vec &ray, const vec &invray, float &dist)
{
- int entry = -1, bbentry = -1;
- INTERSECTPLANES(entry = i, return false);
- INTERSECTBOX(bbentry = i, return false);
- if(exitdist < 0) return false;
- dist = max(enterdist+0.1f, 0.0f);
- if(bbentry>=0) { hitsurface = vec(0, 0, 0); hitsurface[bbentry] = ray[bbentry]>0 ? -1 : 1; }
- else hitsurface = p.p[entry];
- return true;
+ int entry = -1, bbentry = -1;
+ INTERSECTPLANES(entry = i, return false);
+ INTERSECTBOX(bbentry = i, return false);
+ if(exitdist < 0) return false;
+ dist = max(enterdist+0.1f, 0.0f);
+ if(bbentry>=0) { hitsurface = vec(0, 0, 0); hitsurface[bbentry] = ray[bbentry]>0 ? -1 : 1; }
+ else hitsurface = p.p[entry];
+ return true;
}
extern void entselectionbox(const entity &e, vec &eo, vec &es);
@@ -102,257 +102,257 @@ int hitent, hitorient;
static float disttoent(octaentities *oc, const vec &o, const vec &ray, float radius, int mode, extentity *t)
{
- vec eo, es;
- int orient = -1;
- float dist = radius, f = 0.0f;
- const vector<extentity *> &ents = entities::getents();
-
- #define entintersect(mask, type, func) {\
- if((mode&(mask))==(mask)) loopv(oc->type) \
- { \
- extentity &e = *ents[oc->type[i]]; \
- if(!(e.flags&EF_OCTA) || &e==t) continue; \
- func; \
- if(f<dist && f>0 && vec(ray).mul(f).add(o).insidebb(oc->o, oc->size)) \
- { \
- hitentdist = dist = f; \
- hitent = oc->type[i]; \
- hitorient = orient; \
- } \
- } \
- }
-
- entintersect(RAY_POLY, mapmodels,
- if(!mmintersect(e, o, ray, radius, mode, f)) continue;
- );
-
- entintersect(RAY_ENTS, other,
- entselectionbox(e, eo, es);
- if(!rayboxintersect(eo, es, o, ray, f, orient)) continue;
- );
-
- entintersect(RAY_ENTS, mapmodels,
- entselectionbox(e, eo, es);
- if(!rayboxintersect(eo, es, o, ray, f, orient)) continue;
- );
-
- return dist;
+ vec eo, es;
+ int orient = -1;
+ float dist = radius, f = 0.0f;
+ const vector<extentity *> &ents = entities::getents();
+
+ #define entintersect(mask, type, func) {\
+ if((mode&(mask))==(mask)) loopv(oc->type) \
+ { \
+ extentity &e = *ents[oc->type[i]]; \
+ if(!(e.flags&EF_OCTA) || &e==t) continue; \
+ func; \
+ if(f<dist && f>0 && vec(ray).mul(f).add(o).insidebb(oc->o, oc->size)) \
+ { \
+ hitentdist = dist = f; \
+ hitent = oc->type[i]; \
+ hitorient = orient; \
+ } \
+ } \
+ }
+
+ entintersect(RAY_POLY, mapmodels,
+ if(!mmintersect(e, o, ray, radius, mode, f)) continue;
+ );
+
+ entintersect(RAY_ENTS, other,
+ entselectionbox(e, eo, es);
+ if(!rayboxintersect(eo, es, o, ray, f, orient)) continue;
+ );
+
+ entintersect(RAY_ENTS, mapmodels,
+ entselectionbox(e, eo, es);
+ if(!rayboxintersect(eo, es, o, ray, f, orient)) continue;
+ );
+
+ return dist;
}
static float disttooutsideent(const vec &o, const vec &ray, float radius, int mode, extentity *t)
{
- vec eo, es;
- int orient;
- float dist = radius, f = 0.0f;
- const vector<extentity *> &ents = entities::getents();
- loopv(outsideents)
- {
- extentity &e = *ents[outsideents[i]];
- if(!(e.flags&EF_OCTA) || &e==t) continue;
- entselectionbox(e, eo, es);
- if(!rayboxintersect(eo, es, o, ray, f, orient)) continue;
- if(f<dist && f>0)
- {
- hitentdist = dist = f;
- hitent = outsideents[i];
- hitorient = orient;
- }
- }
- return dist;
+ vec eo, es;
+ int orient;
+ float dist = radius, f = 0.0f;
+ const vector<extentity *> &ents = entities::getents();
+ loopv(outsideents)
+ {
+ extentity &e = *ents[outsideents[i]];
+ if(!(e.flags&EF_OCTA) || &e==t) continue;
+ entselectionbox(e, eo, es);
+ if(!rayboxintersect(eo, es, o, ray, f, orient)) continue;
+ if(f<dist && f>0)
+ {
+ hitentdist = dist = f;
+ hitent = outsideents[i];
+ hitorient = orient;
+ }
+ }
+ return dist;
}
// optimized shadow version
static float shadowent(octaentities *oc, const vec &o, const vec &ray, float radius, int mode, extentity *t)
{
- float dist = radius, f = 0.0f;
- const vector<extentity *> &ents = entities::getents();
- loopv(oc->mapmodels)
- {
- extentity &e = *ents[oc->mapmodels[i]];
- if(!(e.flags&EF_OCTA) || &e==t) continue;
- if(!mmintersect(e, o, ray, radius, mode, f)) continue;
- if(f>0 && f<dist) dist = f;
- }
- return dist;
+ float dist = radius, f = 0.0f;
+ const vector<extentity *> &ents = entities::getents();
+ loopv(oc->mapmodels)
+ {
+ extentity &e = *ents[oc->mapmodels[i]];
+ if(!(e.flags&EF_OCTA) || &e==t) continue;
+ if(!mmintersect(e, o, ray, radius, mode, f)) continue;
+ if(f>0 && f<dist) dist = f;
+ }
+ return dist;
}
#define INITRAYCUBE \
- float dist = 0, dent = radius > 0 ? radius : 1e16f; \
- vec v(o), invray(ray.x ? 1/ray.x : 1e16f, ray.y ? 1/ray.y : 1e16f, ray.z ? 1/ray.z : 1e16f); \
- cube *levels[20]; \
- levels[worldscale] = worldroot; \
- int lshift = worldscale, elvl = mode&RAY_BB ? worldscale : 0; \
- ivec lsizemask(invray.x>0 ? 1 : 0, invray.y>0 ? 1 : 0, invray.z>0 ? 1 : 0); \
+ float dist = 0, dent = radius > 0 ? radius : 1e16f; \
+ vec v(o), invray(ray.x ? 1/ray.x : 1e16f, ray.y ? 1/ray.y : 1e16f, ray.z ? 1/ray.z : 1e16f); \
+ cube *levels[20]; \
+ levels[worldscale] = worldroot; \
+ int lshift = worldscale, elvl = mode&RAY_BB ? worldscale : 0; \
+ ivec lsizemask(invray.x>0 ? 1 : 0, invray.y>0 ? 1 : 0, invray.z>0 ? 1 : 0); \
#define CHECKINSIDEWORLD \
- if(!insideworld(o)) \
- { \
- float disttoworld = 0, exitworld = 1e16f; \
- loopi(3) \
- { \
- float c = v[i]; \
- if(c<0 || c>=worldsize) \
- { \
- float d = ((invray[i]>0?0:worldsize)-c)*invray[i]; \
- if(d<0) return (radius>0?radius:-1); \
- disttoworld = max(disttoworld, 0.1f + d); \
- } \
- float e = ((invray[i]>0?worldsize:0)-c)*invray[i]; \
- exitworld = min(exitworld, e); \
- } \
- if(disttoworld > exitworld) return (radius>0?radius:-1); \
- v.add(vec(ray).mul(disttoworld)); \
- dist += disttoworld; \
- }
+ if(!insideworld(o)) \
+ { \
+ float disttoworld = 0, exitworld = 1e16f; \
+ loopi(3) \
+ { \
+ float c = v[i]; \
+ if(c<0 || c>=worldsize) \
+ { \
+ float d = ((invray[i]>0?0:worldsize)-c)*invray[i]; \
+ if(d<0) return (radius>0?radius:-1); \
+ disttoworld = max(disttoworld, 0.1f + d); \
+ } \
+ float e = ((invray[i]>0?worldsize:0)-c)*invray[i]; \
+ exitworld = min(exitworld, e); \
+ } \
+ if(disttoworld > exitworld) return (radius>0?radius:-1); \
+ v.add(vec(ray).mul(disttoworld)); \
+ dist += disttoworld; \
+ }
#define DOWNOCTREE(disttoent, earlyexit) \
- cube *lc = levels[lshift]; \
- for(;;) \
- { \
- lshift--; \
- lc += octastep(x, y, z, lshift); \
- if(lc->ext && lc->ext->ents && lshift < elvl) \
- { \
- float edist = disttoent(lc->ext->ents, o, ray, dent, mode, t); \
- if(edist < dent) \
- { \
- earlyexit return min(edist, dist); \
- elvl = lshift; \
- dent = min(dent, edist); \
- } \
- } \
- if(lc->children==NULL) break; \
- lc = lc->children; \
- levels[lshift] = lc; \
- }
+ cube *lc = levels[lshift]; \
+ for(;;) \
+ { \
+ lshift--; \
+ lc += octastep(x, y, z, lshift); \
+ if(lc->ext && lc->ext->ents && lshift < elvl) \
+ { \
+ float edist = disttoent(lc->ext->ents, o, ray, dent, mode, t); \
+ if(edist < dent) \
+ { \
+ earlyexit return min(edist, dist); \
+ elvl = lshift; \
+ dent = min(dent, edist); \
+ } \
+ } \
+ if(lc->children==NULL) break; \
+ lc = lc->children; \
+ levels[lshift] = lc; \
+ }
#define FINDCLOSEST(xclosest, yclosest, zclosest) \
- float dx = (lo.x+(lsizemask.x<<lshift)-v.x)*invray.x, \
- dy = (lo.y+(lsizemask.y<<lshift)-v.y)*invray.y, \
- dz = (lo.z+(lsizemask.z<<lshift)-v.z)*invray.z; \
- float disttonext = dx; \
- xclosest; \
- if(dy < disttonext) { disttonext = dy; yclosest; } \
- if(dz < disttonext) { disttonext = dz; zclosest; } \
- disttonext += 0.1f; \
- v.add(vec(ray).mul(disttonext)); \
- dist += disttonext;
+ float dx = (lo.x+(lsizemask.x<<lshift)-v.x)*invray.x, \
+ dy = (lo.y+(lsizemask.y<<lshift)-v.y)*invray.y, \
+ dz = (lo.z+(lsizemask.z<<lshift)-v.z)*invray.z; \
+ float disttonext = dx; \
+ xclosest; \
+ if(dy < disttonext) { disttonext = dy; yclosest; } \
+ if(dz < disttonext) { disttonext = dz; zclosest; } \
+ disttonext += 0.1f; \
+ v.add(vec(ray).mul(disttonext)); \
+ dist += disttonext;
#define UPOCTREE(exitworld) \
- x = int(v.x); \
- y = int(v.y); \
- z = int(v.z); \
- uint diff = uint(lo.x^x)|uint(lo.y^y)|uint(lo.z^z); \
- if(diff >= uint(worldsize)) exitworld; \
- diff >>= lshift; \
- if(!diff) exitworld; \
- do \
- { \
- lshift++; \
- diff >>= 1; \
- } while(diff);
+ x = int(v.x); \
+ y = int(v.y); \
+ z = int(v.z); \
+ uint diff = uint(lo.x^x)|uint(lo.y^y)|uint(lo.z^z); \
+ if(diff >= uint(worldsize)) exitworld; \
+ diff >>= lshift; \
+ if(!diff) exitworld; \
+ do \
+ { \
+ lshift++; \
+ diff >>= 1; \
+ } while(diff);
float raycube(const vec &o, const vec &ray, float radius, int mode, int size, extentity *t)
{
- if(ray.iszero()) return 0;
-
- INITRAYCUBE;
- CHECKINSIDEWORLD;
-
- int closest = -1, x = int(v.x), y = int(v.y), z = int(v.z);
- for(;;)
- {
- DOWNOCTREE(disttoent, if(mode&RAY_SHADOW));
-
- int lsize = 1<<lshift;
-
- cube &c = *lc;
- if((dist>0 || !(mode&RAY_SKIPFIRST)) &&
- (((mode&RAY_CLIPMAT) && isclipped(c.material&MATF_VOLUME)) ||
- ((mode&RAY_EDITMAT) && c.material != MAT_AIR) ||
- (!(mode&RAY_PASS) && lsize==size && !isempty(c)) ||
- isentirelysolid(c) ||
- dent < dist))
- {
- if(closest >= 0) { hitsurface = vec(0, 0, 0); hitsurface[closest] = ray[closest]>0 ? -1 : 1; }
- return min(dent, dist);
- }
-
- ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
-
- if(!isempty(c))
- {
- const clipplanes &p = getclipplanes(c, lo, lsize, false, 1);
- float f = 0;
- if(raycubeintersect(p, c, v, ray, invray, f) && (dist+f>0 || !(mode&RAY_SKIPFIRST)))
- return min(dent, dist+f);
- }
-
- FINDCLOSEST(closest = 0, closest = 1, closest = 2);
-
- if(radius>0 && dist>=radius) return min(dent, dist);
-
- UPOCTREE(return min(dent, radius>0 ? radius : dist));
- }
+ if(ray.iszero()) return 0;
+
+ INITRAYCUBE;
+ CHECKINSIDEWORLD;
+
+ int closest = -1, x = int(v.x), y = int(v.y), z = int(v.z);
+ for(;;)
+ {
+ DOWNOCTREE(disttoent, if(mode&RAY_SHADOW));
+
+ int lsize = 1<<lshift;
+
+ cube &c = *lc;
+ if((dist>0 || !(mode&RAY_SKIPFIRST)) &&
+ (((mode&RAY_CLIPMAT) && isclipped(c.material&MATF_VOLUME)) ||
+ ((mode&RAY_EDITMAT) && c.material != MAT_AIR) ||
+ (!(mode&RAY_PASS) && lsize==size && !isempty(c)) ||
+ isentirelysolid(c) ||
+ dent < dist))
+ {
+ if(closest >= 0) { hitsurface = vec(0, 0, 0); hitsurface[closest] = ray[closest]>0 ? -1 : 1; }
+ return min(dent, dist);
+ }
+
+ ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
+
+ if(!isempty(c))
+ {
+ const clipplanes &p = getclipplanes(c, lo, lsize, false, 1);
+ float f = 0;
+ if(raycubeintersect(p, c, v, ray, invray, f) && (dist+f>0 || !(mode&RAY_SKIPFIRST)))
+ return min(dent, dist+f);
+ }
+
+ FINDCLOSEST(closest = 0, closest = 1, closest = 2);
+
+ if(radius>0 && dist>=radius) return min(dent, dist);
+
+ UPOCTREE(return min(dent, radius>0 ? radius : dist));
+ }
}
// optimized version for lightmap shadowing... every cycle here counts!!!
float shadowray(const vec &o, const vec &ray, float radius, int mode, extentity *t)
{
- INITRAYCUBE;
- CHECKINSIDEWORLD;
-
- int side = O_BOTTOM, x = int(v.x), y = int(v.y), z = int(v.z);
- for(;;)
- {
- DOWNOCTREE(shadowent, );
-
- cube &c = *lc;
- ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
-
- if(!isempty(c) && !(c.material&MAT_ALPHA))
- {
- if(isentirelysolid(c))
- {
- if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
- {
- if(mode&RAY_SKYTEX) return radius;
- }
- else return dist;
- }
- else
- {
- const clipplanes &p = getclipplanes(c, lo, 1<<lshift, false, 1);
- INTERSECTPLANES(side = p.side[i], goto nextcube);
- INTERSECTBOX(side = (i<<1) + 1 - lsizemask[i], goto nextcube);
- if(exitdist >= 0)
- {
- if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
- {
- if(mode&RAY_SKYTEX) return radius;
- }
- else return dist+max(enterdist+0.1f, 0.0f);
- }
- }
- }
-
- nextcube:
- FINDCLOSEST(side = O_RIGHT - lsizemask.x, side = O_FRONT - lsizemask.y, side = O_TOP - lsizemask.z);
-
- if(dist>=radius) return dist;
-
- UPOCTREE(return radius);
- }
+ INITRAYCUBE;
+ CHECKINSIDEWORLD;
+
+ int side = O_BOTTOM, x = int(v.x), y = int(v.y), z = int(v.z);
+ for(;;)
+ {
+ DOWNOCTREE(shadowent, );
+
+ cube &c = *lc;
+ ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
+
+ if(!isempty(c) && !(c.material&MAT_ALPHA))
+ {
+ if(isentirelysolid(c))
+ {
+ if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
+ {
+ if(mode&RAY_SKYTEX) return radius;
+ }
+ else return dist;
+ }
+ else
+ {
+ const clipplanes &p = getclipplanes(c, lo, 1<<lshift, false, 1);
+ INTERSECTPLANES(side = p.side[i], goto nextcube);
+ INTERSECTBOX(side = (i<<1) + 1 - lsizemask[i], goto nextcube);
+ if(exitdist >= 0)
+ {
+ if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
+ {
+ if(mode&RAY_SKYTEX) return radius;
+ }
+ else return dist+max(enterdist+0.1f, 0.0f);
+ }
+ }
+ }
+
+ nextcube:
+ FINDCLOSEST(side = O_RIGHT - lsizemask.x, side = O_FRONT - lsizemask.y, side = O_TOP - lsizemask.z);
+
+ if(dist>=radius) return dist;
+
+ UPOCTREE(return radius);
+ }
}
// thread safe version
struct ShadowRayCache
{
- clipplanes clipcache[MAXCLIPPLANES];
- int version;
+ clipplanes clipcache[MAXCLIPPLANES];
+ int version;
- ShadowRayCache() : version(-1) {}
+ ShadowRayCache() : version(-1) {}
};
ShadowRayCache *newshadowraycache() { return new ShadowRayCache; }
@@ -361,106 +361,106 @@ void freeshadowraycache(ShadowRayCache *&cache) { delete cache; cache = NULL; }
void resetshadowraycache(ShadowRayCache *cache)
{
- cache->version++;
- if(!cache->version)
- {
- memclear(cache->clipcache);
- cache->version = 1;
- }
+ cache->version++;
+ if(!cache->version)
+ {
+ memclear(cache->clipcache);
+ cache->version = 1;
+ }
}
float shadowray(ShadowRayCache *cache, const vec &o, const vec &ray, float radius, int mode, extentity *t)
{
- INITRAYCUBE;
- CHECKINSIDEWORLD;
-
- int side = O_BOTTOM, x = int(v.x), y = int(v.y), z = int(v.z);
- for(;;)
- {
- DOWNOCTREE(shadowent, );
-
- cube &c = *lc;
- ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
-
- if(!isempty(c) && !(c.material&MAT_ALPHA))
- {
- if(isentirelysolid(c))
- {
- if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
- {
- if(mode&RAY_SKYTEX) return radius;
- }
- else return dist;
- }
- else
- {
- clipplanes &p = cache->clipcache[int(&c - worldroot)&(MAXCLIPPLANES-1)];
- if(p.owner != &c || p.version != cache->version) { p.owner = &c; p.version = cache->version; genclipplanes(c, lo, 1<<lshift, p, false); }
- INTERSECTPLANES(side = p.side[i], goto nextcube);
- INTERSECTBOX(side = (i<<1) + 1 - lsizemask[i], goto nextcube);
- if(exitdist >= 0)
- {
- if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
- {
- if(mode&RAY_SKYTEX) return radius;
- }
- else return dist+max(enterdist+0.1f, 0.0f);
- }
- }
- }
-
- nextcube:
- FINDCLOSEST(side = O_RIGHT - lsizemask.x, side = O_FRONT - lsizemask.y, side = O_TOP - lsizemask.z);
-
- if(dist>=radius) return dist;
-
- UPOCTREE(return radius);
- }
+ INITRAYCUBE;
+ CHECKINSIDEWORLD;
+
+ int side = O_BOTTOM, x = int(v.x), y = int(v.y), z = int(v.z);
+ for(;;)
+ {
+ DOWNOCTREE(shadowent, );
+
+ cube &c = *lc;
+ ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
+
+ if(!isempty(c) && !(c.material&MAT_ALPHA))
+ {
+ if(isentirelysolid(c))
+ {
+ if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
+ {
+ if(mode&RAY_SKYTEX) return radius;
+ }
+ else return dist;
+ }
+ else
+ {
+ clipplanes &p = cache->clipcache[int(&c - worldroot)&(MAXCLIPPLANES-1)];
+ if(p.owner != &c || p.version != cache->version) { p.owner = &c; p.version = cache->version; genclipplanes(c, lo, 1<<lshift, p, false); }
+ INTERSECTPLANES(side = p.side[i], goto nextcube);
+ INTERSECTBOX(side = (i<<1) + 1 - lsizemask[i], goto nextcube);
+ if(exitdist >= 0)
+ {
+ if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
+ {
+ if(mode&RAY_SKYTEX) return radius;
+ }
+ else return dist+max(enterdist+0.1f, 0.0f);
+ }
+ }
+ }
+
+ nextcube:
+ FINDCLOSEST(side = O_RIGHT - lsizemask.x, side = O_FRONT - lsizemask.y, side = O_TOP - lsizemask.z);
+
+ if(dist>=radius) return dist;
+
+ UPOCTREE(return radius);
+ }
}
float rayent(const vec &o, const vec &ray, float radius, int mode, int size, int &orient, int &ent)
{
- hitent = -1;
- hitentdist = radius;
- hitorient = -1;
- float dist = raycube(o, ray, radius, mode, size);
- if((mode&RAY_ENTS) == RAY_ENTS)
- {
- float dent = disttooutsideent(o, ray, dist < 0 ? 1e16f : dist, mode, NULL);
- if(dent < 1e15f && (dist < 0 || dent < dist)) dist = dent;
- }
- orient = hitorient;
- ent = hitentdist == dist ? hitent : -1;
- return dist;
+ hitent = -1;
+ hitentdist = radius;
+ hitorient = -1;
+ float dist = raycube(o, ray, radius, mode, size);
+ if((mode&RAY_ENTS) == RAY_ENTS)
+ {
+ float dent = disttooutsideent(o, ray, dist < 0 ? 1e16f : dist, mode, NULL);
+ if(dent < 1e15f && (dist < 0 || dent < dist)) dist = dent;
+ }
+ orient = hitorient;
+ ent = hitentdist == dist ? hitent : -1;
+ return dist;
}
float raycubepos(const vec &o, const vec &ray, vec &hitpos, float radius, int mode, int size)
{
- hitpos = ray;
- float dist = raycube(o, ray, radius, mode, size);
- if(radius>0 && dist>=radius) dist = radius;
- hitpos.mul(dist).add(o);
- return dist;
+ hitpos = ray;
+ float dist = raycube(o, ray, radius, mode, size);
+ if(radius>0 && dist>=radius) dist = radius;
+ hitpos.mul(dist).add(o);
+ return dist;
}
bool raycubelos(const vec &o, const vec &dest, vec &hitpos)
{
- vec ray(dest);
- ray.sub(o);
- float mag = ray.magnitude();
- ray.mul(1/mag);
- float distance = raycubepos(o, ray, hitpos, mag, RAY_CLIPMAT|RAY_POLY);
- return distance >= mag;
+ vec ray(dest);
+ ray.sub(o);
+ float mag = ray.magnitude();
+ ray.mul(1/mag);
+ float distance = raycubepos(o, ray, hitpos, mag, RAY_CLIPMAT|RAY_POLY);
+ return distance >= mag;
}
float rayfloor(const vec &o, vec &floor, int mode, float radius)
{
- if(o.z<=0) return -1;
- hitsurface = vec(0, 0, 1);
- float dist = raycube(o, vec(0, 0, -1), radius, mode);
- if(dist<0 || (radius>0 && dist>=radius)) return dist;
- floor = hitsurface;
- return dist;
+ if(o.z<=0) return -1;
+ hitsurface = vec(0, 0, 1);
+ float dist = raycube(o, vec(0, 0, -1), radius, mode);
+ if(dist<0 || (radius>0 && dist>=radius)) return dist;
+ floor = hitsurface;
+ return dist;
}
///////////////////////// entity collision ///////////////////////////////////////////////
@@ -479,96 +479,96 @@ extern const float GRAVITY = 200.0f;
bool ellipseboxcollide(physent *d, const vec &dir, const vec &o, const vec &center, float yaw, float xr, float yr, float hi, float lo)
{
- float below = (o.z+center.z-lo) - (d->o.z+d->aboveeye),
- above = (d->o.z-d->eyeheight) - (o.z+center.z+hi);
- if(below>=0 || above>=0) return false;
-
- vec yo(d->o);
- yo.sub(o);
- yo.rotate_around_z(-yaw*RAD);
- yo.sub(center);
-
- float dx = clamp(yo.x, -xr, xr) - yo.x, dy = clamp(yo.y, -yr, yr) - yo.y,
- dist = sqrtf(dx*dx + dy*dy) - d->radius;
- if(dist < 0)
- {
- int sx = yo.x <= -xr ? -1 : (yo.x >= xr ? 1 : 0),
- sy = yo.y <= -yr ? -1 : (yo.y >= yr ? 1 : 0);
- if(dist > (yo.z < 0 ? below : above) && (sx || sy))
- {
- vec ydir(dir);
- ydir.rotate_around_z(-yaw*RAD);
- if(sx*yo.x - xr > sy*yo.y - yr)
- {
- if(dir.iszero() || sx*ydir.x < -1e-6f)
- {
- collidewall = vec(sx, 0, 0);
- collidewall.rotate_around_z(yaw*RAD);
- return true;
- }
- }
- else if(dir.iszero() || sy*ydir.y < -1e-6f)
- {
- collidewall = vec(0, sy, 0);
- collidewall.rotate_around_z(yaw*RAD);
- return true;
- }
- }
- if(yo.z < 0)
- {
- if(dir.iszero() || (dir.z > 0 && (d->type>=ENT_INANIMATE || below >= d->zmargin-(d->eyeheight+d->aboveeye)/4.0f)))
- {
- collidewall = vec(0, 0, -1);
- return true;
- }
- }
- else if(dir.iszero() || (dir.z < 0 && (d->type>=ENT_INANIMATE || above >= d->zmargin-(d->eyeheight+d->aboveeye)/3.0f)))
- {
- collidewall = vec(0, 0, 1);
- return true;
- }
- collideinside++;
- }
- return false;
+ float below = (o.z+center.z-lo) - (d->o.z+d->aboveeye),
+ above = (d->o.z-d->eyeheight) - (o.z+center.z+hi);
+ if(below>=0 || above>=0) return false;
+
+ vec yo(d->o);
+ yo.sub(o);
+ yo.rotate_around_z(-yaw*RAD);
+ yo.sub(center);
+
+ float dx = clamp(yo.x, -xr, xr) - yo.x, dy = clamp(yo.y, -yr, yr) - yo.y,
+ dist = sqrtf(dx*dx + dy*dy) - d->radius;
+ if(dist < 0)
+ {
+ int sx = yo.x <= -xr ? -1 : (yo.x >= xr ? 1 : 0),
+ sy = yo.y <= -yr ? -1 : (yo.y >= yr ? 1 : 0);
+ if(dist > (yo.z < 0 ? below : above) && (sx || sy))
+ {
+ vec ydir(dir);
+ ydir.rotate_around_z(-yaw*RAD);
+ if(sx*yo.x - xr > sy*yo.y - yr)
+ {
+ if(dir.iszero() || sx*ydir.x < -1e-6f)
+ {
+ collidewall = vec(sx, 0, 0);
+ collidewall.rotate_around_z(yaw*RAD);
+ return true;
+ }
+ }
+ else if(dir.iszero() || sy*ydir.y < -1e-6f)
+ {
+ collidewall = vec(0, sy, 0);
+ collidewall.rotate_around_z(yaw*RAD);
+ return true;
+ }
+ }
+ if(yo.z < 0)
+ {
+ if(dir.iszero() || (dir.z > 0 && (d->type>=ENT_INANIMATE || below >= d->zmargin-(d->eyeheight+d->aboveeye)/4.0f)))
+ {
+ collidewall = vec(0, 0, -1);
+ return true;
+ }
+ }
+ else if(dir.iszero() || (dir.z < 0 && (d->type>=ENT_INANIMATE || above >= d->zmargin-(d->eyeheight+d->aboveeye)/3.0f)))
+ {
+ collidewall = vec(0, 0, 1);
+ return true;
+ }
+ collideinside++;
+ }
+ return false;
}
bool ellipsecollide(physent *d, const vec &dir, const vec &o, const vec &center, float yaw, float xr, float yr, float hi, float lo)
{
- float below = (o.z+center.z-lo) - (d->o.z+d->aboveeye),
- above = (d->o.z-d->eyeheight) - (o.z+center.z+hi);
- if(below>=0 || above>=0) return false;
- vec yo(center);
- yo.rotate_around_z(yaw*RAD);
- yo.add(o);
- float x = yo.x - d->o.x, y = yo.y - d->o.y;
- float angle = atan2f(y, x), dangle = angle-(d->yaw+90)*RAD, eangle = angle-(yaw+90)*RAD;
- float dx = d->xradius*cosf(dangle), dy = d->yradius*sinf(dangle);
- float ex = xr*cosf(eangle), ey = yr*sinf(eangle);
- float dist = sqrtf(x*x + y*y) - sqrtf(dx*dx + dy*dy) - sqrtf(ex*ex + ey*ey);
- if(dist < 0)
- {
- if(dist > (d->o.z < yo.z ? below : above) && (dir.iszero() || x*dir.x + y*dir.y > 0))
- {
- collidewall = vec(-x, -y, 0);
- if(!collidewall.iszero()) collidewall.normalize();
- return true;
- }
- if(d->o.z < yo.z)
- {
- if(dir.iszero() || (dir.z > 0 && (d->type>=ENT_INANIMATE || below >= d->zmargin-(d->eyeheight+d->aboveeye)/4.0f)))
- {
- collidewall = vec(0, 0, -1);
- return true;
- }
- }
- else if(dir.iszero() || (dir.z < 0 && (d->type>=ENT_INANIMATE || above >= d->zmargin-(d->eyeheight+d->aboveeye)/3.0f)))
- {
- collidewall = vec(0, 0, 1);
- return true;
- }
- collideinside++;
- }
- return false;
+ float below = (o.z+center.z-lo) - (d->o.z+d->aboveeye),
+ above = (d->o.z-d->eyeheight) - (o.z+center.z+hi);
+ if(below>=0 || above>=0) return false;
+ vec yo(center);
+ yo.rotate_around_z(yaw*RAD);
+ yo.add(o);
+ float x = yo.x - d->o.x, y = yo.y - d->o.y;
+ float angle = atan2f(y, x), dangle = angle-(d->yaw+90)*RAD, eangle = angle-(yaw+90)*RAD;
+ float dx = d->xradius*cosf(dangle), dy = d->yradius*sinf(dangle);
+ float ex = xr*cosf(eangle), ey = yr*sinf(eangle);
+ float dist = sqrtf(x*x + y*y) - sqrtf(dx*dx + dy*dy) - sqrtf(ex*ex + ey*ey);
+ if(dist < 0)
+ {
+ if(dist > (d->o.z < yo.z ? below : above) && (dir.iszero() || x*dir.x + y*dir.y > 0))
+ {
+ collidewall = vec(-x, -y, 0);
+ if(!collidewall.iszero()) collidewall.normalize();
+ return true;
+ }
+ if(d->o.z < yo.z)
+ {
+ if(dir.iszero() || (dir.z > 0 && (d->type>=ENT_INANIMATE || below >= d->zmargin-(d->eyeheight+d->aboveeye)/4.0f)))
+ {
+ collidewall = vec(0, 0, -1);
+ return true;
+ }
+ }
+ else if(dir.iszero() || (dir.z < 0 && (d->type>=ENT_INANIMATE || above >= d->zmargin-(d->eyeheight+d->aboveeye)/3.0f)))
+ {
+ collidewall = vec(0, 0, 1);
+ return true;
+ }
+ collideinside++;
+ }
+ return false;
}
#define DYNENTCACHESIZE 1024
@@ -577,16 +577,16 @@ static uint dynentframe = 0;
static struct dynentcacheentry
{
- int x, y;
- uint frame;
- vector<physent *> dynents;
+ int x, y;
+ uint frame;
+ vector<physent *> dynents;
} dynentcache[DYNENTCACHESIZE];
void cleardynentcache()
{
- dynentframe++;
- if(!dynentframe || dynentframe == 1) loopi(DYNENTCACHESIZE) dynentcache[i].frame = 0;
- if(!dynentframe) dynentframe = 1;
+ dynentframe++;
+ if(!dynentframe || dynentframe == 1) loopi(DYNENTCACHESIZE) dynentcache[i].frame = 0;
+ if(!dynentframe) dynentframe = 1;
}
VARF(dynentsize, 4, 7, 12, cleardynentcache());
@@ -595,989 +595,962 @@ VARF(dynentsize, 4, 7, 12, cleardynentcache());
const vector<physent *> &checkdynentcache(int x, int y)
{
- dynentcacheentry &dec = dynentcache[DYNENTHASH(x, y)];
- if(dec.x == x && dec.y == y && dec.frame == dynentframe) return dec.dynents;
- dec.x = x;
- dec.y = y;
- dec.frame = dynentframe;
- dec.dynents.shrink(0);
- int numdyns = game::numdynents(), dsize = 1<<dynentsize, dx = x<<dynentsize, dy = y<<dynentsize;
- loopi(numdyns)
- {
- dynent *d = game::iterdynents(i);
- if(d->state != CS_ALIVE ||
- d->o.x+d->radius <= dx || d->o.x-d->radius >= dx+dsize ||
- d->o.y+d->radius <= dy || d->o.y-d->radius >= dy+dsize)
- continue;
- dec.dynents.add(d);
- }
- return dec.dynents;
+ dynentcacheentry &dec = dynentcache[DYNENTHASH(x, y)];
+ if(dec.x == x && dec.y == y && dec.frame == dynentframe) return dec.dynents;
+ dec.x = x;
+ dec.y = y;
+ dec.frame = dynentframe;
+ dec.dynents.shrink(0);
+ int numdyns = game::numdynents(), dsize = 1<<dynentsize, dx = x<<dynentsize, dy = y<<dynentsize;
+ loopi(numdyns)
+ {
+ dynent *d = game::iterdynents(i);
+ if(d->state != CS_ALIVE ||
+ d->o.x+d->radius <= dx || d->o.x-d->radius >= dx+dsize ||
+ d->o.y+d->radius <= dy || d->o.y-d->radius >= dy+dsize)
+ continue;
+ dec.dynents.add(d);
+ }
+ return dec.dynents;
}
#define loopdynentcache(curx, cury, o, radius) \
- for(int curx = max(int(o.x-radius), 0)>>dynentsize, endx = min(int(o.x+radius), worldsize-1)>>dynentsize; curx <= endx; curx++) \
- for(int cury = max(int(o.y-radius), 0)>>dynentsize, endy = min(int(o.y+radius), worldsize-1)>>dynentsize; cury <= endy; cury++)
+ for(int curx = max(int(o.x-radius), 0)>>dynentsize, endx = min(int(o.x+radius), worldsize-1)>>dynentsize; curx <= endx; curx++) \
+ for(int cury = max(int(o.y-radius), 0)>>dynentsize, endy = min(int(o.y+radius), worldsize-1)>>dynentsize; cury <= endy; cury++)
void updatedynentcache(physent *d)
{
- loopdynentcache(x, y, d->o, d->radius)
- {
- dynentcacheentry &dec = dynentcache[DYNENTHASH(x, y)];
- if(dec.x != x || dec.y != y || dec.frame != dynentframe || dec.dynents.find(d) >= 0) continue;
- dec.dynents.add(d);
- }
+ loopdynentcache(x, y, d->o, d->radius)
+ {
+ dynentcacheentry &dec = dynentcache[DYNENTHASH(x, y)];
+ if(dec.x != x || dec.y != y || dec.frame != dynentframe || dec.dynents.find(d) >= 0) continue;
+ dec.dynents.add(d);
+ }
}
bool overlapsdynent(const vec &o, float radius)
{
- loopdynentcache(x, y, o, radius)
- {
- const vector<physent *> &dynents = checkdynentcache(x, y);
- loopv(dynents)
- {
- physent *d = dynents[i];
- if(o.dist(d->o)-d->radius < radius) return true;
- }
- }
- return false;
+ loopdynentcache(x, y, o, radius)
+ {
+ const vector<physent *> &dynents = checkdynentcache(x, y);
+ loopv(dynents)
+ {
+ physent *d = dynents[i];
+ if(o.dist(d->o)-d->radius < radius) return true;
+ }
+ }
+ return false;
}
template<class E, class O>
static inline bool plcollide(physent *d, const vec &dir, physent *o)
{
- E entvol(d);
- O obvol(o);
- vec cp;
- if(mpr::collide(entvol, obvol, NULL, NULL, &cp))
- {
- vec wn = vec(cp).sub(obvol.center());
- collidewall = obvol.contactface(wn, dir.iszero() ? vec(wn).neg() : dir);
- if(!collidewall.iszero()) return true;
- collideinside++;
- }
- return false;
+ E entvol(d);
+ O obvol(o);
+ vec cp;
+ if(mpr::collide(entvol, obvol, NULL, NULL, &cp))
+ {
+ vec wn = vec(cp).sub(obvol.center());
+ collidewall = obvol.contactface(wn, dir.iszero() ? vec(wn).neg() : dir);
+ if(!collidewall.iszero()) return true;
+ collideinside++;
+ }
+ return false;
}
static inline bool plcollide(physent *d, const vec &dir, physent *o)
{
- switch(d->collidetype)
- {
- case COLLIDE_ELLIPSE:
- case COLLIDE_ELLIPSE_PRECISE:
- if(o->collidetype == COLLIDE_OBB) return ellipseboxcollide(d, dir, o->o, vec(0, 0, 0), o->yaw, o->xradius, o->yradius, o->aboveeye, o->eyeheight);
- else return ellipsecollide(d, dir, o->o, vec(0, 0, 0), o->yaw, o->xradius, o->yradius, o->aboveeye, o->eyeheight);
- case COLLIDE_OBB:
- if(o->collidetype == COLLIDE_OBB) return plcollide<mpr::EntOBB, mpr::EntOBB>(d, dir, o);
- else return plcollide<mpr::EntOBB, mpr::EntCylinder>(d, dir, o);
- default: return false;
- }
-}
-
-bool plcollide(physent *d, const vec &dir, bool insideplayercol) // collide with player or monster
-{
- if(d->type==ENT_CAMERA || d->state!=CS_ALIVE) return false;
- int lastinside = collideinside;
- physent *insideplayer = NULL;
- loopdynentcache(x, y, d->o, d->radius)
- {
- const vector<physent *> &dynents = checkdynentcache(x, y);
- loopv(dynents)
- {
- physent *o = dynents[i];
- if(o==d || d->o.reject(o->o, d->radius+o->radius)) continue;
- if(plcollide(d, dir, o))
- {
- collideplayer = o;
- game::dynentcollide(d, o, collidewall);
- return true;
- }
- if(collideinside > lastinside)
- {
- lastinside = collideinside;
- insideplayer = o;
- }
- }
- }
- if(insideplayer && insideplayercol)
- {
- collideplayer = insideplayer;
- game::dynentcollide(d, insideplayer, vec(0, 0, 0));
- return true;
- }
- return false;
+ switch(d->collidetype)
+ {
+ case COLLIDE_ELLIPSE:
+ case COLLIDE_ELLIPSE_PRECISE:
+ if(o->collidetype == COLLIDE_OBB) return ellipseboxcollide(d, dir, o->o, vec(0, 0, 0), o->yaw, o->xradius, o->yradius, o->aboveeye, o->eyeheight);
+ else return ellipsecollide(d, dir, o->o, vec(0, 0, 0), o->yaw, o->xradius, o->yradius, o->aboveeye, o->eyeheight);
+ case COLLIDE_OBB:
+ if(o->collidetype == COLLIDE_OBB) return plcollide<mpr::EntOBB, mpr::EntOBB>(d, dir, o);
+ else return plcollide<mpr::EntOBB, mpr::EntCylinder>(d, dir, o);
+ default: return false;
+ }
+}
+
+bool plcollide(physent *d, const vec &dir, bool insideplayercol) // collide with player or monster
+{
+ if(d->type==ENT_CAMERA || d->state!=CS_ALIVE) return false;
+ int lastinside = collideinside;
+ physent *insideplayer = NULL;
+ loopdynentcache(x, y, d->o, d->radius)
+ {
+ const vector<physent *> &dynents = checkdynentcache(x, y);
+ loopv(dynents)
+ {
+ physent *o = dynents[i];
+ if(o==d || d->o.reject(o->o, d->radius+o->radius)) continue;
+ if(plcollide(d, dir, o))
+ {
+ collideplayer = o;
+ game::dynentcollide(d, o, collidewall);
+ return true;
+ }
+ if(collideinside > lastinside)
+ {
+ lastinside = collideinside;
+ insideplayer = o;
+ }
+ }
+ }
+ if(insideplayer && insideplayercol)
+ {
+ collideplayer = insideplayer;
+ game::dynentcollide(d, insideplayer, vec(0, 0, 0));
+ return true;
+ }
+ return false;
}
void rotatebb(vec &center, vec &radius, int yaw)
{
- if(yaw < 0) yaw = 360 + yaw%360;
- else if(yaw >= 360) yaw %= 360;
- const vec2 &rot = sincos360[yaw];
- vec2 oldcenter(center), oldradius(radius);
- center.x = oldcenter.x*rot.x - oldcenter.y*rot.y;
- center.y = oldcenter.y*rot.x + oldcenter.x*rot.y;
- radius.x = fabs(oldradius.x*rot.x) + fabs(oldradius.y*rot.y);
- radius.y = fabs(oldradius.y*rot.x) + fabs(oldradius.x*rot.y);
+ if(yaw < 0) yaw = 360 + yaw%360;
+ else if(yaw >= 360) yaw %= 360;
+ const vec2 &rot = sincos360[yaw];
+ vec2 oldcenter(center), oldradius(radius);
+ center.x = oldcenter.x*rot.x - oldcenter.y*rot.y;
+ center.y = oldcenter.y*rot.x + oldcenter.x*rot.y;
+ radius.x = fabs(oldradius.x*rot.x) + fabs(oldradius.y*rot.y);
+ radius.y = fabs(oldradius.y*rot.x) + fabs(oldradius.x*rot.y);
}
template<class E, class M>
static inline bool mmcollide(physent *d, const vec &dir, const extentity &e, const vec &center, const vec &radius, float yaw)
{
- E entvol(d);
- M mdlvol(e.o, center, radius, yaw);
- vec cp;
- if(mpr::collide(entvol, mdlvol, NULL, NULL, &cp))
- {
- vec wn = vec(cp).sub(mdlvol.center());
- collidewall = mdlvol.contactface(wn, dir.iszero() ? vec(wn).neg() : dir);
- if(!collidewall.iszero()) return true;
- collideinside++;
- }
- return false;
-}
-
-bool mmcollide(physent *d, const vec &dir, octaentities &oc) // collide with a mapmodel
-{
- const vector<extentity *> &ents = entities::getents();
- loopv(oc.mapmodels)
- {
- extentity &e = *ents[oc.mapmodels[i]];
- if(e.flags&EF_NOCOLLIDE) continue;
- model *m = loadmapmodel(e.attr2);
- if(!m || !m->collide) continue;
-
- vec center, radius;
- float rejectradius = m->collisionbox(center, radius);
- if(d->o.reject(e.o, d->radius + rejectradius)) continue;
-
- float yaw = e.attr1;
- switch(d->collidetype)
- {
- case COLLIDE_ELLIPSE:
- case COLLIDE_ELLIPSE_PRECISE:
- if(m->ellipsecollide)
- {
- if(ellipsecollide(d, dir, e.o, center, yaw, radius.x, radius.y, radius.z, radius.z)) return true;
- }
- else if(ellipseboxcollide(d, dir, e.o, center, yaw, radius.x, radius.y, radius.z, radius.z)) return true;
- break;
- case COLLIDE_OBB:
- if(m->ellipsecollide)
- {
- if(mmcollide<mpr::EntOBB, mpr::ModelEllipse>(d, dir, e, center, radius, yaw)) return true;
- }
- else if(mmcollide<mpr::EntOBB, mpr::ModelOBB>(d, dir, e, center, radius, yaw)) return true;
- break;
- default: continue;
- }
- }
- return false;
+ E entvol(d);
+ M mdlvol(e.o, center, radius, yaw);
+ vec cp;
+ if(mpr::collide(entvol, mdlvol, NULL, NULL, &cp))
+ {
+ vec wn = vec(cp).sub(mdlvol.center());
+ collidewall = mdlvol.contactface(wn, dir.iszero() ? vec(wn).neg() : dir);
+ if(!collidewall.iszero()) return true;
+ collideinside++;
+ }
+ return false;
+}
+
+bool mmcollide(physent *d, const vec &dir, octaentities &oc) // collide with a mapmodel
+{
+ const vector<extentity *> &ents = entities::getents();
+ loopv(oc.mapmodels)
+ {
+ extentity &e = *ents[oc.mapmodels[i]];
+ if(e.flags&EF_NOCOLLIDE) continue;
+ model *m = loadmapmodel(e.attr2);
+ if(!m || !m->collide) continue;
+
+ vec center, radius;
+ float rejectradius = m->collisionbox(center, radius);
+ if(d->o.reject(e.o, d->radius + rejectradius)) continue;
+
+ float yaw = e.attr1;
+ switch(d->collidetype)
+ {
+ case COLLIDE_ELLIPSE:
+ case COLLIDE_ELLIPSE_PRECISE:
+ if(m->ellipsecollide)
+ {
+ if(ellipsecollide(d, dir, e.o, center, yaw, radius.x, radius.y, radius.z, radius.z)) return true;
+ }
+ else if(ellipseboxcollide(d, dir, e.o, center, yaw, radius.x, radius.y, radius.z, radius.z)) return true;
+ break;
+ case COLLIDE_OBB:
+ if(m->ellipsecollide)
+ {
+ if(mmcollide<mpr::EntOBB, mpr::ModelEllipse>(d, dir, e, center, radius, yaw)) return true;
+ }
+ else if(mmcollide<mpr::EntOBB, mpr::ModelOBB>(d, dir, e, center, radius, yaw)) return true;
+ break;
+ default: continue;
+ }
+ }
+ return false;
}
template<class E>
static bool fuzzycollidesolid(physent *d, const vec &dir, float cutoff, const cube &c, const ivec &co, int size) // collide with solid cube geometry
{
- int crad = size/2;
- if(fabs(d->o.x - co.x - crad) > d->radius + crad || fabs(d->o.y - co.y - crad) > d->radius + crad ||
- d->o.z + d->aboveeye < co.z || d->o.z - d->eyeheight > co.z + size)
- return false;
-
- E entvol(d);
- collidewall = vec(0, 0, 0);
- float bestdist = -1e10f;
- int visible = isentirelysolid(c) ? c.visible : 0xFF;
- #define CHECKSIDE(side, distval, dotval, margin, normal) if(visible&(1<<side)) do \
- { \
- float dist = distval; \
- if(dist > 0) return false; \
- if(dist <= bestdist) continue; \
- if(!dir.iszero()) \
- { \
- if(dotval >= -cutoff*dir.magnitude()) continue; \
- if(d->type<ENT_CAMERA && dotval < 0 && dist < margin) continue; \
- } \
- collidewall = normal; \
- bestdist = dist; \
- } while(0)
- CHECKSIDE(O_LEFT, co.x - (d->o.x + d->radius), -dir.x, -d->radius, vec(-1, 0, 0));
- CHECKSIDE(O_RIGHT, d->o.x - d->radius - (co.x + size), dir.x, -d->radius, vec(1, 0, 0));
- CHECKSIDE(O_BACK, co.y - (d->o.y + d->radius), -dir.y, -d->radius, vec(0, -1, 0));
- CHECKSIDE(O_FRONT, d->o.y - d->radius - (co.y + size), dir.y, -d->radius, vec(0, 1, 0));
- CHECKSIDE(O_BOTTOM, co.z - (d->o.z + d->aboveeye), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
- CHECKSIDE(O_TOP, d->o.z - d->eyeheight - (co.z + size), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
-
- if(collidewall.iszero())
- {
- collideinside++;
- return false;
- }
- return true;
+ int crad = size/2;
+ if(fabs(d->o.x - co.x - crad) > d->radius + crad || fabs(d->o.y - co.y - crad) > d->radius + crad ||
+ d->o.z + d->aboveeye < co.z || d->o.z - d->eyeheight > co.z + size)
+ return false;
+
+ E entvol(d);
+ collidewall = vec(0, 0, 0);
+ float bestdist = -1e10f;
+ int visible = isentirelysolid(c) ? c.visible : 0xFF;
+ #define CHECKSIDE(side, distval, dotval, margin, normal) if(visible&(1<<side)) do \
+ { \
+ float dist = distval; \
+ if(dist > 0) return false; \
+ if(dist <= bestdist) continue; \
+ if(!dir.iszero()) \
+ { \
+ if(dotval >= -cutoff*dir.magnitude()) continue; \
+ if(d->type<ENT_CAMERA && dotval < 0 && dist < margin) continue; \
+ } \
+ collidewall = normal; \
+ bestdist = dist; \
+ } while(0)
+ CHECKSIDE(O_LEFT, co.x - (d->o.x + d->radius), -dir.x, -d->radius, vec(-1, 0, 0));
+ CHECKSIDE(O_RIGHT, d->o.x - d->radius - (co.x + size), dir.x, -d->radius, vec(1, 0, 0));
+ CHECKSIDE(O_BACK, co.y - (d->o.y + d->radius), -dir.y, -d->radius, vec(0, -1, 0));
+ CHECKSIDE(O_FRONT, d->o.y - d->radius - (co.y + size), dir.y, -d->radius, vec(0, 1, 0));
+ CHECKSIDE(O_BOTTOM, co.z - (d->o.z + d->aboveeye), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
+ CHECKSIDE(O_TOP, d->o.z - d->eyeheight - (co.z + size), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
+
+ if(collidewall.iszero())
+ {
+ collideinside++;
+ return false;
+ }
+ return true;
}
template<class E>
static inline bool clampcollide(const clipplanes &p, const E &entvol, const plane &w, const vec &pw)
{
- if(w.x && (w.y || w.z) && fabs(pw.x - p.o.x) > p.r.x)
- {
- vec c = entvol.center();
- float fv = pw.x < p.o.x ? p.o.x-p.r.x : p.o.x+p.r.x, fdist = (w.x*fv + w.y*c.y + w.z*c.z + w.offset) / (w.y*w.y + w.z*w.z);
- vec fdir(fv - c.x, -w.y*fdist, -w.z*fdist);
- if((pw.y-c.y-fdir.y)*w.y + (pw.z-c.z-fdir.z)*w.z >= 0 && entvol.supportpoint(fdir).squaredist(c) < fdir.squaredlen()) return true;
- }
- if(w.y && (w.x || w.z) && fabs(pw.y - p.o.y) > p.r.y)
- {
- vec c = entvol.center();
- float fv = pw.y < p.o.y ? p.o.y-p.r.y : p.o.y+p.r.y, fdist = (w.x*c.x + w.y*fv + w.z*c.z + w.offset) / (w.x*w.x + w.z*w.z);
- vec fdir(-w.x*fdist, fv - c.y, -w.z*fdist);
- if((pw.x-c.x-fdir.x)*w.x + (pw.z-c.z-fdir.z)*w.z >= 0 && entvol.supportpoint(fdir).squaredist(c) < fdir.squaredlen()) return true;
- }
- if(w.z && (w.x || w.y) && fabs(pw.z - p.o.z) > p.r.z)
- {
- vec c = entvol.center();
- float fv = pw.z < p.o.z ? p.o.z-p.r.z : p.o.z+p.r.z, fdist = (w.x*c.x + w.y*c.y + w.z*fv + w.offset) / (w.x*w.x + w.y*w.y);
- vec fdir(-w.x*fdist, -w.y*fdist, fv - c.z);
- if((pw.x-c.x-fdir.x)*w.x + (pw.y-c.y-fdir.y)*w.y >= 0 && entvol.supportpoint(fdir).squaredist(c) < fdir.squaredlen()) return true;
- }
- return false;
+ if(w.x && (w.y || w.z) && fabs(pw.x - p.o.x) > p.r.x)
+ {
+ vec c = entvol.center();
+ float fv = pw.x < p.o.x ? p.o.x-p.r.x : p.o.x+p.r.x, fdist = (w.x*fv + w.y*c.y + w.z*c.z + w.offset) / (w.y*w.y + w.z*w.z);
+ vec fdir(fv - c.x, -w.y*fdist, -w.z*fdist);
+ if((pw.y-c.y-fdir.y)*w.y + (pw.z-c.z-fdir.z)*w.z >= 0 && entvol.supportpoint(fdir).squaredist(c) < fdir.squaredlen()) return true;
+ }
+ if(w.y && (w.x || w.z) && fabs(pw.y - p.o.y) > p.r.y)
+ {
+ vec c = entvol.center();
+ float fv = pw.y < p.o.y ? p.o.y-p.r.y : p.o.y+p.r.y, fdist = (w.x*c.x + w.y*fv + w.z*c.z + w.offset) / (w.x*w.x + w.z*w.z);
+ vec fdir(-w.x*fdist, fv - c.y, -w.z*fdist);
+ if((pw.x-c.x-fdir.x)*w.x + (pw.z-c.z-fdir.z)*w.z >= 0 && entvol.supportpoint(fdir).squaredist(c) < fdir.squaredlen()) return true;
+ }
+ if(w.z && (w.x || w.y) && fabs(pw.z - p.o.z) > p.r.z)
+ {
+ vec c = entvol.center();
+ float fv = pw.z < p.o.z ? p.o.z-p.r.z : p.o.z+p.r.z, fdist = (w.x*c.x + w.y*c.y + w.z*fv + w.offset) / (w.x*w.x + w.y*w.y);
+ vec fdir(-w.x*fdist, -w.y*fdist, fv - c.z);
+ if((pw.x-c.x-fdir.x)*w.x + (pw.y-c.y-fdir.y)*w.y >= 0 && entvol.supportpoint(fdir).squaredist(c) < fdir.squaredlen()) return true;
+ }
+ return false;
}
template<class E>
static bool fuzzycollideplanes(physent *d, const vec &dir, float cutoff, const cube &c, const ivec &co, int size) // collide with deformed cube geometry
{
- const clipplanes &p = getclipplanes(c, co, size);
-
- if(fabs(d->o.x - p.o.x) > p.r.x + d->radius || fabs(d->o.y - p.o.y) > p.r.y + d->radius ||
- d->o.z + d->aboveeye < p.o.z - p.r.z || d->o.z - d->eyeheight > p.o.z + p.r.z)
- return false;
-
- collidewall = vec(0, 0, 0);
- float bestdist = -1e10f;
- int visible = p.visible;
- CHECKSIDE(O_LEFT, p.o.x - p.r.x - (d->o.x + d->radius), -dir.x, -d->radius, vec(-1, 0, 0));
- CHECKSIDE(O_RIGHT, d->o.x - d->radius - (p.o.x + p.r.x), dir.x, -d->radius, vec(1, 0, 0));
- CHECKSIDE(O_BACK, p.o.y - p.r.y - (d->o.y + d->radius), -dir.y, -d->radius, vec(0, -1, 0));
- CHECKSIDE(O_FRONT, d->o.y - d->radius - (p.o.y + p.r.y), dir.y, -d->radius, vec(0, 1, 0));
- CHECKSIDE(O_BOTTOM, p.o.z - p.r.z - (d->o.z + d->aboveeye), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
- CHECKSIDE(O_TOP, d->o.z - d->eyeheight - (p.o.z + p.r.z), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
-
- E entvol(d);
- int bestplane = -1;
- loopi(p.size)
- {
- const plane &w = p.p[i];
- vec pw = entvol.supportpoint(vec(w).neg());
- float dist = w.dist(pw);
- if(dist >= 0) return false;
- if(dist <= bestdist) continue;
- bestplane = -1;
- bestdist = dist;
- if(!dir.iszero())
- {
- if(w.dot(dir) >= -cutoff*dir.magnitude()) continue;
- if(d->type<ENT_CAMERA &&
- dist < (dir.z*w.z < 0 ?
- d->zmargin-(d->eyeheight+d->aboveeye)/(dir.z < 0 ? 3.0f : 4.0f) :
- ((dir.x*w.x < 0 || dir.y*w.y < 0) ? -d->radius : 0)))
- continue;
- }
- if(clampcollide(p, entvol, w, pw)) continue;
- bestplane = i;
- }
- if(bestplane >= 0) collidewall = p.p[bestplane];
- else if(collidewall.iszero())
- {
- collideinside++;
- return false;
- }
- return true;
+ const clipplanes &p = getclipplanes(c, co, size);
+
+ if(fabs(d->o.x - p.o.x) > p.r.x + d->radius || fabs(d->o.y - p.o.y) > p.r.y + d->radius ||
+ d->o.z + d->aboveeye < p.o.z - p.r.z || d->o.z - d->eyeheight > p.o.z + p.r.z)
+ return false;
+
+ collidewall = vec(0, 0, 0);
+ float bestdist = -1e10f;
+ int visible = p.visible;
+ CHECKSIDE(O_LEFT, p.o.x - p.r.x - (d->o.x + d->radius), -dir.x, -d->radius, vec(-1, 0, 0));
+ CHECKSIDE(O_RIGHT, d->o.x - d->radius - (p.o.x + p.r.x), dir.x, -d->radius, vec(1, 0, 0));
+ CHECKSIDE(O_BACK, p.o.y - p.r.y - (d->o.y + d->radius), -dir.y, -d->radius, vec(0, -1, 0));
+ CHECKSIDE(O_FRONT, d->o.y - d->radius - (p.o.y + p.r.y), dir.y, -d->radius, vec(0, 1, 0));
+ CHECKSIDE(O_BOTTOM, p.o.z - p.r.z - (d->o.z + d->aboveeye), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
+ CHECKSIDE(O_TOP, d->o.z - d->eyeheight - (p.o.z + p.r.z), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
+
+ E entvol(d);
+ int bestplane = -1;
+ loopi(p.size)
+ {
+ const plane &w = p.p[i];
+ vec pw = entvol.supportpoint(vec(w).neg());
+ float dist = w.dist(pw);
+ if(dist >= 0) return false;
+ if(dist <= bestdist) continue;
+ bestplane = -1;
+ bestdist = dist;
+ if(!dir.iszero())
+ {
+ if(w.dot(dir) >= -cutoff*dir.magnitude()) continue;
+ if(d->type<ENT_CAMERA &&
+ dist < (dir.z*w.z < 0 ?
+ d->zmargin-(d->eyeheight+d->aboveeye)/(dir.z < 0 ? 3.0f : 4.0f) :
+ ((dir.x*w.x < 0 || dir.y*w.y < 0) ? -d->radius : 0)))
+ continue;
+ }
+ if(clampcollide(p, entvol, w, pw)) continue;
+ bestplane = i;
+ }
+ if(bestplane >= 0) collidewall = p.p[bestplane];
+ else if(collidewall.iszero())
+ {
+ collideinside++;
+ return false;
+ }
+ return true;
}
template<class E>
static bool cubecollidesolid(physent *d, const vec &dir, float cutoff, const cube &c, const ivec &co, int size) // collide with solid cube geometry
{
- int crad = size/2;
- if(fabs(d->o.x - co.x - crad) > d->radius + crad || fabs(d->o.y - co.y - crad) > d->radius + crad ||
- d->o.z + d->aboveeye < co.z || d->o.z - d->eyeheight > co.z + size)
- return false;
-
- E entvol(d);
- bool collided = mpr::collide(mpr::SolidCube(co, size), entvol);
- if(!collided) return false;
-
- collidewall = vec(0, 0, 0);
- float bestdist = -1e10f;
- int visible = isentirelysolid(c) ? c.visible : 0xFF;
- CHECKSIDE(O_LEFT, co.x - entvol.right(), -dir.x, -d->radius, vec(-1, 0, 0));
- CHECKSIDE(O_RIGHT, entvol.left() - (co.x + size), dir.x, -d->radius, vec(1, 0, 0));
- CHECKSIDE(O_BACK, co.y - entvol.front(), -dir.y, -d->radius, vec(0, -1, 0));
- CHECKSIDE(O_FRONT, entvol.back() - (co.y + size), dir.y, -d->radius, vec(0, 1, 0));
- CHECKSIDE(O_BOTTOM, co.z - entvol.top(), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
- CHECKSIDE(O_TOP, entvol.bottom() - (co.z + size), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
-
- if(collidewall.iszero())
- {
- collideinside++;
- return false;
- }
- return true;
+ int crad = size/2;
+ if(fabs(d->o.x - co.x - crad) > d->radius + crad || fabs(d->o.y - co.y - crad) > d->radius + crad ||
+ d->o.z + d->aboveeye < co.z || d->o.z - d->eyeheight > co.z + size)
+ return false;
+
+ E entvol(d);
+ bool collided = mpr::collide(mpr::SolidCube(co, size), entvol);
+ if(!collided) return false;
+
+ collidewall = vec(0, 0, 0);
+ float bestdist = -1e10f;
+ int visible = isentirelysolid(c) ? c.visible : 0xFF;
+ CHECKSIDE(O_LEFT, co.x - entvol.right(), -dir.x, -d->radius, vec(-1, 0, 0));
+ CHECKSIDE(O_RIGHT, entvol.left() - (co.x + size), dir.x, -d->radius, vec(1, 0, 0));
+ CHECKSIDE(O_BACK, co.y - entvol.front(), -dir.y, -d->radius, vec(0, -1, 0));
+ CHECKSIDE(O_FRONT, entvol.back() - (co.y + size), dir.y, -d->radius, vec(0, 1, 0));
+ CHECKSIDE(O_BOTTOM, co.z - entvol.top(), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
+ CHECKSIDE(O_TOP, entvol.bottom() - (co.z + size), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
+
+ if(collidewall.iszero())
+ {
+ collideinside++;
+ return false;
+ }
+ return true;
}
template<class E>
static bool cubecollideplanes(physent *d, const vec &dir, float cutoff, const cube &c, const ivec &co, int size) // collide with deformed cube geometry
{
- const clipplanes &p = getclipplanes(c, co, size);
-
- if(fabs(d->o.x - p.o.x) > p.r.x + d->radius || fabs(d->o.y - p.o.y) > p.r.y + d->radius ||
- d->o.z + d->aboveeye < p.o.z - p.r.z || d->o.z - d->eyeheight > p.o.z + p.r.z)
- return false;
-
- E entvol(d);
- bool collided = mpr::collide(mpr::CubePlanes(p), entvol);
- if(!collided) return false;
-
- collidewall = vec(0, 0, 0);
- float bestdist = -1e10f;
- int visible = p.visible;
- CHECKSIDE(O_LEFT, p.o.x - p.r.x - entvol.right(), -dir.x, -d->radius, vec(-1, 0, 0));
- CHECKSIDE(O_RIGHT, entvol.left() - (p.o.x + p.r.x), dir.x, -d->radius, vec(1, 0, 0));
- CHECKSIDE(O_BACK, p.o.y - p.r.y - entvol.front(), -dir.y, -d->radius, vec(0, -1, 0));
- CHECKSIDE(O_FRONT, entvol.back() - (p.o.y + p.r.y), dir.y, -d->radius, vec(0, 1, 0));
- CHECKSIDE(O_BOTTOM, p.o.z - p.r.z - entvol.top(), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
- CHECKSIDE(O_TOP, entvol.bottom() - (p.o.z + p.r.z), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
-
- int bestplane = -1;
- loopi(p.size)
- {
- const plane &w = p.p[i];
- vec pw = entvol.supportpoint(vec(w).neg());
- float dist = w.dist(pw);
- if(dist <= bestdist) continue;
- bestplane = -1;
- bestdist = dist;
- if(!dir.iszero())
- {
- if(w.dot(dir) >= -cutoff*dir.magnitude()) continue;
- if(d->type<ENT_CAMERA &&
- dist < (dir.z*w.z < 0 ?
- d->zmargin-(d->eyeheight+d->aboveeye)/(dir.z < 0 ? 3.0f : 4.0f) :
- ((dir.x*w.x < 0 || dir.y*w.y < 0) ? -d->radius : 0)))
- continue;
- }
- if(clampcollide(p, entvol, w, pw)) continue;
- bestplane = i;
- }
- if(bestplane >= 0) collidewall = p.p[bestplane];
- else if(collidewall.iszero())
- {
- collideinside++;
- return false;
- }
- return true;
+ const clipplanes &p = getclipplanes(c, co, size);
+
+ if(fabs(d->o.x - p.o.x) > p.r.x + d->radius || fabs(d->o.y - p.o.y) > p.r.y + d->radius ||
+ d->o.z + d->aboveeye < p.o.z - p.r.z || d->o.z - d->eyeheight > p.o.z + p.r.z)
+ return false;
+
+ E entvol(d);
+ bool collided = mpr::collide(mpr::CubePlanes(p), entvol);
+ if(!collided) return false;
+
+ collidewall = vec(0, 0, 0);
+ float bestdist = -1e10f;
+ int visible = p.visible;
+ CHECKSIDE(O_LEFT, p.o.x - p.r.x - entvol.right(), -dir.x, -d->radius, vec(-1, 0, 0));
+ CHECKSIDE(O_RIGHT, entvol.left() - (p.o.x + p.r.x), dir.x, -d->radius, vec(1, 0, 0));
+ CHECKSIDE(O_BACK, p.o.y - p.r.y - entvol.front(), -dir.y, -d->radius, vec(0, -1, 0));
+ CHECKSIDE(O_FRONT, entvol.back() - (p.o.y + p.r.y), dir.y, -d->radius, vec(0, 1, 0));
+ CHECKSIDE(O_BOTTOM, p.o.z - p.r.z - entvol.top(), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
+ CHECKSIDE(O_TOP, entvol.bottom() - (p.o.z + p.r.z), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
+
+ int bestplane = -1;
+ loopi(p.size)
+ {
+ const plane &w = p.p[i];
+ vec pw = entvol.supportpoint(vec(w).neg());
+ float dist = w.dist(pw);
+ if(dist <= bestdist) continue;
+ bestplane = -1;
+ bestdist = dist;
+ if(!dir.iszero())
+ {
+ if(w.dot(dir) >= -cutoff*dir.magnitude()) continue;
+ if(d->type<ENT_CAMERA &&
+ dist < (dir.z*w.z < 0 ?
+ d->zmargin-(d->eyeheight+d->aboveeye)/(dir.z < 0 ? 3.0f : 4.0f) :
+ ((dir.x*w.x < 0 || dir.y*w.y < 0) ? -d->radius : 0)))
+ continue;
+ }
+ if(clampcollide(p, entvol, w, pw)) continue;
+ bestplane = i;
+ }
+ if(bestplane >= 0) collidewall = p.p[bestplane];
+ else if(collidewall.iszero())
+ {
+ collideinside++;
+ return false;
+ }
+ return true;
}
static inline bool cubecollide(physent *d, const vec &dir, float cutoff, const cube &c, const ivec &co, int size, bool solid)
{
- switch(d->collidetype)
- {
- case COLLIDE_OBB:
- if(isentirelysolid(c) || solid) return cubecollidesolid<mpr::EntOBB>(d, dir, cutoff, c, co, size);
- else return cubecollideplanes<mpr::EntOBB>(d, dir, cutoff, c, co, size);
- case COLLIDE_ELLIPSE:
- if(isentirelysolid(c) || solid) return fuzzycollidesolid<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
- else return fuzzycollideplanes<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
- case COLLIDE_ELLIPSE_PRECISE:
- if(isentirelysolid(c) || solid) return cubecollidesolid<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
- else return cubecollideplanes<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
- default: return false;
- }
+ switch(d->collidetype)
+ {
+ case COLLIDE_OBB:
+ if(isentirelysolid(c) || solid) return cubecollidesolid<mpr::EntOBB>(d, dir, cutoff, c, co, size);
+ else return cubecollideplanes<mpr::EntOBB>(d, dir, cutoff, c, co, size);
+ case COLLIDE_ELLIPSE:
+ if(isentirelysolid(c) || solid) return fuzzycollidesolid<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
+ else return fuzzycollideplanes<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
+ case COLLIDE_ELLIPSE_PRECISE:
+ if(isentirelysolid(c) || solid) return cubecollidesolid<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
+ else return cubecollideplanes<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
+ default: return false;
+ }
}
static inline bool octacollide(physent *d, const vec &dir, float cutoff, const ivec &bo, const ivec &bs, const cube *c, const ivec &cor, int size) // collide with octants
{
- loopoctabox(cor, size, bo, bs)
- {
- if(c[i].ext && c[i].ext->ents) if(mmcollide(d, dir, *c[i].ext->ents)) return true;
- ivec o(i, cor, size);
- if(c[i].children)
- {
- if(octacollide(d, dir, cutoff, bo, bs, c[i].children, o, size>>1)) return true;
- }
- else
- {
- bool solid = false;
- switch(c[i].material&MATF_CLIP)
- {
- case MAT_NOCLIP: continue;
- case MAT_GAMECLIP: if(d->type==ENT_AI) solid = true; break;
- case MAT_CLIP: if(isclipped(c[i].material&MATF_VOLUME) || d->type<ENT_CAMERA) solid = true; break;
- }
- if(!solid && isempty(c[i])) continue;
- if(cubecollide(d, dir, cutoff, c[i], o, size, solid)) return true;
- }
- }
- return false;
+ loopoctabox(cor, size, bo, bs)
+ {
+ if(c[i].ext && c[i].ext->ents) if(mmcollide(d, dir, *c[i].ext->ents)) return true;
+ ivec o(i, cor, size);
+ if(c[i].children)
+ {
+ if(octacollide(d, dir, cutoff, bo, bs, c[i].children, o, size>>1)) return true;
+ }
+ else
+ {
+ bool solid = false;
+ switch(c[i].material&MATF_CLIP)
+ {
+ case MAT_NOCLIP: continue;
+ case MAT_GAMECLIP: if(d->type==ENT_AI) solid = true; break;
+ case MAT_CLIP: if(isclipped(c[i].material&MATF_VOLUME) || d->type<ENT_CAMERA) solid = true; break;
+ }
+ if(!solid && isempty(c[i])) continue;
+ if(cubecollide(d, dir, cutoff, c[i], o, size, solid)) return true;
+ }
+ }
+ return false;
}
static inline bool octacollide(physent *d, const vec &dir, float cutoff, const ivec &bo, const ivec &bs)
{
- int diff = (bo.x^bs.x) | (bo.y^bs.y) | (bo.z^bs.z),
- scale = worldscale-1;
- if(diff&~((1<<scale)-1) || uint(bo.x|bo.y|bo.z|bs.x|bs.y|bs.z) >= uint(worldsize))
- return octacollide(d, dir, cutoff, bo, bs, worldroot, ivec(0, 0, 0), worldsize>>1);
- const cube *c = &worldroot[octastep(bo.x, bo.y, bo.z, scale)];
- if(c->ext && c->ext->ents && mmcollide(d, dir, *c->ext->ents)) return true;
- scale--;
- while(c->children && !(diff&(1<<scale)))
- {
- c = &c->children[octastep(bo.x, bo.y, bo.z, scale)];
- if(c->ext && c->ext->ents && mmcollide(d, dir, *c->ext->ents)) return true;
- scale--;
- }
- if(c->children) return octacollide(d, dir, cutoff, bo, bs, c->children, ivec(bo).mask(~((2<<scale)-1)), 1<<scale);
- bool solid = false;
- switch(c->material&MATF_CLIP)
- {
- case MAT_NOCLIP: return false;
- case MAT_GAMECLIP: if(d->type==ENT_AI) solid = true; break;
- case MAT_CLIP: if(isclipped(c->material&MATF_VOLUME) || d->type<ENT_CAMERA) solid = true; break;
- }
- if(!solid && isempty(*c)) return false;
- int csize = 2<<scale, cmask = ~(csize-1);
- return cubecollide(d, dir, cutoff, *c, ivec(bo).mask(cmask), csize, solid);
+ int diff = (bo.x^bs.x) | (bo.y^bs.y) | (bo.z^bs.z),
+ scale = worldscale-1;
+ if(diff&~((1<<scale)-1) || uint(bo.x|bo.y|bo.z|bs.x|bs.y|bs.z) >= uint(worldsize))
+ return octacollide(d, dir, cutoff, bo, bs, worldroot, ivec(0, 0, 0), worldsize>>1);
+ const cube *c = &worldroot[octastep(bo.x, bo.y, bo.z, scale)];
+ if(c->ext && c->ext->ents && mmcollide(d, dir, *c->ext->ents)) return true;
+ scale--;
+ while(c->children && !(diff&(1<<scale)))
+ {
+ c = &c->children[octastep(bo.x, bo.y, bo.z, scale)];
+ if(c->ext && c->ext->ents && mmcollide(d, dir, *c->ext->ents)) return true;
+ scale--;
+ }
+ if(c->children) return octacollide(d, dir, cutoff, bo, bs, c->children, ivec(bo).mask(~((2<<scale)-1)), 1<<scale);
+ bool solid = false;
+ switch(c->material&MATF_CLIP)
+ {
+ case MAT_NOCLIP: return false;
+ case MAT_GAMECLIP: if(d->type==ENT_AI) solid = true; break;
+ case MAT_CLIP: if(isclipped(c->material&MATF_VOLUME) || d->type<ENT_CAMERA) solid = true; break;
+ }
+ if(!solid && isempty(*c)) return false;
+ int csize = 2<<scale, cmask = ~(csize-1);
+ return cubecollide(d, dir, cutoff, *c, ivec(bo).mask(cmask), csize, solid);
}
// all collision happens here
bool collide(physent *d, const vec &dir, float cutoff, bool playercol, bool insideplayercol)
{
- collideinside = 0;
- collideplayer = NULL;
- collidewall = vec(0, 0, 0);
- ivec bo(int(d->o.x-d->radius), int(d->o.y-d->radius), int(d->o.z-d->eyeheight)),
- bs(int(d->o.x+d->radius), int(d->o.y+d->radius), int(d->o.z+d->aboveeye));
- bs.add(1); // guard space for rounding errors
- return octacollide(d, dir, cutoff, bo, bs) || (playercol && plcollide(d, dir, insideplayercol));
+ collideinside = 0;
+ collideplayer = NULL;
+ collidewall = vec(0, 0, 0);
+ ivec bo(int(d->o.x-d->radius), int(d->o.y-d->radius), int(d->o.z-d->eyeheight)),
+ bs(int(d->o.x+d->radius), int(d->o.y+d->radius), int(d->o.z+d->aboveeye));
+ bs.add(1); // guard space for rounding errors
+ return octacollide(d, dir, cutoff, bo, bs) || (playercol && plcollide(d, dir, insideplayercol));
}
void recalcdir(physent *d, const vec &oldvel, vec &dir)
{
- float speed = oldvel.magnitude();
- if(speed > 1e-6f)
- {
- float step = dir.magnitude();
- dir = d->vel;
- dir.add(d->falling);
- dir.mul(step/speed);
- }
+ float speed = oldvel.magnitude();
+ if(speed > 1e-6f)
+ {
+ float step = dir.magnitude();
+ dir = d->vel;
+ dir.add(d->falling);
+ dir.mul(step/speed);
+ }
}
void slideagainst(physent *d, vec &dir, const vec &obstacle, bool foundfloor, bool slidecollide)
{
- vec wall(obstacle);
- if(foundfloor ? wall.z > 0 : slidecollide)
- {
- wall.z = 0;
- if(!wall.iszero()) wall.normalize();
- }
- vec oldvel(d->vel);
- oldvel.add(d->falling);
- d->vel.project(wall);
- d->falling.project(wall);
- recalcdir(d, oldvel, dir);
+ vec wall(obstacle);
+ if(foundfloor ? wall.z > 0 : slidecollide)
+ {
+ wall.z = 0;
+ if(!wall.iszero()) wall.normalize();
+ }
+ vec oldvel(d->vel);
+ oldvel.add(d->falling);
+ d->vel.project(wall);
+ d->falling.project(wall);
+ recalcdir(d, oldvel, dir);
}
void switchfloor(physent *d, vec &dir, const vec &floor)
{
- if(floor.z >= FLOORZ) d->falling = vec(0, 0, 0);
-
- vec oldvel(d->vel);
- oldvel.add(d->falling);
- if(dir.dot(floor) >= 0)
- {
- if(d->physstate < PHYS_SLIDE || fabs(dir.dot(d->floor)) > 0.01f*dir.magnitude()) return;
- d->vel.projectxy(floor, 0.0f);
- }
- else d->vel.projectxy(floor);
- d->falling.project(floor);
- recalcdir(d, oldvel, dir);
+ if(floor.z >= FLOORZ) d->falling = vec(0, 0, 0);
+
+ vec oldvel(d->vel);
+ oldvel.add(d->falling);
+ if(dir.dot(floor) >= 0)
+ {
+ if(d->physstate < PHYS_SLIDE || fabs(dir.dot(d->floor)) > 0.01f*dir.magnitude()) return;
+ d->vel.projectxy(floor, 0.0f);
+ }
+ else d->vel.projectxy(floor);
+ d->falling.project(floor);
+ recalcdir(d, oldvel, dir);
}
bool trystepup(physent *d, vec &dir, const vec &obstacle, float maxstep, const vec &floor)
{
- vec old(d->o), stairdir = (obstacle.z >= 0 && obstacle.z < SLOPEZ ? vec(-obstacle.x, -obstacle.y, 0) : vec(dir.x, dir.y, 0)).rescale(1);
- bool cansmooth = true;
- /* check if there is space atop the stair to move to */
- if(d->physstate != PHYS_STEP_UP)
- {
- vec checkdir = stairdir;
- checkdir.mul(0.1f);
- checkdir.z += maxstep + 0.1f;
- d->o.add(checkdir);
- if(collide(d))
- {
- d->o = old;
- if(!collide(d, vec(0, 0, -1), SLOPEZ)) return false;
- cansmooth = false;
- }
- }
-
- if(cansmooth)
- {
- vec checkdir = stairdir;
- checkdir.z += 1;
- checkdir.mul(maxstep);
- d->o = old;
- d->o.add(checkdir);
- int scale = 2;
- if(collide(d, checkdir))
- {
- if(!collide(d, vec(0, 0, -1), SLOPEZ))
- {
- d->o = old;
- return false;
- }
- d->o.add(checkdir);
- if(collide(d, vec(0, 0, -1), SLOPEZ)) scale = 1;
- }
- if(scale != 1)
- {
- d->o = old;
- d->o.sub(checkdir.mul(vec(2, 2, 1)));
- if(!collide(d, vec(0, 0, -1), SLOPEZ)) scale = 1;
- }
-
- d->o = old;
- vec smoothdir(dir.x, dir.y, 0);
- float magxy = smoothdir.magnitude();
- if(magxy > 1e-9f)
- {
- if(magxy > scale*dir.z)
- {
- smoothdir.mul(1/magxy);
- smoothdir.z = 1.0f/scale;
- smoothdir.mul(dir.magnitude()/smoothdir.magnitude());
- }
- else smoothdir.z = dir.z;
- d->o.add(smoothdir);
- d->o.z += maxstep + 0.1f;
- if(!collide(d, smoothdir))
- {
- d->o.z -= maxstep + 0.1f;
- if(d->physstate == PHYS_FALL || d->floor != floor)
- {
- d->timeinair = 0;
- d->floor = floor;
- switchfloor(d, dir, d->floor);
- }
- d->physstate = PHYS_STEP_UP;
- return true;
- }
- }
- }
-
- /* try stepping up */
- d->o = old;
- d->o.z += dir.magnitude();
- if(!collide(d, vec(0, 0, 1)))
- {
- if(d->physstate == PHYS_FALL || d->floor != floor)
- {
- d->timeinair = 0;
- d->floor = floor;
- switchfloor(d, dir, d->floor);
- }
- if(cansmooth) d->physstate = PHYS_STEP_UP;
- return true;
- }
- d->o = old;
- return false;
+ vec old(d->o), stairdir = (obstacle.z >= 0 && obstacle.z < SLOPEZ ? vec(-obstacle.x, -obstacle.y, 0) : vec(dir.x, dir.y, 0)).rescale(1);
+ bool cansmooth = true;
+ /* check if there is space atop the stair to move to */
+ if(d->physstate != PHYS_STEP_UP)
+ {
+ vec checkdir = stairdir;
+ checkdir.mul(0.1f);
+ checkdir.z += maxstep + 0.1f;
+ d->o.add(checkdir);
+ if(collide(d))
+ {
+ d->o = old;
+ if(!collide(d, vec(0, 0, -1), SLOPEZ)) return false;
+ cansmooth = false;
+ }
+ }
+
+ if(cansmooth)
+ {
+ vec checkdir = stairdir;
+ checkdir.z += 1;
+ checkdir.mul(maxstep);
+ d->o = old;
+ d->o.add(checkdir);
+ int scale = 2;
+ if(collide(d, checkdir))
+ {
+ if(!collide(d, vec(0, 0, -1), SLOPEZ))
+ {
+ d->o = old;
+ return false;
+ }
+ d->o.add(checkdir);
+ if(collide(d, vec(0, 0, -1), SLOPEZ)) scale = 1;
+ }
+ if(scale != 1)
+ {
+ d->o = old;
+ d->o.sub(checkdir.mul(vec(2, 2, 1)));
+ if(!collide(d, vec(0, 0, -1), SLOPEZ)) scale = 1;
+ }
+
+ d->o = old;
+ vec smoothdir(dir.x, dir.y, 0);
+ float magxy = smoothdir.magnitude();
+ if(magxy > 1e-9f)
+ {
+ if(magxy > scale*dir.z)
+ {
+ smoothdir.mul(1/magxy);
+ smoothdir.z = 1.0f/scale;
+ smoothdir.mul(dir.magnitude()/smoothdir.magnitude());
+ }
+ else smoothdir.z = dir.z;
+ d->o.add(smoothdir);
+ d->o.z += maxstep + 0.1f;
+ if(!collide(d, smoothdir))
+ {
+ d->o.z -= maxstep + 0.1f;
+ if(d->physstate == PHYS_FALL || d->floor != floor)
+ {
+ d->timeinair = 0;
+ d->floor = floor;
+ switchfloor(d, dir, d->floor);
+ }
+ d->physstate = PHYS_STEP_UP;
+ return true;
+ }
+ }
+ }
+
+ /* try stepping up */
+ d->o = old;
+ d->o.z += dir.magnitude();
+ if(!collide(d, vec(0, 0, 1)))
+ {
+ if(d->physstate == PHYS_FALL || d->floor != floor)
+ {
+ d->timeinair = 0;
+ d->floor = floor;
+ switchfloor(d, dir, d->floor);
+ }
+ if(cansmooth) d->physstate = PHYS_STEP_UP;
+ return true;
+ }
+ d->o = old;
+ return false;
}
bool trystepdown(physent *d, vec &dir, float step, float xy, float z, bool init = false)
{
- vec stepdir(dir.x, dir.y, 0);
- stepdir.z = -stepdir.magnitude2()*z/xy;
- if(!stepdir.z) return false;
- stepdir.normalize();
-
- vec old(d->o);
- d->o.add(vec(stepdir).mul(STAIRHEIGHT/fabs(stepdir.z))).z -= STAIRHEIGHT;
- d->zmargin = -STAIRHEIGHT;
- if(collide(d, vec(0, 0, -1), SLOPEZ))
- {
- d->o = old;
- d->o.add(vec(stepdir).mul(step));
- d->zmargin = 0;
- if(!collide(d, vec(0, 0, -1)))
- {
- vec stepfloor(stepdir);
- stepfloor.mul(-stepfloor.z).z += 1;
- stepfloor.normalize();
- if(d->physstate >= PHYS_SLOPE && d->floor != stepfloor)
- {
- // prevent alternating step-down/step-up states if player would keep bumping into the same floor
- vec stepped(d->o);
- d->o.z -= 0.5f;
- d->zmargin = -0.5f;
- if(collide(d, stepdir) && collidewall == d->floor)
- {
- d->o = old;
- if(!init) { d->o.x += dir.x; d->o.y += dir.y; if(dir.z <= 0 || collide(d, dir)) d->o.z += dir.z; }
- d->zmargin = 0;
- d->physstate = PHYS_STEP_DOWN;
- d->timeinair = 0;
- return true;
- }
- d->o = init ? old : stepped;
- d->zmargin = 0;
- }
- else if(init) d->o = old;
- switchfloor(d, dir, stepfloor);
- d->floor = stepfloor;
- d->physstate = PHYS_STEP_DOWN;
- d->timeinair = 0;
- return true;
- }
- }
- d->o = old;
- d->zmargin = 0;
- return false;
+ vec stepdir(dir.x, dir.y, 0);
+ stepdir.z = -stepdir.magnitude2()*z/xy;
+ if(!stepdir.z) return false;
+ stepdir.normalize();
+
+ vec old(d->o);
+ d->o.add(vec(stepdir).mul(STAIRHEIGHT/fabs(stepdir.z))).z -= STAIRHEIGHT;
+ d->zmargin = -STAIRHEIGHT;
+ if(collide(d, vec(0, 0, -1), SLOPEZ))
+ {
+ d->o = old;
+ d->o.add(vec(stepdir).mul(step));
+ d->zmargin = 0;
+ if(!collide(d, vec(0, 0, -1)))
+ {
+ vec stepfloor(stepdir);
+ stepfloor.mul(-stepfloor.z).z += 1;
+ stepfloor.normalize();
+ if(d->physstate >= PHYS_SLOPE && d->floor != stepfloor)
+ {
+ // prevent alternating step-down/step-up states if player would keep bumping into the same floor
+ vec stepped(d->o);
+ d->o.z -= 0.5f;
+ d->zmargin = -0.5f;
+ if(collide(d, stepdir) && collidewall == d->floor)
+ {
+ d->o = old;
+ if(!init) { d->o.x += dir.x; d->o.y += dir.y; if(dir.z <= 0 || collide(d, dir)) d->o.z += dir.z; }
+ d->zmargin = 0;
+ d->physstate = PHYS_STEP_DOWN;
+ d->timeinair = 0;
+ return true;
+ }
+ d->o = init ? old : stepped;
+ d->zmargin = 0;
+ }
+ else if(init) d->o = old;
+ switchfloor(d, dir, stepfloor);
+ d->floor = stepfloor;
+ d->physstate = PHYS_STEP_DOWN;
+ d->timeinair = 0;
+ return true;
+ }
+ }
+ d->o = old;
+ d->zmargin = 0;
+ return false;
}
bool trystepdown(physent *d, vec &dir, bool init = false)
{
- if((!d->move && !d->strafe) || !game::allowmove(d)) return false;
- vec old(d->o);
- d->o.z -= STAIRHEIGHT;
- d->zmargin = -STAIRHEIGHT;
- if(!collide(d, vec(0, 0, -1), SLOPEZ))
- {
- d->o = old;
- d->zmargin = 0;
- return false;
- }
- d->o = old;
- d->zmargin = 0;
- float step = dir.magnitude();
+ if((!d->move && !d->strafe) || !game::allowmove(d)) return false;
+ vec old(d->o);
+ d->o.z -= STAIRHEIGHT;
+ d->zmargin = -STAIRHEIGHT;
+ if(!collide(d, vec(0, 0, -1), SLOPEZ))
+ {
+ d->o = old;
+ d->zmargin = 0;
+ return false;
+ }
+ d->o = old;
+ d->zmargin = 0;
+ float step = dir.magnitude();
#if 1
- // weaker check, just enough to avoid hopping up slopes
- if(trystepdown(d, dir, step, 4, 1, init)) return true;
+ // weaker check, just enough to avoid hopping up slopes
+ if(trystepdown(d, dir, step, 4, 1, init)) return true;
#else
- if(trystepdown(d, dir, step, 2, 1, init)) return true;
- if(trystepdown(d, dir, step, 1, 1, init)) return true;
- if(trystepdown(d, dir, step, 1, 2, init)) return true;
+ if(trystepdown(d, dir, step, 2, 1, init)) return true;
+ if(trystepdown(d, dir, step, 1, 1, init)) return true;
+ if(trystepdown(d, dir, step, 1, 2, init)) return true;
#endif
- return false;
+ return false;
}
void falling(physent *d, vec &dir, const vec &floor)
{
- if(floor.z > 0.0f && floor.z < SLOPEZ)
- {
- if(floor.z >= WALLZ) switchfloor(d, dir, floor);
- d->timeinair = 0;
- d->physstate = PHYS_SLIDE;
- d->floor = floor;
- }
- else if(d->physstate < PHYS_SLOPE || dir.dot(d->floor) > 0.01f*dir.magnitude() || (floor.z != 0.0f && floor.z != 1.0f) || !trystepdown(d, dir, true))
- d->physstate = PHYS_FALL;
+ if(floor.z > 0.0f && floor.z < SLOPEZ)
+ {
+ if(floor.z >= WALLZ) switchfloor(d, dir, floor);
+ d->timeinair = 0;
+ d->physstate = PHYS_SLIDE;
+ d->floor = floor;
+ }
+ else if(d->physstate < PHYS_SLOPE || dir.dot(d->floor) > 0.01f*dir.magnitude() || (floor.z != 0.0f && floor.z != 1.0f) || !trystepdown(d, dir, true))
+ d->physstate = PHYS_FALL;
}
void landing(physent *d, vec &dir, const vec &floor, bool collided)
{
#if 0
- if(d->physstate == PHYS_FALL)
- {
- d->timeinair = 0;
- if(dir.z < 0.0f) dir.z = d->vel.z = 0.0f;
- }
+ if(d->physstate == PHYS_FALL)
+ {
+ d->timeinair = 0;
+ if(dir.z < 0.0f) dir.z = d->vel.z = 0.0f;
+ }
#endif
- switchfloor(d, dir, floor);
- d->timeinair = 0;
- if((d->physstate!=PHYS_STEP_UP && d->physstate!=PHYS_STEP_DOWN) || !collided)
- d->physstate = floor.z >= FLOORZ ? PHYS_FLOOR : PHYS_SLOPE;
- d->floor = floor;
+ switchfloor(d, dir, floor);
+ d->timeinair = 0;
+ if((d->physstate!=PHYS_STEP_UP && d->physstate!=PHYS_STEP_DOWN) || !collided)
+ d->physstate = floor.z >= FLOORZ ? PHYS_FLOOR : PHYS_SLOPE;
+ d->floor = floor;
}
bool findfloor(physent *d, bool collided, const vec &obstacle, bool &slide, vec &floor)
{
- bool found = false;
- vec moved(d->o);
- d->o.z -= 0.1f;
- if(collide(d, vec(0, 0, -1), d->physstate == PHYS_SLOPE || d->physstate == PHYS_STEP_DOWN ? SLOPEZ : FLOORZ))
- {
- floor = collidewall;
- found = true;
- }
- else if(collided && obstacle.z >= SLOPEZ)
- {
- floor = obstacle;
- found = true;
- slide = false;
- }
- else if(d->physstate == PHYS_STEP_UP || d->physstate == PHYS_SLIDE)
- {
- if(collide(d, vec(0, 0, -1)) && collidewall.z > 0.0f)
- {
- floor = collidewall;
- if(floor.z >= SLOPEZ) found = true;
- }
- }
- else if(d->physstate >= PHYS_SLOPE && d->floor.z < 1.0f)
- {
- if(collide(d, vec(d->floor).neg(), 0.95f) || collide(d, vec(0, 0, -1)))
- {
- floor = collidewall;
- if(floor.z >= SLOPEZ && floor.z < 1.0f) found = true;
- }
- }
- if(collided && (!found || obstacle.z > floor.z))
- {
- floor = obstacle;
- slide = !found && (floor.z < WALLZ || floor.z >= SLOPEZ);
- }
- d->o = moved;
- return found;
+ bool found = false;
+ vec moved(d->o);
+ d->o.z -= 0.1f;
+ if(collide(d, vec(0, 0, -1), d->physstate == PHYS_SLOPE || d->physstate == PHYS_STEP_DOWN ? SLOPEZ : FLOORZ))
+ {
+ floor = collidewall;
+ found = true;
+ }
+ else if(collided && obstacle.z >= SLOPEZ)
+ {
+ floor = obstacle;
+ found = true;
+ slide = false;
+ }
+ else if(d->physstate == PHYS_STEP_UP || d->physstate == PHYS_SLIDE)
+ {
+ if(collide(d, vec(0, 0, -1)) && collidewall.z > 0.0f)
+ {
+ floor = collidewall;
+ if(floor.z >= SLOPEZ) found = true;
+ }
+ }
+ else if(d->physstate >= PHYS_SLOPE && d->floor.z < 1.0f)
+ {
+ if(collide(d, vec(d->floor).neg(), 0.95f) || collide(d, vec(0, 0, -1)))
+ {
+ floor = collidewall;
+ if(floor.z >= SLOPEZ && floor.z < 1.0f) found = true;
+ }
+ }
+ if(collided && (!found || obstacle.z > floor.z))
+ {
+ floor = obstacle;
+ slide = !found && (floor.z < WALLZ || floor.z >= SLOPEZ);
+ }
+ d->o = moved;
+ return found;
}
bool move(physent *d, vec &dir)
{
- vec old(d->o);
- bool collided = false, slidecollide = false;
- vec obstacle = vec(0, 0, 0);
- d->o.add(dir);
- if(collide(d, dir) || ((d->type==ENT_AI || d->type==ENT_INANIMATE) && collide(d, vec(0, 0, 0), 0, false)))
- {
- obstacle = collidewall;
- /* check to see if there is an obstacle that would prevent this one from being used as a floor (or ceiling bump) */
- if(d->type==ENT_PLAYER && ((collidewall.z>=SLOPEZ && dir.z<0) || (collidewall.z<=-SLOPEZ && dir.z>0)) && (dir.x || dir.y) && collide(d, vec(dir.x, dir.y, 0)))
- {
- if(collidewall.dot(dir) >= 0) slidecollide = true;
- obstacle = collidewall;
- }
- d->o = old;
- d->o.z -= STAIRHEIGHT;
- d->zmargin = -STAIRHEIGHT;
- if(d->physstate == PHYS_SLOPE || d->physstate == PHYS_FLOOR || (collide(d, vec(0, 0, -1), SLOPEZ) && (d->physstate==PHYS_STEP_UP || d->physstate==PHYS_STEP_DOWN || collidewall.z>=FLOORZ)))
- {
- d->o = old;
- d->zmargin = 0;
- if(trystepup(d, dir, obstacle, STAIRHEIGHT, d->physstate == PHYS_SLOPE || d->physstate == PHYS_FLOOR ? d->floor : vec(collidewall))) return true;
- }
- else
- {
- d->o = old;
- d->zmargin = 0;
- }
- /* can't step over the obstacle, so just slide against it */
- collided = true;
- }
- else if(d->physstate == PHYS_STEP_UP)
- {
- if(collide(d, vec(0, 0, -1), SLOPEZ))
- {
- d->o = old;
- if(trystepup(d, dir, vec(0, 0, 1), STAIRHEIGHT, vec(collidewall))) return true;
- d->o.add(dir);
- }
- }
- else if(d->physstate == PHYS_STEP_DOWN && dir.dot(d->floor) <= 1e-6f)
- {
- vec moved(d->o);
- d->o = old;
- if(trystepdown(d, dir)) return true;
- d->o = moved;
- }
- vec floor(0, 0, 0);
- bool slide = collided,
- found = findfloor(d, collided, obstacle, slide, floor);
- if(slide || (!collided && floor.z > 0 && floor.z < WALLZ))
- {
- slideagainst(d, dir, slide ? obstacle : floor, found, slidecollide);
- //if(d->type == ENT_AI || d->type == ENT_INANIMATE)
- d->blocked = true;
- }
- if(found) landing(d, dir, floor, collided);
- else falling(d, dir, floor);
- return !collided;
+ vec old(d->o);
+ bool collided = false, slidecollide = false;
+ vec obstacle = vec(0, 0, 0);
+ d->o.add(dir);
+ if(collide(d, dir) || ((d->type==ENT_AI || d->type==ENT_INANIMATE) && collide(d, vec(0, 0, 0), 0, false)))
+ {
+ obstacle = collidewall;
+ /* check to see if there is an obstacle that would prevent this one from being used as a floor (or ceiling bump) */
+ if(d->type==ENT_PLAYER && ((collidewall.z>=SLOPEZ && dir.z<0) || (collidewall.z<=-SLOPEZ && dir.z>0)) && (dir.x || dir.y) && collide(d, vec(dir.x, dir.y, 0)))
+ {
+ if(collidewall.dot(dir) >= 0) slidecollide = true;
+ obstacle = collidewall;
+ }
+ d->o = old;
+ d->o.z -= STAIRHEIGHT;
+ d->zmargin = -STAIRHEIGHT;
+ if(d->physstate == PHYS_SLOPE || d->physstate == PHYS_FLOOR || (collide(d, vec(0, 0, -1), SLOPEZ) && (d->physstate==PHYS_STEP_UP || d->physstate==PHYS_STEP_DOWN || collidewall.z>=FLOORZ)))
+ {
+ d->o = old;
+ d->zmargin = 0;
+ if(trystepup(d, dir, obstacle, STAIRHEIGHT, d->physstate == PHYS_SLOPE || d->physstate == PHYS_FLOOR ? d->floor : vec(collidewall))) return true;
+ }
+ else
+ {
+ d->o = old;
+ d->zmargin = 0;
+ }
+ /* can't step over the obstacle, so just slide against it */
+ collided = true;
+ }
+ else if(d->physstate == PHYS_STEP_UP)
+ {
+ if(collide(d, vec(0, 0, -1), SLOPEZ))
+ {
+ d->o = old;
+ if(trystepup(d, dir, vec(0, 0, 1), STAIRHEIGHT, vec(collidewall))) return true;
+ d->o.add(dir);
+ }
+ }
+ else if(d->physstate == PHYS_STEP_DOWN && dir.dot(d->floor) <= 1e-6f)
+ {
+ vec moved(d->o);
+ d->o = old;
+ if(trystepdown(d, dir)) return true;
+ d->o = moved;
+ }
+ vec floor(0, 0, 0);
+ bool slide = collided,
+ found = findfloor(d, collided, obstacle, slide, floor);
+ if(slide || (!collided && floor.z > 0 && floor.z < WALLZ))
+ {
+ slideagainst(d, dir, slide ? obstacle : floor, found, slidecollide);
+ //if(d->type == ENT_AI || d->type == ENT_INANIMATE)
+ d->blocked = true;
+ }
+ if(found) landing(d, dir, floor, collided);
+ else falling(d, dir, floor);
+ return !collided;
}
bool bounce(physent *d, float secs, float elasticity, float waterfric, float grav)
{
- // make sure bouncers don't start inside geometry
- if(d->physstate!=PHYS_BOUNCE && collide(d, vec(0, 0, 0), 0, false)) return true;
- int mat = lookupmaterial(vec(d->o.x, d->o.y, d->o.z + (d->aboveeye - d->eyeheight)/2));
- bool water = isliquid(mat);
- if(water)
- {
- d->vel.z -= grav*GRAVITY/16*secs;
- d->vel.mul(max(1.0f - secs/waterfric, 0.0f));
- }
- else d->vel.z -= grav*GRAVITY*secs;
- vec old(d->o);
- loopi(2)
- {
- vec dir(d->vel);
- dir.mul(secs);
- d->o.add(dir);
- if(!collide(d, dir, 0, true, true))
- {
- if(collideinside)
- {
- d->o = old;
- d->vel.mul(-elasticity);
- }
- break;
- }
- else if(collideplayer) break;
- d->o = old;
- game::bounced(d, collidewall);
- float c = collidewall.dot(d->vel),
- k = 1.0f + (1.0f-elasticity)*c/d->vel.magnitude();
- d->vel.mul(k);
- d->vel.sub(vec(collidewall).mul(elasticity*2.0f*c));
- }
- if(d->physstate!=PHYS_BOUNCE)
- {
- // make sure bouncers don't start inside geometry
- if(d->o == old) return !collideplayer;
- d->physstate = PHYS_BOUNCE;
- }
- return collideplayer!=NULL;
+ // make sure bouncers don't start inside geometry
+ if(d->physstate!=PHYS_BOUNCE && collide(d, vec(0, 0, 0), 0, false)) return true;
+ int mat = lookupmaterial(vec(d->o.x, d->o.y, d->o.z + (d->aboveeye - d->eyeheight)/2));
+ bool water = isliquid(mat);
+ if(water)
+ {
+ d->vel.z -= grav*GRAVITY/16*secs;
+ d->vel.mul(max(1.0f - secs/waterfric, 0.0f));
+ }
+ else d->vel.z -= grav*GRAVITY*secs;
+ vec old(d->o);
+ loopi(2)
+ {
+ vec dir(d->vel);
+ dir.mul(secs);
+ d->o.add(dir);
+ if(!collide(d, dir, 0, true, true))
+ {
+ if(collideinside)
+ {
+ d->o = old;
+ d->vel.mul(-elasticity);
+ }
+ break;
+ }
+ else if(collideplayer) break;
+ d->o = old;
+ game::bounced(d, collidewall);
+ float c = collidewall.dot(d->vel),
+ k = 1.0f + (1.0f-elasticity)*c/d->vel.magnitude();
+ d->vel.mul(k);
+ d->vel.sub(vec(collidewall).mul(elasticity*2.0f*c));
+ }
+ if(d->physstate!=PHYS_BOUNCE)
+ {
+ // make sure bouncers don't start inside geometry
+ if(d->o == old) return !collideplayer;
+ d->physstate = PHYS_BOUNCE;
+ }
+ return collideplayer!=NULL;
}
void avoidcollision(physent *d, const vec &dir, physent *obstacle, float space)
{
- float rad = obstacle->radius+d->radius;
- vec bbmin(obstacle->o);
- bbmin.x -= rad;
- bbmin.y -= rad;
- bbmin.z -= obstacle->eyeheight+d->aboveeye;
- bbmin.sub(space);
- vec bbmax(obstacle->o);
- bbmax.x += rad;
- bbmax.y += rad;
- bbmax.z += obstacle->aboveeye+d->eyeheight;
- bbmax.add(space);
-
- loopi(3) if(d->o[i] <= bbmin[i] || d->o[i] >= bbmax[i]) return;
-
- float mindist = 1e16f;
- loopi(3) if(dir[i] != 0)
- {
- float dist = ((dir[i] > 0 ? bbmax[i] : bbmin[i]) - d->o[i]) / dir[i];
- mindist = min(mindist, dist);
- }
- if(mindist >= 0.0f && mindist < 1e15f) d->o.add(vec(dir).mul(mindist));
+ float rad = obstacle->radius+d->radius;
+ vec bbmin(obstacle->o);
+ bbmin.x -= rad;
+ bbmin.y -= rad;
+ bbmin.z -= obstacle->eyeheight+d->aboveeye;
+ bbmin.sub(space);
+ vec bbmax(obstacle->o);
+ bbmax.x += rad;
+ bbmax.y += rad;
+ bbmax.z += obstacle->aboveeye+d->eyeheight;
+ bbmax.add(space);
+
+ loopi(3) if(d->o[i] <= bbmin[i] || d->o[i] >= bbmax[i]) return;
+
+ float mindist = 1e16f;
+ loopi(3) if(dir[i] != 0)
+ {
+ float dist = ((dir[i] > 0 ? bbmax[i] : bbmin[i]) - d->o[i]) / dir[i];
+ mindist = min(mindist, dist);
+ }
+ if(mindist >= 0.0f && mindist < 1e15f) d->o.add(vec(dir).mul(mindist));
}
bool movecamera(physent *pl, const vec &dir, float dist, float stepdist)
{
- int steps = (int)ceil(dist/stepdist);
- if(steps <= 0) return true;
-
- vec d(dir);
- d.mul(dist/steps);
- loopi(steps)
- {
- vec oldpos(pl->o);
- pl->o.add(d);
- if(collide(pl, vec(0, 0, 0), 0, false))
- {
- pl->o = oldpos;
- return false;
- }
- }
- return true;
-}
-
-bool droptofloor(vec &o, float radius, float height)
-{
- static struct dropent : physent
- {
- dropent()
- {
- type = ENT_BOUNCE;
- vel = vec(0, 0, -1);
- }
- } d;
- d.o = o;
- if(!insideworld(d.o))
- {
- if(d.o.z < worldsize) return false;
- d.o.z = worldsize - 1e-3f;
- if(!insideworld(d.o)) return false;
- }
- vec v(0.0001f, 0.0001f, -1);
- v.normalize();
- if(raycube(d.o, v, worldsize) >= worldsize) return false;
- d.radius = d.xradius = d.yradius = radius;
- d.eyeheight = height;
- d.aboveeye = radius;
- if(!movecamera(&d, d.vel, worldsize, 1))
- {
- o = d.o;
- return true;
- }
- return false;
-}
+ int steps = (int)ceil(dist/stepdist);
+ if(steps <= 0) return true;
-float dropheight(entity &e)
-{
- switch(e.type)
- {
- case ET_PARTICLES:
- case ET_MAPMODEL:
- return 0.0f;
- default:
- return 4.0f;
- }
+ vec d(dir);
+ d.mul(dist/steps);
+ loopi(steps)
+ {
+ vec oldpos(pl->o);
+ pl->o.add(d);
+ if(collide(pl, vec(0, 0, 0), 0, false))
+ {
+ pl->o = oldpos;
+ return false;
+ }
+ }
+ return true;
}
void dropenttofloor(entity *e)
{
- droptofloor(e->o, 1.0f, 4.0f);
+ float radius = 1.0f;
+ float height = 4.0f;
+ vec o = e->o;
+ static struct dropent : physent
+ {
+ dropent()
+ {
+ type = ENT_BOUNCE;
+ vel = vec(0, 0, -1);
+ }
+ } d;
+ d.o = o;
+ if(!insideworld(d.o))
+ {
+ if(d.o.z < worldsize) return;
+ d.o.z = worldsize - 1e-3f;
+ if(!insideworld(d.o)) return;
+ }
+ vec v(0.0001f, 0.0001f, -1);
+ v.normalize();
+ if(raycube(d.o, v, worldsize) >= worldsize) return;
+ d.radius = d.xradius = d.yradius = radius;
+ d.eyeheight = height;
+ d.aboveeye = radius;
+ if(!movecamera(&d, d.vel, worldsize, 1))
+ o = d.o;
}
-void phystest()
+void vecfromyawpitch(float yaw, float pitch, int move, int strafe, vec &m)
{
- static const char * const states[] = {"float", "fall", "slide", "slope", "floor", "step up", "step down", "bounce"};
- printf ("PHYS(pl): %s, air %d, floor: (%f, %f, %f), vel: (%f, %f, %f), g: (%f, %f, %f)\n", states[player->physstate], player->timeinair, player->floor.x, player->floor.y, player->floor.z, player->vel.x, player->vel.y, player->vel.z, player->falling.x, player->falling.y, player->falling.z);
- printf ("PHYS(cam): %s, air %d, floor: (%f, %f, %f), vel: (%f, %f, %f), g: (%f, %f, %f)\n", states[camera1->physstate], camera1->timeinair, camera1->floor.x, camera1->floor.y, camera1->floor.z, camera1->vel.x, camera1->vel.y, camera1->vel.z, camera1->falling.x, camera1->falling.y, camera1->falling.z);
-}
+ if(move)
+ {
+ m.x = move*-sinf(RAD*yaw);
+ m.y = move*cosf(RAD*yaw);
+ }
+ else m.x = m.y = 0;
-COMMAND(phystest, "");
+ if(pitch)
+ {
+ m.x *= cosf(RAD*pitch);
+ m.y *= cosf(RAD*pitch);
+ m.z = move*sinf(RAD*pitch);
+ }
+ else m.z = 0;
-void vecfromyawpitch(float yaw, float pitch, int move, int strafe, vec &m)
-{
- if(move)
- {
- m.x = move*-sinf(RAD*yaw);
- m.y = move*cosf(RAD*yaw);
- }
- else m.x = m.y = 0;
-
- if(pitch)
- {
- m.x *= cosf(RAD*pitch);
- m.y *= cosf(RAD*pitch);
- m.z = move*sinf(RAD*pitch);
- }
- else m.z = 0;
-
- if(strafe)
- {
- m.x += strafe*cosf(RAD*yaw);
- m.y += strafe*sinf(RAD*yaw);
- }
+ if(strafe)
+ {
+ m.x += strafe*cosf(RAD*yaw);
+ m.y += strafe*sinf(RAD*yaw);
+ }
}
void vectoyawpitch(const vec &v, float &yaw, float &pitch)
{
- if(v.iszero()) yaw = pitch = 0;
- else
- {
- yaw = -atan2(v.x, v.y)/RAD;
- pitch = asin(v.z/v.magnitude())/RAD;
- }
+ if(v.iszero()) yaw = pitch = 0;
+ else
+ {
+ yaw = -atan2(v.x, v.y)/RAD;
+ pitch = asin(v.z/v.magnitude())/RAD;
+ }
}
#define PHYSFRAMETIME 5
@@ -1589,93 +1562,93 @@ VAR(floatspeed, 1, 100, 10000);
void modifyvelocity(physent *pl, bool local, bool water, bool floating, int curtime)
{
- bool allowmove = game::allowmove(pl);
- if(floating)
- {
- if(pl->jumping && allowmove)
- {
- pl->jumping = false;
- pl->vel.z = max(pl->vel.z, JUMPVEL);
- }
- }
- else if(pl->physstate >= PHYS_SLOPE || water)
- {
- if(water && !pl->inwater) pl->vel.div(8);
- if(pl->jumping && allowmove)
- {
- pl->jumping = false;
-
- pl->vel.z = max(pl->vel.z, JUMPVEL); // physics impulse upwards
- if(water) { pl->vel.x /= 8.0f; pl->vel.y /= 8.0f; } // dampen velocity change even harder, gives correct water feel
-
- game::physicstrigger(pl, local, 1, 0);
- }
- }
- if(!floating && pl->physstate == PHYS_FALL) pl->timeinair = min(pl->timeinair + curtime, 1000);
-
- vec m(0.0f, 0.0f, 0.0f);
- if((pl->move || pl->strafe) && allowmove)
- {
- vecfromyawpitch(pl->yaw, floating || water || pl->type==ENT_CAMERA ? pl->pitch : 0, pl->move, pl->strafe, m);
-
- if(!floating && pl->physstate >= PHYS_SLOPE)
- {
- /* move up or down slopes in air
- * but only move up slopes in water
- */
- float dz = -(m.x*pl->floor.x + m.y*pl->floor.y)/pl->floor.z;
- m.z = water ? max(m.z, dz) : dz;
- }
-
- m.normalize();
- }
-
- vec d(m);
- speedmodifier*=(pl->physstate!=PHYS_FLOOR)*(speedmodifier>0);
- speedmodifier=(speedmodifier>100.0f)?100.0f:speedmodifier;
- d.mul(pl->maxspeed + speedmodifier);
-
- if(pl->type==ENT_PLAYER)
- {
- if(floating)
- {
- if(pl==player) d.mul(floatspeed/100.0f);
- }
- else if(!water && allowmove) d.mul((pl->move && !pl->strafe ? 1.3f : 1.0f) * (pl->physstate < PHYS_SLOPE ? 1.3f : 1.0f));
- }
- float fric = water && !floating ? 20.0f : (pl->physstate >= PHYS_SLOPE || floating ? 6.0f : 30.0f);
- pl->vel.lerp(d, pl->vel, pow(1 - 1/fric, curtime/20.0f));
+ bool allowmove = game::allowmove(pl);
+ if(floating)
+ {
+ if(pl->jumping && allowmove)
+ {
+ pl->jumping = false;
+ pl->vel.z = max(pl->vel.z, JUMPVEL);
+ }
+ }
+ else if(pl->physstate >= PHYS_SLOPE || water)
+ {
+ if(water && !pl->inwater) pl->vel.div(8);
+ if(pl->jumping && allowmove)
+ {
+ pl->jumping = false;
+
+ pl->vel.z = max(pl->vel.z, JUMPVEL); // physics impulse upwards
+ if(water) { pl->vel.x /= 8.0f; pl->vel.y /= 8.0f; } // dampen velocity change even harder, gives correct water feel
+
+ game::physicstrigger(pl, local, 1, 0);
+ }
+ }
+ if(!floating && pl->physstate == PHYS_FALL) pl->timeinair = min(pl->timeinair + curtime, 1000);
+
+ vec m(0.0f, 0.0f, 0.0f);
+ if((pl->move || pl->strafe) && allowmove)
+ {
+ vecfromyawpitch(pl->yaw, floating || water || pl->type==ENT_CAMERA ? pl->pitch : 0, pl->move, pl->strafe, m);
+
+ if(!floating && pl->physstate >= PHYS_SLOPE)
+ {
+ /* move up or down slopes in air
+ * but only move up slopes in water
+ */
+ float dz = -(m.x*pl->floor.x + m.y*pl->floor.y)/pl->floor.z;
+ m.z = water ? max(m.z, dz) : dz;
+ }
+
+ m.normalize();
+ }
+
+ vec d(m);
+ speedmodifier*=(pl->physstate!=PHYS_FLOOR)*(speedmodifier>0);
+ speedmodifier=(speedmodifier>100.0f)?100.0f:speedmodifier;
+ d.mul(pl->maxspeed + speedmodifier);
+
+ if(pl->type==ENT_PLAYER)
+ {
+ if(floating)
+ {
+ if(pl==player) d.mul(floatspeed/100.0f);
+ }
+ else if(!water && allowmove) d.mul((pl->move && !pl->strafe ? 1.3f : 1.0f) * (pl->physstate < PHYS_SLOPE ? 1.3f : 1.0f));
+ }
+ float fric = water && !floating ? 20.0f : (pl->physstate >= PHYS_SLOPE || floating ? 6.0f : 30.0f);
+ pl->vel.lerp(d, pl->vel, pow(1 - 1/fric, curtime/20.0f));
// old fps friction
-// float friction = water && !floating ? 20.0f : (pl->physstate >= PHYS_SLOPE || floating ? 6.0f : 30.0f);
-// float fpsfric = min(curtime/(20.0f*friction), 1.0f);
-// pl->vel.lerp(pl->vel, d, fpsfric);
+// float friction = water && !floating ? 20.0f : (pl->physstate >= PHYS_SLOPE || floating ? 6.0f : 30.0f);
+// float fpsfric = min(curtime/(20.0f*friction), 1.0f);
+// pl->vel.lerp(pl->vel, d, fpsfric);
}
void modifygravity(physent *pl, bool water, int curtime)
{
- float secs = curtime/1000.0f;
- vec g(0, 0, 0);
- if(pl->physstate == PHYS_FALL) g.z -= GRAVITY*secs;
- else if(pl->floor.z > 0 && pl->floor.z < FLOORZ)
- {
- g.z = -1;
- g.project(pl->floor);
- g.normalize();
- g.mul(GRAVITY*secs);
- }
- if(!water || !game::allowmove(pl) || (!pl->move && !pl->strafe)) pl->falling.add(g);
-
- if(water || pl->physstate >= PHYS_SLOPE)
- {
- float fric = water ? 2.0f : 6.0f,
- c = water ? 1.0f : clamp((pl->floor.z - SLOPEZ)/(FLOORZ-SLOPEZ), 0.0f, 1.0f);
- pl->falling.mul(pow(1 - c/fric, curtime/20.0f));
+ float secs = curtime/1000.0f;
+ vec g(0, 0, 0);
+ if(pl->physstate == PHYS_FALL) g.z -= GRAVITY*secs;
+ else if(pl->floor.z > 0 && pl->floor.z < FLOORZ)
+ {
+ g.z = -1;
+ g.project(pl->floor);
+ g.normalize();
+ g.mul(GRAVITY*secs);
+ }
+ if(!water || !game::allowmove(pl) || (!pl->move && !pl->strafe)) pl->falling.add(g);
+
+ if(water || pl->physstate >= PHYS_SLOPE)
+ {
+ float fric = water ? 2.0f : 6.0f,
+ c = water ? 1.0f : clamp((pl->floor.z - SLOPEZ)/(FLOORZ-SLOPEZ), 0.0f, 1.0f);
+ pl->falling.mul(pow(1 - c/fric, curtime/20.0f));
// old fps friction
-// float friction = water ? 2.0f : 6.0f,
-// fpsfric = friction/curtime*20.0f,
-// c = water ? 1.0f : clamp((pl->floor.z - SLOPEZ)/(FLOORZ-SLOPEZ), 0.0f, 1.0f);
-// pl->falling.mul(1 - c/fpsfric);
- }
+// float friction = water ? 2.0f : 6.0f,
+// fpsfric = friction/curtime*20.0f,
+// c = water ? 1.0f : clamp((pl->floor.z - SLOPEZ)/(FLOORZ-SLOPEZ), 0.0f, 1.0f);
+// pl->falling.mul(1 - c/fpsfric);
+ }
}
// main physics routine, moves a player/monster for a curtime step
@@ -1684,377 +1657,218 @@ void modifygravity(physent *pl, bool water, int curtime)
bool moveplayer(physent *pl, int moveres, bool local, int curtime)
{
- int material = lookupmaterial(vec(pl->o.x, pl->o.y, pl->o.z + (3*pl->aboveeye - pl->eyeheight)/4));
- bool water = isliquid(material&MATF_VOLUME);
- bool floating = pl->type==ENT_PLAYER && (pl->state==CS_EDITING || pl->state==CS_SPECTATOR);
- float secs = curtime/1000.f;
-
- // apply gravity
- if(!floating) modifygravity(pl, water, curtime);
- // apply any player generated changes in velocity
- modifyvelocity(pl, local, water, floating, curtime);
-
- vec d(pl->vel);
- if(!floating && water) d.mul(0.5f);
- d.add(pl->falling);
- d.mul(secs);
-
- pl->blocked = false;
-
- if(floating) // just apply velocity
- {
- if(pl->physstate != PHYS_FLOAT)
- {
- pl->physstate = PHYS_FLOAT;
- pl->timeinair = 0;
- pl->falling = vec(0, 0, 0);
- }
- pl->o.add(d);
- }
- else // apply velocity with collision
- {
- const float f = 1.0f/moveres;
- const int timeinair = pl->timeinair;
- int collisions = 0;
-
- d.mul(f);
- loopi(moveres) if(!move(pl, d) && ++collisions<5) i--; // discrete steps collision detection & sliding
- if(timeinair > 800 && !pl->timeinair && !water) // if we land after long time must have been a high jump, make thud sound
- {
- game::physicstrigger(pl, local, -1, 0);
- }
- }
-
- if(pl->state==CS_ALIVE) updatedynentcache(pl);
-
- // automatically apply smooth roll when strafing
-
- if(pl->strafe && maxroll) pl->roll = clamp(pl->roll - pow(clamp(1.0f + pl->strafe*pl->roll/maxroll, 0.0f, 1.0f), 0.33f)*pl->strafe*curtime*straferoll, -maxroll, maxroll);
- else pl->roll *= curtime == PHYSFRAMETIME ? faderoll : pow(faderoll, curtime/float(PHYSFRAMETIME));
-
- // play sounds on water transitions
-
- if(pl->inwater && !water)
- {
- material = lookupmaterial(vec(pl->o.x, pl->o.y, pl->o.z + (pl->aboveeye - pl->eyeheight)/2));
- water = isliquid(material&MATF_VOLUME);
- }
- if(!pl->inwater && water) game::physicstrigger(pl, local, 0, -1, material&MATF_VOLUME);
- else if(pl->inwater && !water) game::physicstrigger(pl, local, 0, 1, pl->inwater);
- pl->inwater = water ? material&MATF_VOLUME : MAT_AIR;
-
- if(pl->state==CS_ALIVE && (pl->o.z < 0 || material&MAT_DEATH)) game::suicide(pl);
-
- return true;
+ int material = lookupmaterial(vec(pl->o.x, pl->o.y, pl->o.z + (3*pl->aboveeye - pl->eyeheight)/4));
+ bool water = isliquid(material&MATF_VOLUME);
+ bool floating = pl->type==ENT_PLAYER && (pl->state==CS_EDITING || pl->state==CS_SPECTATOR);
+ float secs = curtime/1000.f;
+
+ // apply gravity
+ if(!floating) modifygravity(pl, water, curtime);
+ // apply any player generated changes in velocity
+ modifyvelocity(pl, local, water, floating, curtime);
+
+ vec d(pl->vel);
+ if(!floating && water) d.mul(0.5f);
+ d.add(pl->falling);
+ d.mul(secs);
+
+ pl->blocked = false;
+
+ if(floating) // just apply velocity
+ {
+ if(pl->physstate != PHYS_FLOAT)
+ {
+ pl->physstate = PHYS_FLOAT;
+ pl->timeinair = 0;
+ pl->falling = vec(0, 0, 0);
+ }
+ pl->o.add(d);
+ }
+ else // apply velocity with collision
+ {
+ const float f = 1.0f/moveres;
+ const int timeinair = pl->timeinair;
+ int collisions = 0;
+
+ d.mul(f);
+ loopi(moveres) if(!move(pl, d) && ++collisions<5) i--; // discrete steps collision detection & sliding
+ if(timeinair > 800 && !pl->timeinair && !water) // if we land after long time must have been a high jump, make thud sound
+ {
+ game::physicstrigger(pl, local, -1, 0);
+ }
+ }
+
+ if(pl->state==CS_ALIVE) updatedynentcache(pl);
+
+ // automatically apply smooth roll when strafing
+
+ if(pl->strafe && maxroll) pl->roll = clamp(pl->roll - pow(clamp(1.0f + pl->strafe*pl->roll/maxroll, 0.0f, 1.0f), 0.33f)*pl->strafe*curtime*straferoll, -maxroll, maxroll);
+ else pl->roll *= curtime == PHYSFRAMETIME ? faderoll : pow(faderoll, curtime/float(PHYSFRAMETIME));
+
+ // play sounds on water transitions
+
+ if(pl->inwater && !water)
+ {
+ material = lookupmaterial(vec(pl->o.x, pl->o.y, pl->o.z + (pl->aboveeye - pl->eyeheight)/2));
+ water = isliquid(material&MATF_VOLUME);
+ }
+ if(!pl->inwater && water) game::physicstrigger(pl, local, 0, -1, material&MATF_VOLUME);
+ else if(pl->inwater && !water) game::physicstrigger(pl, local, 0, 1, pl->inwater);
+ pl->inwater = water ? material&MATF_VOLUME : MAT_AIR;
+
+ if(pl->state==CS_ALIVE && (pl->o.z < 0 || material&MAT_DEATH)) game::suicide(pl);
+
+ return true;
}
int physsteps = 0, physframetime = PHYSFRAMETIME, lastphysframe = 0;
-void physicsframe() // optimally schedule physics frames inside the graphics frames
+void physicsframe() // optimally schedule physics frames inside the graphics frames
{
- int diff = lastmillis - lastphysframe;
- if(diff <= 0) physsteps = 0;
- else
- {
- physframetime = clamp(game::scaletime(PHYSFRAMETIME)/100, 1, PHYSFRAMETIME);
- physsteps = (diff + physframetime - 1)/physframetime;
- lastphysframe += physsteps * physframetime;
- }
- cleardynentcache();
+ int diff = lastmillis - lastphysframe;
+ if(diff <= 0) physsteps = 0;
+ else
+ {
+ physframetime = clamp(game::scaletime(PHYSFRAMETIME)/100, 1, PHYSFRAMETIME);
+ physsteps = (diff + physframetime - 1)/physframetime;
+ lastphysframe += physsteps * physframetime;
+ }
+ cleardynentcache();
}
VAR(physinterp, 0, 1, 1);
void interppos(physent *pl)
{
- pl->o = pl->newpos;
+ pl->o = pl->newpos;
- int diff = lastphysframe - lastmillis;
- if(diff <= 0 || !physinterp) return;
+ int diff = lastphysframe - lastmillis;
+ if(diff <= 0 || !physinterp) return;
- vec deltapos(pl->deltapos);
- deltapos.mul(min(diff, physframetime)/float(physframetime));
- pl->o.add(deltapos);
+ vec deltapos(pl->deltapos);
+ deltapos.mul(min(diff, physframetime)/float(physframetime));
+ pl->o.add(deltapos);
}
void moveplayer(physent *pl, int moveres, bool local)
{
- if(physsteps <= 0)
- {
- if(local) interppos(pl);
- return;
- }
-
- if(local) pl->o = pl->newpos;
- loopi(physsteps-1) moveplayer(pl, moveres, local, physframetime);
- if(local) pl->deltapos = pl->o;
- moveplayer(pl, moveres, local, physframetime);
- if(local)
- {
- pl->newpos = pl->o;
- pl->deltapos.sub(pl->newpos);
- interppos(pl);
- }
+ if(physsteps <= 0)
+ {
+ if(local) interppos(pl);
+ return;
+ }
+
+ if(local) pl->o = pl->newpos;
+ loopi(physsteps-1) moveplayer(pl, moveres, local, physframetime);
+ if(local) pl->deltapos = pl->o;
+ moveplayer(pl, moveres, local, physframetime);
+ if(local)
+ {
+ pl->newpos = pl->o;
+ pl->deltapos.sub(pl->newpos);
+ interppos(pl);
+ }
}
bool bounce(physent *d, float elasticity, float waterfric, float grav)
{
- if(physsteps <= 0)
- {
- interppos(d);
- return false;
- }
-
- d->o = d->newpos;
- bool hitplayer = false;
- loopi(physsteps-1)
- {
- if(bounce(d, physframetime/1000.0f, elasticity, waterfric, grav)) hitplayer = true;
- }
- d->deltapos = d->o;
- if(bounce(d, physframetime/1000.0f, elasticity, waterfric, grav)) hitplayer = true;
- d->newpos = d->o;
- d->deltapos.sub(d->newpos);
- interppos(d);
- return hitplayer;
+ if(physsteps <= 0)
+ {
+ interppos(d);
+ return false;
+ }
+
+ d->o = d->newpos;
+ bool hitplayer = false;
+ loopi(physsteps-1)
+ {
+ if(bounce(d, physframetime/1000.0f, elasticity, waterfric, grav)) hitplayer = true;
+ }
+ d->deltapos = d->o;
+ if(bounce(d, physframetime/1000.0f, elasticity, waterfric, grav)) hitplayer = true;
+ d->newpos = d->o;
+ d->deltapos.sub(d->newpos);
+ interppos(d);
+ return hitplayer;
}
void updatephysstate(physent *d)
{
- if(d->physstate == PHYS_FALL) return;
- d->timeinair = 0;
- vec old(d->o);
- /* Attempt to reconstruct the floor state.
- * May be inaccurate since movement collisions are not considered.
- * If good floor is not found, just keep the old floor and hope it's correct enough.
- */
- switch(d->physstate)
- {
- case PHYS_SLOPE:
- case PHYS_FLOOR:
- case PHYS_STEP_DOWN:
- d->o.z -= 0.15f;
- if(collide(d, vec(0, 0, -1), d->physstate == PHYS_SLOPE || d->physstate == PHYS_STEP_DOWN ? SLOPEZ : FLOORZ))
- d->floor = collidewall;
- break;
-
- case PHYS_STEP_UP:
- d->o.z -= STAIRHEIGHT+0.15f;
- if(collide(d, vec(0, 0, -1), SLOPEZ))
- d->floor = collidewall;
- break;
-
- case PHYS_SLIDE:
- d->o.z -= 0.15f;
- if(collide(d, vec(0, 0, -1)) && collidewall.z < SLOPEZ)
- d->floor = collidewall;
- break;
- }
- if(d->physstate > PHYS_FALL && d->floor.z <= 0) d->floor = vec(0, 0, 1);
- d->o = old;
-}
-
-const float PLATFORMMARGIN = 0.2f;
-const float PLATFORMBORDER = 10.0f;
-
-struct platforment
-{
- physent *d;
- int stacks, chains;
-
- platforment() {}
- platforment(physent *d) : d(d), stacks(-1), chains(-1) {}
-
- bool operator==(const physent *o) const { return d == o; }
-};
-
-struct platformcollision
-{
- platforment *ent;
- int next;
-
- platformcollision() {}
- platformcollision(platforment *ent, int next) : ent(ent), next(next) {}
-};
-
-template<class E, class O>
-static inline bool platformcollide(physent *d, const vec &dir, physent *o, float margin)
-{
- E entvol(d);
- O obvol(o, margin);
- vec cp;
- if(mpr::collide(entvol, obvol, NULL, NULL, &cp))
- {
- vec wn = vec(cp).sub(obvol.center());
- return !obvol.contactface(wn, dir.iszero() ? vec(wn).neg() : dir).iszero();
- }
- return false;
-}
-
-bool platformcollide(physent *d, physent *o, const vec &dir, float margin = 0)
-{
- if(d->collidetype == COLLIDE_OBB)
- {
- if(o->collidetype == COLLIDE_OBB) return platformcollide<mpr::EntOBB, mpr::EntOBB>(d, dir, o, margin);
- else return platformcollide<mpr::EntOBB, mpr::EntCylinder>(d, dir, o, margin);
-
- }
- else if(o->collidetype == COLLIDE_OBB) return ellipseboxcollide(d, dir, o->o, vec(0, 0, 0), o->yaw, o->xradius, o->yradius, o->aboveeye, o->eyeheight + margin);
- else return ellipsecollide(d, dir, o->o, vec(0, 0, 0), o->yaw, o->xradius, o->yradius, o->aboveeye, o->eyeheight + margin);
-}
-
-bool moveplatform(physent *p, const vec &dir)
-{
- if(!insideworld(p->newpos)) return false;
-
- vec oldpos(p->o);
- (p->o = p->newpos).add(dir);
- if(collide(p, dir, 0, dir.z<=0))
- {
- p->o = oldpos;
- return false;
- }
- p->o = oldpos;
-
- static vector<platforment> ents;
- ents.setsize(0);
- for(int x = int(max(p->o.x-p->radius-PLATFORMBORDER, 0.0f))>>dynentsize, ex = int(min(p->o.x+p->radius+PLATFORMBORDER, worldsize-1.0f))>>dynentsize; x <= ex; x++)
- for(int y = int(max(p->o.y-p->radius-PLATFORMBORDER, 0.0f))>>dynentsize, ey = int(min(p->o.y+p->radius+PLATFORMBORDER, worldsize-1.0f))>>dynentsize; y <= ey; y++)
- {
- const vector<physent *> &dynents = checkdynentcache(x, y);
- loopv(dynents)
- {
- physent *d = dynents[i];
- if(p==d || d->o.z-d->eyeheight < p->o.z+p->aboveeye || p->o.reject(d->o, p->radius+PLATFORMBORDER+d->radius) || ents.find(d) >= 0) continue;
- ents.add(d);
- }
- }
- static vector<platforment *> passengers, colliders;
- passengers.setsize(0);
- colliders.setsize(0);
- static vector<platformcollision> collisions;
- collisions.setsize(0);
- // build up collision DAG of colliders to be pushed off, and DAG of stacked passengers
- loopv(ents)
- {
- platforment &ent = ents[i];
- physent *d = ent.d;
- // check if the dynent is on top of the platform
- if(platformcollide(p, d, vec(0, 0, 1), PLATFORMMARGIN)) passengers.add(&ent);
- vec doldpos(d->o);
- (d->o = d->newpos).add(dir);
- if(collide(d, dir, 0, false)) colliders.add(&ent);
- d->o = doldpos;
- loopvj(ents)
- {
- platforment &o = ents[j];
- if(platformcollide(d, o.d, dir))
- {
- collisions.add(platformcollision(&ent, o.chains));
- o.chains = collisions.length() - 1;
- }
- if(d->o.z < o.d->o.z && platformcollide(d, o.d, vec(0, 0, 1), PLATFORMMARGIN))
- {
- collisions.add(platformcollision(&o, ent.stacks));
- ent.stacks = collisions.length() - 1;
- }
- }
- }
- loopv(colliders) // propagate collisions
- {
- platforment *ent = colliders[i];
- for(int n = ent->chains; n>=0; n = collisions[n].next)
- {
- platforment *o = collisions[n].ent;
- if(colliders.find(o)<0) colliders.add(o);
- }
- }
- if(dir.z>0)
- {
- loopv(passengers) // if any stacked passengers collide, stop the platform
- {
- platforment *ent = passengers[i];
- if(colliders.find(ent)>=0) return false;
- for(int n = ent->stacks; n>=0; n = collisions[n].next)
- {
- platforment *o = collisions[n].ent;
- if(passengers.find(o)<0) passengers.add(o);
- }
- }
- loopv(passengers)
- {
- physent *d = passengers[i]->d;
- d->o.add(dir);
- d->newpos.add(dir);
- if(dir.x || dir.y) updatedynentcache(d);
- }
- }
- else loopv(passengers) // move any stacked passengers who aren't colliding with non-passengers
- {
- platforment *ent = passengers[i];
- if(colliders.find(ent)>=0) continue;
-
- physent *d = ent->d;
- d->o.add(dir);
- d->newpos.add(dir);
- if(dir.x || dir.y) updatedynentcache(d);
-
- for(int n = ent->stacks; n>=0; n = collisions[n].next)
- {
- platforment *o = collisions[n].ent;
- if(passengers.find(o)<0) passengers.add(o);
- }
- }
-
- p->o.add(dir);
- p->newpos.add(dir);
- if(dir.x || dir.y) updatedynentcache(p);
-
- return true;
+ if(d->physstate == PHYS_FALL) return;
+ d->timeinair = 0;
+ vec old(d->o);
+ /* Attempt to reconstruct the floor state.
+ * May be inaccurate since movement collisions are not considered.
+ * If good floor is not found, just keep the old floor and hope it's correct enough.
+ */
+ switch(d->physstate)
+ {
+ case PHYS_SLOPE:
+ case PHYS_FLOOR:
+ case PHYS_STEP_DOWN:
+ d->o.z -= 0.15f;
+ if(collide(d, vec(0, 0, -1), d->physstate == PHYS_SLOPE || d->physstate == PHYS_STEP_DOWN ? SLOPEZ : FLOORZ))
+ d->floor = collidewall;
+ break;
+
+ case PHYS_STEP_UP:
+ d->o.z -= STAIRHEIGHT+0.15f;
+ if(collide(d, vec(0, 0, -1), SLOPEZ))
+ d->floor = collidewall;
+ break;
+
+ case PHYS_SLIDE:
+ d->o.z -= 0.15f;
+ if(collide(d, vec(0, 0, -1)) && collidewall.z < SLOPEZ)
+ d->floor = collidewall;
+ break;
+ }
+ if(d->physstate > PHYS_FALL && d->floor.z <= 0) d->floor = vec(0, 0, 1);
+ d->o = old;
}
#define dir(name,v,d,s,os) ICOMMAND(name, "D", (int *down), { player->s = *down!=0; player->v = player->s ? d : (player->os ? -(d) : 0); });
dir(backward, move, -1, k_down, k_up);
-dir(forward, move, 1, k_up, k_down);
-dir(left, strafe, 1, k_left, k_right);
-dir(right, strafe, -1, k_right, k_left);
+dir(forward, move, 1, k_up, k_down);
+dir(left, strafe, 1, k_left, k_right);
+dir(right, strafe, -1, k_right, k_left);
ICOMMAND(jump, "D", (int *down), { if(!*down || game::canjump()) player->jumping = *down!=0; });
ICOMMAND(attack, "D", (int *down), { game::doattack(*down!=0); });
-bool entinmap(dynent *d, bool avoidplayers) // brute force but effective way to find a free spawn spot in the map
-{
- d->o.z += d->eyeheight; // pos specified is at feet
- vec orig = d->o;
- loopi(100) // try max 100 times
- {
- if(i)
- {
- d->o = orig;
- d->o.x += (rnd(21)-10)*i/5; // increasing distance
- d->o.y += (rnd(21)-10)*i/5;
- d->o.z += (rnd(21)-10)*i/5;
- }
-
- if(!collide(d) && !collideinside)
- {
- if(collideplayer)
- {
- if(!avoidplayers) continue;
- d->o = orig;
- d->resetinterp();
- return false;
- }
-
- d->resetinterp();
- return true;
- }
- }
- // leave ent at original pos, possibly stuck
- d->o = orig;
- d->resetinterp();
- conoutf(CON_WARN, "can't find entity spawn spot! (%.1f, %.1f, %.1f)", d->o.x, d->o.y, d->o.z);
- return false;
+bool entinmap(dynent *d, bool avoidplayers) // brute force but effective way to find a free spawn spot in the map
+{
+ d->o.z += d->eyeheight; // pos specified is at feet
+ vec orig = d->o;
+ loopi(100) // try max 100 times
+ {
+ if(i)
+ {
+ d->o = orig;
+ d->o.x += (rnd(21)-10)*i/5; // increasing distance
+ d->o.y += (rnd(21)-10)*i/5;
+ d->o.z += (rnd(21)-10)*i/5;
+ }
+
+ if(!collide(d) && !collideinside)
+ {
+ if(collideplayer)
+ {
+ if(!avoidplayers) continue;
+ d->o = orig;
+ d->resetinterp();
+ return false;
+ }
+
+ d->resetinterp();
+ return true;
+ }
+ }
+ // leave ent at original pos, possibly stuck
+ d->o = orig;
+ d->resetinterp();
+ conoutf(CON_WARN, "can't find entity spawn spot! (%.1f, %.1f, %.1f)", d->o.x, d->o.y, d->o.z);
+ return false;
}
diff --git a/src/engine/ragdoll.h b/src/engine/ragdoll.h
index 2704abd..cf68c01 100644
--- a/src/engine/ragdoll.h
+++ b/src/engine/ragdoll.h
@@ -1,331 +1,331 @@
struct ragdollskel
{
- struct vert
- {
- vec pos;
- float radius, weight;
- };
-
- struct tri
- {
- int vert[3];
-
- bool shareverts(const tri &t) const
- {
- loopi(3) loopj(3) if(vert[i] == t.vert[j]) return true;
- return false;
- }
- };
-
- struct distlimit
- {
- int vert[2];
- float mindist, maxdist;
- };
-
- struct rotlimit
- {
- int tri[2];
- float maxangle;
- matrix3 middle;
- };
-
- struct rotfriction
- {
- int tri[2];
- matrix3 middle;
- };
-
- struct joint
- {
- int bone, tri, vert[3];
- float weight;
- matrix4x3 orient;
- };
-
- struct reljoint
- {
- int bone, parent;
- };
-
- bool loaded, animjoints;
- int eye;
- vector<vert> verts;
- vector<tri> tris;
- vector<distlimit> distlimits;
- vector<rotlimit> rotlimits;
- vector<rotfriction> rotfrictions;
- vector<joint> joints;
- vector<reljoint> reljoints;
-
- ragdollskel() : loaded(false), animjoints(false), eye(-1) {}
-
- void setupjoints()
- {
- loopv(verts) verts[i].weight = 0;
- loopv(joints)
- {
- joint &j = joints[i];
- j.weight = 0;
- vec pos(0, 0, 0);
- loopk(3) if(j.vert[k]>=0)
- {
- pos.add(verts[j.vert[k]].pos);
- j.weight++;
- verts[j.vert[k]].weight++;
- }
- if(j.weight) j.weight = 1/j.weight;
- pos.mul(j.weight);
-
- tri &t = tris[j.tri];
- matrix4x3 &m = j.orient;
- const vec &v1 = verts[t.vert[0]].pos,
- &v2 = verts[t.vert[1]].pos,
- &v3 = verts[t.vert[2]].pos;
- m.a = vec(v2).sub(v1).normalize();
- m.c.cross(m.a, vec(v3).sub(v1)).normalize();
- m.b.cross(m.c, m.a);
- m.d = pos;
- m.transpose();
- }
- loopv(verts) if(verts[i].weight) verts[i].weight = 1/verts[i].weight;
- reljoints.shrink(0);
- }
-
- void setuprotfrictions()
- {
- rotfrictions.shrink(0);
- loopv(tris) for(int j = i+1; j < tris.length(); j++) if(tris[i].shareverts(tris[j]))
- {
- rotfriction &r = rotfrictions.add();
- r.tri[0] = i;
- r.tri[1] = j;
- }
- }
-
- void setup()
- {
- setupjoints();
- setuprotfrictions();
-
- loaded = true;
- }
-
- void addreljoint(int bone, int parent)
- {
- reljoint &r = reljoints.add();
- r.bone = bone;
- r.parent = parent;
- }
+ struct vert
+ {
+ vec pos;
+ float radius, weight;
+ };
+
+ struct tri
+ {
+ int vert[3];
+
+ bool shareverts(const tri &t) const
+ {
+ loopi(3) loopj(3) if(vert[i] == t.vert[j]) return true;
+ return false;
+ }
+ };
+
+ struct distlimit
+ {
+ int vert[2];
+ float mindist, maxdist;
+ };
+
+ struct rotlimit
+ {
+ int tri[2];
+ float maxangle;
+ matrix3 middle;
+ };
+
+ struct rotfriction
+ {
+ int tri[2];
+ matrix3 middle;
+ };
+
+ struct joint
+ {
+ int bone, tri, vert[3];
+ float weight;
+ matrix4x3 orient;
+ };
+
+ struct reljoint
+ {
+ int bone, parent;
+ };
+
+ bool loaded, animjoints;
+ int eye;
+ vector<vert> verts;
+ vector<tri> tris;
+ vector<distlimit> distlimits;
+ vector<rotlimit> rotlimits;
+ vector<rotfriction> rotfrictions;
+ vector<joint> joints;
+ vector<reljoint> reljoints;
+
+ ragdollskel() : loaded(false), animjoints(false), eye(-1) {}
+
+ void setupjoints()
+ {
+ loopv(verts) verts[i].weight = 0;
+ loopv(joints)
+ {
+ joint &j = joints[i];
+ j.weight = 0;
+ vec pos(0, 0, 0);
+ loopk(3) if(j.vert[k]>=0)
+ {
+ pos.add(verts[j.vert[k]].pos);
+ j.weight++;
+ verts[j.vert[k]].weight++;
+ }
+ if(j.weight) j.weight = 1/j.weight;
+ pos.mul(j.weight);
+
+ tri &t = tris[j.tri];
+ matrix4x3 &m = j.orient;
+ const vec &v1 = verts[t.vert[0]].pos,
+ &v2 = verts[t.vert[1]].pos,
+ &v3 = verts[t.vert[2]].pos;
+ m.a = vec(v2).sub(v1).normalize();
+ m.c.cross(m.a, vec(v3).sub(v1)).normalize();
+ m.b.cross(m.c, m.a);
+ m.d = pos;
+ m.transpose();
+ }
+ loopv(verts) if(verts[i].weight) verts[i].weight = 1/verts[i].weight;
+ reljoints.shrink(0);
+ }
+
+ void setuprotfrictions()
+ {
+ rotfrictions.shrink(0);
+ loopv(tris) for(int j = i+1; j < tris.length(); j++) if(tris[i].shareverts(tris[j]))
+ {
+ rotfriction &r = rotfrictions.add();
+ r.tri[0] = i;
+ r.tri[1] = j;
+ }
+ }
+
+ void setup()
+ {
+ setupjoints();
+ setuprotfrictions();
+
+ loaded = true;
+ }
+
+ void addreljoint(int bone, int parent)
+ {
+ reljoint &r = reljoints.add();
+ r.bone = bone;
+ r.parent = parent;
+ }
};
struct ragdolldata
{
- struct vert
- {
- vec oldpos, pos, newpos;
- float weight;
- bool collided, stuck;
-
- vert() : pos(0, 0, 0), newpos(0, 0, 0), weight(0), collided(false), stuck(true) {}
- };
-
- ragdollskel *skel;
- int millis, collidemillis, collisions, floating, lastmove, unsticks;
- vec offset, center;
- float radius, timestep, scale;
- vert *verts;
- matrix3 *tris;
- matrix4x3 *animjoints;
- dualquat *reljoints;
-
- ragdolldata(ragdollskel *skel, float scale = 1)
- : skel(skel),
- millis(lastmillis),
- collidemillis(0),
- collisions(0),
- floating(0),
- lastmove(lastmillis),
- unsticks(INT_MAX),
- radius(0),
- timestep(0),
- scale(scale),
- verts(new vert[skel->verts.length()]),
- tris(new matrix3[skel->tris.length()]),
- animjoints(!skel->animjoints || skel->joints.empty() ? NULL : new matrix4x3[skel->joints.length()]),
- reljoints(skel->reljoints.empty() ? NULL : new dualquat[skel->reljoints.length()])
- {
- }
-
- ~ragdolldata()
- {
- delete[] verts;
- delete[] tris;
- if(animjoints) delete[] animjoints;
- if(reljoints) delete[] reljoints;
- }
-
- void calcanimjoint(int i, const matrix4x3 &anim)
- {
- if(!animjoints) return;
- ragdollskel::joint &j = skel->joints[i];
- vec pos(0, 0, 0);
- loopk(3) if(j.vert[k]>=0) pos.add(verts[j.vert[k]].pos);
- pos.mul(j.weight);
-
- ragdollskel::tri &t = skel->tris[j.tri];
- matrix4x3 m;
- const vec &v1 = verts[t.vert[0]].pos,
- &v2 = verts[t.vert[1]].pos,
- &v3 = verts[t.vert[2]].pos;
- m.a = vec(v2).sub(v1).normalize();
- m.c.cross(m.a, vec(v3).sub(v1)).normalize();
- m.b.cross(m.c, m.a);
- m.d = pos;
- animjoints[i].transposemul(m, anim);
- }
-
- void calctris()
- {
- loopv(skel->tris)
- {
- ragdollskel::tri &t = skel->tris[i];
- matrix3 &m = tris[i];
- const vec &v1 = verts[t.vert[0]].pos,
- &v2 = verts[t.vert[1]].pos,
- &v3 = verts[t.vert[2]].pos;
- m.a = vec(v2).sub(v1).normalize();
- m.c.cross(m.a, vec(v3).sub(v1)).normalize();
- m.b.cross(m.c, m.a);
- }
- }
-
- void calcboundsphere()
- {
- center = vec(0, 0, 0);
- loopv(skel->verts) center.add(verts[i].pos);
- center.div(skel->verts.length());
- radius = 0;
- loopv(skel->verts) radius = max(radius, verts[i].pos.dist(center));
- }
-
- void init(dynent *d)
- {
- extern int ragdolltimestepmin;
- float ts = ragdolltimestepmin/1000.0f;
- loopv(skel->verts) (verts[i].oldpos = verts[i].pos).sub(vec(d->vel).add(d->falling).mul(ts));
- timestep = ts;
-
- calctris();
- calcboundsphere();
- offset = d->o;
- offset.sub(skel->eye >= 0 ? verts[skel->eye].pos : center);
- offset.z += (d->eyeheight + d->aboveeye)/2;
- }
-
- void move(dynent *pl, float ts);
- void updatepos();
- void constrain();
- void constraindist();
- void applyrotlimit(ragdollskel::tri &t1, ragdollskel::tri &t2, float angle, const vec &axis);
- void constrainrot();
- void calcrotfriction();
- void applyrotfriction(float ts);
- void tryunstick(float speed);
-
- static inline bool collidevert(const vec &pos, const vec &dir, float radius)
- {
- static struct vertent : physent
- {
- vertent()
- {
- type = ENT_BOUNCE;
- radius = xradius = yradius = eyeheight = aboveeye = 1;
- }
- } v;
- v.o = pos;
- if(v.radius != radius) v.radius = v.xradius = v.yradius = v.eyeheight = v.aboveeye = radius;
- return collide(&v, dir, 0, false);
- }
+ struct vert
+ {
+ vec oldpos, pos, newpos;
+ float weight;
+ bool collided, stuck;
+
+ vert() : pos(0, 0, 0), newpos(0, 0, 0), weight(0), collided(false), stuck(true) {}
+ };
+
+ ragdollskel *skel;
+ int millis, collidemillis, collisions, floating, lastmove, unsticks;
+ vec offset, center;
+ float radius, timestep, scale;
+ vert *verts;
+ matrix3 *tris;
+ matrix4x3 *animjoints;
+ dualquat *reljoints;
+
+ ragdolldata(ragdollskel *skel, float scale = 1)
+ : skel(skel),
+ millis(lastmillis),
+ collidemillis(0),
+ collisions(0),
+ floating(0),
+ lastmove(lastmillis),
+ unsticks(INT_MAX),
+ radius(0),
+ timestep(0),
+ scale(scale),
+ verts(new vert[skel->verts.length()]),
+ tris(new matrix3[skel->tris.length()]),
+ animjoints(!skel->animjoints || skel->joints.empty() ? NULL : new matrix4x3[skel->joints.length()]),
+ reljoints(skel->reljoints.empty() ? NULL : new dualquat[skel->reljoints.length()])
+ {
+ }
+
+ ~ragdolldata()
+ {
+ delete[] verts;
+ delete[] tris;
+ if(animjoints) delete[] animjoints;
+ if(reljoints) delete[] reljoints;
+ }
+
+ void calcanimjoint(int i, const matrix4x3 &anim)
+ {
+ if(!animjoints) return;
+ ragdollskel::joint &j = skel->joints[i];
+ vec pos(0, 0, 0);
+ loopk(3) if(j.vert[k]>=0) pos.add(verts[j.vert[k]].pos);
+ pos.mul(j.weight);
+
+ ragdollskel::tri &t = skel->tris[j.tri];
+ matrix4x3 m;
+ const vec &v1 = verts[t.vert[0]].pos,
+ &v2 = verts[t.vert[1]].pos,
+ &v3 = verts[t.vert[2]].pos;
+ m.a = vec(v2).sub(v1).normalize();
+ m.c.cross(m.a, vec(v3).sub(v1)).normalize();
+ m.b.cross(m.c, m.a);
+ m.d = pos;
+ animjoints[i].transposemul(m, anim);
+ }
+
+ void calctris()
+ {
+ loopv(skel->tris)
+ {
+ ragdollskel::tri &t = skel->tris[i];
+ matrix3 &m = tris[i];
+ const vec &v1 = verts[t.vert[0]].pos,
+ &v2 = verts[t.vert[1]].pos,
+ &v3 = verts[t.vert[2]].pos;
+ m.a = vec(v2).sub(v1).normalize();
+ m.c.cross(m.a, vec(v3).sub(v1)).normalize();
+ m.b.cross(m.c, m.a);
+ }
+ }
+
+ void calcboundsphere()
+ {
+ center = vec(0, 0, 0);
+ loopv(skel->verts) center.add(verts[i].pos);
+ center.div(skel->verts.length());
+ radius = 0;
+ loopv(skel->verts) radius = max(radius, verts[i].pos.dist(center));
+ }
+
+ void init(dynent *d)
+ {
+ extern int ragdolltimestepmin;
+ float ts = ragdolltimestepmin/1000.0f;
+ loopv(skel->verts) (verts[i].oldpos = verts[i].pos).sub(vec(d->vel).add(d->falling).mul(ts));
+ timestep = ts;
+
+ calctris();
+ calcboundsphere();
+ offset = d->o;
+ offset.sub(skel->eye >= 0 ? verts[skel->eye].pos : center);
+ offset.z += (d->eyeheight + d->aboveeye)/2;
+ }
+
+ void move(dynent *pl, float ts);
+ void updatepos();
+ void constrain();
+ void constraindist();
+ void applyrotlimit(ragdollskel::tri &t1, ragdollskel::tri &t2, float angle, const vec &axis);
+ void constrainrot();
+ void calcrotfriction();
+ void applyrotfriction(float ts);
+ void tryunstick(float speed);
+
+ static inline bool collidevert(const vec &pos, const vec &dir, float radius)
+ {
+ static struct vertent : physent
+ {
+ vertent()
+ {
+ type = ENT_BOUNCE;
+ radius = xradius = yradius = eyeheight = aboveeye = 1;
+ }
+ } v;
+ v.o = pos;
+ if(v.radius != radius) v.radius = v.xradius = v.yradius = v.eyeheight = v.aboveeye = radius;
+ return collide(&v, dir, 0, false);
+ }
};
/*
- seed particle position = avg(modelview * base2anim * spherepos)
- mapped transform = invert(curtri) * origtrig
- parented transform = parent{invert(curtri) * origtrig} * (invert(parent{base2anim}) * base2anim)
+ seed particle position = avg(modelview * base2anim * spherepos)
+ mapped transform = invert(curtri) * origtrig
+ parented transform = parent{invert(curtri) * origtrig} * (invert(parent{base2anim}) * base2anim)
*/
void ragdolldata::constraindist()
{
- float invscale = 1.0f/scale;
- loopv(skel->distlimits)
- {
- ragdollskel::distlimit &d = skel->distlimits[i];
- vert &v1 = verts[d.vert[0]], &v2 = verts[d.vert[1]];
- vec dir = vec(v2.pos).sub(v1.pos);
- float dist = dir.magnitude()*invscale, cdist;
- if(dist < d.mindist) cdist = d.mindist;
- else if(dist > d.maxdist) cdist = d.maxdist;
- else continue;
- if(dist > 1e-4f) dir.mul(cdist*0.5f/dist);
- else dir = vec(0, 0, cdist*0.5f/invscale);
- vec center = vec(v1.pos).add(v2.pos).mul(0.5f);
- v1.newpos.add(vec(center).sub(dir));
- v1.weight++;
- v2.newpos.add(vec(center).add(dir));
- v2.weight++;
- }
+ float invscale = 1.0f/scale;
+ loopv(skel->distlimits)
+ {
+ ragdollskel::distlimit &d = skel->distlimits[i];
+ vert &v1 = verts[d.vert[0]], &v2 = verts[d.vert[1]];
+ vec dir = vec(v2.pos).sub(v1.pos);
+ float dist = dir.magnitude()*invscale, cdist;
+ if(dist < d.mindist) cdist = d.mindist;
+ else if(dist > d.maxdist) cdist = d.maxdist;
+ else continue;
+ if(dist > 1e-4f) dir.mul(cdist*0.5f/dist);
+ else dir = vec(0, 0, cdist*0.5f/invscale);
+ vec center = vec(v1.pos).add(v2.pos).mul(0.5f);
+ v1.newpos.add(vec(center).sub(dir));
+ v1.weight++;
+ v2.newpos.add(vec(center).add(dir));
+ v2.weight++;
+ }
}
inline void ragdolldata::applyrotlimit(ragdollskel::tri &t1, ragdollskel::tri &t2, float angle, const vec &axis)
{
- vert &v1a = verts[t1.vert[0]], &v1b = verts[t1.vert[1]], &v1c = verts[t1.vert[2]],
- &v2a = verts[t2.vert[0]], &v2b = verts[t2.vert[1]], &v2c = verts[t2.vert[2]];
- vec m1 = vec(v1a.pos).add(v1b.pos).add(v1c.pos).div(3),
- m2 = vec(v2a.pos).add(v2b.pos).add(v2c.pos).div(3),
- q1a, q1b, q1c, q2a, q2b, q2c;
- float w1 = q1a.cross(axis, vec(v1a.pos).sub(m1)).magnitude() +
- q1b.cross(axis, vec(v1b.pos).sub(m1)).magnitude() +
- q1c.cross(axis, vec(v1c.pos).sub(m1)).magnitude(),
- w2 = q2a.cross(axis, vec(v2a.pos).sub(m2)).magnitude() +
- q2b.cross(axis, vec(v2b.pos).sub(m2)).magnitude() +
- q2c.cross(axis, vec(v2c.pos).sub(m2)).magnitude();
- angle /= w1 + w2 + 1e-9f;
- float a1 = angle*w2, a2 = -angle*w1,
- s1 = sinf(a1), s2 = sinf(a2);
- vec c1 = vec(axis).mul(1 - cosf(a1)), c2 = vec(axis).mul(1 - cosf(a2));
- v1a.newpos.add(vec().cross(c1, q1a).madd(q1a, s1).add(v1a.pos));
- v1a.weight++;
- v1b.newpos.add(vec().cross(c1, q1b).madd(q1b, s1).add(v1b.pos));
- v1b.weight++;
- v1c.newpos.add(vec().cross(c1, q1c).madd(q1c, s1).add(v1c.pos));
- v1c.weight++;
- v2a.newpos.add(vec().cross(c2, q2a).madd(q2a, s2).add(v2a.pos));
- v2a.weight++;
- v2b.newpos.add(vec().cross(c2, q2b).madd(q2b, s2).add(v2b.pos));
- v2b.weight++;
- v2c.newpos.add(vec().cross(c2, q2c).madd(q2c, s2).add(v2c.pos));
- v2c.weight++;
+ vert &v1a = verts[t1.vert[0]], &v1b = verts[t1.vert[1]], &v1c = verts[t1.vert[2]],
+ &v2a = verts[t2.vert[0]], &v2b = verts[t2.vert[1]], &v2c = verts[t2.vert[2]];
+ vec m1 = vec(v1a.pos).add(v1b.pos).add(v1c.pos).div(3),
+ m2 = vec(v2a.pos).add(v2b.pos).add(v2c.pos).div(3),
+ q1a, q1b, q1c, q2a, q2b, q2c;
+ float w1 = q1a.cross(axis, vec(v1a.pos).sub(m1)).magnitude() +
+ q1b.cross(axis, vec(v1b.pos).sub(m1)).magnitude() +
+ q1c.cross(axis, vec(v1c.pos).sub(m1)).magnitude(),
+ w2 = q2a.cross(axis, vec(v2a.pos).sub(m2)).magnitude() +
+ q2b.cross(axis, vec(v2b.pos).sub(m2)).magnitude() +
+ q2c.cross(axis, vec(v2c.pos).sub(m2)).magnitude();
+ angle /= w1 + w2 + 1e-9f;
+ float a1 = angle*w2, a2 = -angle*w1,
+ s1 = sinf(a1), s2 = sinf(a2);
+ vec c1 = vec(axis).mul(1 - cosf(a1)), c2 = vec(axis).mul(1 - cosf(a2));
+ v1a.newpos.add(vec().cross(c1, q1a).madd(q1a, s1).add(v1a.pos));
+ v1a.weight++;
+ v1b.newpos.add(vec().cross(c1, q1b).madd(q1b, s1).add(v1b.pos));
+ v1b.weight++;
+ v1c.newpos.add(vec().cross(c1, q1c).madd(q1c, s1).add(v1c.pos));
+ v1c.weight++;
+ v2a.newpos.add(vec().cross(c2, q2a).madd(q2a, s2).add(v2a.pos));
+ v2a.weight++;
+ v2b.newpos.add(vec().cross(c2, q2b).madd(q2b, s2).add(v2b.pos));
+ v2b.weight++;
+ v2c.newpos.add(vec().cross(c2, q2c).madd(q2c, s2).add(v2c.pos));
+ v2c.weight++;
}
void ragdolldata::constrainrot()
{
- loopv(skel->rotlimits)
- {
- ragdollskel::rotlimit &r = skel->rotlimits[i];
- matrix3 rot;
- rot.mul(tris[r.tri[0]], r.middle);
- rot.multranspose(tris[r.tri[1]]);
-
- vec axis;
- float angle;
- if(!rot.calcangleaxis(angle, axis)) continue;
- angle = r.maxangle - fabs(angle);
- if(angle >= 0) continue;
- angle += 1e-3f;
-
- applyrotlimit(skel->tris[r.tri[0]], skel->tris[r.tri[1]], angle, axis);
- }
+ loopv(skel->rotlimits)
+ {
+ ragdollskel::rotlimit &r = skel->rotlimits[i];
+ matrix3 rot;
+ rot.mul(tris[r.tri[0]], r.middle);
+ rot.multranspose(tris[r.tri[1]]);
+
+ vec axis;
+ float angle;
+ if(!rot.calcangleaxis(angle, axis)) continue;
+ angle = r.maxangle - fabs(angle);
+ if(angle >= 0) continue;
+ angle += 1e-3f;
+
+ applyrotlimit(skel->tris[r.tri[0]], skel->tris[r.tri[1]], angle, axis);
+ }
}
VAR(ragdolltimestepmin, 1, 5, 50);
@@ -335,103 +335,103 @@ FVAR(ragdollrotfricstop, 0, 0.1f, 1);
void ragdolldata::calcrotfriction()
{
- loopv(skel->rotfrictions)
- {
- ragdollskel::rotfriction &r = skel->rotfrictions[i];
- r.middle.transposemul(tris[r.tri[0]], tris[r.tri[1]]);
- }
+ loopv(skel->rotfrictions)
+ {
+ ragdollskel::rotfriction &r = skel->rotfrictions[i];
+ r.middle.transposemul(tris[r.tri[0]], tris[r.tri[1]]);
+ }
}
void ragdolldata::applyrotfriction(float ts)
{
- calctris();
- float stopangle = 2*M_PI*ts*ragdollrotfricstop, rotfric = 1.0f - pow(ragdollrotfric, ts*1000.0f/ragdolltimestepmin);
- loopv(skel->rotfrictions)
- {
- ragdollskel::rotfriction &r = skel->rotfrictions[i];
- matrix3 rot;
- rot.mul(tris[r.tri[0]], r.middle);
- rot.multranspose(tris[r.tri[1]]);
-
- vec axis;
- float angle;
- if(rot.calcangleaxis(angle, axis))
- {
- angle *= -(fabs(angle) >= stopangle ? rotfric : 1.0f);
- applyrotlimit(skel->tris[r.tri[0]], skel->tris[r.tri[1]], angle, axis);
- }
- }
- loopv(skel->verts)
- {
- vert &v = verts[i];
- if(v.weight) v.pos = v.newpos.div(v.weight);
- v.newpos = vec(0, 0, 0);
- v.weight = 0;
- }
+ calctris();
+ float stopangle = 2*M_PI*ts*ragdollrotfricstop, rotfric = 1.0f - pow(ragdollrotfric, ts*1000.0f/ragdolltimestepmin);
+ loopv(skel->rotfrictions)
+ {
+ ragdollskel::rotfriction &r = skel->rotfrictions[i];
+ matrix3 rot;
+ rot.mul(tris[r.tri[0]], r.middle);
+ rot.multranspose(tris[r.tri[1]]);
+
+ vec axis;
+ float angle;
+ if(rot.calcangleaxis(angle, axis))
+ {
+ angle *= -(fabs(angle) >= stopangle ? rotfric : 1.0f);
+ applyrotlimit(skel->tris[r.tri[0]], skel->tris[r.tri[1]], angle, axis);
+ }
+ }
+ loopv(skel->verts)
+ {
+ vert &v = verts[i];
+ if(v.weight) v.pos = v.newpos.div(v.weight);
+ v.newpos = vec(0, 0, 0);
+ v.weight = 0;
+ }
}
void ragdolldata::tryunstick(float speed)
{
- vec unstuck(0, 0, 0);
- int stuck = 0;
- loopv(skel->verts)
- {
- vert &v = verts[i];
- if(v.stuck)
- {
- if(collidevert(v.pos, vec(0, 0, 0), skel->verts[i].radius)) { stuck++; continue; }
- v.stuck = false;
- }
- unstuck.add(v.pos);
- }
- unsticks = 0;
- if(!stuck || stuck >= skel->verts.length()) return;
- unstuck.div(skel->verts.length() - stuck);
- loopv(skel->verts)
- {
- vert &v = verts[i];
- if(v.stuck)
- {
- v.pos.add(vec(unstuck).sub(v.pos).rescale(speed));
- unsticks++;
- }
- }
+ vec unstuck(0, 0, 0);
+ int stuck = 0;
+ loopv(skel->verts)
+ {
+ vert &v = verts[i];
+ if(v.stuck)
+ {
+ if(collidevert(v.pos, vec(0, 0, 0), skel->verts[i].radius)) { stuck++; continue; }
+ v.stuck = false;
+ }
+ unstuck.add(v.pos);
+ }
+ unsticks = 0;
+ if(!stuck || stuck >= skel->verts.length()) return;
+ unstuck.div(skel->verts.length() - stuck);
+ loopv(skel->verts)
+ {
+ vert &v = verts[i];
+ if(v.stuck)
+ {
+ v.pos.add(vec(unstuck).sub(v.pos).rescale(speed));
+ unsticks++;
+ }
+ }
}
void ragdolldata::updatepos()
{
- loopv(skel->verts)
- {
- vert &v = verts[i];
- if(v.weight)
- {
- v.newpos.div(v.weight);
- if(!collidevert(v.newpos, vec(v.newpos).sub(v.pos), skel->verts[i].radius)) v.pos = v.newpos;
- else
- {
- vec dir = vec(v.newpos).sub(v.oldpos);
- if(dir.dot(collidewall) < 0) v.oldpos = vec(v.pos).sub(dir.reflect(collidewall));
- v.collided = true;
- }
- }
- v.newpos = vec(0, 0, 0);
- v.weight = 0;
- }
+ loopv(skel->verts)
+ {
+ vert &v = verts[i];
+ if(v.weight)
+ {
+ v.newpos.div(v.weight);
+ if(!collidevert(v.newpos, vec(v.newpos).sub(v.pos), skel->verts[i].radius)) v.pos = v.newpos;
+ else
+ {
+ vec dir = vec(v.newpos).sub(v.oldpos);
+ if(dir.dot(collidewall) < 0) v.oldpos = vec(v.pos).sub(dir.reflect(collidewall));
+ v.collided = true;
+ }
+ }
+ v.newpos = vec(0, 0, 0);
+ v.weight = 0;
+ }
}
VAR(ragdollconstrain, 1, 5, 100);
void ragdolldata::constrain()
{
- loopi(ragdollconstrain)
- {
- constraindist();
- updatepos();
-
- calctris();
- constrainrot();
- updatepos();
- }
+ loopi(ragdollconstrain)
+ {
+ constraindist();
+ updatepos();
+
+ calctris();
+ constrainrot();
+ updatepos();
+ }
}
FVAR(ragdollbodyfric, 0, 0.95f, 1);
@@ -445,62 +445,62 @@ VAR(ragdollwaterexpireoffset, 0, 3000, 30000);
void ragdolldata::move(dynent *pl, float ts)
{
- extern const float GRAVITY;
- if(collidemillis && lastmillis > collidemillis) return;
-
- int material = lookupmaterial(vec(center.x, center.y, center.z + radius/2));
- bool water = isliquid(material&MATF_VOLUME);
- if(!pl->inwater && water) game::physicstrigger(pl, true, 0, -1, material&MATF_VOLUME);
- else if(pl->inwater && !water)
- {
- material = lookupmaterial(center);
- water = isliquid(material&MATF_VOLUME);
- if(!water) game::physicstrigger(pl, true, 0, 1, pl->inwater);
- }
- pl->inwater = water ? material&MATF_VOLUME : MAT_AIR;
-
- calcrotfriction();
+ extern const float GRAVITY;
+ if(collidemillis && lastmillis > collidemillis) return;
+
+ int material = lookupmaterial(vec(center.x, center.y, center.z + radius/2));
+ bool water = isliquid(material&MATF_VOLUME);
+ if(!pl->inwater && water) game::physicstrigger(pl, true, 0, -1, material&MATF_VOLUME);
+ else if(pl->inwater && !water)
+ {
+ material = lookupmaterial(center);
+ water = isliquid(material&MATF_VOLUME);
+ if(!water) game::physicstrigger(pl, true, 0, 1, pl->inwater);
+ }
+ pl->inwater = water ? material&MATF_VOLUME : MAT_AIR;
+
+ calcrotfriction();
float tsfric = timestep ? ts/timestep : 1,
airfric = ragdollairfric + min((ragdollbodyfricscale*collisions)/skel->verts.length(), 1.0f)*(ragdollbodyfric - ragdollairfric);
- collisions = 0;
- loopv(skel->verts)
- {
- vert &v = verts[i];
- vec dpos = vec(v.pos).sub(v.oldpos);
- dpos.z -= GRAVITY*ts*ts;
- if(water) dpos.z += 0.25f*sinf(detrnd(size_t(this)+i, 360)*RAD + lastmillis/10000.0f*M_PI)*ts;
- dpos.mul(pow((water ? ragdollwaterfric : 1.0f) * (v.collided ? ragdollgroundfric : airfric), ts*1000.0f/ragdolltimestepmin)*tsfric);
- v.oldpos = v.pos;
- v.pos.add(dpos);
- }
- applyrotfriction(ts);
- loopv(skel->verts)
- {
- vert &v = verts[i];
- if(v.pos.z < 0) { v.pos.z = 0; v.oldpos = v.pos; collisions++; }
- vec dir = vec(v.pos).sub(v.oldpos);
- v.collided = collidevert(v.pos, dir, skel->verts[i].radius);
- if(v.collided)
- {
- v.pos = v.oldpos;
- v.oldpos.sub(dir.reflect(collidewall));
- collisions++;
- }
- }
-
- if(unsticks && ragdollunstick) tryunstick(ts*ragdollunstick);
-
- timestep = ts;
- if(collisions)
- {
- floating = 0;
- if(!collidemillis) collidemillis = lastmillis + (water ? ragdollwaterexpireoffset : ragdollexpireoffset);
- }
- else if(++floating > 1 && lastmillis < collidemillis) collidemillis = 0;
-
- constrain();
- calctris();
- calcboundsphere();
+ collisions = 0;
+ loopv(skel->verts)
+ {
+ vert &v = verts[i];
+ vec dpos = vec(v.pos).sub(v.oldpos);
+ dpos.z -= GRAVITY*ts*ts;
+ if(water) dpos.z += 0.25f*sinf(detrnd(size_t(this)+i, 360)*RAD + lastmillis/10000.0f*M_PI)*ts;
+ dpos.mul(pow((water ? ragdollwaterfric : 1.0f) * (v.collided ? ragdollgroundfric : airfric), ts*1000.0f/ragdolltimestepmin)*tsfric);
+ v.oldpos = v.pos;
+ v.pos.add(dpos);
+ }
+ applyrotfriction(ts);
+ loopv(skel->verts)
+ {
+ vert &v = verts[i];
+ if(v.pos.z < 0) { v.pos.z = 0; v.oldpos = v.pos; collisions++; }
+ vec dir = vec(v.pos).sub(v.oldpos);
+ v.collided = collidevert(v.pos, dir, skel->verts[i].radius);
+ if(v.collided)
+ {
+ v.pos = v.oldpos;
+ v.oldpos.sub(dir.reflect(collidewall));
+ collisions++;
+ }
+ }
+
+ if(unsticks && ragdollunstick) tryunstick(ts*ragdollunstick);
+
+ timestep = ts;
+ if(collisions)
+ {
+ floating = 0;
+ if(!collidemillis) collidemillis = lastmillis + (water ? ragdollwaterexpireoffset : ragdollexpireoffset);
+ }
+ else if(++floating > 1 && lastmillis < collidemillis) collidemillis = 0;
+
+ constrain();
+ calctris();
+ calcboundsphere();
}
FVAR(ragdolleyesmooth, 0, 0.5f, 1);
@@ -508,26 +508,26 @@ VAR(ragdolleyesmoothmillis, 1, 250, 10000);
void moveragdoll(dynent *d)
{
- if(!curtime || !d->ragdoll) return;
-
- if(!d->ragdoll->collidemillis || lastmillis < d->ragdoll->collidemillis)
- {
- int lastmove = d->ragdoll->lastmove;
- while(d->ragdoll->lastmove + (lastmove == d->ragdoll->lastmove ? ragdolltimestepmin : ragdolltimestepmax) <= lastmillis)
- {
- int timestep = min(ragdolltimestepmax, lastmillis - d->ragdoll->lastmove);
- d->ragdoll->move(d, timestep/1000.0f);
- d->ragdoll->lastmove += timestep;
- }
- }
-
- vec eye = d->ragdoll->skel->eye >= 0 ? d->ragdoll->verts[d->ragdoll->skel->eye].pos : d->ragdoll->center;
- eye.add(d->ragdoll->offset);
- float k = pow(ragdolleyesmooth, float(curtime)/ragdolleyesmoothmillis);
- d->o.mul(k).add(eye.mul(1-k));
+ if(!curtime || !d->ragdoll) return;
+
+ if(!d->ragdoll->collidemillis || lastmillis < d->ragdoll->collidemillis)
+ {
+ int lastmove = d->ragdoll->lastmove;
+ while(d->ragdoll->lastmove + (lastmove == d->ragdoll->lastmove ? ragdolltimestepmin : ragdolltimestepmax) <= lastmillis)
+ {
+ int timestep = min(ragdolltimestepmax, lastmillis - d->ragdoll->lastmove);
+ d->ragdoll->move(d, timestep/1000.0f);
+ d->ragdoll->lastmove += timestep;
+ }
+ }
+
+ vec eye = d->ragdoll->skel->eye >= 0 ? d->ragdoll->verts[d->ragdoll->skel->eye].pos : d->ragdoll->center;
+ eye.add(d->ragdoll->offset);
+ float k = pow(ragdolleyesmooth, float(curtime)/ragdolleyesmoothmillis);
+ d->o.mul(k).add(eye.mul(1-k));
}
void cleanragdoll(dynent *d)
{
- DELETEP(d->ragdoll);
+ DELETEP(d->ragdoll);
}
diff --git a/src/engine/rendergl.cpp b/src/engine/rendergl.cpp
index b05be7d..997d15a 100644
--- a/src/engine/rendergl.cpp
+++ b/src/engine/rendergl.cpp
@@ -12,140 +12,140 @@ VAR(glcompat, 1, 0, 0);
PFNGLMULTIDRAWARRAYSPROC glMultiDrawArrays_ = NULL;
PFNGLMULTIDRAWELEMENTSPROC glMultiDrawElements_ = NULL;
-PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate_ = NULL;
+PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate_ = NULL;
PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate_ = NULL;
-PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate_ = NULL;
+PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate_ = NULL;
PFNGLSTENCILFUNCSEPARATEPROC glStencilFuncSeparate_ = NULL;
PFNGLSTENCILMASKSEPARATEPROC glStencilMaskSeparate_ = NULL;
-PFNGLGENBUFFERSPROC glGenBuffers_ = NULL;
-PFNGLBINDBUFFERPROC glBindBuffer_ = NULL;
-PFNGLMAPBUFFERPROC glMapBuffer_ = NULL;
-PFNGLUNMAPBUFFERPROC glUnmapBuffer_ = NULL;
-PFNGLBUFFERDATAPROC glBufferData_ = NULL;
-PFNGLBUFFERSUBDATAPROC glBufferSubData_ = NULL;
-PFNGLDELETEBUFFERSPROC glDeleteBuffers_ = NULL;
+PFNGLGENBUFFERSPROC glGenBuffers_ = NULL;
+PFNGLBINDBUFFERPROC glBindBuffer_ = NULL;
+PFNGLMAPBUFFERPROC glMapBuffer_ = NULL;
+PFNGLUNMAPBUFFERPROC glUnmapBuffer_ = NULL;
+PFNGLBUFFERDATAPROC glBufferData_ = NULL;
+PFNGLBUFFERSUBDATAPROC glBufferSubData_ = NULL;
+PFNGLDELETEBUFFERSPROC glDeleteBuffers_ = NULL;
PFNGLGETBUFFERSUBDATAPROC glGetBufferSubData_ = NULL;
-PFNGLGENQUERIESPROC glGenQueries_ = NULL;
-PFNGLDELETEQUERIESPROC glDeleteQueries_ = NULL;
-PFNGLBEGINQUERYPROC glBeginQuery_ = NULL;
-PFNGLENDQUERYPROC glEndQuery_ = NULL;
-PFNGLGETQUERYIVPROC glGetQueryiv_ = NULL;
+PFNGLGENQUERIESPROC glGenQueries_ = NULL;
+PFNGLDELETEQUERIESPROC glDeleteQueries_ = NULL;
+PFNGLBEGINQUERYPROC glBeginQuery_ = NULL;
+PFNGLENDQUERYPROC glEndQuery_ = NULL;
+PFNGLGETQUERYIVPROC glGetQueryiv_ = NULL;
PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv_ = NULL;
PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv_ = NULL;
-PFNGLCREATEPROGRAMPROC glCreateProgram_ = NULL;
-PFNGLDELETEPROGRAMPROC glDeleteProgram_ = NULL;
-PFNGLUSEPROGRAMPROC glUseProgram_ = NULL;
-PFNGLCREATESHADERPROC glCreateShader_ = NULL;
-PFNGLDELETESHADERPROC glDeleteShader_ = NULL;
-PFNGLSHADERSOURCEPROC glShaderSource_ = NULL;
-PFNGLCOMPILESHADERPROC glCompileShader_ = NULL;
-PFNGLGETSHADERIVPROC glGetShaderiv_ = NULL;
-PFNGLGETPROGRAMIVPROC glGetProgramiv_ = NULL;
-PFNGLATTACHSHADERPROC glAttachShader_ = NULL;
-PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog_ = NULL;
-PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog_ = NULL;
-PFNGLLINKPROGRAMPROC glLinkProgram_ = NULL;
-PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation_ = NULL;
-PFNGLUNIFORM1FPROC glUniform1f_ = NULL;
-PFNGLUNIFORM2FPROC glUniform2f_ = NULL;
-PFNGLUNIFORM3FPROC glUniform3f_ = NULL;
-PFNGLUNIFORM4FPROC glUniform4f_ = NULL;
-PFNGLUNIFORM1FVPROC glUniform1fv_ = NULL;
-PFNGLUNIFORM2FVPROC glUniform2fv_ = NULL;
-PFNGLUNIFORM3FVPROC glUniform3fv_ = NULL;
-PFNGLUNIFORM4FVPROC glUniform4fv_ = NULL;
-PFNGLUNIFORM1IPROC glUniform1i_ = NULL;
-PFNGLUNIFORM2IPROC glUniform2i_ = NULL;
-PFNGLUNIFORM3IPROC glUniform3i_ = NULL;
-PFNGLUNIFORM4IPROC glUniform4i_ = NULL;
-PFNGLUNIFORM1IVPROC glUniform1iv_ = NULL;
-PFNGLUNIFORM2IVPROC glUniform2iv_ = NULL;
-PFNGLUNIFORM3IVPROC glUniform3iv_ = NULL;
-PFNGLUNIFORM4IVPROC glUniform4iv_ = NULL;
-PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv_ = NULL;
-PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv_ = NULL;
-PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv_ = NULL;
-PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation_ = NULL;
-PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform_ = NULL;
+PFNGLCREATEPROGRAMPROC glCreateProgram_ = NULL;
+PFNGLDELETEPROGRAMPROC glDeleteProgram_ = NULL;
+PFNGLUSEPROGRAMPROC glUseProgram_ = NULL;
+PFNGLCREATESHADERPROC glCreateShader_ = NULL;
+PFNGLDELETESHADERPROC glDeleteShader_ = NULL;
+PFNGLSHADERSOURCEPROC glShaderSource_ = NULL;
+PFNGLCOMPILESHADERPROC glCompileShader_ = NULL;
+PFNGLGETSHADERIVPROC glGetShaderiv_ = NULL;
+PFNGLGETPROGRAMIVPROC glGetProgramiv_ = NULL;
+PFNGLATTACHSHADERPROC glAttachShader_ = NULL;
+PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog_ = NULL;
+PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog_ = NULL;
+PFNGLLINKPROGRAMPROC glLinkProgram_ = NULL;
+PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation_ = NULL;
+PFNGLUNIFORM1FPROC glUniform1f_ = NULL;
+PFNGLUNIFORM2FPROC glUniform2f_ = NULL;
+PFNGLUNIFORM3FPROC glUniform3f_ = NULL;
+PFNGLUNIFORM4FPROC glUniform4f_ = NULL;
+PFNGLUNIFORM1FVPROC glUniform1fv_ = NULL;
+PFNGLUNIFORM2FVPROC glUniform2fv_ = NULL;
+PFNGLUNIFORM3FVPROC glUniform3fv_ = NULL;
+PFNGLUNIFORM4FVPROC glUniform4fv_ = NULL;
+PFNGLUNIFORM1IPROC glUniform1i_ = NULL;
+PFNGLUNIFORM2IPROC glUniform2i_ = NULL;
+PFNGLUNIFORM3IPROC glUniform3i_ = NULL;
+PFNGLUNIFORM4IPROC glUniform4i_ = NULL;
+PFNGLUNIFORM1IVPROC glUniform1iv_ = NULL;
+PFNGLUNIFORM2IVPROC glUniform2iv_ = NULL;
+PFNGLUNIFORM3IVPROC glUniform3iv_ = NULL;
+PFNGLUNIFORM4IVPROC glUniform4iv_ = NULL;
+PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv_ = NULL;
+PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv_ = NULL;
+PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv_ = NULL;
+PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation_ = NULL;
+PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform_ = NULL;
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray_ = NULL;
PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray_ = NULL;
-PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f_ = NULL;
-PFNGLVERTEXATTRIB1FVPROC glVertexAttrib1fv_ = NULL;
-PFNGLVERTEXATTRIB1SPROC glVertexAttrib1s_ = NULL;
-PFNGLVERTEXATTRIB1SVPROC glVertexAttrib1sv_ = NULL;
-PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f_ = NULL;
-PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv_ = NULL;
-PFNGLVERTEXATTRIB2SPROC glVertexAttrib2s_ = NULL;
-PFNGLVERTEXATTRIB2SVPROC glVertexAttrib2sv_ = NULL;
-PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f_ = NULL;
-PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv_ = NULL;
-PFNGLVERTEXATTRIB3SPROC glVertexAttrib3s_ = NULL;
-PFNGLVERTEXATTRIB3SVPROC glVertexAttrib3sv_ = NULL;
-PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f_ = NULL;
-PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv_ = NULL;
-PFNGLVERTEXATTRIB4SPROC glVertexAttrib4s_ = NULL;
-PFNGLVERTEXATTRIB4SVPROC glVertexAttrib4sv_ = NULL;
-PFNGLVERTEXATTRIB4BVPROC glVertexAttrib4bv_ = NULL;
-PFNGLVERTEXATTRIB4IVPROC glVertexAttrib4iv_ = NULL;
-PFNGLVERTEXATTRIB4UBVPROC glVertexAttrib4ubv_ = NULL;
-PFNGLVERTEXATTRIB4UIVPROC glVertexAttrib4uiv_ = NULL;
-PFNGLVERTEXATTRIB4USVPROC glVertexAttrib4usv_ = NULL;
-PFNGLVERTEXATTRIB4NBVPROC glVertexAttrib4Nbv_ = NULL;
-PFNGLVERTEXATTRIB4NIVPROC glVertexAttrib4Niv_ = NULL;
-PFNGLVERTEXATTRIB4NUBPROC glVertexAttrib4Nub_ = NULL;
-PFNGLVERTEXATTRIB4NUBVPROC glVertexAttrib4Nubv_ = NULL;
-PFNGLVERTEXATTRIB4NUIVPROC glVertexAttrib4Nuiv_ = NULL;
-PFNGLVERTEXATTRIB4NUSVPROC glVertexAttrib4Nusv_ = NULL;
-PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer_ = NULL;
+PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f_ = NULL;
+PFNGLVERTEXATTRIB1FVPROC glVertexAttrib1fv_ = NULL;
+PFNGLVERTEXATTRIB1SPROC glVertexAttrib1s_ = NULL;
+PFNGLVERTEXATTRIB1SVPROC glVertexAttrib1sv_ = NULL;
+PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f_ = NULL;
+PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv_ = NULL;
+PFNGLVERTEXATTRIB2SPROC glVertexAttrib2s_ = NULL;
+PFNGLVERTEXATTRIB2SVPROC glVertexAttrib2sv_ = NULL;
+PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f_ = NULL;
+PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv_ = NULL;
+PFNGLVERTEXATTRIB3SPROC glVertexAttrib3s_ = NULL;
+PFNGLVERTEXATTRIB3SVPROC glVertexAttrib3sv_ = NULL;
+PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f_ = NULL;
+PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv_ = NULL;
+PFNGLVERTEXATTRIB4SPROC glVertexAttrib4s_ = NULL;
+PFNGLVERTEXATTRIB4SVPROC glVertexAttrib4sv_ = NULL;
+PFNGLVERTEXATTRIB4BVPROC glVertexAttrib4bv_ = NULL;
+PFNGLVERTEXATTRIB4IVPROC glVertexAttrib4iv_ = NULL;
+PFNGLVERTEXATTRIB4UBVPROC glVertexAttrib4ubv_ = NULL;
+PFNGLVERTEXATTRIB4UIVPROC glVertexAttrib4uiv_ = NULL;
+PFNGLVERTEXATTRIB4USVPROC glVertexAttrib4usv_ = NULL;
+PFNGLVERTEXATTRIB4NBVPROC glVertexAttrib4Nbv_ = NULL;
+PFNGLVERTEXATTRIB4NIVPROC glVertexAttrib4Niv_ = NULL;
+PFNGLVERTEXATTRIB4NUBPROC glVertexAttrib4Nub_ = NULL;
+PFNGLVERTEXATTRIB4NUBVPROC glVertexAttrib4Nubv_ = NULL;
+PFNGLVERTEXATTRIB4NUIVPROC glVertexAttrib4Nuiv_ = NULL;
+PFNGLVERTEXATTRIB4NUSVPROC glVertexAttrib4Nusv_ = NULL;
+PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer_ = NULL;
PFNGLDRAWBUFFERSPROC glDrawBuffers_ = NULL;
// OpenGL 3.0
-PFNGLGETSTRINGIPROC glGetStringi_ = NULL;
+PFNGLGETSTRINGIPROC glGetStringi_ = NULL;
PFNGLBINDFRAGDATALOCATIONPROC glBindFragDataLocation_ = NULL;
// GL_EXT_framebuffer_object
-PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer_ = NULL;
-PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers_ = NULL;
-PFNGLGENFRAMEBUFFERSPROC glGenRenderbuffers_ = NULL;
-PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage_ = NULL;
+PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer_ = NULL;
+PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers_ = NULL;
+PFNGLGENFRAMEBUFFERSPROC glGenRenderbuffers_ = NULL;
+PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage_ = NULL;
PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus_ = NULL;
-PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer_ = NULL;
-PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers_ = NULL;
-PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers_ = NULL;
-PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D_ = NULL;
+PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer_ = NULL;
+PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers_ = NULL;
+PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers_ = NULL;
+PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D_ = NULL;
PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer_ = NULL;
-PFNGLGENERATEMIPMAPPROC glGenerateMipmap_ = NULL;
+PFNGLGENERATEMIPMAPPROC glGenerateMipmap_ = NULL;
// GL_EXT_framebuffer_blit
-PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer_ = NULL;
+PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer_ = NULL;
// GL_ARB_uniform_buffer_object
-PFNGLGETUNIFORMINDICESPROC glGetUniformIndices_ = NULL;
-PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv_ = NULL;
-PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex_ = NULL;
+PFNGLGETUNIFORMINDICESPROC glGetUniformIndices_ = NULL;
+PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv_ = NULL;
+PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex_ = NULL;
PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv_ = NULL;
-PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding_ = NULL;
-PFNGLBINDBUFFERBASEPROC glBindBufferBase_ = NULL;
-PFNGLBINDBUFFERRANGEPROC glBindBufferRange_ = NULL;
+PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding_ = NULL;
+PFNGLBINDBUFFERBASEPROC glBindBufferBase_ = NULL;
+PFNGLBINDBUFFERRANGEPROC glBindBufferRange_ = NULL;
// GL_ARB_map_buffer_range
-PFNGLMAPBUFFERRANGEPROC glMapBufferRange_ = NULL;
+PFNGLMAPBUFFERRANGEPROC glMapBufferRange_ = NULL;
PFNGLFLUSHMAPPEDBUFFERRANGEPROC glFlushMappedBufferRange_ = NULL;
// GL_ARB_vertex_array_object
-PFNGLBINDVERTEXARRAYPROC glBindVertexArray_ = NULL;
+PFNGLBINDVERTEXARRAYPROC glBindVertexArray_ = NULL;
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays_ = NULL;
-PFNGLGENVERTEXARRAYSPROC glGenVertexArrays_ = NULL;
-PFNGLISVERTEXARRAYPROC glIsVertexArray_ = NULL;
+PFNGLGENVERTEXARRAYSPROC glGenVertexArrays_ = NULL;
+PFNGLISVERTEXARRAYPROC glIsVertexArray_ = NULL;
void *getprocaddress(const char *name)
{
- return SDL_GL_GetProcAddress(name);
+ return SDL_GL_GetProcAddress(name);
}
VARP(ati_skybox_bug, 0, 0, 1);
@@ -161,406 +161,383 @@ VAR(rtscissor, 0, 1, 1);
VAR(blurtile, 0, 1, 1);
VAR(rtsharefb, 0, 1, 1);
-VAR(dbgexts, 0, 0, 1);
-
hashset<const char *> glexts;
void parseglexts()
{
- if(glversion >= 300)
- {
- GLint numexts = 0;
- glGetIntegerv(GL_NUM_EXTENSIONS, &numexts);
- loopi(numexts)
- {
- const char *ext = (const char *)glGetStringi_(GL_EXTENSIONS, i);
- glexts.add(newstring(ext));
- }
- }
- else
- {
- const char *exts = (const char *)glGetString(GL_EXTENSIONS);
- for(;;)
- {
- while(*exts == ' ') exts++;
- if(!*exts) break;
- const char *ext = exts;
- while(*exts && *exts != ' ') exts++;
- if(exts > ext) glexts.add(newstring(ext, size_t(exts-ext)));
- }
- }
+ if(glversion >= 300)
+ {
+ GLint numexts = 0;
+ glGetIntegerv(GL_NUM_EXTENSIONS, &numexts);
+ loopi(numexts)
+ {
+ const char *ext = (const char *)glGetStringi_(GL_EXTENSIONS, i);
+ glexts.add(newstring(ext));
+ }
+ }
+ else
+ {
+ const char *exts = (const char *)glGetString(GL_EXTENSIONS);
+ for(;;)
+ {
+ while(*exts == ' ') exts++;
+ if(!*exts) break;
+ const char *ext = exts;
+ while(*exts && *exts != ' ') exts++;
+ if(exts > ext) glexts.add(newstring(ext, size_t(exts-ext)));
+ }
+ }
}
bool hasext(const char *ext)
{
- return glexts.access(ext)!=NULL;
+ return glexts.access(ext)!=NULL;
}
void gl_checkextensions()
{
- const char *vendor = (const char *)glGetString(GL_VENDOR);
- const char *renderer = (const char *)glGetString(GL_RENDERER);
- const char *version = (const char *)glGetString(GL_VERSION);
- conoutf(CON_INIT, "Renderer: %s (%s)", renderer, vendor);
- conoutf(CON_INIT, "Driver: %s", version);
-
- bool mesa = false, intel = false, ati = false, nvidia = false;
- if(strstr(renderer, "Mesa") || strstr(version, "Mesa"))
- {
- mesa = true;
- if(strstr(renderer, "Intel")) intel = true;
- }
- else if(strstr(vendor, "NVIDIA"))
- nvidia = true;
- else if(strstr(vendor, "ATI") || strstr(vendor, "Advanced Micro Devices"))
- ati = true;
- else if(strstr(vendor, "Intel"))
- intel = true;
-
- uint glmajorversion, glminorversion;
- if(sscanf(version, " %u.%u", &glmajorversion, &glminorversion) != 2) glversion = 100;
- else glversion = glmajorversion*100 + glminorversion*10;
-
- if(glversion < 200) fatal("OpenGL 2.0 or greater is required!");
-
- glMultiDrawArrays_ = (PFNGLMULTIDRAWARRAYSPROC) getprocaddress("glMultiDrawArrays");
- glMultiDrawElements_ = (PFNGLMULTIDRAWELEMENTSPROC) getprocaddress("glMultiDrawElements");
-
- glBlendFuncSeparate_ = (PFNGLBLENDFUNCSEPARATEPROC) getprocaddress("glBlendFuncSeparate");
- glBlendEquationSeparate_ = (PFNGLBLENDEQUATIONSEPARATEPROC) getprocaddress("glBlendEquationSeparate");
- glStencilOpSeparate_ = (PFNGLSTENCILOPSEPARATEPROC) getprocaddress("glStencilOpSeparate");
- glStencilFuncSeparate_ = (PFNGLSTENCILFUNCSEPARATEPROC) getprocaddress("glStencilFuncSeparate");
- glStencilMaskSeparate_ = (PFNGLSTENCILMASKSEPARATEPROC) getprocaddress("glStencilMaskSeparate");
-
- glGenBuffers_ = (PFNGLGENBUFFERSPROC) getprocaddress("glGenBuffers");
- glBindBuffer_ = (PFNGLBINDBUFFERPROC) getprocaddress("glBindBuffer");
- glMapBuffer_ = (PFNGLMAPBUFFERPROC) getprocaddress("glMapBuffer");
- glUnmapBuffer_ = (PFNGLUNMAPBUFFERPROC) getprocaddress("glUnmapBuffer");
- glBufferData_ = (PFNGLBUFFERDATAPROC) getprocaddress("glBufferData");
- glBufferSubData_ = (PFNGLBUFFERSUBDATAPROC) getprocaddress("glBufferSubData");
- glDeleteBuffers_ = (PFNGLDELETEBUFFERSPROC) getprocaddress("glDeleteBuffers");
- glGetBufferSubData_ = (PFNGLGETBUFFERSUBDATAPROC) getprocaddress("glGetBufferSubData");
-
- glGetQueryiv_ = (PFNGLGETQUERYIVPROC) getprocaddress("glGetQueryiv");
- glGenQueries_ = (PFNGLGENQUERIESPROC) getprocaddress("glGenQueries");
- glDeleteQueries_ = (PFNGLDELETEQUERIESPROC) getprocaddress("glDeleteQueries");
- glBeginQuery_ = (PFNGLBEGINQUERYPROC) getprocaddress("glBeginQuery");
- glEndQuery_ = (PFNGLENDQUERYPROC) getprocaddress("glEndQuery");
- glGetQueryObjectiv_ = (PFNGLGETQUERYOBJECTIVPROC) getprocaddress("glGetQueryObjectiv");
- glGetQueryObjectuiv_ = (PFNGLGETQUERYOBJECTUIVPROC) getprocaddress("glGetQueryObjectuiv");
-
- glCreateProgram_ = (PFNGLCREATEPROGRAMPROC) getprocaddress("glCreateProgram");
- glDeleteProgram_ = (PFNGLDELETEPROGRAMPROC) getprocaddress("glDeleteProgram");
- glUseProgram_ = (PFNGLUSEPROGRAMPROC) getprocaddress("glUseProgram");
- glCreateShader_ = (PFNGLCREATESHADERPROC) getprocaddress("glCreateShader");
- glDeleteShader_ = (PFNGLDELETESHADERPROC) getprocaddress("glDeleteShader");
- glShaderSource_ = (PFNGLSHADERSOURCEPROC) getprocaddress("glShaderSource");
- glCompileShader_ = (PFNGLCOMPILESHADERPROC) getprocaddress("glCompileShader");
- glGetShaderiv_ = (PFNGLGETSHADERIVPROC) getprocaddress("glGetShaderiv");
- glGetProgramiv_ = (PFNGLGETPROGRAMIVPROC) getprocaddress("glGetProgramiv");
- glAttachShader_ = (PFNGLATTACHSHADERPROC) getprocaddress("glAttachShader");
- glGetProgramInfoLog_ = (PFNGLGETPROGRAMINFOLOGPROC) getprocaddress("glGetProgramInfoLog");
- glGetShaderInfoLog_ = (PFNGLGETSHADERINFOLOGPROC) getprocaddress("glGetShaderInfoLog");
- glLinkProgram_ = (PFNGLLINKPROGRAMPROC) getprocaddress("glLinkProgram");
- glGetUniformLocation_ = (PFNGLGETUNIFORMLOCATIONPROC) getprocaddress("glGetUniformLocation");
- glUniform1f_ = (PFNGLUNIFORM1FPROC) getprocaddress("glUniform1f");
- glUniform2f_ = (PFNGLUNIFORM2FPROC) getprocaddress("glUniform2f");
- glUniform3f_ = (PFNGLUNIFORM3FPROC) getprocaddress("glUniform3f");
- glUniform4f_ = (PFNGLUNIFORM4FPROC) getprocaddress("glUniform4f");
- glUniform1fv_ = (PFNGLUNIFORM1FVPROC) getprocaddress("glUniform1fv");
- glUniform2fv_ = (PFNGLUNIFORM2FVPROC) getprocaddress("glUniform2fv");
- glUniform3fv_ = (PFNGLUNIFORM3FVPROC) getprocaddress("glUniform3fv");
- glUniform4fv_ = (PFNGLUNIFORM4FVPROC) getprocaddress("glUniform4fv");
- glUniform1i_ = (PFNGLUNIFORM1IPROC) getprocaddress("glUniform1i");
- glUniform2i_ = (PFNGLUNIFORM2IPROC) getprocaddress("glUniform2i");
- glUniform3i_ = (PFNGLUNIFORM3IPROC) getprocaddress("glUniform3i");
- glUniform4i_ = (PFNGLUNIFORM4IPROC) getprocaddress("glUniform4i");
- glUniform1iv_ = (PFNGLUNIFORM1IVPROC) getprocaddress("glUniform1iv");
- glUniform2iv_ = (PFNGLUNIFORM2IVPROC) getprocaddress("glUniform2iv");
- glUniform3iv_ = (PFNGLUNIFORM3IVPROC) getprocaddress("glUniform3iv");
- glUniform4iv_ = (PFNGLUNIFORM4IVPROC) getprocaddress("glUniform4iv");
- glUniformMatrix2fv_ = (PFNGLUNIFORMMATRIX2FVPROC) getprocaddress("glUniformMatrix2fv");
- glUniformMatrix3fv_ = (PFNGLUNIFORMMATRIX3FVPROC) getprocaddress("glUniformMatrix3fv");
- glUniformMatrix4fv_ = (PFNGLUNIFORMMATRIX4FVPROC) getprocaddress("glUniformMatrix4fv");
- glBindAttribLocation_ = (PFNGLBINDATTRIBLOCATIONPROC) getprocaddress("glBindAttribLocation");
- glGetActiveUniform_ = (PFNGLGETACTIVEUNIFORMPROC) getprocaddress("glGetActiveUniform");
- glEnableVertexAttribArray_ = (PFNGLENABLEVERTEXATTRIBARRAYPROC) getprocaddress("glEnableVertexAttribArray");
- glDisableVertexAttribArray_ = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) getprocaddress("glDisableVertexAttribArray");
-
- glVertexAttrib1f_ = (PFNGLVERTEXATTRIB1FPROC) getprocaddress("glVertexAttrib1f");
- glVertexAttrib1fv_ = (PFNGLVERTEXATTRIB1FVPROC) getprocaddress("glVertexAttrib1fv");
- glVertexAttrib1s_ = (PFNGLVERTEXATTRIB1SPROC) getprocaddress("glVertexAttrib1s");
- glVertexAttrib1sv_ = (PFNGLVERTEXATTRIB1SVPROC) getprocaddress("glVertexAttrib1sv");
- glVertexAttrib2f_ = (PFNGLVERTEXATTRIB2FPROC) getprocaddress("glVertexAttrib2f");
- glVertexAttrib2fv_ = (PFNGLVERTEXATTRIB2FVPROC) getprocaddress("glVertexAttrib2fv");
- glVertexAttrib2s_ = (PFNGLVERTEXATTRIB2SPROC) getprocaddress("glVertexAttrib2s");
- glVertexAttrib2sv_ = (PFNGLVERTEXATTRIB2SVPROC) getprocaddress("glVertexAttrib2sv");
- glVertexAttrib3f_ = (PFNGLVERTEXATTRIB3FPROC) getprocaddress("glVertexAttrib3f");
- glVertexAttrib3fv_ = (PFNGLVERTEXATTRIB3FVPROC) getprocaddress("glVertexAttrib3fv");
- glVertexAttrib3s_ = (PFNGLVERTEXATTRIB3SPROC) getprocaddress("glVertexAttrib3s");
- glVertexAttrib3sv_ = (PFNGLVERTEXATTRIB3SVPROC) getprocaddress("glVertexAttrib3sv");
- glVertexAttrib4f_ = (PFNGLVERTEXATTRIB4FPROC) getprocaddress("glVertexAttrib4f");
- glVertexAttrib4fv_ = (PFNGLVERTEXATTRIB4FVPROC) getprocaddress("glVertexAttrib4fv");
- glVertexAttrib4s_ = (PFNGLVERTEXATTRIB4SPROC) getprocaddress("glVertexAttrib4s");
- glVertexAttrib4sv_ = (PFNGLVERTEXATTRIB4SVPROC) getprocaddress("glVertexAttrib4sv");
- glVertexAttrib4bv_ = (PFNGLVERTEXATTRIB4BVPROC) getprocaddress("glVertexAttrib4bv");
- glVertexAttrib4iv_ = (PFNGLVERTEXATTRIB4IVPROC) getprocaddress("glVertexAttrib4iv");
- glVertexAttrib4ubv_ = (PFNGLVERTEXATTRIB4UBVPROC) getprocaddress("glVertexAttrib4ubv");
- glVertexAttrib4uiv_ = (PFNGLVERTEXATTRIB4UIVPROC) getprocaddress("glVertexAttrib4uiv");
- glVertexAttrib4usv_ = (PFNGLVERTEXATTRIB4USVPROC) getprocaddress("glVertexAttrib4usv");
- glVertexAttrib4Nbv_ = (PFNGLVERTEXATTRIB4NBVPROC) getprocaddress("glVertexAttrib4Nbv");
- glVertexAttrib4Niv_ = (PFNGLVERTEXATTRIB4NIVPROC) getprocaddress("glVertexAttrib4Niv");
- glVertexAttrib4Nub_ = (PFNGLVERTEXATTRIB4NUBPROC) getprocaddress("glVertexAttrib4Nub");
- glVertexAttrib4Nubv_ = (PFNGLVERTEXATTRIB4NUBVPROC) getprocaddress("glVertexAttrib4Nubv");
- glVertexAttrib4Nuiv_ = (PFNGLVERTEXATTRIB4NUIVPROC) getprocaddress("glVertexAttrib4Nuiv");
- glVertexAttrib4Nusv_ = (PFNGLVERTEXATTRIB4NUSVPROC) getprocaddress("glVertexAttrib4Nusv");
- glVertexAttribPointer_ = (PFNGLVERTEXATTRIBPOINTERPROC) getprocaddress("glVertexAttribPointer");
-
- glDrawBuffers_ = (PFNGLDRAWBUFFERSPROC) getprocaddress("glDrawBuffers");
-
- if(glversion >= 300)
- {
- glGetStringi_ = (PFNGLGETSTRINGIPROC) getprocaddress("glGetStringi");
- glBindFragDataLocation_ = (PFNGLBINDFRAGDATALOCATIONPROC)getprocaddress("glBindFragDataLocation");
- }
-
- const char *glslstr = (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
- uint glslmajorversion, glslminorversion;
- if(glslstr && sscanf(glslstr, " %u.%u", &glslmajorversion, &glslminorversion) == 2) glslversion = glslmajorversion*100 + glslminorversion;
-
- if(glslversion < 120) fatal("GLSL 1.20 or greater is required!");
-
- parseglexts();
-
- GLint val;
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &val);
- hwtexsize = val;
- glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &val);
- hwcubetexsize = val;
-
- if(glversion >= 300 || hasext("GL_ARB_texture_float") || hasext("GL_ATI_texture_float"))
- {
- hasTF = true;
- if(glversion < 300 && dbgexts) conoutf(CON_INIT, "Using GL_ARB_texture_float extension.");
- shadowmap = 1;
- extern int smoothshadowmappeel;
- smoothshadowmappeel = 1;
- }
-
- if(glversion >= 300 || hasext("GL_ARB_texture_rg"))
- {
- hasTRG = true;
- if(glversion < 300 && dbgexts) conoutf(CON_INIT, "Using GL_ARB_texture_rg extension.");
- }
-
- if(glversion >= 300 || hasext("GL_ARB_framebuffer_object"))
- {
- glBindRenderbuffer_ = (PFNGLBINDRENDERBUFFERPROC) getprocaddress("glBindRenderbuffer");
- glDeleteRenderbuffers_ = (PFNGLDELETERENDERBUFFERSPROC) getprocaddress("glDeleteRenderbuffers");
- glGenRenderbuffers_ = (PFNGLGENFRAMEBUFFERSPROC) getprocaddress("glGenRenderbuffers");
- glRenderbufferStorage_ = (PFNGLRENDERBUFFERSTORAGEPROC) getprocaddress("glRenderbufferStorage");
- glCheckFramebufferStatus_ = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) getprocaddress("glCheckFramebufferStatus");
- glBindFramebuffer_ = (PFNGLBINDFRAMEBUFFERPROC) getprocaddress("glBindFramebuffer");
- glDeleteFramebuffers_ = (PFNGLDELETEFRAMEBUFFERSPROC) getprocaddress("glDeleteFramebuffers");
- glGenFramebuffers_ = (PFNGLGENFRAMEBUFFERSPROC) getprocaddress("glGenFramebuffers");
- glFramebufferTexture2D_ = (PFNGLFRAMEBUFFERTEXTURE2DPROC) getprocaddress("glFramebufferTexture2D");
- glFramebufferRenderbuffer_ = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)getprocaddress("glFramebufferRenderbuffer");
- glGenerateMipmap_ = (PFNGLGENERATEMIPMAPPROC) getprocaddress("glGenerateMipmap");
- glBlitFramebuffer_ = (PFNGLBLITFRAMEBUFFERPROC) getprocaddress("glBlitFramebuffer");
- hasAFBO = hasFBO = hasFBB = hasDS = true;
- if(glversion < 300 && dbgexts) conoutf(CON_INIT, "Using GL_ARB_framebuffer_object extension.");
- }
- else if(hasext("GL_EXT_framebuffer_object"))
- {
- glBindRenderbuffer_ = (PFNGLBINDRENDERBUFFERPROC) getprocaddress("glBindRenderbufferEXT");
- glDeleteRenderbuffers_ = (PFNGLDELETERENDERBUFFERSPROC) getprocaddress("glDeleteRenderbuffersEXT");
- glGenRenderbuffers_ = (PFNGLGENFRAMEBUFFERSPROC) getprocaddress("glGenRenderbuffersEXT");
- glRenderbufferStorage_ = (PFNGLRENDERBUFFERSTORAGEPROC) getprocaddress("glRenderbufferStorageEXT");
- glCheckFramebufferStatus_ = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) getprocaddress("glCheckFramebufferStatusEXT");
- glBindFramebuffer_ = (PFNGLBINDFRAMEBUFFERPROC) getprocaddress("glBindFramebufferEXT");
- glDeleteFramebuffers_ = (PFNGLDELETEFRAMEBUFFERSPROC) getprocaddress("glDeleteFramebuffersEXT");
- glGenFramebuffers_ = (PFNGLGENFRAMEBUFFERSPROC) getprocaddress("glGenFramebuffersEXT");
- glFramebufferTexture2D_ = (PFNGLFRAMEBUFFERTEXTURE2DPROC) getprocaddress("glFramebufferTexture2DEXT");
- glFramebufferRenderbuffer_ = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)getprocaddress("glFramebufferRenderbufferEXT");
- glGenerateMipmap_ = (PFNGLGENERATEMIPMAPPROC) getprocaddress("glGenerateMipmapEXT");
- hasFBO = true;
- if(dbgexts) conoutf(CON_INIT, "Using GL_EXT_framebuffer_object extension.");
-
- if(hasext("GL_EXT_framebuffer_blit"))
- {
- glBlitFramebuffer_ = (PFNGLBLITFRAMEBUFFERPROC) getprocaddress("glBlitFramebufferEXT");
- hasFBB = true;
- if(dbgexts) conoutf(CON_INIT, "Using GL_EXT_framebuffer_blit extension.");
- }
-
- if(hasext("GL_EXT_packed_depth_stencil") || hasext("GL_NV_packed_depth_stencil"))
- {
- hasDS = true;
- if(dbgexts) conoutf(CON_INIT, "Using GL_EXT_packed_depth_stencil extension.");
- }
- }
- else fatal("Framebuffer object support is required!");
-
- extern int fpdepthfx;
- if(ati)
- {
- //conoutf(CON_WARN, "WARNING: ATI cards may show garbage in skybox. (use \"/ati_skybox_bug 1\" to fix)");
-
- minimizetcusage = 1;
- if(hasTF && hasTRG) fpdepthfx = 1;
- // On Catalyst 10.2, issuing an occlusion query on the first draw using a given cubemap texture causes a nasty crash
- ati_cubemap_bug = 1;
- }
- else if(nvidia)
- {
- reservevpparams = 10;
- rtsharefb = 0; // work-around for strange driver stalls involving when using many FBOs
- extern int filltjoints;
- if(glversion < 300 && !hasext("GL_EXT_gpu_shader4")) filltjoints = 0; // DX9 or less NV cards seem to not cause many sparklies
-
- if(hasTF && hasTRG) fpdepthfx = 1;
- }
- else
- {
- reservevpparams = 20;
-
- if(mesa) mesa_swap_bug = 1;
- }
-
- if(glversion >= 300 || hasext("GL_ARB_map_buffer_range"))
- {
- glMapBufferRange_ = (PFNGLMAPBUFFERRANGEPROC) getprocaddress("glMapBufferRange");
- glFlushMappedBufferRange_ = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)getprocaddress("glFlushMappedBufferRange");
- hasMBR = true;
- if(glversion < 300 && dbgexts) conoutf(CON_INIT, "Using GL_ARB_map_buffer_range.");
- }
-
- if(glversion >= 310 || hasext("GL_ARB_uniform_buffer_object"))
- {
- glGetUniformIndices_ = (PFNGLGETUNIFORMINDICESPROC) getprocaddress("glGetUniformIndices");
- glGetActiveUniformsiv_ = (PFNGLGETACTIVEUNIFORMSIVPROC) getprocaddress("glGetActiveUniformsiv");
- glGetUniformBlockIndex_ = (PFNGLGETUNIFORMBLOCKINDEXPROC) getprocaddress("glGetUniformBlockIndex");
- glGetActiveUniformBlockiv_ = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)getprocaddress("glGetActiveUniformBlockiv");
- glUniformBlockBinding_ = (PFNGLUNIFORMBLOCKBINDINGPROC) getprocaddress("glUniformBlockBinding");
- glBindBufferBase_ = (PFNGLBINDBUFFERBASEPROC) getprocaddress("glBindBufferBase");
- glBindBufferRange_ = (PFNGLBINDBUFFERRANGEPROC) getprocaddress("glBindBufferRange");
-
- useubo = 1;
- hasUBO = true;
- if(glversion < 310 && dbgexts) conoutf(CON_INIT, "Using GL_ARB_uniform_buffer_object extension.");
- }
-
- if(glversion >= 300 || hasext("GL_ARB_vertex_array_object"))
- {
- glBindVertexArray_ = (PFNGLBINDVERTEXARRAYPROC) getprocaddress("glBindVertexArray");
- glDeleteVertexArrays_ = (PFNGLDELETEVERTEXARRAYSPROC)getprocaddress("glDeleteVertexArrays");
- glGenVertexArrays_ = (PFNGLGENVERTEXARRAYSPROC) getprocaddress("glGenVertexArrays");
- glIsVertexArray_ = (PFNGLISVERTEXARRAYPROC) getprocaddress("glIsVertexArray");
- hasVAO = true;
- if(glversion < 300 && dbgexts) conoutf(CON_INIT, "Using GL_ARB_vertex_array_object extension.");
- }
- else if(hasext("GL_APPLE_vertex_array_object"))
- {
- glBindVertexArray_ = (PFNGLBINDVERTEXARRAYPROC) getprocaddress("glBindVertexArrayAPPLE");
- glDeleteVertexArrays_ = (PFNGLDELETEVERTEXARRAYSPROC)getprocaddress("glDeleteVertexArraysAPPLE");
- glGenVertexArrays_ = (PFNGLGENVERTEXARRAYSPROC) getprocaddress("glGenVertexArraysAPPLE");
- glIsVertexArray_ = (PFNGLISVERTEXARRAYPROC) getprocaddress("glIsVertexArrayAPPLE");
- hasVAO = true;
- if(dbgexts) conoutf(CON_INIT, "Using GL_APPLE_vertex_array_object extension.");
- }
-
- if(glversion >= 330 || hasext("GL_ARB_texture_swizzle") || hasext("GL_EXT_texture_swizzle"))
- {
- hasTSW = true;
- if(glversion < 330 && dbgexts) conoutf(CON_INIT, "Using GL_ARB_texture_swizzle extension.");
- }
-
- if(hasext("GL_EXT_texture_compression_s3tc"))
- {
- hasS3TC = true;
-#ifdef __APPLE__
- usetexcompress = 1;
-#else
- if(!mesa) usetexcompress = 2;
-#endif
- if(dbgexts) conoutf(CON_INIT, "Using GL_EXT_texture_compression_s3tc extension.");
- }
- else if(hasext("GL_EXT_texture_compression_dxt1") && hasext("GL_ANGLE_texture_compression_dxt3") && hasext("GL_ANGLE_texture_compression_dxt5"))
- {
- hasS3TC = true;
- if(dbgexts) conoutf(CON_INIT, "Using GL_EXT_texture_compression_dxt1 extension.");
- }
- if(hasext("GL_3DFX_texture_compression_FXT1"))
- {
- hasFXT1 = true;
- if(mesa) usetexcompress = max(usetexcompress, 1);
- if(dbgexts) conoutf(CON_INIT, "Using GL_3DFX_texture_compression_FXT1.");
- }
- if(hasext("GL_EXT_texture_compression_latc"))
- {
- hasLATC = true;
- if(dbgexts) conoutf(CON_INIT, "Using GL_EXT_texture_compression_latc extension.");
- }
- if(glversion >= 300 || hasext("GL_ARB_texture_compression_rgtc") || hasext("GL_EXT_texture_compression_rgtc"))
- {
- hasRGTC = true;
- if(glversion < 300 && dbgexts) conoutf(CON_INIT, "Using GL_ARB_texture_compression_rgtc extension.");
- }
-
- if(hasext("GL_EXT_texture_filter_anisotropic"))
- {
- GLint val;
- glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &val);
- hwmaxaniso = val;
- hasAF = true;
- if(dbgexts) conoutf(CON_INIT, "Using GL_EXT_texture_filter_anisotropic extension.");
- }
-
- if(glversion >= 300 || hasext("GL_EXT_gpu_shader4"))
- {
- // on DX10 or above class cards (i.e. GF8 or RadeonHD) enable expensive features
- extern int glare, maxdynlights, depthfxsize, blurdepthfx, texcompress;
- waterfallrefract = 1;
- glare = 1;
- maxdynlights = MAXDYNLIGHTS;
- depthfxsize = 10;
- blurdepthfx = 0;
- texcompress = max(texcompress, 1024 + 1);
- }
+ const char *vendor = (const char *)glGetString(GL_VENDOR);
+ const char *renderer = (const char *)glGetString(GL_RENDERER);
+ const char *version = (const char *)glGetString(GL_VERSION);
+ conoutf(CON_INIT, "Renderer: %s (%s)", renderer, vendor);
+ conoutf(CON_INIT, "Driver: %s", version);
+
+ bool mesa = false, intel = false, ati = false, nvidia = false;
+ if(strstr(renderer, "Mesa") || strstr(version, "Mesa"))
+ {
+ mesa = true;
+ if(strstr(renderer, "Intel")) intel = true;
+ }
+ else if(strstr(vendor, "NVIDIA"))
+ nvidia = true;
+ else if(strstr(vendor, "ATI") || strstr(vendor, "Advanced Micro Devices"))
+ ati = true;
+ else if(strstr(vendor, "Intel"))
+ intel = true;
+
+ uint glmajorversion, glminorversion;
+ if(sscanf(version, " %u.%u", &glmajorversion, &glminorversion) != 2) glversion = 100;
+ else glversion = glmajorversion*100 + glminorversion*10;
+
+ if(glversion < 200) fatal("OpenGL 2.0 or greater is required!");
+
+ glMultiDrawArrays_ = (PFNGLMULTIDRAWARRAYSPROC) getprocaddress("glMultiDrawArrays");
+ glMultiDrawElements_ = (PFNGLMULTIDRAWELEMENTSPROC) getprocaddress("glMultiDrawElements");
+
+ glBlendFuncSeparate_ = (PFNGLBLENDFUNCSEPARATEPROC) getprocaddress("glBlendFuncSeparate");
+ glBlendEquationSeparate_ = (PFNGLBLENDEQUATIONSEPARATEPROC) getprocaddress("glBlendEquationSeparate");
+ glStencilOpSeparate_ = (PFNGLSTENCILOPSEPARATEPROC) getprocaddress("glStencilOpSeparate");
+ glStencilFuncSeparate_ = (PFNGLSTENCILFUNCSEPARATEPROC) getprocaddress("glStencilFuncSeparate");
+ glStencilMaskSeparate_ = (PFNGLSTENCILMASKSEPARATEPROC) getprocaddress("glStencilMaskSeparate");
+
+ glGenBuffers_ = (PFNGLGENBUFFERSPROC) getprocaddress("glGenBuffers");
+ glBindBuffer_ = (PFNGLBINDBUFFERPROC) getprocaddress("glBindBuffer");
+ glMapBuffer_ = (PFNGLMAPBUFFERPROC) getprocaddress("glMapBuffer");
+ glUnmapBuffer_ = (PFNGLUNMAPBUFFERPROC) getprocaddress("glUnmapBuffer");
+ glBufferData_ = (PFNGLBUFFERDATAPROC) getprocaddress("glBufferData");
+ glBufferSubData_ = (PFNGLBUFFERSUBDATAPROC) getprocaddress("glBufferSubData");
+ glDeleteBuffers_ = (PFNGLDELETEBUFFERSPROC) getprocaddress("glDeleteBuffers");
+ glGetBufferSubData_ = (PFNGLGETBUFFERSUBDATAPROC) getprocaddress("glGetBufferSubData");
+
+ glGetQueryiv_ = (PFNGLGETQUERYIVPROC) getprocaddress("glGetQueryiv");
+ glGenQueries_ = (PFNGLGENQUERIESPROC) getprocaddress("glGenQueries");
+ glDeleteQueries_ = (PFNGLDELETEQUERIESPROC) getprocaddress("glDeleteQueries");
+ glBeginQuery_ = (PFNGLBEGINQUERYPROC) getprocaddress("glBeginQuery");
+ glEndQuery_ = (PFNGLENDQUERYPROC) getprocaddress("glEndQuery");
+ glGetQueryObjectiv_ = (PFNGLGETQUERYOBJECTIVPROC) getprocaddress("glGetQueryObjectiv");
+ glGetQueryObjectuiv_ = (PFNGLGETQUERYOBJECTUIVPROC) getprocaddress("glGetQueryObjectuiv");
+
+ glCreateProgram_ = (PFNGLCREATEPROGRAMPROC) getprocaddress("glCreateProgram");
+ glDeleteProgram_ = (PFNGLDELETEPROGRAMPROC) getprocaddress("glDeleteProgram");
+ glUseProgram_ = (PFNGLUSEPROGRAMPROC) getprocaddress("glUseProgram");
+ glCreateShader_ = (PFNGLCREATESHADERPROC) getprocaddress("glCreateShader");
+ glDeleteShader_ = (PFNGLDELETESHADERPROC) getprocaddress("glDeleteShader");
+ glShaderSource_ = (PFNGLSHADERSOURCEPROC) getprocaddress("glShaderSource");
+ glCompileShader_ = (PFNGLCOMPILESHADERPROC) getprocaddress("glCompileShader");
+ glGetShaderiv_ = (PFNGLGETSHADERIVPROC) getprocaddress("glGetShaderiv");
+ glGetProgramiv_ = (PFNGLGETPROGRAMIVPROC) getprocaddress("glGetProgramiv");
+ glAttachShader_ = (PFNGLATTACHSHADERPROC) getprocaddress("glAttachShader");
+ glGetProgramInfoLog_ = (PFNGLGETPROGRAMINFOLOGPROC) getprocaddress("glGetProgramInfoLog");
+ glGetShaderInfoLog_ = (PFNGLGETSHADERINFOLOGPROC) getprocaddress("glGetShaderInfoLog");
+ glLinkProgram_ = (PFNGLLINKPROGRAMPROC) getprocaddress("glLinkProgram");
+ glGetUniformLocation_ = (PFNGLGETUNIFORMLOCATIONPROC) getprocaddress("glGetUniformLocation");
+ glUniform1f_ = (PFNGLUNIFORM1FPROC) getprocaddress("glUniform1f");
+ glUniform2f_ = (PFNGLUNIFORM2FPROC) getprocaddress("glUniform2f");
+ glUniform3f_ = (PFNGLUNIFORM3FPROC) getprocaddress("glUniform3f");
+ glUniform4f_ = (PFNGLUNIFORM4FPROC) getprocaddress("glUniform4f");
+ glUniform1fv_ = (PFNGLUNIFORM1FVPROC) getprocaddress("glUniform1fv");
+ glUniform2fv_ = (PFNGLUNIFORM2FVPROC) getprocaddress("glUniform2fv");
+ glUniform3fv_ = (PFNGLUNIFORM3FVPROC) getprocaddress("glUniform3fv");
+ glUniform4fv_ = (PFNGLUNIFORM4FVPROC) getprocaddress("glUniform4fv");
+ glUniform1i_ = (PFNGLUNIFORM1IPROC) getprocaddress("glUniform1i");
+ glUniform2i_ = (PFNGLUNIFORM2IPROC) getprocaddress("glUniform2i");
+ glUniform3i_ = (PFNGLUNIFORM3IPROC) getprocaddress("glUniform3i");
+ glUniform4i_ = (PFNGLUNIFORM4IPROC) getprocaddress("glUniform4i");
+ glUniform1iv_ = (PFNGLUNIFORM1IVPROC) getprocaddress("glUniform1iv");
+ glUniform2iv_ = (PFNGLUNIFORM2IVPROC) getprocaddress("glUniform2iv");
+ glUniform3iv_ = (PFNGLUNIFORM3IVPROC) getprocaddress("glUniform3iv");
+ glUniform4iv_ = (PFNGLUNIFORM4IVPROC) getprocaddress("glUniform4iv");
+ glUniformMatrix2fv_ = (PFNGLUNIFORMMATRIX2FVPROC) getprocaddress("glUniformMatrix2fv");
+ glUniformMatrix3fv_ = (PFNGLUNIFORMMATRIX3FVPROC) getprocaddress("glUniformMatrix3fv");
+ glUniformMatrix4fv_ = (PFNGLUNIFORMMATRIX4FVPROC) getprocaddress("glUniformMatrix4fv");
+ glBindAttribLocation_ = (PFNGLBINDATTRIBLOCATIONPROC) getprocaddress("glBindAttribLocation");
+ glGetActiveUniform_ = (PFNGLGETACTIVEUNIFORMPROC) getprocaddress("glGetActiveUniform");
+ glEnableVertexAttribArray_ = (PFNGLENABLEVERTEXATTRIBARRAYPROC) getprocaddress("glEnableVertexAttribArray");
+ glDisableVertexAttribArray_ = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) getprocaddress("glDisableVertexAttribArray");
+
+ glVertexAttrib1f_ = (PFNGLVERTEXATTRIB1FPROC) getprocaddress("glVertexAttrib1f");
+ glVertexAttrib1fv_ = (PFNGLVERTEXATTRIB1FVPROC) getprocaddress("glVertexAttrib1fv");
+ glVertexAttrib1s_ = (PFNGLVERTEXATTRIB1SPROC) getprocaddress("glVertexAttrib1s");
+ glVertexAttrib1sv_ = (PFNGLVERTEXATTRIB1SVPROC) getprocaddress("glVertexAttrib1sv");
+ glVertexAttrib2f_ = (PFNGLVERTEXATTRIB2FPROC) getprocaddress("glVertexAttrib2f");
+ glVertexAttrib2fv_ = (PFNGLVERTEXATTRIB2FVPROC) getprocaddress("glVertexAttrib2fv");
+ glVertexAttrib2s_ = (PFNGLVERTEXATTRIB2SPROC) getprocaddress("glVertexAttrib2s");
+ glVertexAttrib2sv_ = (PFNGLVERTEXATTRIB2SVPROC) getprocaddress("glVertexAttrib2sv");
+ glVertexAttrib3f_ = (PFNGLVERTEXATTRIB3FPROC) getprocaddress("glVertexAttrib3f");
+ glVertexAttrib3fv_ = (PFNGLVERTEXATTRIB3FVPROC) getprocaddress("glVertexAttrib3fv");
+ glVertexAttrib3s_ = (PFNGLVERTEXATTRIB3SPROC) getprocaddress("glVertexAttrib3s");
+ glVertexAttrib3sv_ = (PFNGLVERTEXATTRIB3SVPROC) getprocaddress("glVertexAttrib3sv");
+ glVertexAttrib4f_ = (PFNGLVERTEXATTRIB4FPROC) getprocaddress("glVertexAttrib4f");
+ glVertexAttrib4fv_ = (PFNGLVERTEXATTRIB4FVPROC) getprocaddress("glVertexAttrib4fv");
+ glVertexAttrib4s_ = (PFNGLVERTEXATTRIB4SPROC) getprocaddress("glVertexAttrib4s");
+ glVertexAttrib4sv_ = (PFNGLVERTEXATTRIB4SVPROC) getprocaddress("glVertexAttrib4sv");
+ glVertexAttrib4bv_ = (PFNGLVERTEXATTRIB4BVPROC) getprocaddress("glVertexAttrib4bv");
+ glVertexAttrib4iv_ = (PFNGLVERTEXATTRIB4IVPROC) getprocaddress("glVertexAttrib4iv");
+ glVertexAttrib4ubv_ = (PFNGLVERTEXATTRIB4UBVPROC) getprocaddress("glVertexAttrib4ubv");
+ glVertexAttrib4uiv_ = (PFNGLVERTEXATTRIB4UIVPROC) getprocaddress("glVertexAttrib4uiv");
+ glVertexAttrib4usv_ = (PFNGLVERTEXATTRIB4USVPROC) getprocaddress("glVertexAttrib4usv");
+ glVertexAttrib4Nbv_ = (PFNGLVERTEXATTRIB4NBVPROC) getprocaddress("glVertexAttrib4Nbv");
+ glVertexAttrib4Niv_ = (PFNGLVERTEXATTRIB4NIVPROC) getprocaddress("glVertexAttrib4Niv");
+ glVertexAttrib4Nub_ = (PFNGLVERTEXATTRIB4NUBPROC) getprocaddress("glVertexAttrib4Nub");
+ glVertexAttrib4Nubv_ = (PFNGLVERTEXATTRIB4NUBVPROC) getprocaddress("glVertexAttrib4Nubv");
+ glVertexAttrib4Nuiv_ = (PFNGLVERTEXATTRIB4NUIVPROC) getprocaddress("glVertexAttrib4Nuiv");
+ glVertexAttrib4Nusv_ = (PFNGLVERTEXATTRIB4NUSVPROC) getprocaddress("glVertexAttrib4Nusv");
+ glVertexAttribPointer_ = (PFNGLVERTEXATTRIBPOINTERPROC) getprocaddress("glVertexAttribPointer");
+
+ glDrawBuffers_ = (PFNGLDRAWBUFFERSPROC) getprocaddress("glDrawBuffers");
+
+ if(glversion >= 300)
+ {
+ glGetStringi_ = (PFNGLGETSTRINGIPROC) getprocaddress("glGetStringi");
+ glBindFragDataLocation_ = (PFNGLBINDFRAGDATALOCATIONPROC)getprocaddress("glBindFragDataLocation");
+ }
+
+ const char *glslstr = (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
+ uint glslmajorversion, glslminorversion;
+ if(glslstr && sscanf(glslstr, " %u.%u", &glslmajorversion, &glslminorversion) == 2) glslversion = glslmajorversion*100 + glslminorversion;
+
+ if(glslversion < 120) fatal("GLSL 1.20 or greater is required!");
+
+ parseglexts();
+
+ GLint val;
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &val);
+ hwtexsize = val;
+ glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &val);
+ hwcubetexsize = val;
+
+ if(glversion >= 300 || hasext("GL_ARB_texture_float") || hasext("GL_ATI_texture_float"))
+ {
+ hasTF = true;
+ shadowmap = 1;
+ extern int smoothshadowmappeel;
+ smoothshadowmappeel = 1;
+ }
+
+ if(glversion >= 300 || hasext("GL_ARB_texture_rg"))
+ {
+ hasTRG = true;
+ }
+
+ if(glversion >= 300 || hasext("GL_ARB_framebuffer_object"))
+ {
+ glBindRenderbuffer_ = (PFNGLBINDRENDERBUFFERPROC) getprocaddress("glBindRenderbuffer");
+ glDeleteRenderbuffers_ = (PFNGLDELETERENDERBUFFERSPROC) getprocaddress("glDeleteRenderbuffers");
+ glGenRenderbuffers_ = (PFNGLGENFRAMEBUFFERSPROC) getprocaddress("glGenRenderbuffers");
+ glRenderbufferStorage_ = (PFNGLRENDERBUFFERSTORAGEPROC) getprocaddress("glRenderbufferStorage");
+ glCheckFramebufferStatus_ = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) getprocaddress("glCheckFramebufferStatus");
+ glBindFramebuffer_ = (PFNGLBINDFRAMEBUFFERPROC) getprocaddress("glBindFramebuffer");
+ glDeleteFramebuffers_ = (PFNGLDELETEFRAMEBUFFERSPROC) getprocaddress("glDeleteFramebuffers");
+ glGenFramebuffers_ = (PFNGLGENFRAMEBUFFERSPROC) getprocaddress("glGenFramebuffers");
+ glFramebufferTexture2D_ = (PFNGLFRAMEBUFFERTEXTURE2DPROC) getprocaddress("glFramebufferTexture2D");
+ glFramebufferRenderbuffer_ = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)getprocaddress("glFramebufferRenderbuffer");
+ glGenerateMipmap_ = (PFNGLGENERATEMIPMAPPROC) getprocaddress("glGenerateMipmap");
+ glBlitFramebuffer_ = (PFNGLBLITFRAMEBUFFERPROC) getprocaddress("glBlitFramebuffer");
+ hasAFBO = hasFBO = hasFBB = hasDS = true;
+ }
+ else if(hasext("GL_EXT_framebuffer_object"))
+ {
+ glBindRenderbuffer_ = (PFNGLBINDRENDERBUFFERPROC) getprocaddress("glBindRenderbufferEXT");
+ glDeleteRenderbuffers_ = (PFNGLDELETERENDERBUFFERSPROC) getprocaddress("glDeleteRenderbuffersEXT");
+ glGenRenderbuffers_ = (PFNGLGENFRAMEBUFFERSPROC) getprocaddress("glGenRenderbuffersEXT");
+ glRenderbufferStorage_ = (PFNGLRENDERBUFFERSTORAGEPROC) getprocaddress("glRenderbufferStorageEXT");
+ glCheckFramebufferStatus_ = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) getprocaddress("glCheckFramebufferStatusEXT");
+ glBindFramebuffer_ = (PFNGLBINDFRAMEBUFFERPROC) getprocaddress("glBindFramebufferEXT");
+ glDeleteFramebuffers_ = (PFNGLDELETEFRAMEBUFFERSPROC) getprocaddress("glDeleteFramebuffersEXT");
+ glGenFramebuffers_ = (PFNGLGENFRAMEBUFFERSPROC) getprocaddress("glGenFramebuffersEXT");
+ glFramebufferTexture2D_ = (PFNGLFRAMEBUFFERTEXTURE2DPROC) getprocaddress("glFramebufferTexture2DEXT");
+ glFramebufferRenderbuffer_ = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)getprocaddress("glFramebufferRenderbufferEXT");
+ glGenerateMipmap_ = (PFNGLGENERATEMIPMAPPROC) getprocaddress("glGenerateMipmapEXT");
+ hasFBO = true;
+
+ if(hasext("GL_EXT_framebuffer_blit"))
+ {
+ glBlitFramebuffer_ = (PFNGLBLITFRAMEBUFFERPROC) getprocaddress("glBlitFramebufferEXT");
+ hasFBB = true;
+ }
+
+ if(hasext("GL_EXT_packed_depth_stencil") || hasext("GL_NV_packed_depth_stencil"))
+ {
+ hasDS = true;
+ }
+ }
+ else fatal("Framebuffer object support is required!");
+
+ extern int fpdepthfx;
+ if(ati)
+ {
+ //conoutf(CON_WARN, "WARNING: ATI cards may show garbage in skybox. (use \"/ati_skybox_bug 1\" to fix)");
+
+ minimizetcusage = 1;
+ if(hasTF && hasTRG) fpdepthfx = 1;
+ // On Catalyst 10.2, issuing an occlusion query on the first draw using a given cubemap texture causes a nasty crash
+ ati_cubemap_bug = 1;
+ }
+ else if(nvidia)
+ {
+ reservevpparams = 10;
+ rtsharefb = 0; // work-around for strange driver stalls involving when using many FBOs
+ extern int filltjoints;
+ if(glversion < 300 && !hasext("GL_EXT_gpu_shader4")) filltjoints = 0; // DX9 or less NV cards seem to not cause many sparklies
+
+ if(hasTF && hasTRG) fpdepthfx = 1;
+ }
+ else
+ {
+ reservevpparams = 20;
+
+ if(mesa) mesa_swap_bug = 1;
+ }
+
+ if(glversion >= 300 || hasext("GL_ARB_map_buffer_range"))
+ {
+ glMapBufferRange_ = (PFNGLMAPBUFFERRANGEPROC) getprocaddress("glMapBufferRange");
+ glFlushMappedBufferRange_ = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)getprocaddress("glFlushMappedBufferRange");
+ hasMBR = true;
+ }
+
+ if(glversion >= 310 || hasext("GL_ARB_uniform_buffer_object"))
+ {
+ glGetUniformIndices_ = (PFNGLGETUNIFORMINDICESPROC) getprocaddress("glGetUniformIndices");
+ glGetActiveUniformsiv_ = (PFNGLGETACTIVEUNIFORMSIVPROC) getprocaddress("glGetActiveUniformsiv");
+ glGetUniformBlockIndex_ = (PFNGLGETUNIFORMBLOCKINDEXPROC) getprocaddress("glGetUniformBlockIndex");
+ glGetActiveUniformBlockiv_ = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)getprocaddress("glGetActiveUniformBlockiv");
+ glUniformBlockBinding_ = (PFNGLUNIFORMBLOCKBINDINGPROC) getprocaddress("glUniformBlockBinding");
+ glBindBufferBase_ = (PFNGLBINDBUFFERBASEPROC) getprocaddress("glBindBufferBase");
+ glBindBufferRange_ = (PFNGLBINDBUFFERRANGEPROC) getprocaddress("glBindBufferRange");
+
+ useubo = 1;
+ hasUBO = true;
+ }
+
+ if(glversion >= 300 || hasext("GL_ARB_vertex_array_object"))
+ {
+ glBindVertexArray_ = (PFNGLBINDVERTEXARRAYPROC) getprocaddress("glBindVertexArray");
+ glDeleteVertexArrays_ = (PFNGLDELETEVERTEXARRAYSPROC)getprocaddress("glDeleteVertexArrays");
+ glGenVertexArrays_ = (PFNGLGENVERTEXARRAYSPROC) getprocaddress("glGenVertexArrays");
+ glIsVertexArray_ = (PFNGLISVERTEXARRAYPROC) getprocaddress("glIsVertexArray");
+ hasVAO = true;
+ }
+ else if(hasext("GL_APPLE_vertex_array_object"))
+ {
+ glBindVertexArray_ = (PFNGLBINDVERTEXARRAYPROC) getprocaddress("glBindVertexArrayAPPLE");
+ glDeleteVertexArrays_ = (PFNGLDELETEVERTEXARRAYSPROC)getprocaddress("glDeleteVertexArraysAPPLE");
+ glGenVertexArrays_ = (PFNGLGENVERTEXARRAYSPROC) getprocaddress("glGenVertexArraysAPPLE");
+ glIsVertexArray_ = (PFNGLISVERTEXARRAYPROC) getprocaddress("glIsVertexArrayAPPLE");
+ hasVAO = true;
+ }
+
+ if(glversion >= 330 || hasext("GL_ARB_texture_swizzle") || hasext("GL_EXT_texture_swizzle"))
+ {
+ hasTSW = true;
+ }
+
+ if(hasext("GL_EXT_texture_compression_s3tc"))
+ {
+ hasS3TC = true;
+ if(!mesa) usetexcompress = 2;
+ }
+ else if(hasext("GL_EXT_texture_compression_dxt1") && hasext("GL_ANGLE_texture_compression_dxt3") && hasext("GL_ANGLE_texture_compression_dxt5"))
+ {
+ hasS3TC = true;
+ }
+ if(hasext("GL_3DFX_texture_compression_FXT1"))
+ {
+ hasFXT1 = true;
+ if(mesa) usetexcompress = max(usetexcompress, 1);
+ }
+ if(hasext("GL_EXT_texture_compression_latc"))
+ {
+ hasLATC = true;
+ }
+ if(glversion >= 300 || hasext("GL_ARB_texture_compression_rgtc") || hasext("GL_EXT_texture_compression_rgtc"))
+ {
+ hasRGTC = true;
+ }
+
+ if(hasext("GL_EXT_texture_filter_anisotropic"))
+ {
+ GLint val;
+ glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &val);
+ hwmaxaniso = val;
+ hasAF = true;
+ }
+
+ if(glversion >= 300 || hasext("GL_EXT_gpu_shader4"))
+ {
+ // on DX10 or above class cards (i.e. GF8 or RadeonHD) enable expensive features
+ extern int glare, maxdynlights, depthfxsize, blurdepthfx, texcompress;
+ waterfallrefract = 1;
+ glare = 1;
+ maxdynlights = MAXDYNLIGHTS;
+ depthfxsize = 10;
+ blurdepthfx = 0;
+ texcompress = max(texcompress, 1024 + 1);
+ }
}
void glext(char *ext)
{
- intret(hasext(ext) ? 1 : 0);
+ intret(hasext(ext) ? 1 : 0);
}
COMMAND(glext, "s");
void gl_resize()
{
- glViewport(0, 0, screenw, screenh);
+ glViewport(0, 0, screenw, screenh);
}
void gl_init()
{
- glClearColor(0, 0, 0, 0);
- glClearDepth(1);
- glDepthFunc(GL_LESS);
- glDisable(GL_DEPTH_TEST);
+ glClearColor(0, 0, 0, 0);
+ glClearDepth(1);
+ glDepthFunc(GL_LESS);
+ glDisable(GL_DEPTH_TEST);
- glEnable(GL_LINE_SMOOTH);
- //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+ glEnable(GL_LINE_SMOOTH);
+ //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
- glFrontFace(GL_CW);
- glCullFace(GL_BACK);
- glDisable(GL_CULL_FACE);
+ glFrontFace(GL_CW);
+ glCullFace(GL_BACK);
+ glDisable(GL_CULL_FACE);
- gle::setup();
+ gle::setup();
- setupshaders();
+ setupshaders();
- setuptexcompress();
+ setuptexcompress();
- gl_resize();
+ gl_resize();
}
VAR(wireframe, 0, 0, 1);
@@ -570,64 +547,64 @@ ICOMMAND(getcampitch, "", (), floatret(camera1->pitch));
ICOMMAND(getcamroll, "", (), floatret(camera1->roll));
ICOMMAND(getcampos, "", (),
{
- defformatstring(pos, "%s %s %s", floatstr(camera1->o.x), floatstr(camera1->o.y), floatstr(camera1->o.z));
- result(pos);
+ defformatstring(pos, "%s %s %s", floatstr(camera1->o.x), floatstr(camera1->o.y), floatstr(camera1->o.z));
+ result(pos);
});
vec worldpos, camdir, camright, camup;
void setcammatrix()
{
- // move from RH to Z-up LH quake style worldspace
- cammatrix = viewmatrix;
- cammatrix.rotate_around_y(camera1->roll*RAD);
- cammatrix.rotate_around_x(camera1->pitch*-RAD);
- cammatrix.rotate_around_z(camera1->yaw*-RAD);
- cammatrix.translate(vec(camera1->o).neg());
+ // move from RH to Z-up LH quake style worldspace
+ cammatrix = viewmatrix;
+ cammatrix.rotate_around_y(camera1->roll*RAD);
+ cammatrix.rotate_around_x(camera1->pitch*-RAD);
+ cammatrix.rotate_around_z(camera1->yaw*-RAD);
+ cammatrix.translate(vec(camera1->o).neg());
- cammatrix.transposedtransformnormal(vec(viewmatrix.b), camdir);
- cammatrix.transposedtransformnormal(vec(viewmatrix.a).neg(), camright);
- cammatrix.transposedtransformnormal(vec(viewmatrix.c), camup);
+ cammatrix.transposedtransformnormal(vec(viewmatrix.b), camdir);
+ cammatrix.transposedtransformnormal(vec(viewmatrix.a).neg(), camright);
+ cammatrix.transposedtransformnormal(vec(viewmatrix.c), camup);
- if(!drawtex)
- {
- if(raycubepos(camera1->o, camdir, worldpos, 0, RAY_CLIPMAT|RAY_SKIPFIRST) == -1)
- worldpos = vec(camdir).mul(2*worldsize).add(camera1->o); // if nothing is hit, just far away in the view direction
- }
+ if(!drawtex)
+ {
+ if(raycubepos(camera1->o, camdir, worldpos, 0, RAY_CLIPMAT|RAY_SKIPFIRST) == -1)
+ worldpos = vec(camdir).mul(2*worldsize).add(camera1->o); // if nothing is hit, just far away in the view direction
+ }
}
void setcamprojmatrix(bool init = true, bool flush = false)
{
- if(init)
- {
- setcammatrix();
- }
+ if(init)
+ {
+ setcammatrix();
+ }
- camprojmatrix.muld(projmatrix, cammatrix);
+ camprojmatrix.muld(projmatrix, cammatrix);
- if(init)
- {
- invcammatrix.invert(cammatrix);
- invcamprojmatrix.invert(camprojmatrix);
- }
+ if(init)
+ {
+ invcammatrix.invert(cammatrix);
+ invcamprojmatrix.invert(camprojmatrix);
+ }
- GLOBALPARAM(camprojmatrix, camprojmatrix);
+ GLOBALPARAM(camprojmatrix, camprojmatrix);
- if(fogging)
- {
- vec fogplane(cammatrix.c);
- fogplane.x /= projmatrix.a.x;
- fogplane.y /= projmatrix.b.y;
- fogplane.z /= projmatrix.c.w;
- GLOBALPARAMF(fogplane, fogplane.x, fogplane.y, 0, fogplane.z);
- }
- else
- {
- vec2 lineardepthscale = projmatrix.lineardepthscale();
- GLOBALPARAMF(fogplane, 0, 0, lineardepthscale.x, lineardepthscale.y);
- }
+ if(fogging)
+ {
+ vec fogplane(cammatrix.c);
+ fogplane.x /= projmatrix.a.x;
+ fogplane.y /= projmatrix.b.y;
+ fogplane.z /= projmatrix.c.w;
+ GLOBALPARAMF(fogplane, fogplane.x, fogplane.y, 0, fogplane.z);
+ }
+ else
+ {
+ vec2 lineardepthscale = projmatrix.lineardepthscale();
+ GLOBALPARAMF(fogplane, 0, 0, lineardepthscale.x, lineardepthscale.y);
+ }
- if(flush && Shader::lastshader) Shader::lastshader->flushparams();
+ if(flush && Shader::lastshader) Shader::lastshader->flushparams();
}
matrix4 hudmatrix, hudmatrixstack[64];
@@ -635,47 +612,47 @@ int hudmatrixpos = 0;
void resethudmatrix()
{
- hudmatrixpos = 0;
- GLOBALPARAM(hudmatrix, hudmatrix);
+ hudmatrixpos = 0;
+ GLOBALPARAM(hudmatrix, hudmatrix);
}
void pushhudmatrix()
{
- if(hudmatrixpos >= 0 && hudmatrixpos < int(sizeof(hudmatrixstack)/sizeof(hudmatrixstack[0]))) hudmatrixstack[hudmatrixpos] = hudmatrix;
- ++hudmatrixpos;
+ if(hudmatrixpos >= 0 && hudmatrixpos < int(sizeof(hudmatrixstack)/sizeof(hudmatrixstack[0]))) hudmatrixstack[hudmatrixpos] = hudmatrix;
+ ++hudmatrixpos;
}
void flushhudmatrix(bool flushparams)
{
- GLOBALPARAM(hudmatrix, hudmatrix);
- if(flushparams && Shader::lastshader) Shader::lastshader->flushparams();
+ GLOBALPARAM(hudmatrix, hudmatrix);
+ if(flushparams && Shader::lastshader) Shader::lastshader->flushparams();
}
void pophudmatrix(bool flush, bool flushparams)
{
- --hudmatrixpos;
- if(hudmatrixpos >= 0 && hudmatrixpos < int(sizeof(hudmatrixstack)/sizeof(hudmatrixstack[0])))
- {
- hudmatrix = hudmatrixstack[hudmatrixpos];
- if(flush) flushhudmatrix(flushparams);
- }
+ --hudmatrixpos;
+ if(hudmatrixpos >= 0 && hudmatrixpos < int(sizeof(hudmatrixstack)/sizeof(hudmatrixstack[0])))
+ {
+ hudmatrix = hudmatrixstack[hudmatrixpos];
+ if(flush) flushhudmatrix(flushparams);
+ }
}
void pushhudscale(float sx, float sy)
{
- if(!sy) sy = sx;
- pushhudmatrix();
- hudmatrix.scale(sx, sy, 1);
- flushhudmatrix();
+ if(!sy) sy = sx;
+ pushhudmatrix();
+ hudmatrix.scale(sx, sy, 1);
+ flushhudmatrix();
}
void pushhudtranslate(float tx, float ty, float sx, float sy)
{
- if(!sy) sy = sx;
- pushhudmatrix();
- hudmatrix.translate(tx, ty, 0);
- if(sy) hudmatrix.scale(sx, sy, 1);
- flushhudmatrix();
+ if(!sy) sy = sx;
+ pushhudmatrix();
+ hudmatrix.translate(tx, ty, 0);
+ if(sy) hudmatrix.scale(sx, sy, 1);
+ flushhudmatrix();
}
float curfov = 100, curavatarfov = 65, fovy, aspect;
@@ -694,21 +671,21 @@ VAR(zoom, -1, 0, 1);
void disablezoom()
{
- zoom = 0;
- zoomprogress = 0;
+ zoom = 0;
+ zoomprogress = 0;
}
void computezoom()
{
- if(!zoom) { zoomprogress = 0; curfov = fov; curavatarfov = avatarfov; return; }
- if(zoom > 0) zoomprogress = zoominvel ? min(zoomprogress + float(elapsedtime) / zoominvel, 1.0f) : 1;
- else
- {
- zoomprogress = zoomoutvel ? max(zoomprogress - float(elapsedtime) / zoomoutvel, 0.0f) : 0;
- if(zoomprogress <= 0) zoom = 0;
- }
- curfov = zoomfov*zoomprogress + fov*(1 - zoomprogress);
- curavatarfov = avatarzoomfov*zoomprogress + avatarfov*(1 - zoomprogress);
+ if(!zoom) { zoomprogress = 0; curfov = fov; curavatarfov = avatarfov; return; }
+ if(zoom > 0) zoomprogress = zoominvel ? min(zoomprogress + float(elapsedtime) / zoominvel, 1.0f) : 1;
+ else
+ {
+ zoomprogress = zoomoutvel ? max(zoomprogress - float(elapsedtime) / zoomoutvel, 0.0f) : 0;
+ if(zoomprogress <= 0) zoom = 0;
+ }
+ curfov = zoomfov*zoomprogress + fov*(1 - zoomprogress);
+ curavatarfov = avatarzoomfov*zoomprogress + avatarfov*(1 - zoomprogress);
}
FVARP(zoomsens, 1e-3f, 1, 1000);
@@ -729,105 +706,105 @@ bool isthirdperson() { return player!=camera1 || detachedcamera || reflecting; }
void fixcamerarange()
{
- const float MAXPITCH = 90.0f;
- if(camera1->pitch>MAXPITCH) camera1->pitch = MAXPITCH;
- if(camera1->pitch<-MAXPITCH) camera1->pitch = -MAXPITCH;
- while(camera1->yaw<0.0f) camera1->yaw += 360.0f;
- while(camera1->yaw>=360.0f) camera1->yaw -= 360.0f;
+ const float MAXPITCH = 90.0f;
+ if(camera1->pitch>MAXPITCH) camera1->pitch = MAXPITCH;
+ if(camera1->pitch<-MAXPITCH) camera1->pitch = -MAXPITCH;
+ while(camera1->yaw<0.0f) camera1->yaw += 360.0f;
+ while(camera1->yaw>=360.0f) camera1->yaw -= 360.0f;
}
void mousemove(int dx, int dy)
{
- if(!game::allowmouselook()) return;
- float cursens = sensitivity, curaccel = mouseaccel;
- if(zoom)
- {
- if(zoomautosens)
- {
- cursens = float(sensitivity*zoomfov)/fov;
- curaccel = float(mouseaccel*zoomfov)/fov;
- }
- else
- {
- cursens = zoomsens;
- curaccel = zoomaccel;
- }
- }
- if(curaccel && curtime && (dx || dy)) cursens += curaccel * sqrtf(dx*dx + dy*dy)/curtime;
- cursens /= 33.0f*sensitivityscale;
- camera1->yaw += dx*cursens;
- camera1->pitch -= dy*cursens*(invmouse ? -1 : 1);
- fixcamerarange();
- if(camera1!=player && !detachedcamera)
- {
- player->yaw = camera1->yaw;
- player->pitch = camera1->pitch;
- }
- speedmodifier += abs(dx)-abs(dy);
+ if(!game::allowmouselook()) return;
+ float cursens = sensitivity, curaccel = mouseaccel;
+ if(zoom)
+ {
+ if(zoomautosens)
+ {
+ cursens = float(sensitivity*zoomfov)/fov;
+ curaccel = float(mouseaccel*zoomfov)/fov;
+ }
+ else
+ {
+ cursens = zoomsens;
+ curaccel = zoomaccel;
+ }
+ }
+ if(curaccel && curtime && (dx || dy)) cursens += curaccel * sqrtf(dx*dx + dy*dy)/curtime;
+ cursens /= 33.0f*sensitivityscale;
+ camera1->yaw += dx*cursens;
+ camera1->pitch -= dy*cursens*(invmouse ? -1 : 1);
+ fixcamerarange();
+ if(camera1!=player && !detachedcamera)
+ {
+ player->yaw = camera1->yaw;
+ player->pitch = camera1->pitch;
+ }
+ speedmodifier += abs(dx)-abs(dy);
}
void recomputecamera()
{
- game::setupcamera();
- computezoom();
-
- bool allowthirdperson = game::allowthirdperson();
- bool shoulddetach = (allowthirdperson && thirdperson > 1) || game::detachcamera();
- if((!allowthirdperson || !thirdperson) && !shoulddetach)
- {
- camera1 = player;
- detachedcamera = false;
- }
- else
- {
- static physent tempcamera;
- camera1 = &tempcamera;
- if(detachedcamera && shoulddetach) camera1->o = player->o;
- else
- {
- *camera1 = *player;
- detachedcamera = shoulddetach;
- }
- camera1->reset();
- camera1->type = ENT_CAMERA;
- camera1->move = -1;
- camera1->eyeheight = camera1->aboveeye = camera1->radius = camera1->xradius = camera1->yradius = 2;
-
- matrix3 orient;
- orient.identity();
- orient.rotate_around_z(camera1->yaw*RAD);
- orient.rotate_around_x(camera1->pitch*RAD);
- orient.rotate_around_y(camera1->roll*-RAD);
- vec dir = vec(orient.b).neg(), side = vec(orient.a).neg(), up = orient.c;
-
- if(game::collidecamera())
- {
- movecamera(camera1, dir, thirdpersondistance, 1);
- movecamera(camera1, dir, clamp(thirdpersondistance - camera1->o.dist(player->o), 0.0f, 1.0f), 0.1f);
- if(thirdpersonup)
- {
- vec pos = camera1->o;
- float dist = fabs(thirdpersonup);
- if(thirdpersonup < 0) up.neg();
- movecamera(camera1, up, dist, 1);
- movecamera(camera1, up, clamp(dist - camera1->o.dist(pos), 0.0f, 1.0f), 0.1f);
- }
- if(thirdpersonside)
- {
- vec pos = camera1->o;
- float dist = fabs(thirdpersonside);
- if(thirdpersonside < 0) side.neg();
- movecamera(camera1, side, dist, 1);
- movecamera(camera1, side, clamp(dist - camera1->o.dist(pos), 0.0f, 1.0f), 0.1f);
- }
- }
- else
- {
- camera1->o.add(vec(dir).mul(thirdpersondistance));
- if(thirdpersonup) camera1->o.add(vec(up).mul(thirdpersonup));
- if(thirdpersonside) camera1->o.add(vec(side).mul(thirdpersonside));
- }
- }
+ game::setupcamera();
+ computezoom();
+
+ bool allowthirdperson = game::allowthirdperson();
+ bool shoulddetach = (allowthirdperson && thirdperson > 1) || game::detachcamera();
+ if((!allowthirdperson || !thirdperson) && !shoulddetach)
+ {
+ camera1 = player;
+ detachedcamera = false;
+ }
+ else
+ {
+ static physent tempcamera;
+ camera1 = &tempcamera;
+ if(detachedcamera && shoulddetach) camera1->o = player->o;
+ else
+ {
+ *camera1 = *player;
+ detachedcamera = shoulddetach;
+ }
+ camera1->reset();
+ camera1->type = ENT_CAMERA;
+ camera1->move = -1;
+ camera1->eyeheight = camera1->aboveeye = camera1->radius = camera1->xradius = camera1->yradius = 2;
+
+ matrix3 orient;
+ orient.identity();
+ orient.rotate_around_z(camera1->yaw*RAD);
+ orient.rotate_around_x(camera1->pitch*RAD);
+ orient.rotate_around_y(camera1->roll*-RAD);
+ vec dir = vec(orient.b).neg(), side = vec(orient.a).neg(), up = orient.c;
+
+ if(game::collidecamera())
+ {
+ movecamera(camera1, dir, thirdpersondistance, 1);
+ movecamera(camera1, dir, clamp(thirdpersondistance - camera1->o.dist(player->o), 0.0f, 1.0f), 0.1f);
+ if(thirdpersonup)
+ {
+ vec pos = camera1->o;
+ float dist = fabs(thirdpersonup);
+ if(thirdpersonup < 0) up.neg();
+ movecamera(camera1, up, dist, 1);
+ movecamera(camera1, up, clamp(dist - camera1->o.dist(pos), 0.0f, 1.0f), 0.1f);
+ }
+ if(thirdpersonside)
+ {
+ vec pos = camera1->o;
+ float dist = fabs(thirdpersonside);
+ if(thirdpersonside < 0) side.neg();
+ movecamera(camera1, side, dist, 1);
+ movecamera(camera1, side, clamp(dist - camera1->o.dist(pos), 0.0f, 1.0f), 0.1f);
+ }
+ }
+ else
+ {
+ camera1->o.add(vec(dir).mul(thirdpersondistance));
+ if(thirdpersonup) camera1->o.add(vec(up).mul(thirdpersonup));
+ if(thirdpersonside) camera1->o.add(vec(side).mul(thirdpersonside));
+ }
+ }
}
extern const matrix4 viewmatrix(vec(-1, 0, 0), vec(0, 0, 1), vec(0, -1, 0));
@@ -837,18 +814,18 @@ FVAR(nearplane, 0.01f, 0.54f, 2.0f);
vec calcavatarpos(const vec &pos, float dist)
{
- vec eyepos;
- cammatrix.transform(pos, eyepos);
- GLdouble ydist = nearplane * tan(curavatarfov/2*RAD), xdist = ydist * aspect;
- vec4 scrpos;
- scrpos.x = eyepos.x*nearplane/xdist;
- scrpos.y = eyepos.y*nearplane/ydist;
- scrpos.z = (eyepos.z*(farplane + nearplane) - 2*nearplane*farplane) / (farplane - nearplane);
- scrpos.w = -eyepos.z;
+ vec eyepos;
+ cammatrix.transform(pos, eyepos);
+ GLdouble ydist = nearplane * tan(curavatarfov/2*RAD), xdist = ydist * aspect;
+ vec4 scrpos;
+ scrpos.x = eyepos.x*nearplane/xdist;
+ scrpos.y = eyepos.y*nearplane/ydist;
+ scrpos.z = (eyepos.z*(farplane + nearplane) - 2*nearplane*farplane) / (farplane - nearplane);
+ scrpos.w = -eyepos.z;
- vec worldpos = invcamprojmatrix.perspectivetransform(scrpos);
- vec dir = vec(worldpos).sub(camera1->o).rescale(dist);
- return dir.add(camera1->o);
+ vec worldpos = invcamprojmatrix.perspectivetransform(scrpos);
+ vec dir = vec(worldpos).sub(camera1->o).rescale(dist);
+ return dir.add(camera1->o);
}
VAR(reflectclip, 0, 6, 64);
@@ -858,17 +835,17 @@ matrix4 clipmatrix, noclipmatrix;
void renderavatar()
{
- if(isthirdperson()) return;
+ if(isthirdperson()) return;
- matrix4 oldprojmatrix = projmatrix;
- projmatrix.perspective(curavatarfov, aspect, nearplane, farplane);
- projmatrix.scalez(avatardepth);
- setcamprojmatrix(false);
+ matrix4 oldprojmatrix = projmatrix;
+ projmatrix.perspective(curavatarfov, aspect, nearplane, farplane);
+ projmatrix.scalez(avatardepth);
+ setcamprojmatrix(false);
- game::renderavatar();
+ game::renderavatar();
- projmatrix = oldprojmatrix;
- setcamprojmatrix(false);
+ projmatrix = oldprojmatrix;
+ setcamprojmatrix(false);
}
FVAR(polygonoffsetfactor, -1e4f, -3.0f, 1e4f);
@@ -879,67 +856,67 @@ matrix4 nooffsetmatrix;
void enablepolygonoffset(GLenum type)
{
- if(!depthoffset)
- {
- glPolygonOffset(polygonoffsetfactor, polygonoffsetunits);
- glEnable(type);
- return;
- }
+ if(!depthoffset)
+ {
+ glPolygonOffset(polygonoffsetfactor, polygonoffsetunits);
+ glEnable(type);
+ return;
+ }
- bool clipped = reflectz < 1e15f && reflectclip;
+ bool clipped = reflectz < 1e15f && reflectclip;
- nooffsetmatrix = projmatrix;
- projmatrix.d.z += depthoffset * (clipped ? noclipmatrix.c.z : projmatrix.c.z);
- setcamprojmatrix(false, true);
+ nooffsetmatrix = projmatrix;
+ projmatrix.d.z += depthoffset * (clipped ? noclipmatrix.c.z : projmatrix.c.z);
+ setcamprojmatrix(false, true);
}
void disablepolygonoffset(GLenum type)
{
- if(!depthoffset)
- {
- glDisable(type);
- return;
- }
+ if(!depthoffset)
+ {
+ glDisable(type);
+ return;
+ }
- projmatrix = nooffsetmatrix;
- setcamprojmatrix(false, true);
+ projmatrix = nooffsetmatrix;
+ setcamprojmatrix(false, true);
}
void calcspherescissor(const vec &center, float size, float &sx1, float &sy1, float &sx2, float &sy2)
{
- vec worldpos(center), e;
- if(reflecting) worldpos.z = 2*reflectz - worldpos.z;
- cammatrix.transform(worldpos, e);
- if(e.z > 2*size) { sx1 = sy1 = 1; sx2 = sy2 = -1; return; }
- float zzrr = e.z*e.z - size*size,
- dx = e.x*e.x + zzrr, dy = e.y*e.y + zzrr,
- focaldist = 1.0f/tan(fovy*0.5f*RAD);
- sx1 = sy1 = -1;
- sx2 = sy2 = 1;
- #define CHECKPLANE(c, dir, focaldist, low, high) \
- do { \
- float nzc = (cz*cz + 1) / (cz dir drt) - cz, \
- pz = (d##c)/(nzc*e.c - e.z); \
- if(pz > 0) \
- { \
- float c = (focaldist)*nzc, \
- pc = pz*nzc; \
- if(pc < e.c) low = c; \
- else if(pc > e.c) high = c; \
- } \
- } while(0)
- if(dx > 0)
- {
- float cz = e.x/e.z, drt = sqrtf(dx)/size;
- CHECKPLANE(x, -, focaldist/aspect, sx1, sx2);
- CHECKPLANE(x, +, focaldist/aspect, sx1, sx2);
- }
- if(dy > 0)
- {
- float cz = e.y/e.z, drt = sqrtf(dy)/size;
- CHECKPLANE(y, -, focaldist, sy1, sy2);
- CHECKPLANE(y, +, focaldist, sy1, sy2);
- }
+ vec worldpos(center), e;
+ if(reflecting) worldpos.z = 2*reflectz - worldpos.z;
+ cammatrix.transform(worldpos, e);
+ if(e.z > 2*size) { sx1 = sy1 = 1; sx2 = sy2 = -1; return; }
+ float zzrr = e.z*e.z - size*size,
+ dx = e.x*e.x + zzrr, dy = e.y*e.y + zzrr,
+ focaldist = 1.0f/tan(fovy*0.5f*RAD);
+ sx1 = sy1 = -1;
+ sx2 = sy2 = 1;
+ #define CHECKPLANE(c, dir, focaldist, low, high) \
+ do { \
+ float nzc = (cz*cz + 1) / (cz dir drt) - cz, \
+ pz = (d##c)/(nzc*e.c - e.z); \
+ if(pz > 0) \
+ { \
+ float c = (focaldist)*nzc, \
+ pc = pz*nzc; \
+ if(pc < e.c) low = c; \
+ else if(pc > e.c) high = c; \
+ } \
+ } while(0)
+ if(dx > 0)
+ {
+ float cz = e.x/e.z, drt = sqrtf(dx)/size;
+ CHECKPLANE(x, -, focaldist/aspect, sx1, sx2);
+ CHECKPLANE(x, +, focaldist/aspect, sx1, sx2);
+ }
+ if(dy > 0)
+ {
+ float cz = e.y/e.z, drt = sqrtf(dy)/size;
+ CHECKPLANE(y, -, focaldist, sy1, sy2);
+ CHECKPLANE(y, +, focaldist, sy1, sy2);
+ }
}
static int scissoring = 0;
@@ -947,191 +924,191 @@ static GLint oldscissor[4];
int pushscissor(float sx1, float sy1, float sx2, float sy2)
{
- scissoring = 0;
+ scissoring = 0;
- if(sx1 <= -1 && sy1 <= -1 && sx2 >= 1 && sy2 >= 1) return 0;
+ if(sx1 <= -1 && sy1 <= -1 && sx2 >= 1 && sy2 >= 1) return 0;
- sx1 = max(sx1, -1.0f);
- sy1 = max(sy1, -1.0f);
- sx2 = min(sx2, 1.0f);
- sy2 = min(sy2, 1.0f);
+ sx1 = max(sx1, -1.0f);
+ sy1 = max(sy1, -1.0f);
+ sx2 = min(sx2, 1.0f);
+ sy2 = min(sy2, 1.0f);
- GLint viewport[4];
- glGetIntegerv(GL_VIEWPORT, viewport);
- int sx = viewport[0] + int(floor((sx1+1)*0.5f*viewport[2])),
- sy = viewport[1] + int(floor((sy1+1)*0.5f*viewport[3])),
- sw = viewport[0] + int(ceil((sx2+1)*0.5f*viewport[2])) - sx,
- sh = viewport[1] + int(ceil((sy2+1)*0.5f*viewport[3])) - sy;
- if(sw <= 0 || sh <= 0) return 0;
+ GLint viewport[4];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ int sx = viewport[0] + int(floor((sx1+1)*0.5f*viewport[2])),
+ sy = viewport[1] + int(floor((sy1+1)*0.5f*viewport[3])),
+ sw = viewport[0] + int(ceil((sx2+1)*0.5f*viewport[2])) - sx,
+ sh = viewport[1] + int(ceil((sy2+1)*0.5f*viewport[3])) - sy;
+ if(sw <= 0 || sh <= 0) return 0;
- if(glIsEnabled(GL_SCISSOR_TEST))
- {
- glGetIntegerv(GL_SCISSOR_BOX, oldscissor);
- sw += sx;
- sh += sy;
- sx = max(sx, int(oldscissor[0]));
- sy = max(sy, int(oldscissor[1]));
- sw = min(sw, int(oldscissor[0] + oldscissor[2])) - sx;
- sh = min(sh, int(oldscissor[1] + oldscissor[3])) - sy;
- if(sw <= 0 || sh <= 0) return 0;
- scissoring = 2;
- }
- else scissoring = 1;
+ if(glIsEnabled(GL_SCISSOR_TEST))
+ {
+ glGetIntegerv(GL_SCISSOR_BOX, oldscissor);
+ sw += sx;
+ sh += sy;
+ sx = max(sx, int(oldscissor[0]));
+ sy = max(sy, int(oldscissor[1]));
+ sw = min(sw, int(oldscissor[0] + oldscissor[2])) - sx;
+ sh = min(sh, int(oldscissor[1] + oldscissor[3])) - sy;
+ if(sw <= 0 || sh <= 0) return 0;
+ scissoring = 2;
+ }
+ else scissoring = 1;
- glScissor(sx, sy, sw, sh);
- if(scissoring<=1) glEnable(GL_SCISSOR_TEST);
+ glScissor(sx, sy, sw, sh);
+ if(scissoring<=1) glEnable(GL_SCISSOR_TEST);
- return scissoring;
+ return scissoring;
}
void popscissor()
{
- if(scissoring>1) glScissor(oldscissor[0], oldscissor[1], oldscissor[2], oldscissor[3]);
- else if(scissoring) glDisable(GL_SCISSOR_TEST);
- scissoring = 0;
+ if(scissoring>1) glScissor(oldscissor[0], oldscissor[1], oldscissor[2], oldscissor[3]);
+ else if(scissoring) glDisable(GL_SCISSOR_TEST);
+ scissoring = 0;
}
static GLuint screenquadvbo = 0;
static void setupscreenquad()
{
- if(!screenquadvbo)
- {
- glGenBuffers_(1, &screenquadvbo);
- gle::bindvbo(screenquadvbo);
- vec2 verts[4] = { vec2(1, -1), vec2(-1, -1), vec2(1, 1), vec2(-1, 1) };
- glBufferData_(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
- gle::clearvbo();
- }
+ if(!screenquadvbo)
+ {
+ glGenBuffers_(1, &screenquadvbo);
+ gle::bindvbo(screenquadvbo);
+ vec2 verts[4] = { vec2(1, -1), vec2(-1, -1), vec2(1, 1), vec2(-1, 1) };
+ glBufferData_(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
+ gle::clearvbo();
+ }
}
static void cleanupscreenquad()
{
- if(screenquadvbo) { glDeleteBuffers_(1, &screenquadvbo); screenquadvbo = 0; }
+ if(screenquadvbo) { glDeleteBuffers_(1, &screenquadvbo); screenquadvbo = 0; }
}
void screenquad()
{
- setupscreenquad();
- gle::bindvbo(screenquadvbo);
- gle::enablevertex();
- gle::vertexpointer(sizeof(vec2), (const vec2 *)0, GL_FLOAT, 2);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- gle::disablevertex();
- gle::clearvbo();
+ setupscreenquad();
+ gle::bindvbo(screenquadvbo);
+ gle::enablevertex();
+ gle::vertexpointer(sizeof(vec2), (const vec2 *)0, GL_FLOAT, 2);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ gle::disablevertex();
+ gle::clearvbo();
}
static LocalShaderParam screentexcoord[2] = { LocalShaderParam("screentexcoord0"), LocalShaderParam("screentexcoord1") };
static inline void setscreentexcoord(int i, float w, float h, float x = 0, float y = 0)
{
- screentexcoord[i].setf(w*0.5f, h*0.5f, x + w*0.5f, y + fabs(h)*0.5f);
+ screentexcoord[i].setf(w*0.5f, h*0.5f, x + w*0.5f, y + fabs(h)*0.5f);
}
void screenquad(float sw, float sh)
{
- setscreentexcoord(0, sw, sh);
- screenquad();
+ setscreentexcoord(0, sw, sh);
+ screenquad();
}
void screenquadflipped(float sw, float sh)
{
- setscreentexcoord(0, sw, -sh);
- screenquad();
+ setscreentexcoord(0, sw, -sh);
+ screenquad();
}
void screenquad(float sw, float sh, float sw2, float sh2)
{
- setscreentexcoord(0, sw, sh);
- setscreentexcoord(1, sw2, sh2);
- screenquad();
+ setscreentexcoord(0, sw, sh);
+ setscreentexcoord(1, sw2, sh2);
+ screenquad();
}
void screenquadoffset(float x, float y, float w, float h)
{
- setscreentexcoord(0, w, h, x, y);
- screenquad();
+ setscreentexcoord(0, w, h, x, y);
+ screenquad();
}
void screenquadoffset(float x, float y, float w, float h, float x2, float y2, float w2, float h2)
{
- setscreentexcoord(0, w, h, x, y);
- setscreentexcoord(1, w2, h2, x2, y2);
- screenquad();
+ setscreentexcoord(0, w, h, x, y);
+ setscreentexcoord(1, w2, h2, x2, y2);
+ screenquad();
}
#define HUDQUAD(x1, y1, x2, y2, sx1, sy1, sx2, sy2) { \
- gle::defvertex(2); \
- gle::deftexcoord0(); \
- gle::begin(GL_TRIANGLE_STRIP); \
- gle::attribf(x2, y1); gle::attribf(sx2, sy1); \
- gle::attribf(x1, y1); gle::attribf(sx1, sy1); \
- gle::attribf(x2, y2); gle::attribf(sx2, sy2); \
- gle::attribf(x1, y2); gle::attribf(sx1, sy2); \
- gle::end(); \
+ gle::defvertex(2); \
+ gle::deftexcoord0(); \
+ gle::begin(GL_TRIANGLE_STRIP); \
+ gle::attribf(x2, y1); gle::attribf(sx2, sy1); \
+ gle::attribf(x1, y1); gle::attribf(sx1, sy1); \
+ gle::attribf(x2, y2); gle::attribf(sx2, sy2); \
+ gle::attribf(x1, y2); gle::attribf(sx1, sy2); \
+ gle::end(); \
}
void hudquad(float x, float y, float w, float h, float tx, float ty, float tw, float th)
{
- HUDQUAD(x, y, x+w, y+h, tx, ty, tx+tw, ty+th);
+ HUDQUAD(x, y, x+w, y+h, tx, ty, tx+tw, ty+th);
}
VARR(fog, 16, 4000, 1000024);
bvec fogcolor(0x80, 0x99, 0xB3);
HVARFR(fogcolour, 0, 0x8099B3, 0xFFFFFF,
{
- fogcolor = bvec((fogcolour>>16)&0xFF, (fogcolour>>8)&0xFF, fogcolour&0xFF);
+ fogcolor = bvec((fogcolour>>16)&0xFF, (fogcolour>>8)&0xFF, fogcolour&0xFF);
});
static float findsurface(int fogmat, const vec &v, int &abovemat)
{
- fogmat &= MATF_VOLUME;
- ivec o(v), co;
- int csize;
- do
- {
- cube &c = lookupcube(o, 0, co, csize);
- int mat = c.material&MATF_VOLUME;
- if(mat != fogmat)
- {
- abovemat = isliquid(mat) ? (int) c.material : (int) MAT_AIR;
- return o.z;
- }
- o.z = co.z + csize;
- }
- while(o.z < worldsize);
- abovemat = MAT_AIR;
- return worldsize;
+ fogmat &= MATF_VOLUME;
+ ivec o(v), co;
+ int csize;
+ do
+ {
+ cube &c = lookupcube(o, 0, co, csize);
+ int mat = c.material&MATF_VOLUME;
+ if(mat != fogmat)
+ {
+ abovemat = isliquid(mat) ? (int) c.material : (int) MAT_AIR;
+ return o.z;
+ }
+ o.z = co.z + csize;
+ }
+ while(o.z < worldsize);
+ abovemat = MAT_AIR;
+ return worldsize;
}
static void blendfog(int fogmat, float blend, float logblend, float &start, float &end, vec &fogc)
{
- switch(fogmat&MATF_VOLUME)
- {
- case MAT_WATER:
- {
- const bvec &wcol = getwatercolor(fogmat);
- int wfog = getwaterfog(fogmat);
- fogc.madd(wcol.tocolor(), blend);
- end += logblend*min(fog, max(wfog*4, 32));
- break;
- }
-
- case MAT_LAVA:
- {
- const bvec &lcol = getlavacolor(fogmat);
- int lfog = getlavafog(fogmat);
- fogc.madd(lcol.tocolor(), blend);
- end += logblend*min(fog, max(lfog*4, 32));
- break;
- }
-
- default:
- fogc.madd(fogcolor.tocolor(), blend);
- start += logblend*(fog+64)/8;
- end += logblend*fog;
- break;
- }
+ switch(fogmat&MATF_VOLUME)
+ {
+ case MAT_WATER:
+ {
+ const bvec &wcol = getwatercolor(fogmat);
+ int wfog = getwaterfog(fogmat);
+ fogc.madd(wcol.tocolor(), blend);
+ end += logblend*min(fog, max(wfog*4, 32));
+ break;
+ }
+
+ case MAT_LAVA:
+ {
+ const bvec &lcol = getlavacolor(fogmat);
+ int lfog = getlavafog(fogmat);
+ fogc.madd(lcol.tocolor(), blend);
+ end += logblend*min(fog, max(lfog*4, 32));
+ break;
+ }
+
+ default:
+ fogc.madd(fogcolor.tocolor(), blend);
+ start += logblend*(fog+64)/8;
+ end += logblend*fog;
+ break;
+ }
}
vec oldfogcolor(0, 0, 0), curfogcolor(0, 0, 0);
@@ -1139,163 +1116,163 @@ float oldfogstart = 0, oldfogend = 1000000, curfogstart = 0, curfogend = 1000000
void setfogcolor(const vec &v)
{
- GLOBALPARAM(fogcolor, v);
+ GLOBALPARAM(fogcolor, v);
}
void zerofogcolor()
{
- setfogcolor(vec(0, 0, 0));
+ setfogcolor(vec(0, 0, 0));
}
void resetfogcolor()
{
- setfogcolor(curfogcolor);
+ setfogcolor(curfogcolor);
}
void pushfogcolor(const vec &v)
{
- oldfogcolor = curfogcolor;
- curfogcolor = v;
- resetfogcolor();
+ oldfogcolor = curfogcolor;
+ curfogcolor = v;
+ resetfogcolor();
}
void popfogcolor()
{
- curfogcolor = oldfogcolor;
- resetfogcolor();
+ curfogcolor = oldfogcolor;
+ resetfogcolor();
}
void setfogdist(float start, float end)
{
- GLOBALPARAMF(fogparams, 1/(end - start), end/(end - start));
+ GLOBALPARAMF(fogparams, 1/(end - start), end/(end - start));
}
void clearfogdist()
{
- setfogdist(0, 1000000);
+ setfogdist(0, 1000000);
}
void resetfogdist()
{
- setfogdist(curfogstart, curfogend);
+ setfogdist(curfogstart, curfogend);
}
void pushfogdist(float start, float end)
{
- oldfogstart = curfogstart;
- oldfogend = curfogend;
- curfogstart = start;
- curfogend = end;
- resetfogdist();
+ oldfogstart = curfogstart;
+ oldfogend = curfogend;
+ curfogstart = start;
+ curfogend = end;
+ resetfogdist();
}
void popfogdist()
{
- curfogstart = oldfogstart;
- curfogend = oldfogend;
- resetfogdist();
+ curfogstart = oldfogstart;
+ curfogend = oldfogend;
+ resetfogdist();
}
static void resetfog()
{
- resetfogcolor();
- resetfogdist();
+ resetfogcolor();
+ resetfogdist();
- glClearColor(curfogcolor.r, curfogcolor.g, curfogcolor.b, 1.0f);
+ glClearColor(curfogcolor.r, curfogcolor.g, curfogcolor.b, 1.0f);
}
static void setfog(int fogmat, float below = 1, int abovemat = MAT_AIR)
{
- float logscale = 256, logblend = log(1 + (logscale - 1)*below) / log(logscale);
+ float logscale = 256, logblend = log(1 + (logscale - 1)*below) / log(logscale);
- curfogstart = curfogend = 0;
- curfogcolor = vec(0, 0, 0);
- blendfog(fogmat, below, logblend, curfogstart, curfogend, curfogcolor);
- if(below < 1) blendfog(abovemat, 1-below, 1-logblend, curfogstart, curfogend, curfogcolor);
+ curfogstart = curfogend = 0;
+ curfogcolor = vec(0, 0, 0);
+ blendfog(fogmat, below, logblend, curfogstart, curfogend, curfogcolor);
+ if(below < 1) blendfog(abovemat, 1-below, 1-logblend, curfogstart, curfogend, curfogcolor);
- resetfog();
+ resetfog();
}
static void blendfogoverlay(int fogmat, float blend, vec &overlay)
{
- float maxc;
- switch(fogmat&MATF_VOLUME)
- {
- case MAT_WATER:
- {
- const bvec &wcol = getwatercolor(fogmat);
- maxc = max(wcol.r, max(wcol.g, wcol.b));
- overlay.madd(vec(wcol.r, wcol.g, wcol.b).div(min(32.0f + maxc*7.0f/8.0f, 255.0f)).max(0.4f), blend);
- break;
- }
-
- case MAT_LAVA:
- {
- const bvec &lcol = getlavacolor(fogmat);
- maxc = max(lcol.r, max(lcol.g, lcol.b));
- overlay.madd(vec(lcol.r, lcol.g, lcol.b).div(min(32.0f + maxc*7.0f/8.0f, 255.0f)).max(0.4f), blend);
- break;
- }
-
- default:
- overlay.add(blend);
- break;
- }
+ float maxc;
+ switch(fogmat&MATF_VOLUME)
+ {
+ case MAT_WATER:
+ {
+ const bvec &wcol = getwatercolor(fogmat);
+ maxc = max(wcol.r, max(wcol.g, wcol.b));
+ overlay.madd(vec(wcol.r, wcol.g, wcol.b).div(min(32.0f + maxc*7.0f/8.0f, 255.0f)).max(0.4f), blend);
+ break;
+ }
+
+ case MAT_LAVA:
+ {
+ const bvec &lcol = getlavacolor(fogmat);
+ maxc = max(lcol.r, max(lcol.g, lcol.b));
+ overlay.madd(vec(lcol.r, lcol.g, lcol.b).div(min(32.0f + maxc*7.0f/8.0f, 255.0f)).max(0.4f), blend);
+ break;
+ }
+
+ default:
+ overlay.add(blend);
+ break;
+ }
}
void drawfogoverlay(int fogmat, float fogblend, int abovemat)
{
- SETSHADER(fogoverlay);
+ SETSHADER(fogoverlay);
- glEnable(GL_BLEND);
- glBlendFunc(GL_ZERO, GL_SRC_COLOR);
- vec overlay(0, 0, 0);
- blendfogoverlay(fogmat, fogblend, overlay);
- blendfogoverlay(abovemat, 1-fogblend, overlay);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ZERO, GL_SRC_COLOR);
+ vec overlay(0, 0, 0);
+ blendfogoverlay(fogmat, fogblend, overlay);
+ blendfogoverlay(abovemat, 1-fogblend, overlay);
- gle::color(overlay);
- screenquad();
+ gle::color(overlay);
+ screenquad();
- glDisable(GL_BLEND);
+ glDisable(GL_BLEND);
}
bool renderedgame = false;
void rendergame(bool mainpass)
{
- game::rendergame(mainpass);
- if(!shadowmapping) renderedgame = true;
+ game::rendergame(mainpass);
+ if(!shadowmapping) renderedgame = true;
}
VARP(skyboxglare, 0, 1, 1);
void drawglare()
{
- glaring = true;
- refracting = -1;
+ glaring = true;
+ refracting = -1;
- pushfogcolor(vec(0, 0, 0));
+ pushfogcolor(vec(0, 0, 0));
- glClearColor(0, 0, 0, 1);
- glClear((skyboxglare && !shouldclearskyboxglare() ? 0 : GL_COLOR_BUFFER_BIT) | GL_DEPTH_BUFFER_BIT);
+ glClearColor(0, 0, 0, 1);
+ glClear((skyboxglare && !shouldclearskyboxglare() ? 0 : GL_COLOR_BUFFER_BIT) | GL_DEPTH_BUFFER_BIT);
- rendergeom();
+ rendergeom();
- if(skyboxglare) drawskybox(farplane, false);
+ if(skyboxglare) drawskybox(farplane, false);
- renderreflectedmapmodels();
- rendergame();
- renderavatar();
+ renderreflectedmapmodels();
+ rendergame();
+ renderavatar();
- renderwater();
- rendermaterials();
- renderalphageom();
- renderparticles();
+ renderwater();
+ rendermaterials();
+ renderalphageom();
+ renderparticles();
- popfogcolor();
+ popfogcolor();
- refracting = 0;
- glaring = false;
+ refracting = 0;
+ glaring = false;
}
VARP(reflectmms, 0, 1, 1);
@@ -1305,201 +1282,201 @@ matrix4 noreflectmatrix;
void drawreflection(float z, bool refract, int fogdepth, const bvec &col)
{
- reflectz = z < 0 ? 1e16f : z;
- reflecting = !refract;
- refracting = refract ? (z < 0 || camera1->o.z >= z ? -1 : 1) : 0;
- fading = waterrefract && waterfade && z>=0;
- fogging = refracting<0 && z>=0;
- refractfog = fogdepth;
-
- if(fogging)
- {
- pushfogdist(camera1->o.z - z, camera1->o.z - (z - max(refractfog, 1)));
- pushfogcolor(col.tocolor());
- }
- else
- {
- vec color(0, 0, 0);
- float start = 0, end = 0;
- blendfog(MAT_AIR, 1, 1, start, end, color);
- pushfogdist(start, end);
- pushfogcolor(color);
- }
-
- if(fading)
- {
- float scale = fogging ? -0.25f : 0.25f, offset = 2*fabs(scale) - scale*z;
- GLOBALPARAMF(waterfadeparams, scale, offset, -scale, offset + camera1->o.z*scale);
- }
-
- if(reflecting)
- {
- noreflectmatrix = cammatrix;
- cammatrix.reflectz(z);
-
- glFrontFace(GL_CCW);
- }
-
- if(reflectclip && z>=0)
- {
- float zoffset = reflectclip/4.0f, zclip;
- if(refracting<0)
- {
- zclip = z+zoffset;
- if(camera1->o.z<=zclip) zclip = z;
- }
- else
- {
- zclip = z-zoffset;
- if(camera1->o.z>=zclip && camera1->o.z<=z+4.0f) zclip = z;
- if(reflecting) zclip = 2*z - zclip;
- }
- plane clipplane;
- invcammatrix.transposedtransform(plane(0, 0, refracting>0 ? 1 : -1, refracting>0 ? -zclip : zclip), clipplane);
- clipmatrix.clip(clipplane, projmatrix);
- noclipmatrix = projmatrix;
- projmatrix = clipmatrix;
- }
-
- setcamprojmatrix(false, true);
-
- renderreflectedgeom(refracting<0 && z>=0 && caustics, fogging);
-
- if(reflecting || refracting>0 || (refracting<0 && refractsky) || z<0)
- {
- if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- if(reflectclip && z>=0)
- {
- projmatrix = noclipmatrix;
- setcamprojmatrix(false, true);
- }
- drawskybox(farplane, false);
- if(reflectclip && z>=0)
- {
- projmatrix = clipmatrix;
- setcamprojmatrix(false, true);
- }
- if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
- }
- else if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
-
- renderdecals();
-
- if(reflectmms) renderreflectedmapmodels();
- rendergame();
-
- if(refracting && z>=0 && !isthirdperson() && fabs(camera1->o.z-z) <= 0.5f*(player->eyeheight + player->aboveeye))
- {
- matrix4 oldprojmatrix = projmatrix, avatarproj;
- avatarproj.perspective(curavatarfov, aspect, nearplane, farplane);
- if(reflectclip)
- {
- matrix4 avatarclip;
- plane clipplane;
- invcammatrix.transposedtransform(plane(0, 0, refracting, reflectclipavatar/4.0f - refracting*z), clipplane);
- avatarclip.clip(clipplane, avatarproj);
- projmatrix = avatarclip;
- }
- else projmatrix = avatarproj;
- setcamprojmatrix(false, true);
- game::renderavatar();
- projmatrix = oldprojmatrix;
- setcamprojmatrix(false, true);
- }
-
- rendermaterials();
- renderalphageom(fogging);
- renderparticles();
-
- if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-
- if(reflectclip && z>=0) projmatrix = noclipmatrix;
-
- if(reflecting)
- {
- cammatrix = noreflectmatrix;
-
- glFrontFace(GL_CW);
- }
-
- popfogdist();
- popfogcolor();
-
- reflectz = 1e16f;
- refracting = 0;
- reflecting = fading = fogging = false;
-
- setcamprojmatrix(false, true);
+ reflectz = z < 0 ? 1e16f : z;
+ reflecting = !refract;
+ refracting = refract ? (z < 0 || camera1->o.z >= z ? -1 : 1) : 0;
+ fading = waterrefract && waterfade && z>=0;
+ fogging = refracting<0 && z>=0;
+ refractfog = fogdepth;
+
+ if(fogging)
+ {
+ pushfogdist(camera1->o.z - z, camera1->o.z - (z - max(refractfog, 1)));
+ pushfogcolor(col.tocolor());
+ }
+ else
+ {
+ vec color(0, 0, 0);
+ float start = 0, end = 0;
+ blendfog(MAT_AIR, 1, 1, start, end, color);
+ pushfogdist(start, end);
+ pushfogcolor(color);
+ }
+
+ if(fading)
+ {
+ float scale = fogging ? -0.25f : 0.25f, offset = 2*fabs(scale) - scale*z;
+ GLOBALPARAMF(waterfadeparams, scale, offset, -scale, offset + camera1->o.z*scale);
+ }
+
+ if(reflecting)
+ {
+ noreflectmatrix = cammatrix;
+ cammatrix.reflectz(z);
+
+ glFrontFace(GL_CCW);
+ }
+
+ if(reflectclip && z>=0)
+ {
+ float zoffset = reflectclip/4.0f, zclip;
+ if(refracting<0)
+ {
+ zclip = z+zoffset;
+ if(camera1->o.z<=zclip) zclip = z;
+ }
+ else
+ {
+ zclip = z-zoffset;
+ if(camera1->o.z>=zclip && camera1->o.z<=z+4.0f) zclip = z;
+ if(reflecting) zclip = 2*z - zclip;
+ }
+ plane clipplane;
+ invcammatrix.transposedtransform(plane(0, 0, refracting>0 ? 1 : -1, refracting>0 ? -zclip : zclip), clipplane);
+ clipmatrix.clip(clipplane, projmatrix);
+ noclipmatrix = projmatrix;
+ projmatrix = clipmatrix;
+ }
+
+ setcamprojmatrix(false, true);
+
+ renderreflectedgeom(refracting<0 && z>=0 && caustics, fogging);
+
+ if(reflecting || refracting>0 || (refracting<0 && refractsky) || z<0)
+ {
+ if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ if(reflectclip && z>=0)
+ {
+ projmatrix = noclipmatrix;
+ setcamprojmatrix(false, true);
+ }
+ drawskybox(farplane, false);
+ if(reflectclip && z>=0)
+ {
+ projmatrix = clipmatrix;
+ setcamprojmatrix(false, true);
+ }
+ if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+ }
+ else if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+
+ renderdecals();
+
+ if(reflectmms) renderreflectedmapmodels();
+ rendergame();
+
+ if(refracting && z>=0 && !isthirdperson() && fabs(camera1->o.z-z) <= 0.5f*(player->eyeheight + player->aboveeye))
+ {
+ matrix4 oldprojmatrix = projmatrix, avatarproj;
+ avatarproj.perspective(curavatarfov, aspect, nearplane, farplane);
+ if(reflectclip)
+ {
+ matrix4 avatarclip;
+ plane clipplane;
+ invcammatrix.transposedtransform(plane(0, 0, refracting, reflectclipavatar/4.0f - refracting*z), clipplane);
+ avatarclip.clip(clipplane, avatarproj);
+ projmatrix = avatarclip;
+ }
+ else projmatrix = avatarproj;
+ setcamprojmatrix(false, true);
+ game::renderavatar();
+ projmatrix = oldprojmatrix;
+ setcamprojmatrix(false, true);
+ }
+
+ rendermaterials();
+ renderalphageom(fogging);
+ renderparticles();
+
+ if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+ if(reflectclip && z>=0) projmatrix = noclipmatrix;
+
+ if(reflecting)
+ {
+ cammatrix = noreflectmatrix;
+
+ glFrontFace(GL_CW);
+ }
+
+ popfogdist();
+ popfogcolor();
+
+ reflectz = 1e16f;
+ refracting = 0;
+ reflecting = fading = fogging = false;
+
+ setcamprojmatrix(false, true);
}
int drawtex = 0;
void drawcubemap(int size, const vec &o, float yaw, float pitch, const cubemapside &side, bool onlysky)
{
- drawtex = DRAWTEX_ENVMAP;
+ drawtex = DRAWTEX_ENVMAP;
- physent *oldcamera = camera1;
- static physent cmcamera;
- cmcamera = *player;
- cmcamera.reset();
- cmcamera.type = ENT_CAMERA;
- cmcamera.o = o;
- cmcamera.yaw = yaw;
- cmcamera.pitch = pitch;
- cmcamera.roll = 0;
- camera1 = &cmcamera;
+ physent *oldcamera = camera1;
+ static physent cmcamera;
+ cmcamera = *player;
+ cmcamera.reset();
+ cmcamera.type = ENT_CAMERA;
+ cmcamera.o = o;
+ cmcamera.yaw = yaw;
+ cmcamera.pitch = pitch;
+ cmcamera.roll = 0;
+ camera1 = &cmcamera;
- int fogmat = lookupmaterial(o)&(MATF_VOLUME|MATF_INDEX);
+ int fogmat = lookupmaterial(o)&(MATF_VOLUME|MATF_INDEX);
- setfog(fogmat);
+ setfog(fogmat);
- int farplane = worldsize*2;
+ int farplane = worldsize*2;
- projmatrix.perspective(90.0f, 1.0f, nearplane, farplane);
- if(!side.flipx || !side.flipy) projmatrix.scalexy(!side.flipx ? -1 : 1, !side.flipy ? -1 : 1);
- if(side.swapxy)
- {
- swap(projmatrix.a.x, projmatrix.a.y);
- swap(projmatrix.b.x, projmatrix.b.y);
- swap(projmatrix.c.x, projmatrix.c.y);
- swap(projmatrix.d.x, projmatrix.d.y);
- }
- setcamprojmatrix();
+ projmatrix.perspective(90.0f, 1.0f, nearplane, farplane);
+ if(!side.flipx || !side.flipy) projmatrix.scalexy(!side.flipx ? -1 : 1, !side.flipy ? -1 : 1);
+ if(side.swapxy)
+ {
+ swap(projmatrix.a.x, projmatrix.a.y);
+ swap(projmatrix.b.x, projmatrix.b.y);
+ swap(projmatrix.c.x, projmatrix.c.y);
+ swap(projmatrix.d.x, projmatrix.d.y);
+ }
+ setcamprojmatrix();
- xtravertsva = xtraverts = glde = gbatches = 0;
+ xtravertsva = xtraverts = glde = gbatches = 0;
- visiblecubes();
+ visiblecubes();
- if(onlysky) drawskybox(farplane, false, true);
- else
- {
- glClear(GL_DEPTH_BUFFER_BIT);
+ if(onlysky) drawskybox(farplane, false, true);
+ else
+ {
+ glClear(GL_DEPTH_BUFFER_BIT);
- glEnable(GL_CULL_FACE);
- glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
- if(limitsky()) drawskybox(farplane, true);
+ if(limitsky()) drawskybox(farplane, true);
- rendergeom();
+ rendergeom();
- if(!limitsky()) drawskybox(farplane, false);
+ if(!limitsky()) drawskybox(farplane, false);
-// queryreflections();
+// queryreflections();
- rendermapmodels();
- renderalphageom();
+ rendermapmodels();
+ renderalphageom();
-// drawreflections();
+// drawreflections();
-// renderwater();
-// rendermaterials();
+// renderwater();
+// rendermaterials();
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_CULL_FACE);
- }
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ }
- camera1 = oldcamera;
- drawtex = 0;
+ camera1 = oldcamera;
+ drawtex = 0;
}
VAR(modelpreviewfov, 10, 20, 100);
@@ -1507,254 +1484,197 @@ VAR(modelpreviewpitch, -90, -15, 90);
namespace modelpreview
{
- physent *oldcamera;
- physent camera;
+ physent *oldcamera;
+ physent camera;
- float oldaspect, oldfovy, oldfov;
- int oldfarplane;
- matrix4 oldprojmatrix;
+ float oldaspect, oldfovy, oldfov;
+ int oldfarplane;
+ matrix4 oldprojmatrix;
- void start(int x, int y, int w, int h, bool background)
- {
- drawtex = DRAWTEX_MODELPREVIEW;
+ void start(int x, int y, int w, int h, bool background)
+ {
+ drawtex = DRAWTEX_MODELPREVIEW;
- glViewport(x, y, w, h);
- glScissor(x, y, w, h);
- glEnable(GL_SCISSOR_TEST);
+ glViewport(x, y, w, h);
+ glScissor(x, y, w, h);
+ glEnable(GL_SCISSOR_TEST);
- oldcamera = camera1;
- camera = *camera1;
- camera.reset();
- camera.type = ENT_CAMERA;
- camera.o = vec(0, 0, 0);
- camera.yaw = 0;
- camera.pitch = modelpreviewpitch;
- camera.roll = 0;
- camera1 = &camera;
+ oldcamera = camera1;
+ camera = *camera1;
+ camera.reset();
+ camera.type = ENT_CAMERA;
+ camera.o = vec(0, 0, 0);
+ camera.yaw = 0;
+ camera.pitch = modelpreviewpitch;
+ camera.roll = 0;
+ camera1 = &camera;
- oldaspect = aspect;
- oldfovy = fovy;
- oldfov = curfov;
- oldfarplane = farplane;
- oldprojmatrix = projmatrix;
+ oldaspect = aspect;
+ oldfovy = fovy;
+ oldfov = curfov;
+ oldfarplane = farplane;
+ oldprojmatrix = projmatrix;
- aspect = w/float(h);
- fovy = modelpreviewfov;
- curfov = 2*atan2(tan(fovy/2*RAD), 1/aspect)/RAD;
- farplane = 1024;
+ aspect = w/float(h);
+ fovy = modelpreviewfov;
+ curfov = 2*atan2(tan(fovy/2*RAD), 1/aspect)/RAD;
+ farplane = 1024;
- clearfogdist();
- zerofogcolor();
- glClearColor(0, 0, 0, 1);
+ clearfogdist();
+ zerofogcolor();
+ glClearColor(0, 0, 0, 1);
- glClear((background ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT);
+ glClear((background ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT);
- projmatrix.perspective(fovy, aspect, nearplane, farplane);
- setcamprojmatrix();
+ projmatrix.perspective(fovy, aspect, nearplane, farplane);
+ setcamprojmatrix();
- glEnable(GL_CULL_FACE);
- glEnable(GL_DEPTH_TEST);
- }
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
+ }
- void end()
- {
- glDisable(GL_CULL_FACE);
- glDisable(GL_DEPTH_TEST);
+ void end()
+ {
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
- resetfogdist();
- resetfogcolor();
- glClearColor(curfogcolor.r, curfogcolor.g, curfogcolor.b, 1);
+ resetfogdist();
+ resetfogcolor();
+ glClearColor(curfogcolor.r, curfogcolor.g, curfogcolor.b, 1);
- aspect = oldaspect;
- fovy = oldfovy;
- curfov = oldfov;
- farplane = oldfarplane;
+ aspect = oldaspect;
+ fovy = oldfovy;
+ curfov = oldfov;
+ farplane = oldfarplane;
- camera1 = oldcamera;
- drawtex = 0;
+ camera1 = oldcamera;
+ drawtex = 0;
- glDisable(GL_SCISSOR_TEST);
- glViewport(0, 0, screenw, screenh);
+ glDisable(GL_SCISSOR_TEST);
+ glViewport(0, 0, screenw, screenh);
- projmatrix = oldprojmatrix;
- setcamprojmatrix();
- }
+ projmatrix = oldprojmatrix;
+ setcamprojmatrix();
+ }
}
vec calcmodelpreviewpos(const vec &radius, float &yaw)
{
- yaw = fmod(lastmillis/10000.0f*360.0f, 360.0f);
- float dist = 1.15f*max(radius.magnitude2()/aspect, radius.magnitude())/sinf(fovy/2*RAD);
- return vec(0, dist, 0).rotate_around_x(camera1->pitch*RAD);
+ yaw = fmod(lastmillis/10000.0f*360.0f, 360.0f);
+ float dist = 1.15f*max(radius.magnitude2()/aspect, radius.magnitude())/sinf(fovy/2*RAD);
+ return vec(0, dist, 0).rotate_around_x(camera1->pitch*RAD);
}
bool deferdrawtextures = false;
void drawtextures()
{
- if(minimized) { deferdrawtextures = true; return; }
- deferdrawtextures = false;
- genenvmaps();
-}
-
-GLuint motiontex = 0;
-int motionw = 0, motionh = 0, lastmotion = 0;
-
-void cleanupmotionblur()
-{
- if(motiontex) { glDeleteTextures(1, &motiontex); motiontex = 0; }
- motionw = motionh = 0;
- lastmotion = 0;
-}
-
-VARFP(motionblur, 0, 0, 1, { if(!motionblur) cleanupmotionblur(); });
-VARP(motionblurmillis, 1, 5, 1000);
-FVARP(motionblurscale, 0, 0.5f, 1);
-
-void addmotionblur()
-{
- if(!motionblur || max(screenw, screenh) > hwtexsize) return;
-
- if(game::ispaused()) { lastmotion = 0; return; }
-
- if(!motiontex || motionw != screenw || motionh != screenh)
- {
- if(!motiontex) glGenTextures(1, &motiontex);
- motionw = screenw;
- motionh = screenh;
- lastmotion = 0;
- createtexture(motiontex, motionw, motionh, NULL, 3, 0, GL_RGB);
- }
-
- glBindTexture(GL_TEXTURE_2D, motiontex);
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- SETSHADER(screenrect);
-
- gle::colorf(1, 1, 1, lastmotion ? pow(motionblurscale, max(float(lastmillis - lastmotion)/motionblurmillis, 1.0f)) : 0);
- screenquad(1, 1);
-
- glDisable(GL_BLEND);
-
- if(lastmillis - lastmotion >= motionblurmillis)
- {
- lastmotion = lastmillis - lastmillis%motionblurmillis;
-
- glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, screenw, screenh);
- }
-}
-
-bool dopostfx = false;
-
-void invalidatepostfx()
-{
- dopostfx = false;
+ if(minimized) { deferdrawtextures = true; return; }
+ deferdrawtextures = false;
+ genenvmaps();
}
int xtraverts, xtravertsva;
void gl_drawframe()
{
- if(deferdrawtextures) drawtextures();
+ if(deferdrawtextures) drawtextures();
- updatedynlights();
+ updatedynlights();
- int w = screenw, h = screenh;
- aspect = forceaspect ? forceaspect : w/float(h);
- fovy = 2*atan2(tan(curfov/2*RAD), aspect)/RAD;
+ int w = screenw, h = screenh;
+ aspect = forceaspect ? forceaspect : w/float(h);
+ fovy = 2*atan2(tan(curfov/2*RAD), aspect)/RAD;
- int fogmat = lookupmaterial(camera1->o)&(MATF_VOLUME|MATF_INDEX), abovemat = MAT_AIR;
- float fogblend = 1.0f, causticspass = 0.0f;
- if(isliquid(fogmat&MATF_VOLUME))
- {
- float z = findsurface(fogmat, camera1->o, abovemat) - WATER_OFFSET;
- if(camera1->o.z < z + 1) fogblend = min(z + 1 - camera1->o.z, 1.0f);
- else fogmat = abovemat;
- if(caustics && (fogmat&MATF_VOLUME)==MAT_WATER && camera1->o.z < z)
- causticspass = min(z - camera1->o.z, 1.0f);
- }
- else fogmat = MAT_AIR;
- setfog(fogmat, fogblend, abovemat);
- if(fogmat!=MAT_AIR)
- {
- float blend = abovemat==MAT_AIR ? fogblend : 1.0f;
- fovy += blend*sinf(lastmillis/1000.0)*2.0f;
- aspect += blend*sinf(lastmillis/1000.0+M_PI)*0.1f;
- }
+ int fogmat = lookupmaterial(camera1->o)&(MATF_VOLUME|MATF_INDEX), abovemat = MAT_AIR;
+ float fogblend = 1.0f, causticspass = 0.0f;
+ if(isliquid(fogmat&MATF_VOLUME))
+ {
+ float z = findsurface(fogmat, camera1->o, abovemat) - WATER_OFFSET;
+ if(camera1->o.z < z + 1) fogblend = min(z + 1 - camera1->o.z, 1.0f);
+ else fogmat = abovemat;
+ if(caustics && (fogmat&MATF_VOLUME)==MAT_WATER && camera1->o.z < z)
+ causticspass = min(z - camera1->o.z, 1.0f);
+ }
+ else fogmat = MAT_AIR;
+ setfog(fogmat, fogblend, abovemat);
+ if(fogmat!=MAT_AIR)
+ {
+ float blend = abovemat==MAT_AIR ? fogblend : 1.0f;
+ fovy += blend*sinf(lastmillis/1000.0)*2.0f;
+ aspect += blend*sinf(lastmillis/1000.0+M_PI)*0.1f;
+ }
- farplane = worldsize*2;
+ farplane = worldsize*2;
- projmatrix.perspective(fovy, aspect, nearplane, farplane);
- setcamprojmatrix();
+ projmatrix.perspective(fovy, aspect, nearplane, farplane);
+ setcamprojmatrix();
- glEnable(GL_CULL_FACE);
- glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
- xtravertsva = xtraverts = glde = gbatches = 0;
+ xtravertsva = xtraverts = glde = gbatches = 0;
- visiblecubes();
+ visiblecubes();
- glClear(GL_DEPTH_BUFFER_BIT|(wireframe && editmode ? GL_COLOR_BUFFER_BIT : 0));
+ glClear(GL_DEPTH_BUFFER_BIT|(wireframe && editmode ? GL_COLOR_BUFFER_BIT : 0));
- if(wireframe && editmode) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ if(wireframe && editmode) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- if(limitsky()) drawskybox(farplane, true);
+ if(limitsky()) drawskybox(farplane, true);
- rendergeom(causticspass);
+ rendergeom(causticspass);
- extern int outline;
- if(!wireframe && editmode && outline) renderoutline();
+ extern int outline;
+ if(!wireframe && editmode && outline) renderoutline();
- queryreflections();
+ queryreflections();
- if(!limitsky()) drawskybox(farplane, false);
+ if(!limitsky()) drawskybox(farplane, false);
- renderdecals(true);
+ renderdecals(true);
- rendermapmodels();
- rendergame(true);
- renderavatar();
+ rendermapmodels();
+ rendergame(true);
+ renderavatar();
- if(wireframe && editmode) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ if(wireframe && editmode) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- drawglaretex();
- drawdepthfxtex();
- drawreflections();
+ drawglaretex();
+ drawdepthfxtex();
+ drawreflections();
- if(wireframe && editmode) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ if(wireframe && editmode) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- renderwater();
+ renderwater();
- rendermaterials();
- renderalphageom();
+ rendermaterials();
+ renderalphageom();
- if(wireframe && editmode) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ if(wireframe && editmode) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- renderparticles(true);
+ renderparticles(true);
- glDisable(GL_CULL_FACE);
- glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
- addmotionblur();
- addglare();
- if(isliquid(fogmat&MATF_VOLUME)) drawfogoverlay(fogmat, fogblend, abovemat);
- renderpostfx();
+ addglare();
+ if(isliquid(fogmat&MATF_VOLUME)) drawfogoverlay(fogmat, fogblend, abovemat);
+ renderpostfx();
- gl_drawhud();
+ gl_drawhud();
- renderedgame = false;
+ renderedgame = false;
}
void gl_drawmainmenu()
{
- xtravertsva = xtraverts = glde = gbatches = 0;
+ xtravertsva = xtraverts = glde = gbatches = 0;
- renderbackground(NULL, NULL, NULL, NULL, true, true);
- renderpostfx();
+ renderbackground(NULL, NULL, NULL, NULL, true, true);
+ renderpostfx();
- gl_drawhud();
+ gl_drawhud();
}
VARNP(damagecompass, usedamagecompass, 0, 1, 1);
@@ -1768,99 +1688,58 @@ float damagedirs[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
void damagecompass(int n, const vec &loc)
{
- if(!usedamagecompass || minimized) return;
- vec delta(loc);
- delta.sub(camera1->o);
- float yaw = 0, pitch;
- if(delta.magnitude() > 4)
- {
- vectoyawpitch(delta, yaw, pitch);
- yaw -= camera1->yaw;
- }
- if(yaw >= 360) yaw = fmod(yaw, 360);
- else if(yaw < 0) yaw = 360 - fmod(-yaw, 360);
- int dir = (int(yaw+22.5f)%360)/45;
- damagedirs[dir] += max(n, damagecompassmin)/float(damagecompassmax);
- if(damagedirs[dir]>1) damagedirs[dir] = 1;
+ if(!usedamagecompass || minimized) return;
+ vec delta(loc);
+ delta.sub(camera1->o);
+ float yaw = 0, pitch;
+ if(delta.magnitude() > 4)
+ {
+ vectoyawpitch(delta, yaw, pitch);
+ yaw -= camera1->yaw;
+ }
+ if(yaw >= 360) yaw = fmod(yaw, 360);
+ else if(yaw < 0) yaw = 360 - fmod(-yaw, 360);
+ int dir = (int(yaw+22.5f)%360)/45;
+ damagedirs[dir] += max(n, damagecompassmin)/float(damagecompassmax);
+ if(damagedirs[dir]>1) damagedirs[dir] = 1;
}
void drawdamagecompass(int w, int h)
{
- hudnotextureshader->set();
-
- int dirs = 0;
- float size = damagecompasssize/100.0f*min(h, w)/2.0f;
- loopi(8) if(damagedirs[i]>0)
- {
- if(!dirs)
- {
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- gle::colorf(1, 0, 0, damagecompassalpha/100.0f);
- gle::defvertex();
- gle::begin(GL_TRIANGLES);
- }
- dirs++;
-
- float logscale = 32,
- scale = log(1 + (logscale - 1)*damagedirs[i]) / log(logscale),
- offset = -size/2.0f-min(h, w)/4.0f;
- matrix4x3 m;
- m.identity();
- m.settranslation(w/2, h/2, 0);
- m.rotate_around_z(i*45*RAD);
- m.translate(0, offset, 0);
- m.scale(size*scale);
-
- gle::attrib(m.transform(vec2(1, 1)));
- gle::attrib(m.transform(vec2(-1, 1)));
- gle::attrib(m.transform(vec2(0, 0)));
-
- // fade in log space so short blips don't disappear too quickly
- scale -= float(curtime)/damagecompassfade;
- damagedirs[i] = scale > 0 ? (pow(logscale, scale) - 1) / (logscale - 1) : 0;
- }
- if(dirs) gle::end();
-}
-
-int damageblendmillis = 0;
-
-VARFP(damagescreen, 0, 1, 1, { if(!damagescreen) damageblendmillis = 0; });
-VARP(damagescreenfactor, 1, 7, 100);
-VARP(damagescreenalpha, 1, 45, 100);
-VARP(damagescreenfade, 0, 125, 1000);
-VARP(damagescreenmin, 1, 10, 1000);
-VARP(damagescreenmax, 1, 100, 1000);
-
-void damageblend(int n)
-{
- if(!damagescreen || minimized) return;
- if(lastmillis > damageblendmillis) damageblendmillis = lastmillis;
- damageblendmillis += clamp(n, damagescreenmin, damagescreenmax)*damagescreenfactor;
-}
-
-void drawdamagescreen(int w, int h)
-{
- if(lastmillis >= damageblendmillis) return;
-
- hudshader->set();
-
- static Texture *damagetex = NULL;
- if(!damagetex) damagetex = textureload("packages/hud/damage.png", 3);
-
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glBindTexture(GL_TEXTURE_2D, damagetex->id);
- float fade = damagescreenalpha/100.0f;
- if(damageblendmillis - lastmillis < damagescreenfade)
- fade *= float(damageblendmillis - lastmillis)/damagescreenfade;
- gle::colorf(fade, fade, fade, fade);
-
- hudquad(0, 0, w, h);
-}
-
-void cleardamagescreen()
-{
- damageblendmillis = 0;
- loopi(8) damagedirs[i] = 0;
+ hudnotextureshader->set();
+
+ int dirs = 0;
+ float size = damagecompasssize/100.0f*min(h, w)/2.0f;
+ loopi(8) if(damagedirs[i]>0)
+ {
+ if(!dirs)
+ {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ gle::colorf(1, 0, 0, damagecompassalpha/100.0f);
+ gle::defvertex();
+ gle::begin(GL_TRIANGLES);
+ }
+ dirs++;
+
+ float logscale = 32,
+ scale = log(1 + (logscale - 1)*damagedirs[i]) / log(logscale),
+ offset = -size/2.0f-min(h, w)/4.0f;
+ matrix4x3 m;
+ m.identity();
+ m.settranslation(w/2, h/2, 0);
+ m.rotate_around_z(i*45*RAD);
+ m.translate(0, offset, 0);
+ m.scale(size*scale);
+
+ gle::attrib(m.transform(vec2(1, 1)));
+ gle::attrib(m.transform(vec2(-1, 1)));
+ gle::attrib(m.transform(vec2(0, 0)));
+
+ // fade in log space so short blips don't disappear too quickly
+ scale -= float(curtime)/damagecompassfade;
+ damagedirs[i] = scale > 0 ? (pow(logscale, scale) - 1) / (logscale - 1) : 0;
+ }
+ if(dirs) gle::end();
}
VAR(hidestats, 0, 0, 1);
@@ -1876,14 +1755,14 @@ static Texture *crosshairs[MAXCROSSHAIRS] = { NULL, NULL, NULL, NULL };
void loadcrosshair(const char *name, int i)
{
- if(i < 0 || i >= MAXCROSSHAIRS) return;
+ if(i < 0 || i >= MAXCROSSHAIRS) return;
crosshairs[i] = name ? textureload(name, 3, true) : notexture;
- if(crosshairs[i] == notexture)
- {
- name = game::defaultcrosshair(i);
- if(!name) name = "data/crosshair.png";
- crosshairs[i] = textureload(name, 3, true);
- }
+ if(crosshairs[i] == notexture)
+ {
+ name = game::defaultcrosshair(i);
+ if(!name) name = "data/crosshair.png";
+ crosshairs[i] = textureload(name, 3, true);
+ }
}
void loadcrosshair_(const char *name, int *i)
@@ -1895,68 +1774,62 @@ COMMANDN(loadcrosshair, loadcrosshair_, "si");
ICOMMAND(getcrosshair, "i", (int *i),
{
- const char *name = "";
- if(*i >= 0 && *i < MAXCROSSHAIRS)
- {
- name = crosshairs[*i] ? crosshairs[*i]->name : game::defaultcrosshair(*i);
- if(!name) name = "data/crosshair.png";
- }
- result(name);
+ const char *name = "";
+ if(*i >= 0 && *i < MAXCROSSHAIRS)
+ {
+ name = crosshairs[*i] ? crosshairs[*i]->name : game::defaultcrosshair(*i);
+ if(!name) name = "data/crosshair.png";
+ }
+ result(name);
});
void writecrosshairs(stream *f)
{
- loopi(MAXCROSSHAIRS) if(crosshairs[i] && crosshairs[i]!=notexture)
- f->printf("loadcrosshair %s %d\n", escapestring(crosshairs[i]->name), i);
- f->printf("\n");
+ loopi(MAXCROSSHAIRS) if(crosshairs[i] && crosshairs[i]!=notexture)
+ f->printf("loadcrosshair %s %d\n", escapestring(crosshairs[i]->name), i);
+ f->printf("\n");
}
void drawcrosshair(int w, int h)
{
- bool windowhit = g3d_windowhit(true, false);
- if(!windowhit && (hidehud || mainmenu)) return; //(hidehud || player->state==CS_SPECTATOR || player->state==CS_DEAD)) return;
-
- vec color(1, 1, 1);
- float cx = 0.5f, cy = 0.5f, chsize;
- Texture *crosshair;
- if(windowhit)
- {
- static Texture *cursor = NULL;
- if(!cursor) cursor = textureload("data/guicursor.png", 3, true);
- crosshair = cursor;
- chsize = cursorsize*w/900.0f;
- g3d_cursorpos(cx, cy);
- }
- else
- {
- int index = game::selectcrosshair(color);
- if(index < 0) return;
- if(!crosshairfx) index = 0;
- if(!crosshairfx || !crosshaircolors) color = vec(1, 1, 1);
- crosshair = crosshairs[index];
- if(!crosshair)
- {
- loadcrosshair(NULL, index);
- crosshair = crosshairs[index];
- }
- chsize = crosshairsize*w/900.0f;
- }
- if(crosshair->type&Texture::ALPHA) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- else glBlendFunc(GL_ONE, GL_ONE);
- float x = cx*w - (windowhit ? 0 : chsize/2.0f);
- float y = cy*h - (windowhit ? 0 : chsize/2.0f);
- glBindTexture(GL_TEXTURE_2D, crosshair->id);
-
- hudshader->set();
- gle::color(color);
- hudquad(x, y, chsize, chsize);
-}
-
-VARP(wallclock, 0, 0, 1);
-VARP(wallclock24, 0, 0, 1);
-VARP(wallclocksecs, 0, 0, 1);
-
-static time_t walltime = 0;
+ bool windowhit = g3d_windowhit(true, false);
+ if(!windowhit && (hidehud || mainmenu)) return; //(hidehud || player->state==CS_SPECTATOR || player->state==CS_DEAD)) return;
+
+ vec color(1, 1, 1);
+ float cx = 0.5f, cy = 0.5f, chsize;
+ Texture *crosshair;
+ if(windowhit)
+ {
+ static Texture *cursor = NULL;
+ if(!cursor) cursor = textureload("data/guicursor.png", 3, true);
+ crosshair = cursor;
+ chsize = cursorsize*w/900.0f;
+ g3d_cursorpos(cx, cy);
+ }
+ else
+ {
+ int index = game::selectcrosshair(color);
+ if(index < 0) return;
+ if(!crosshairfx) index = 0;
+ if(!crosshairfx || !crosshaircolors) color = vec(1, 1, 1);
+ crosshair = crosshairs[index];
+ if(!crosshair)
+ {
+ loadcrosshair(NULL, index);
+ crosshair = crosshairs[index];
+ }
+ chsize = crosshairsize*w/900.0f;
+ }
+ if(crosshair->type&Texture::ALPHA) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ else glBlendFunc(GL_ONE, GL_ONE);
+ float x = cx*w - (windowhit ? 0 : chsize/2.0f);
+ float y = cy*h - (windowhit ? 0 : chsize/2.0f);
+ glBindTexture(GL_TEXTURE_2D, crosshair->id);
+
+ hudshader->set();
+ gle::color(color);
+ hudquad(x, y, chsize, chsize);
+}
VARP(showfps, 0, 1, 1);
VARP(showfpsrange, 0, 0, 1);
@@ -1967,187 +1840,165 @@ FVARP(conscale, 1e-3f, 0.33f, 1e3f);
void gl_drawhud()
{
- g3d_render();
-
- int w = screenw, h = screenh;
- if(forceaspect) w = int(ceil(h*forceaspect));
-
- if(editmode && !hidehud && !mainmenu)
- {
- glEnable(GL_DEPTH_TEST);
- glDepthMask(GL_FALSE);
-
- renderblendbrush();
-
- rendereditcursor();
-
- glDepthMask(GL_TRUE);
- glDisable(GL_DEPTH_TEST);
- }
-
- gettextres(w, h);
-
- hudmatrix.ortho(0, w, h, 0, -1, 1);
- resethudmatrix();
-
- gle::colorf(1, 1, 1);
-
- glEnable(GL_BLEND);
-
- if(!mainmenu)
- {
- drawdamagescreen(w, h);
- drawdamagecompass(w, h);
- }
-
- hudshader->set();
-
- int conw = int(w/conscale), conh = int(h/conscale), abovehud = conh - FONTH, limitgui = abovehud;
- if(!hidehud && !mainmenu)
- {
- if(!hidestats)
- {
- pushhudmatrix();
- hudmatrix.scale(conscale, conscale, 1);
- flushhudmatrix();
-
- int roffset = 0;
- if(showfps)
- {
- static int lastfps = 0, prevfps[3] = { 0, 0, 0 }, curfps[3] = { 0, 0, 0 };
- if(totalmillis - lastfps >= statrate)
- {
- memcpy(prevfps, curfps, sizeof(prevfps));
- lastfps = totalmillis - (totalmillis%statrate);
- }
- int nextfps[3];
- getfps(nextfps[0], nextfps[1], nextfps[2]);
- loopi(3) if(prevfps[i]==curfps[i]) curfps[i] = nextfps[i];
- if(showfpsrange) draw_textf("fps %d+%d-%d", conw-7*FONTH, conh-FONTH*3/2, curfps[0], curfps[1], curfps[2]);
- else draw_textf("fps %d", conw-5*FONTH, conh-FONTH*3/2, curfps[0]);
- roffset += FONTH;
- }
-
- if(wallclock)
- {
- if(!walltime) { walltime = time(NULL); walltime -= totalmillis/1000; if(!walltime) walltime++; }
- time_t walloffset = walltime + totalmillis/1000;
- struct tm *localvals = localtime(&walloffset);
- static string buf;
- if(localvals && strftime(buf, sizeof(buf), wallclocksecs ? (wallclock24 ? "%H:%M:%S" : "%I:%M:%S%p") : (wallclock24 ? "%H:%M" : "%I:%M%p"), localvals))
- {
- // hack because not all platforms (windows) support %P lowercase option
- // also strip leading 0 from 12 hour time
- char *dst = buf;
- const char *src = &buf[!wallclock24 && buf[0]=='0' ? 1 : 0];
- while(*src) *dst++ = tolower(*src++);
- *dst++ = '\0';
- draw_text(buf, conw-5*FONTH, conh-FONTH*3/2-roffset);
- roffset += FONTH;
- }
- }
-
- if(editmode || showeditstats)
- {
- static int laststats = 0, prevstats[8] = { 0, 0, 0, 0, 0, 0, 0 }, curstats[8] = { 0, 0, 0, 0, 0, 0, 0 };
- if(totalmillis - laststats >= statrate)
- {
- memcpy(prevstats, curstats, sizeof(prevstats));
- laststats = totalmillis - (totalmillis%statrate);
- }
- int nextstats[8] =
- {
- vtris*100/max(wtris, 1),
- vverts*100/max(wverts, 1),
- xtraverts/1024,
- xtravertsva/1024,
- glde,
- gbatches,
- getnumqueries(),
- rplanes
- };
- loopi(8) if(prevstats[i]==curstats[i]) curstats[i] = nextstats[i];
-
- abovehud -= 2*FONTH;
- draw_textf("wtr:%dk(%d%%) wvt:%dk(%d%%) evt:%dk eva:%dk", FONTH/2, abovehud, wtris/1024, curstats[0], wverts/1024, curstats[1], curstats[2], curstats[3]);
- draw_textf("ond:%d va:%d gl:%d(%d) oq:%d lm:%d rp:%d", FONTH/2, abovehud+FONTH, allocnodes*8, allocva, curstats[4], curstats[5], curstats[6], lightmaps.length(), curstats[7]);
- limitgui = abovehud;
- }
-
- if(editmode)
- {
- abovehud -= FONTH;
- draw_textf("cube %s%d%s", FONTH/2, abovehud, selchildcount<0 ? "1/" : "", abs(selchildcount), showmat && selchildmat > 0 ? getmaterialdesc(selchildmat, ": ") : "");
-
- if(char *editinfo = execidentstr("edithud"))
- {
- if(editinfo[0])
- {
- int tw, th;
- text_bounds(editinfo, tw, th);
- th += FONTH-1; th -= th%FONTH;
- abovehud -= max(th, FONTH);
- draw_text(editinfo, FONTH/2, abovehud);
- }
- DELETEA(editinfo);
- }
- }
- else if(char *gameinfo = execidentstr("gamehud"))
- {
- if(gameinfo[0])
- {
- int tw, th;
- text_bounds(gameinfo, tw, th);
- th += FONTH-1; th -= th%FONTH;
- roffset += max(th, FONTH);
- draw_text(gameinfo, conw-max(5*FONTH, 2*FONTH+tw), conh-FONTH/2-roffset);
- }
- DELETEA(gameinfo);
- }
-
- pophudmatrix();
- }
-
- if(hidestats || (!editmode && !showeditstats))
- {
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- game::gameplayhud(w, h);
- limitgui = abovehud = min(abovehud, int(conh*game::abovegameplayhud(w, h)));
- }
-
- rendertexturepanel(w, h);
- }
-
- glDisable(GL_BLEND);
-
- g3d_limitscale((2*limitgui - conh) / float(conh));
- g3d_render2d();
-
- glEnable(GL_BLEND);
-
- hudmatrix.ortho(0, w, h, 0, -1, 1);
- resethudmatrix();
-
- pushhudmatrix();
- hudmatrix.scale(conscale, conscale, 1);
- flushhudmatrix();
- abovehud -= rendercommand(FONTH/2, abovehud - FONTH/2, conw-FONTH);
- extern int fullconsole;
- if(!hidehud || fullconsole) renderconsole(conw, conh, abovehud - FONTH/2);
- pophudmatrix();
-
- drawcrosshair(w, h);
-
- glDisable(GL_BLEND);
+ g3d_render();
+
+ int w = screenw, h = screenh;
+ if(forceaspect) w = int(ceil(h*forceaspect));
+
+ if(editmode && !hidehud && !mainmenu)
+ {
+ glEnable(GL_DEPTH_TEST);
+ glDepthMask(GL_FALSE);
+
+ renderblendbrush();
+
+ rendereditcursor();
+
+ glDepthMask(GL_TRUE);
+ glDisable(GL_DEPTH_TEST);
+ }
+
+ gettextres(w, h);
+
+ hudmatrix.ortho(0, w, h, 0, -1, 1);
+ resethudmatrix();
+
+ gle::colorf(1, 1, 1);
+
+ glEnable(GL_BLEND);
+
+ if(!mainmenu)
+ {
+ drawdamagecompass(w, h);
+ }
+
+ hudshader->set();
+
+ int conw = int(w/conscale), conh = int(h/conscale), abovehud = conh - FONTH, limitgui = abovehud;
+ if(!hidehud && !mainmenu)
+ {
+ if(!hidestats)
+ {
+ pushhudmatrix();
+ hudmatrix.scale(conscale, conscale, 1);
+ flushhudmatrix();
+
+ int roffset = 0;
+ if(showfps)
+ {
+ static int lastfps = 0, prevfps[3] = { 0, 0, 0 }, curfps[3] = { 0, 0, 0 };
+ if(totalmillis - lastfps >= statrate)
+ {
+ memcpy(prevfps, curfps, sizeof(prevfps));
+ lastfps = totalmillis - (totalmillis%statrate);
+ }
+ int nextfps[3];
+ getfps(nextfps[0], nextfps[1], nextfps[2]);
+ loopi(3) if(prevfps[i]==curfps[i]) curfps[i] = nextfps[i];
+ if(showfpsrange) draw_textf("fps %d+%d-%d", conw-7*FONTH, conh-FONTH*3/2, curfps[0], curfps[1], curfps[2]);
+ else draw_textf("fps %d", conw-5*FONTH, conh-FONTH*3/2, curfps[0]);
+ roffset += FONTH;
+ }
+
+ if(editmode || showeditstats)
+ {
+ static int laststats = 0, prevstats[8] = { 0, 0, 0, 0, 0, 0, 0 }, curstats[8] = { 0, 0, 0, 0, 0, 0, 0 };
+ if(totalmillis - laststats >= statrate)
+ {
+ memcpy(prevstats, curstats, sizeof(prevstats));
+ laststats = totalmillis - (totalmillis%statrate);
+ }
+ int nextstats[8] =
+ {
+ vtris*100/max(wtris, 1),
+ vverts*100/max(wverts, 1),
+ xtraverts/1024,
+ xtravertsva/1024,
+ glde,
+ gbatches,
+ getnumqueries(),
+ rplanes
+ };
+ loopi(8) if(prevstats[i]==curstats[i]) curstats[i] = nextstats[i];
+
+ abovehud -= 2*FONTH;
+ draw_textf("wtr:%dk(%d%%) wvt:%dk(%d%%) evt:%dk eva:%dk", FONTH/2, abovehud, wtris/1024, curstats[0], wverts/1024, curstats[1], curstats[2], curstats[3]);
+ draw_textf("ond:%d va:%d gl:%d(%d) oq:%d lm:%d rp:%d", FONTH/2, abovehud+FONTH, allocnodes*8, allocva, curstats[4], curstats[5], curstats[6], lightmaps.length(), curstats[7]);
+ limitgui = abovehud;
+ }
+
+ if(editmode)
+ {
+ abovehud -= FONTH;
+ draw_textf("cube %s%d%s", FONTH/2, abovehud, selchildcount<0 ? "1/" : "", abs(selchildcount), showmat && selchildmat > 0 ? getmaterialdesc(selchildmat, ": ") : "");
+
+ if(char *editinfo = execidentstr("edithud"))
+ {
+ if(editinfo[0])
+ {
+ int tw, th;
+ text_bounds(editinfo, tw, th);
+ th += FONTH-1; th -= th%FONTH;
+ abovehud -= max(th, FONTH);
+ draw_text(editinfo, FONTH/2, abovehud);
+ }
+ DELETEA(editinfo);
+ }
+ }
+ else if(char *gameinfo = execidentstr("gamehud"))
+ {
+ if(gameinfo[0])
+ {
+ int tw, th;
+ text_bounds(gameinfo, tw, th);
+ th += FONTH-1; th -= th%FONTH;
+ roffset += max(th, FONTH);
+ draw_text(gameinfo, conw-max(5*FONTH, 2*FONTH+tw), conh-FONTH/2-roffset);
+ }
+ DELETEA(gameinfo);
+ }
+
+ pophudmatrix();
+ }
+
+ if(hidestats || (!editmode && !showeditstats))
+ {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ game::gameplayhud(w, h);
+ limitgui = abovehud = min(abovehud, int(conh*game::abovegameplayhud(w, h)));
+ }
+
+ rendertexturepanel(w, h);
+ }
+
+ glDisable(GL_BLEND);
+
+ g3d_limitscale((2*limitgui - conh) / float(conh));
+ g3d_render2d();
+
+ glEnable(GL_BLEND);
+
+ hudmatrix.ortho(0, w, h, 0, -1, 1);
+ resethudmatrix();
+
+ pushhudmatrix();
+ hudmatrix.scale(conscale, conscale, 1);
+ flushhudmatrix();
+ abovehud -= rendercommand(FONTH/2, abovehud - FONTH/2, conw-FONTH);
+ extern int fullconsole;
+ if(!hidehud || fullconsole) renderconsole(conw, conh, abovehud - FONTH/2);
+ pophudmatrix();
+
+ drawcrosshair(w, h);
+
+ glDisable(GL_BLEND);
}
void cleanupgl()
{
- cleanupmotionblur();
-
- cleanupscreenquad();
+ cleanupscreenquad();
- gle::cleanup();
+ gle::cleanup();
}
diff --git a/src/engine/rendermodel.cpp b/src/engine/rendermodel.cpp
index c8a7eb6..5c8a219 100644
--- a/src/engine/rendermodel.cpp
+++ b/src/engine/rendermodel.cpp
@@ -14,14 +14,14 @@ static model *(__cdecl *modeltypes[NUMMODELTYPES])(const char *);
static int addmodeltype(int type, model *(__cdecl *loader)(const char *))
{
- modeltypes[type] = loader;
- return type;
+ modeltypes[type] = loader;
+ return type;
}
#define MODELTYPE(modeltype, modelclass) \
static model *__loadmodel__##modelclass(const char *filename) \
{ \
- return new modelclass(filename); \
+ return new modelclass(filename); \
} \
UNUSED static int __dummy__##modelclass = addmodeltype((modeltype), __loadmodel__##modelclass);
@@ -37,281 +37,281 @@ MODELTYPE(MDL_IQM, iqm);
void mdlcullface(int *cullface)
{
- checkmdl;
- loadingmodel->setcullface(*cullface!=0);
+ checkmdl;
+ loadingmodel->setcullface(*cullface!=0);
}
COMMAND(mdlcullface, "i");
void mdlcollide(int *collide)
{
- checkmdl;
- loadingmodel->collide = *collide!=0;
+ checkmdl;
+ loadingmodel->collide = *collide!=0;
}
COMMAND(mdlcollide, "i");
void mdlellipsecollide(int *collide)
{
- checkmdl;
- loadingmodel->ellipsecollide = *collide!=0;
+ checkmdl;
+ loadingmodel->ellipsecollide = *collide!=0;
}
COMMAND(mdlellipsecollide, "i");
void mdlspec(int *percent)
{
- checkmdl;
- float spec = 1.0f;
- if(*percent>0) spec = *percent/100.0f;
- else if(*percent<0) spec = 0.0f;
- loadingmodel->setspec(spec);
+ checkmdl;
+ float spec = 1.0f;
+ if(*percent>0) spec = *percent/100.0f;
+ else if(*percent<0) spec = 0.0f;
+ loadingmodel->setspec(spec);
}
COMMAND(mdlspec, "i");
void mdlambient(int *percent)
{
- checkmdl;
- float ambient = 0.3f;
- if(*percent>0) ambient = *percent/100.0f;
- else if(*percent<0) ambient = 0.0f;
- loadingmodel->setambient(ambient);
+ checkmdl;
+ float ambient = 0.3f;
+ if(*percent>0) ambient = *percent/100.0f;
+ else if(*percent<0) ambient = 0.0f;
+ loadingmodel->setambient(ambient);
}
COMMAND(mdlambient, "i");
void mdlalphatest(float *cutoff)
{
- checkmdl;
- loadingmodel->setalphatest(max(0.0f, min(1.0f, *cutoff)));
+ checkmdl;
+ loadingmodel->setalphatest(max(0.0f, min(1.0f, *cutoff)));
}
COMMAND(mdlalphatest, "f");
void mdlalphablend(int *blend)
{
- checkmdl;
- loadingmodel->setalphablend(*blend!=0);
+ checkmdl;
+ loadingmodel->setalphablend(*blend!=0);
}
COMMAND(mdlalphablend, "i");
void mdlalphadepth(int *depth)
{
- checkmdl;
- loadingmodel->alphadepth = *depth!=0;
+ checkmdl;
+ loadingmodel->alphadepth = *depth!=0;
}
COMMAND(mdlalphadepth, "i");
void mdldepthoffset(int *offset)
{
- checkmdl;
- loadingmodel->depthoffset = *offset!=0;
+ checkmdl;
+ loadingmodel->depthoffset = *offset!=0;
}
COMMAND(mdldepthoffset, "i");
void mdlglow(int *percent, int *delta, float *pulse)
{
- checkmdl;
- float glow = 3.0f, glowdelta = *delta/100.0f, glowpulse = *pulse > 0 ? *pulse/1000.0f : 0;
- if(*percent>0) glow = *percent/100.0f;
- else if(*percent<0) glow = 0.0f;
- glowdelta -= glow;
- loadingmodel->setglow(glow, glowdelta, glowpulse);
+ checkmdl;
+ float glow = 3.0f, glowdelta = *delta/100.0f, glowpulse = *pulse > 0 ? *pulse/1000.0f : 0;
+ if(*percent>0) glow = *percent/100.0f;
+ else if(*percent<0) glow = 0.0f;
+ glowdelta -= glow;
+ loadingmodel->setglow(glow, glowdelta, glowpulse);
}
COMMAND(mdlglow, "iif");
void mdlglare(float *specglare, float *glowglare)
{
- checkmdl;
- loadingmodel->setglare(*specglare, *glowglare);
+ checkmdl;
+ loadingmodel->setglare(*specglare, *glowglare);
}
COMMAND(mdlglare, "ff");
void mdlenvmap(float *envmapmax, float *envmapmin, char *envmap)
{
- checkmdl;
- loadingmodel->setenvmap(*envmapmin, *envmapmax, envmap[0] ? cubemapload(envmap) : NULL);
+ checkmdl;
+ loadingmodel->setenvmap(*envmapmin, *envmapmax, envmap[0] ? cubemapload(envmap) : NULL);
}
COMMAND(mdlenvmap, "ffs");
void mdlfullbright(float *fullbright)
{
- checkmdl;
- loadingmodel->setfullbright(*fullbright);
+ checkmdl;
+ loadingmodel->setfullbright(*fullbright);
}
COMMAND(mdlfullbright, "f");
void mdlshader(char *shader)
{
- checkmdl;
- loadingmodel->setshader(lookupshaderbyname(shader));
+ checkmdl;
+ loadingmodel->setshader(lookupshaderbyname(shader));
}
COMMAND(mdlshader, "s");
void mdlspin(float *yaw, float *pitch)
{
- checkmdl;
- loadingmodel->spinyaw = *yaw;
- loadingmodel->spinpitch = *pitch;
+ checkmdl;
+ loadingmodel->spinyaw = *yaw;
+ loadingmodel->spinpitch = *pitch;
}
COMMAND(mdlspin, "ff");
void mdlscale(int *percent)
{
- checkmdl;
- float scale = 1.0f;
- if(*percent>0) scale = *percent/100.0f;
- loadingmodel->scale = scale;
+ checkmdl;
+ float scale = 1.0f;
+ if(*percent>0) scale = *percent/100.0f;
+ loadingmodel->scale = scale;
}
COMMAND(mdlscale, "i");
void mdltrans(float *x, float *y, float *z)
{
- checkmdl;
- loadingmodel->translate = vec(*x, *y, *z);
+ checkmdl;
+ loadingmodel->translate = vec(*x, *y, *z);
}
COMMAND(mdltrans, "fff");
void mdlyaw(float *angle)
{
- checkmdl;
- loadingmodel->offsetyaw = *angle;
+ checkmdl;
+ loadingmodel->offsetyaw = *angle;
}
COMMAND(mdlyaw, "f");
void mdlpitch(float *angle)
{
- checkmdl;
- loadingmodel->offsetpitch = *angle;
+ checkmdl;
+ loadingmodel->offsetpitch = *angle;
}
COMMAND(mdlpitch, "f");
void mdlshadow(int *shadow)
{
- checkmdl;
- loadingmodel->shadow = *shadow!=0;
+ checkmdl;
+ loadingmodel->shadow = *shadow!=0;
}
COMMAND(mdlshadow, "i");
void mdlbb(float *rad, float *h, float *eyeheight)
{
- checkmdl;
- loadingmodel->collidexyradius = *rad;
- loadingmodel->collideheight = *h;
- loadingmodel->eyeheight = *eyeheight;
+ checkmdl;
+ loadingmodel->collidexyradius = *rad;
+ loadingmodel->collideheight = *h;
+ loadingmodel->eyeheight = *eyeheight;
}
COMMAND(mdlbb, "fff");
void mdlextendbb(float *x, float *y, float *z)
{
- checkmdl;
- loadingmodel->bbextend = vec(*x, *y, *z);
+ checkmdl;
+ loadingmodel->bbextend = vec(*x, *y, *z);
}
COMMAND(mdlextendbb, "fff");
void mdlname()
{
- checkmdl;
- result(loadingmodel->name);
+ checkmdl;
+ result(loadingmodel->name);
}
COMMAND(mdlname, "");
#define checkragdoll \
- checkmdl; \
- if(!loadingmodel->skeletal()) { conoutf(CON_ERROR, "not loading a skeletal model"); return; } \
- skelmodel *m = (skelmodel *)loadingmodel; \
- if(m->parts.empty()) return; \
- skelmodel::skelmeshgroup *meshes = (skelmodel::skelmeshgroup *)m->parts.last()->meshes; \
- if(!meshes) return; \
- skelmodel::skeleton *skel = meshes->skel; \
- if(!skel->ragdoll) skel->ragdoll = new ragdollskel; \
- ragdollskel *ragdoll = skel->ragdoll; \
- if(ragdoll->loaded) return;
+ checkmdl; \
+ if(!loadingmodel->skeletal()) { conoutf(CON_ERROR, "not loading a skeletal model"); return; } \
+ skelmodel *m = (skelmodel *)loadingmodel; \
+ if(m->parts.empty()) return; \
+ skelmodel::skelmeshgroup *meshes = (skelmodel::skelmeshgroup *)m->parts.last()->meshes; \
+ if(!meshes) return; \
+ skelmodel::skeleton *skel = meshes->skel; \
+ if(!skel->ragdoll) skel->ragdoll = new ragdollskel; \
+ ragdollskel *ragdoll = skel->ragdoll; \
+ if(ragdoll->loaded) return;
void rdvert(float *x, float *y, float *z, float *radius)
{
- checkragdoll;
- ragdollskel::vert &v = ragdoll->verts.add();
- v.pos = vec(*x, *y, *z);
- v.radius = *radius > 0 ? *radius : 1;
+ checkragdoll;
+ ragdollskel::vert &v = ragdoll->verts.add();
+ v.pos = vec(*x, *y, *z);
+ v.radius = *radius > 0 ? *radius : 1;
}
COMMAND(rdvert, "ffff");
void rdeye(int *v)
{
- checkragdoll;
- ragdoll->eye = *v;
+ checkragdoll;
+ ragdoll->eye = *v;
}
COMMAND(rdeye, "i");
void rdtri(int *v1, int *v2, int *v3)
{
- checkragdoll;
- ragdollskel::tri &t = ragdoll->tris.add();
- t.vert[0] = *v1;
- t.vert[1] = *v2;
- t.vert[2] = *v3;
+ checkragdoll;
+ ragdollskel::tri &t = ragdoll->tris.add();
+ t.vert[0] = *v1;
+ t.vert[1] = *v2;
+ t.vert[2] = *v3;
}
COMMAND(rdtri, "iii");
void rdjoint(int *n, int *t, int *v1, int *v2, int *v3)
{
- checkragdoll;
- if(*n < 0 || *n >= skel->numbones) return;
- ragdollskel::joint &j = ragdoll->joints.add();
- j.bone = *n;
- j.tri = *t;
- j.vert[0] = *v1;
- j.vert[1] = *v2;
- j.vert[2] = *v3;
+ checkragdoll;
+ if(*n < 0 || *n >= skel->numbones) return;
+ ragdollskel::joint &j = ragdoll->joints.add();
+ j.bone = *n;
+ j.tri = *t;
+ j.vert[0] = *v1;
+ j.vert[1] = *v2;
+ j.vert[2] = *v3;
}
COMMAND(rdjoint, "iibbb");
void rdlimitdist(int *v1, int *v2, float *mindist, float *maxdist)
{
- checkragdoll;
- ragdollskel::distlimit &d = ragdoll->distlimits.add();
- d.vert[0] = *v1;
- d.vert[1] = *v2;
- d.mindist = *mindist;
- d.maxdist = max(*maxdist, *mindist);
+ checkragdoll;
+ ragdollskel::distlimit &d = ragdoll->distlimits.add();
+ d.vert[0] = *v1;
+ d.vert[1] = *v2;
+ d.mindist = *mindist;
+ d.maxdist = max(*maxdist, *mindist);
}
COMMAND(rdlimitdist, "iiff");
void rdlimitrot(int *t1, int *t2, float *maxangle, float *qx, float *qy, float *qz, float *qw)
{
- checkragdoll;
- ragdollskel::rotlimit &r = ragdoll->rotlimits.add();
- r.tri[0] = *t1;
- r.tri[1] = *t2;
- r.maxangle = *maxangle * RAD;
- r.middle = matrix3(quat(*qx, *qy, *qz, *qw));
+ checkragdoll;
+ ragdollskel::rotlimit &r = ragdoll->rotlimits.add();
+ r.tri[0] = *t1;
+ r.tri[1] = *t2;
+ r.maxangle = *maxangle * RAD;
+ r.middle = matrix3(quat(*qx, *qy, *qz, *qw));
}
COMMAND(rdlimitrot, "iifffff");
void rdanimjoints(int *on)
{
- checkragdoll;
- ragdoll->animjoints = *on!=0;
+ checkragdoll;
+ ragdoll->animjoints = *on!=0;
}
COMMAND(rdanimjoints, "i");
@@ -321,20 +321,20 @@ vector<mapmodelinfo> mapmodels;
void mmodel(char *name)
{
- mapmodelinfo &mmi = mapmodels.add();
- copystring(mmi.name, name);
- mmi.m = NULL;
+ mapmodelinfo &mmi = mapmodels.add();
+ copystring(mmi.name, name);
+ mmi.m = NULL;
}
void mapmodelcompat(int *rad, int *h, int *tex, char *name, char *shadow)
{
- mmodel(name);
+ mmodel(name);
}
void mapmodelreset(int *n)
{
- if(!(identflags&IDF_OVERRIDDEN) && !game::allowedittoggle()) return;
- mapmodels.shrink(clamp(*n, 0, mapmodels.length()));
+ if(!(identflags&IDF_OVERRIDDEN) && !game::allowedittoggle()) return;
+ mapmodels.shrink(clamp(*n, 0, mapmodels.length()));
}
mapmodelinfo *getmminfo(int i) { return mapmodels.inrange(i) ? &mapmodels[i] : 0; }
@@ -355,185 +355,185 @@ vector<const char *> preloadmodels;
void preloadmodel(const char *name)
{
- if(!name || !name[0] || models.access(name)) return;
- preloadmodels.add(newstring(name));
+ if(!name || !name[0] || models.access(name)) return;
+ preloadmodels.add(newstring(name));
}
void flushpreloadedmodels(bool msg)
{
- loopv(preloadmodels)
- {
- loadprogress = float(i+1)/preloadmodels.length();
- model *m = loadmodel(preloadmodels[i], -1, msg);
- if(!m) { if(msg) conoutf(CON_WARN, "could not load model: %s", preloadmodels[i]); }
- else
- {
- m->preloadmeshes();
- }
- }
- preloadmodels.deletearrays();
- loadprogress = 0;
+ loopv(preloadmodels)
+ {
+ loadprogress = float(i+1)/preloadmodels.length();
+ model *m = loadmodel(preloadmodels[i], -1, msg);
+ if(!m) { if(msg) conoutf(CON_WARN, "could not load model: %s", preloadmodels[i]); }
+ else
+ {
+ m->preloadmeshes();
+ }
+ }
+ preloadmodels.deletearrays();
+ loadprogress = 0;
}
void preloadusedmapmodels(bool msg, bool bih)
{
- vector<extentity *> &ents = entities::getents();
- vector<int> mapmodels;
- loopv(ents)
- {
- extentity &e = *ents[i];
- if(e.type==ET_MAPMODEL && e.attr2 >= 0 && mapmodels.find(e.attr2) < 0) mapmodels.add(e.attr2);
- }
-
- loopv(mapmodels)
- {
- loadprogress = float(i+1)/mapmodels.length();
- int mmindex = mapmodels[i];
- mapmodelinfo *mmi = getmminfo(mmindex);
- if(!mmi) { if(msg) conoutf(CON_WARN, "could not find map model: %d", mmindex); }
- else if(mmi->name[0] && !loadmodel(NULL, mmindex, msg)) { if(msg) conoutf(CON_WARN, "could not load model: %s", mmi->name); }
- else if(mmi->m)
- {
- if(bih) mmi->m->preloadBIH();
- mmi->m->preloadmeshes();
- }
- }
- loadprogress = 0;
+ vector<extentity *> &ents = entities::getents();
+ vector<int> mapmodels;
+ loopv(ents)
+ {
+ extentity &e = *ents[i];
+ if(e.type==ET_MAPMODEL && e.attr2 >= 0 && mapmodels.find(e.attr2) < 0) mapmodels.add(e.attr2);
+ }
+
+ loopv(mapmodels)
+ {
+ loadprogress = float(i+1)/mapmodels.length();
+ int mmindex = mapmodels[i];
+ mapmodelinfo *mmi = getmminfo(mmindex);
+ if(!mmi) { if(msg) conoutf(CON_WARN, "could not find map model: %d", mmindex); }
+ else if(mmi->name[0] && !loadmodel(NULL, mmindex, msg)) { if(msg) conoutf(CON_WARN, "could not load model: %s", mmi->name); }
+ else if(mmi->m)
+ {
+ if(bih) mmi->m->preloadBIH();
+ mmi->m->preloadmeshes();
+ }
+ }
+ loadprogress = 0;
}
bool modelloaded(const char *name)
{
- return models.find(name, NULL) != NULL;
+ return models.find(name, NULL) != NULL;
}
model *loadmodel(const char *name, int i, bool msg)
{
- if(!name)
- {
- if(!mapmodels.inrange(i)) return NULL;
- mapmodelinfo &mmi = mapmodels[i];
- if(mmi.m) return mmi.m;
- name = mmi.name;
- }
- model **mm = models.access(name);
- model *m;
- if(mm) m = *mm;
- else
- {
- if(!name[0] || loadingmodel || lightmapping > 1) return NULL;
- if(msg)
- {
- defformatstring(filename, "packages/models/%s", name);
- renderprogress(loadprogress, filename);
- }
- loopi(NUMMODELTYPES)
- {
- m = modeltypes[i](name);
- if(!m) continue;
- loadingmodel = m;
- if(m->load()) break;
- DELETEP(m);
- }
- loadingmodel = NULL;
- if(!m) return NULL;
- models.access(m->name, m);
- m->preloadshaders();
- }
- if(mapmodels.inrange(i) && !mapmodels[i].m) mapmodels[i].m = m;
- return m;
+ if(!name)
+ {
+ if(!mapmodels.inrange(i)) return NULL;
+ mapmodelinfo &mmi = mapmodels[i];
+ if(mmi.m) return mmi.m;
+ name = mmi.name;
+ }
+ model **mm = models.access(name);
+ model *m;
+ if(mm) m = *mm;
+ else
+ {
+ if(!name[0] || loadingmodel || lightmapping > 1) return NULL;
+ if(msg)
+ {
+ defformatstring(filename, "packages/models/%s", name);
+ renderprogress(loadprogress, filename);
+ }
+ loopi(NUMMODELTYPES)
+ {
+ m = modeltypes[i](name);
+ if(!m) continue;
+ loadingmodel = m;
+ if(m->load()) break;
+ DELETEP(m);
+ }
+ loadingmodel = NULL;
+ if(!m) return NULL;
+ models.access(m->name, m);
+ m->preloadshaders();
+ }
+ if(mapmodels.inrange(i) && !mapmodels[i].m) mapmodels[i].m = m;
+ return m;
}
void preloadmodelshaders(bool force)
{
- if(initing) return;
- enumerate(models, model *, m, m->preloadshaders(force));
+ if(initing) return;
+ enumerate(models, model *, m, m->preloadshaders(force));
}
void clear_mdls()
{
- enumerate(models, model *, m, delete m);
+ enumerate(models, model *, m, delete m);
}
void cleanupmodels()
{
- enumerate(models, model *, m, m->cleanup());
+ enumerate(models, model *, m, m->cleanup());
}
void clearmodel(char *name)
{
- model **m = models.access(name);
- if(!m) { conoutf(CON_WARN, "model %s is not loaded", name); return; }
- loopv(mapmodels) if(mapmodels[i].m==*m) mapmodels[i].m = NULL;
- models.remove(name);
- (*m)->cleanup();
- delete *m;
- conoutf("cleared model %s", name);
+ model **m = models.access(name);
+ if(!m) { conoutf(CON_WARN, "model %s is not loaded", name); return; }
+ loopv(mapmodels) if(mapmodels[i].m==*m) mapmodels[i].m = NULL;
+ models.remove(name);
+ (*m)->cleanup();
+ delete *m;
+ conoutf("cleared model %s", name);
}
COMMAND(clearmodel, "s");
bool modeloccluded(const vec &center, float radius)
{
- ivec bbmin(vec(center).sub(radius)), bbmax(ivec(center).add(radius+1));
- return bboccluded(bbmin, bbmax);
+ ivec bbmin(vec(center).sub(radius)), bbmax(ivec(center).add(radius+1));
+ return bboccluded(bbmin, bbmax);
}
VAR(showboundingbox, 0, 0, 2);
void render2dbox(vec &o, float x, float y, float z)
{
- gle::begin(GL_LINE_LOOP);
- gle::attribf(o.x, o.y, o.z);
- gle::attribf(o.x, o.y, o.z+z);
- gle::attribf(o.x+x, o.y+y, o.z+z);
- gle::attribf(o.x+x, o.y+y, o.z);
- xtraverts += gle::end();
+ gle::begin(GL_LINE_LOOP);
+ gle::attribf(o.x, o.y, o.z);
+ gle::attribf(o.x, o.y, o.z+z);
+ gle::attribf(o.x+x, o.y+y, o.z+z);
+ gle::attribf(o.x+x, o.y+y, o.z);
+ xtraverts += gle::end();
}
void render3dbox(vec &o, float tofloor, float toceil, float xradius, float yradius)
{
- if(yradius<=0) yradius = xradius;
- vec c = o;
- c.sub(vec(xradius, yradius, tofloor));
- float xsz = xradius*2, ysz = yradius*2;
- float h = tofloor+toceil;
- gle::colorf(1, 1, 1);
- gle::defvertex();
- render2dbox(c, xsz, 0, h);
- render2dbox(c, 0, ysz, h);
- c.add(vec(xsz, ysz, 0));
- render2dbox(c, -xsz, 0, h);
- render2dbox(c, 0, -ysz, h);
+ if(yradius<=0) yradius = xradius;
+ vec c = o;
+ c.sub(vec(xradius, yradius, tofloor));
+ float xsz = xradius*2, ysz = yradius*2;
+ float h = tofloor+toceil;
+ gle::colorf(1, 1, 1);
+ gle::defvertex();
+ render2dbox(c, xsz, 0, h);
+ render2dbox(c, 0, ysz, h);
+ c.add(vec(xsz, ysz, 0));
+ render2dbox(c, -xsz, 0, h);
+ render2dbox(c, 0, -ysz, h);
}
void renderellipse(vec &o, float xradius, float yradius, float yaw)
{
- gle::colorf(0.5f, 0.5f, 0.5f);
- gle::defvertex();
- gle::begin(GL_LINE_LOOP);
- loopi(15)
- {
- const vec2 &sc = sincos360[i*(360/15)];
- gle::attrib(vec(xradius*sc.x, yradius*sc.y, 0).rotate_around_z((yaw+90)*RAD).add(o));
- }
- xtraverts += gle::end();
+ gle::colorf(0.5f, 0.5f, 0.5f);
+ gle::defvertex();
+ gle::begin(GL_LINE_LOOP);
+ loopi(15)
+ {
+ const vec2 &sc = sincos360[i*(360/15)];
+ gle::attrib(vec(xradius*sc.x, yradius*sc.y, 0).rotate_around_z((yaw+90)*RAD).add(o));
+ }
+ xtraverts += gle::end();
}
struct batchedmodel
{
- vec pos, color, dir;
- int anim;
- float yaw, pitch, transparent;
- int basetime, basetime2, flags;
- dynent *d;
- int attached;
- occludequery *query;
+ vec pos, color, dir;
+ int anim;
+ float yaw, pitch, transparent;
+ int basetime, basetime2, flags;
+ dynent *d;
+ int attached;
+ occludequery *query;
};
struct modelbatch
{
- model *m;
- int flags;
- vector<batchedmodel> batched;
+ model *m;
+ int flags;
+ vector<batchedmodel> batched;
};
static vector<modelbatch *> batches;
static vector<modelattach> modelattached;
@@ -542,487 +542,487 @@ static occludequery *modelquery = NULL;
void startmodelbatches()
{
- numbatches = 0;
- modelattached.setsize(0);
+ numbatches = 0;
+ modelattached.setsize(0);
}
modelbatch &addbatchedmodel(model *m)
{
- modelbatch *b = NULL;
- if(m->batch>=0 && m->batch<numbatches && batches[m->batch]->m==m) b = batches[m->batch];
- else
- {
- if(numbatches<batches.length())
- {
- b = batches[numbatches];
- b->batched.setsize(0);
- }
- else b = batches.add(new modelbatch);
- b->m = m;
- b->flags = 0;
- m->batch = numbatches++;
- }
- return *b;
+ modelbatch *b = NULL;
+ if(m->batch>=0 && m->batch<numbatches && batches[m->batch]->m==m) b = batches[m->batch];
+ else
+ {
+ if(numbatches<batches.length())
+ {
+ b = batches[numbatches];
+ b->batched.setsize(0);
+ }
+ else b = batches.add(new modelbatch);
+ b->m = m;
+ b->flags = 0;
+ m->batch = numbatches++;
+ }
+ return *b;
}
void renderbatchedmodel(model *m, batchedmodel &b)
{
- modelattach *a = NULL;
- if(b.attached>=0) a = &modelattached[b.attached];
+ modelattach *a = NULL;
+ if(b.attached>=0) a = &modelattached[b.attached];
- int anim = b.anim;
- if(shadowmapping)
- {
- anim |= ANIM_NOSKIN;
- GLOBALPARAMF(shadowintensity, b.transparent);
- }
- else
- {
- if(b.flags&MDL_FULLBRIGHT) anim |= ANIM_FULLBRIGHT;
- if(b.flags&MDL_GHOST) anim |= ANIM_GHOST;
- }
+ int anim = b.anim;
+ if(shadowmapping)
+ {
+ anim |= ANIM_NOSKIN;
+ GLOBALPARAMF(shadowintensity, b.transparent);
+ }
+ else
+ {
+ if(b.flags&MDL_FULLBRIGHT) anim |= ANIM_FULLBRIGHT;
+ if(b.flags&MDL_GHOST) anim |= ANIM_GHOST;
+ }
- m->render(anim, b.basetime, b.basetime2, b.pos, b.yaw, b.pitch, b.d, a, b.color, b.dir, b.transparent);
+ m->render(anim, b.basetime, b.basetime2, b.pos, b.yaw, b.pitch, b.d, a, b.color, b.dir, b.transparent);
}
struct transparentmodel
{
- model *m;
- batchedmodel *batched;
- float dist;
+ model *m;
+ batchedmodel *batched;
+ float dist;
};
static inline bool sorttransparentmodels(const transparentmodel &x, const transparentmodel &y)
{
- return x.dist < y.dist;
+ return x.dist < y.dist;
}
void endmodelbatches()
{
- vector<transparentmodel> transparent;
- loopi(numbatches)
- {
- modelbatch &b = *batches[i];
- if(b.batched.empty()) continue;
-
- bool rendered = false;
- occludequery *query = NULL;
- if(b.flags&MDL_GHOST)
- {
- loopvj(b.batched)
- {
- batchedmodel &bm = b.batched[j];
- if((bm.flags&(MDL_CULL_VFC|MDL_GHOST))!=MDL_GHOST || bm.query) continue;
- if(!rendered) { b.m->startrender(); rendered = true; }
- renderbatchedmodel(b.m, bm);
- }
- if(rendered)
- {
- b.m->endrender();
- rendered = false;
- }
- }
- loopvj(b.batched)
- {
- batchedmodel &bm = b.batched[j];
- if(bm.flags&(MDL_CULL_VFC|MDL_GHOST)) continue;
- if(bm.query!=query)
- {
- if(query) endquery(query);
- query = bm.query;
- if(query) startquery(query);
- }
- if(bm.transparent < 1 && (!query || query->owner==bm.d) && !shadowmapping)
- {
- transparentmodel &tm = transparent.add();
- tm.m = b.m;
- tm.batched = &bm;
- tm.dist = camera1->o.dist(bm.d && bm.d->ragdoll ? bm.d->ragdoll->center : bm.pos);
- continue;
- }
- if(!rendered) { b.m->startrender(); rendered = true; }
- renderbatchedmodel(b.m, bm);
- }
- if(query) endquery(query);
- if(rendered) b.m->endrender();
- }
- if(transparent.length())
- {
- transparent.sort(sorttransparentmodels);
- model *lastmodel = NULL;
- occludequery *query = NULL;
- loopv(transparent)
- {
- transparentmodel &tm = transparent[i];
- if(lastmodel!=tm.m)
- {
- if(lastmodel) lastmodel->endrender();
- (lastmodel = tm.m)->startrender();
- }
- if(query!=tm.batched->query)
- {
- if(query) endquery(query);
- query = tm.batched->query;
- if(query) startquery(query);
- }
- renderbatchedmodel(tm.m, *tm.batched);
- }
- if(query) endquery(query);
- if(lastmodel) lastmodel->endrender();
- }
- numbatches = -1;
+ vector<transparentmodel> transparent;
+ loopi(numbatches)
+ {
+ modelbatch &b = *batches[i];
+ if(b.batched.empty()) continue;
+
+ bool rendered = false;
+ occludequery *query = NULL;
+ if(b.flags&MDL_GHOST)
+ {
+ loopvj(b.batched)
+ {
+ batchedmodel &bm = b.batched[j];
+ if((bm.flags&(MDL_CULL_VFC|MDL_GHOST))!=MDL_GHOST || bm.query) continue;
+ if(!rendered) { b.m->startrender(); rendered = true; }
+ renderbatchedmodel(b.m, bm);
+ }
+ if(rendered)
+ {
+ b.m->endrender();
+ rendered = false;
+ }
+ }
+ loopvj(b.batched)
+ {
+ batchedmodel &bm = b.batched[j];
+ if(bm.flags&(MDL_CULL_VFC|MDL_GHOST)) continue;
+ if(bm.query!=query)
+ {
+ if(query) endquery(query);
+ query = bm.query;
+ if(query) startquery(query);
+ }
+ if(bm.transparent < 1 && (!query || query->owner==bm.d) && !shadowmapping)
+ {
+ transparentmodel &tm = transparent.add();
+ tm.m = b.m;
+ tm.batched = &bm;
+ tm.dist = camera1->o.dist(bm.d && bm.d->ragdoll ? bm.d->ragdoll->center : bm.pos);
+ continue;
+ }
+ if(!rendered) { b.m->startrender(); rendered = true; }
+ renderbatchedmodel(b.m, bm);
+ }
+ if(query) endquery(query);
+ if(rendered) b.m->endrender();
+ }
+ if(transparent.length())
+ {
+ transparent.sort(sorttransparentmodels);
+ model *lastmodel = NULL;
+ occludequery *query = NULL;
+ loopv(transparent)
+ {
+ transparentmodel &tm = transparent[i];
+ if(lastmodel!=tm.m)
+ {
+ if(lastmodel) lastmodel->endrender();
+ (lastmodel = tm.m)->startrender();
+ }
+ if(query!=tm.batched->query)
+ {
+ if(query) endquery(query);
+ query = tm.batched->query;
+ if(query) startquery(query);
+ }
+ renderbatchedmodel(tm.m, *tm.batched);
+ }
+ if(query) endquery(query);
+ if(lastmodel) lastmodel->endrender();
+ }
+ numbatches = -1;
}
void startmodelquery(occludequery *query)
{
- modelquery = query;
+ modelquery = query;
}
void endmodelquery()
{
- int querybatches = 0;
- loopi(numbatches)
- {
- modelbatch &b = *batches[i];
- if(b.batched.empty() || b.batched.last().query!=modelquery) continue;
- querybatches++;
- }
- if(querybatches<=1)
- {
- if(!querybatches) modelquery->fragments = 0;
- modelquery = NULL;
- return;
- }
- int minattached = modelattached.length();
- startquery(modelquery);
- loopi(numbatches)
- {
- modelbatch &b = *batches[i];
- if(b.batched.empty() || b.batched.last().query!=modelquery) continue;
- b.m->startrender();
- do
- {
- batchedmodel &bm = b.batched.pop();
- if(bm.attached>=0) minattached = min(minattached, bm.attached);
- renderbatchedmodel(b.m, bm);
- }
- while(b.batched.length() && b.batched.last().query==modelquery);
- b.m->endrender();
- }
- endquery(modelquery);
- modelquery = NULL;
- modelattached.setsize(minattached);
+ int querybatches = 0;
+ loopi(numbatches)
+ {
+ modelbatch &b = *batches[i];
+ if(b.batched.empty() || b.batched.last().query!=modelquery) continue;
+ querybatches++;
+ }
+ if(querybatches<=1)
+ {
+ if(!querybatches) modelquery->fragments = 0;
+ modelquery = NULL;
+ return;
+ }
+ int minattached = modelattached.length();
+ startquery(modelquery);
+ loopi(numbatches)
+ {
+ modelbatch &b = *batches[i];
+ if(b.batched.empty() || b.batched.last().query!=modelquery) continue;
+ b.m->startrender();
+ do
+ {
+ batchedmodel &bm = b.batched.pop();
+ if(bm.attached>=0) minattached = min(minattached, bm.attached);
+ renderbatchedmodel(b.m, bm);
+ }
+ while(b.batched.length() && b.batched.last().query==modelquery);
+ b.m->endrender();
+ }
+ endquery(modelquery);
+ modelquery = NULL;
+ modelattached.setsize(minattached);
}
VAR(maxmodelradiusdistance, 10, 200, 1000);
static inline void enablecullmodelquery()
{
- startbb();
+ startbb();
}
static inline void rendercullmodelquery(model *m, dynent *d, const vec &center, float radius)
{
- if(fabs(camera1->o.x-center.x) < radius+1 &&
- fabs(camera1->o.y-center.y) < radius+1 &&
- fabs(camera1->o.z-center.z) < radius+1)
- {
- d->query = NULL;
- return;
- }
- d->query = newquery(d);
- if(!d->query) return;
- startquery(d->query);
- int br = int(radius*2)+1;
- drawbb(ivec(int(center.x-radius), int(center.y-radius), int(center.z-radius)), ivec(br, br, br));
- endquery(d->query);
+ if(fabs(camera1->o.x-center.x) < radius+1 &&
+ fabs(camera1->o.y-center.y) < radius+1 &&
+ fabs(camera1->o.z-center.z) < radius+1)
+ {
+ d->query = NULL;
+ return;
+ }
+ d->query = newquery(d);
+ if(!d->query) return;
+ startquery(d->query);
+ int br = int(radius*2)+1;
+ drawbb(ivec(int(center.x-radius), int(center.y-radius), int(center.z-radius)), ivec(br, br, br));
+ endquery(d->query);
}
static inline void disablecullmodelquery()
{
- endbb();
+ endbb();
}
static inline int cullmodel(model *m, const vec &center, float radius, int flags, dynent *d = NULL, bool shadow = false)
{
- if(flags&MDL_CULL_DIST && center.dist(camera1->o)/radius>maxmodelradiusdistance) return MDL_CULL_DIST;
- if(flags&MDL_CULL_VFC)
- {
- if(reflecting || refracting)
- {
- if(reflecting || refracting>0)
- {
- if(center.z+radius<=reflectz) return MDL_CULL_VFC;
- }
- else
- {
- if(fogging && center.z+radius<reflectz-refractfog) return MDL_CULL_VFC;
- if(!shadow && center.z-radius>=reflectz) return MDL_CULL_VFC;
- }
- if(center.dist(camera1->o)-radius>reflectdist) return MDL_CULL_VFC;
- }
- if(isfoggedsphere(radius, center)) return MDL_CULL_VFC;
- if(shadowmapping && !isshadowmapcaster(center, radius)) return MDL_CULL_VFC;
- }
- if(shadowmapping)
- {
- if(d)
- {
- if(flags&MDL_CULL_OCCLUDED && d->occluded>=OCCLUDE_PARENT) return MDL_CULL_OCCLUDED;
- if(flags&MDL_CULL_QUERY && d->occluded+1>=OCCLUDE_BB && d->query && d->query->owner==d && checkquery(d->query)) return MDL_CULL_QUERY;
- }
- if(!addshadowmapcaster(center, radius, radius)) return MDL_CULL_VFC;
- }
- else if(flags&MDL_CULL_OCCLUDED && modeloccluded(center, radius))
- {
- if(!reflecting && !refracting && d) d->occluded = OCCLUDE_PARENT;
- return MDL_CULL_OCCLUDED;
- }
- else if(flags&MDL_CULL_QUERY && d->query && d->query->owner==d && checkquery(d->query))
- {
- if(!reflecting && !refracting && d->occluded<OCCLUDE_BB) d->occluded++;
- return MDL_CULL_QUERY;
- }
- return 0;
+ if(flags&MDL_CULL_DIST && center.dist(camera1->o)/radius>maxmodelradiusdistance) return MDL_CULL_DIST;
+ if(flags&MDL_CULL_VFC)
+ {
+ if(reflecting || refracting)
+ {
+ if(reflecting || refracting>0)
+ {
+ if(center.z+radius<=reflectz) return MDL_CULL_VFC;
+ }
+ else
+ {
+ if(fogging && center.z+radius<reflectz-refractfog) return MDL_CULL_VFC;
+ if(!shadow && center.z-radius>=reflectz) return MDL_CULL_VFC;
+ }
+ if(center.dist(camera1->o)-radius>reflectdist) return MDL_CULL_VFC;
+ }
+ if(isfoggedsphere(radius, center)) return MDL_CULL_VFC;
+ if(shadowmapping && !isshadowmapcaster(center, radius)) return MDL_CULL_VFC;
+ }
+ if(shadowmapping)
+ {
+ if(d)
+ {
+ if(flags&MDL_CULL_OCCLUDED && d->occluded>=OCCLUDE_PARENT) return MDL_CULL_OCCLUDED;
+ if(flags&MDL_CULL_QUERY && d->occluded+1>=OCCLUDE_BB && d->query && d->query->owner==d && checkquery(d->query)) return MDL_CULL_QUERY;
+ }
+ if(!addshadowmapcaster(center, radius, radius)) return MDL_CULL_VFC;
+ }
+ else if(flags&MDL_CULL_OCCLUDED && modeloccluded(center, radius))
+ {
+ if(!reflecting && !refracting && d) d->occluded = OCCLUDE_PARENT;
+ return MDL_CULL_OCCLUDED;
+ }
+ else if(flags&MDL_CULL_QUERY && d->query && d->query->owner==d && checkquery(d->query))
+ {
+ if(!reflecting && !refracting && d->occluded<OCCLUDE_BB) d->occluded++;
+ return MDL_CULL_QUERY;
+ }
+ return 0;
}
void rendermodel(entitylight *light, const char *mdl, int anim, const vec &o, float yaw, float pitch, int flags, dynent *d, modelattach *a, int basetime, int basetime2, float trans)
{
- if(shadowmapping && !(flags&(MDL_SHADOW|MDL_DYNSHADOW))) return;
- model *m = loadmodel(mdl);
- if(!m) return;
- vec center(0, 0, 0), bbradius(0, 0, 0);
- float radius = 0;
- bool shadow = !shadowmap && !glaring && (flags&(MDL_SHADOW|MDL_DYNSHADOW));
-
- if(flags&(MDL_CULL_VFC|MDL_CULL_DIST|MDL_CULL_OCCLUDED|MDL_CULL_QUERY|MDL_SHADOW|MDL_DYNSHADOW))
- {
- if(flags&MDL_CULL_QUERY)
- {
- if(!oqfrags || !oqdynent || !d) flags &= ~MDL_CULL_QUERY;
- }
-
- m->boundbox(center, bbradius);
- radius = bbradius.magnitude();
- if(d && d->ragdoll)
- {
- radius = max(radius, d->ragdoll->radius);
- center = d->ragdoll->center;
- }
- else
- {
- center.rotate_around_z(yaw*RAD);
- center.add(o);
- }
-
- int culled = cullmodel(m, center, radius, flags, d, shadow);
- if(culled)
- {
- if(culled&(MDL_CULL_OCCLUDED|MDL_CULL_QUERY) && flags&MDL_CULL_QUERY && !reflecting && !refracting)
- {
- enablecullmodelquery();
- rendercullmodelquery(m, d, center, radius);
- disablecullmodelquery();
- }
- return;
- }
-
- if(reflecting || refracting || shadowmapping) flags &= ~MDL_CULL_QUERY;
- }
-
- if(flags&MDL_NORENDER) anim |= ANIM_NORENDER;
- else if(showboundingbox && !shadowmapping && !reflecting && !refracting && editmode)
- {
- notextureshader->set();
- if(d && showboundingbox==1)
- {
- render3dbox(d->o, d->eyeheight, d->aboveeye, d->radius);
- renderellipse(d->o, d->xradius, d->yradius, d->yaw);
- }
- else
- {
- vec center, radius;
- if(showboundingbox==1) m->collisionbox(center, radius);
- else m->boundbox(center, radius);
- rotatebb(center, radius, int(yaw));
- center.add(o);
- render3dbox(center, radius.z, radius.z, radius.x, radius.y);
- }
- }
-
- vec lightcolor(1, 1, 1), lightdir(0, 0, 1);
- if(!shadowmapping)
- {
- vec pos = o;
- if(d)
- {
- if(!reflecting && !refracting) d->occluded = OCCLUDE_NOTHING;
- if(!light) light = &d->light;
- if(flags&MDL_LIGHT && light->millis!=lastmillis)
- {
- if(d->ragdoll)
- {
- pos = d->ragdoll->center;
- pos.z += radius/2;
- }
- else if(d->type < ENT_CAMERA) pos.z += 0.75f*(d->eyeheight + d->aboveeye);
- lightreaching(pos, light->color, light->dir, (flags&MDL_LIGHT_FAST)!=0);
- dynlightreaching(pos, light->color, light->dir, (flags&MDL_HUD)!=0);
- game::lighteffects(d, light->color, light->dir);
- light->millis = lastmillis;
- }
- }
- else if(flags&MDL_LIGHT)
- {
- if(!light)
- {
- lightreaching(pos, lightcolor, lightdir, (flags&MDL_LIGHT_FAST)!=0);
- dynlightreaching(pos, lightcolor, lightdir, (flags&MDL_HUD)!=0);
- }
- else if(light->millis!=lastmillis)
- {
- lightreaching(pos, light->color, light->dir, (flags&MDL_LIGHT_FAST)!=0);
- dynlightreaching(pos, light->color, light->dir, (flags&MDL_HUD)!=0);
- light->millis = lastmillis;
- }
- }
- if(light) { lightcolor = light->color; lightdir = light->dir; }
- if(flags&MDL_DYNLIGHT) dynlightreaching(pos, lightcolor, lightdir, (flags&MDL_HUD)!=0);
- }
-
- if(a) for(int i = 0; a[i].tag; i++)
- {
- if(a[i].name) a[i].m = loadmodel(a[i].name);
- //if(a[i].m && a[i].m->type()!=m->type()) a[i].m = NULL;
- }
-
- if(numbatches>=0)
- {
- modelbatch &mb = addbatchedmodel(m);
- batchedmodel &b = mb.batched.add();
- b.query = modelquery;
- b.pos = o;
- b.color = lightcolor;
- b.dir = lightdir;
- b.anim = anim;
- b.yaw = yaw;
- b.pitch = pitch;
- b.basetime = basetime;
- b.basetime2 = basetime2;
- b.transparent = trans;
- b.flags = flags & ~(MDL_CULL_VFC | MDL_CULL_DIST | MDL_CULL_OCCLUDED);
- if(!shadow || reflecting || refracting>0)
- {
- b.flags &= ~(MDL_SHADOW|MDL_DYNSHADOW);
- if((flags&MDL_CULL_VFC) && refracting<0 && center.z-radius>=reflectz) b.flags |= MDL_CULL_VFC;
- }
- mb.flags |= b.flags;
- b.d = d;
- b.attached = a ? modelattached.length() : -1;
- if(a) for(int i = 0;; i++) { modelattached.add(a[i]); if(!a[i].tag) break; }
- if(flags&MDL_CULL_QUERY) d->query = b.query = newquery(d);
- return;
- }
-
- m->startrender();
-
- if(shadowmapping)
- {
- anim |= ANIM_NOSKIN;
- GLOBALPARAMF(shadowintensity, trans);
- }
- else
- {
- if(flags&MDL_FULLBRIGHT) anim |= ANIM_FULLBRIGHT;
- if(flags&MDL_GHOST) anim |= ANIM_GHOST;
- }
-
- if(flags&MDL_CULL_QUERY)
- {
- d->query = newquery(d);
- if(d->query) startquery(d->query);
- }
-
- m->render(anim, basetime, basetime2, o, yaw, pitch, d, a, lightcolor, lightdir, trans);
-
- if(flags&MDL_CULL_QUERY && d->query) endquery(d->query);
-
- m->endrender();
+ if(shadowmapping && !(flags&(MDL_SHADOW|MDL_DYNSHADOW))) return;
+ model *m = loadmodel(mdl);
+ if(!m) return;
+ vec center(0, 0, 0), bbradius(0, 0, 0);
+ float radius = 0;
+ bool shadow = !shadowmap && !glaring && (flags&(MDL_SHADOW|MDL_DYNSHADOW));
+
+ if(flags&(MDL_CULL_VFC|MDL_CULL_DIST|MDL_CULL_OCCLUDED|MDL_CULL_QUERY|MDL_SHADOW|MDL_DYNSHADOW))
+ {
+ if(flags&MDL_CULL_QUERY)
+ {
+ if(!oqfrags || !oqdynent || !d) flags &= ~MDL_CULL_QUERY;
+ }
+
+ m->boundbox(center, bbradius);
+ radius = bbradius.magnitude();
+ if(d && d->ragdoll)
+ {
+ radius = max(radius, d->ragdoll->radius);
+ center = d->ragdoll->center;
+ }
+ else
+ {
+ center.rotate_around_z(yaw*RAD);
+ center.add(o);
+ }
+
+ int culled = cullmodel(m, center, radius, flags, d, shadow);
+ if(culled)
+ {
+ if(culled&(MDL_CULL_OCCLUDED|MDL_CULL_QUERY) && flags&MDL_CULL_QUERY && !reflecting && !refracting)
+ {
+ enablecullmodelquery();
+ rendercullmodelquery(m, d, center, radius);
+ disablecullmodelquery();
+ }
+ return;
+ }
+
+ if(reflecting || refracting || shadowmapping) flags &= ~MDL_CULL_QUERY;
+ }
+
+ if(flags&MDL_NORENDER) anim |= ANIM_NORENDER;
+ else if(showboundingbox && !shadowmapping && !reflecting && !refracting && editmode)
+ {
+ notextureshader->set();
+ if(d && showboundingbox==1)
+ {
+ render3dbox(d->o, d->eyeheight, d->aboveeye, d->radius);
+ renderellipse(d->o, d->xradius, d->yradius, d->yaw);
+ }
+ else
+ {
+ vec center, radius;
+ if(showboundingbox==1) m->collisionbox(center, radius);
+ else m->boundbox(center, radius);
+ rotatebb(center, radius, int(yaw));
+ center.add(o);
+ render3dbox(center, radius.z, radius.z, radius.x, radius.y);
+ }
+ }
+
+ vec lightcolor(1, 1, 1), lightdir(0, 0, 1);
+ if(!shadowmapping)
+ {
+ vec pos = o;
+ if(d)
+ {
+ if(!reflecting && !refracting) d->occluded = OCCLUDE_NOTHING;
+ if(!light) light = &d->light;
+ if(flags&MDL_LIGHT && light->millis!=lastmillis)
+ {
+ if(d->ragdoll)
+ {
+ pos = d->ragdoll->center;
+ pos.z += radius/2;
+ }
+ else if(d->type < ENT_CAMERA) pos.z += 0.75f*(d->eyeheight + d->aboveeye);
+ lightreaching(pos, light->color, light->dir, (flags&MDL_LIGHT_FAST)!=0);
+ dynlightreaching(pos, light->color, light->dir, (flags&MDL_HUD)!=0);
+ game::lighteffects(d, light->color, light->dir);
+ light->millis = lastmillis;
+ }
+ }
+ else if(flags&MDL_LIGHT)
+ {
+ if(!light)
+ {
+ lightreaching(pos, lightcolor, lightdir, (flags&MDL_LIGHT_FAST)!=0);
+ dynlightreaching(pos, lightcolor, lightdir, (flags&MDL_HUD)!=0);
+ }
+ else if(light->millis!=lastmillis)
+ {
+ lightreaching(pos, light->color, light->dir, (flags&MDL_LIGHT_FAST)!=0);
+ dynlightreaching(pos, light->color, light->dir, (flags&MDL_HUD)!=0);
+ light->millis = lastmillis;
+ }
+ }
+ if(light) { lightcolor = light->color; lightdir = light->dir; }
+ if(flags&MDL_DYNLIGHT) dynlightreaching(pos, lightcolor, lightdir, (flags&MDL_HUD)!=0);
+ }
+
+ if(a) for(int i = 0; a[i].tag; i++)
+ {
+ if(a[i].name) a[i].m = loadmodel(a[i].name);
+ //if(a[i].m && a[i].m->type()!=m->type()) a[i].m = NULL;
+ }
+
+ if(numbatches>=0)
+ {
+ modelbatch &mb = addbatchedmodel(m);
+ batchedmodel &b = mb.batched.add();
+ b.query = modelquery;
+ b.pos = o;
+ b.color = lightcolor;
+ b.dir = lightdir;
+ b.anim = anim;
+ b.yaw = yaw;
+ b.pitch = pitch;
+ b.basetime = basetime;
+ b.basetime2 = basetime2;
+ b.transparent = trans;
+ b.flags = flags & ~(MDL_CULL_VFC | MDL_CULL_DIST | MDL_CULL_OCCLUDED);
+ if(!shadow || reflecting || refracting>0)
+ {
+ b.flags &= ~(MDL_SHADOW|MDL_DYNSHADOW);
+ if((flags&MDL_CULL_VFC) && refracting<0 && center.z-radius>=reflectz) b.flags |= MDL_CULL_VFC;
+ }
+ mb.flags |= b.flags;
+ b.d = d;
+ b.attached = a ? modelattached.length() : -1;
+ if(a) for(int i = 0;; i++) { modelattached.add(a[i]); if(!a[i].tag) break; }
+ if(flags&MDL_CULL_QUERY) d->query = b.query = newquery(d);
+ return;
+ }
+
+ m->startrender();
+
+ if(shadowmapping)
+ {
+ anim |= ANIM_NOSKIN;
+ GLOBALPARAMF(shadowintensity, trans);
+ }
+ else
+ {
+ if(flags&MDL_FULLBRIGHT) anim |= ANIM_FULLBRIGHT;
+ if(flags&MDL_GHOST) anim |= ANIM_GHOST;
+ }
+
+ if(flags&MDL_CULL_QUERY)
+ {
+ d->query = newquery(d);
+ if(d->query) startquery(d->query);
+ }
+
+ m->render(anim, basetime, basetime2, o, yaw, pitch, d, a, lightcolor, lightdir, trans);
+
+ if(flags&MDL_CULL_QUERY && d->query) endquery(d->query);
+
+ m->endrender();
}
void abovemodel(vec &o, const char *mdl)
{
- model *m = loadmodel(mdl);
- if(!m) return;
- o.z += m->above();
+ model *m = loadmodel(mdl);
+ if(!m) return;
+ o.z += m->above();
}
bool matchanim(const char *name, const char *pattern)
{
- for(;; pattern++)
- {
- const char *s = name;
- char c;
- for(;; pattern++)
- {
- c = *pattern;
- if(!c || c=='|') break;
- else if(c=='*')
- {
- if(!*s || iscubespace(*s)) break;
- do s++; while(*s && !iscubespace(*s));
- }
- else if(c!=*s) break;
- else s++;
- }
- if(!*s && (!c || c=='|')) return true;
- pattern = strchr(pattern, '|');
- if(!pattern) break;
- }
- return false;
+ for(;; pattern++)
+ {
+ const char *s = name;
+ char c;
+ for(;; pattern++)
+ {
+ c = *pattern;
+ if(!c || c=='|') break;
+ else if(c=='*')
+ {
+ if(!*s || iscubespace(*s)) break;
+ do s++; while(*s && !iscubespace(*s));
+ }
+ else if(c!=*s) break;
+ else s++;
+ }
+ if(!*s && (!c || c=='|')) return true;
+ pattern = strchr(pattern, '|');
+ if(!pattern) break;
+ }
+ return false;
}
void findanims(const char *pattern, vector<int> &anims)
{
- loopi(sizeof(animnames)/sizeof(animnames[0])) if(matchanim(animnames[i], pattern)) anims.add(i);
+ loopi(sizeof(animnames)/sizeof(animnames[0])) if(matchanim(animnames[i], pattern)) anims.add(i);
}
ICOMMAND(findanims, "s", (char *name),
{
- vector<int> anims;
- findanims(name, anims);
- vector<char> buf;
- string num;
- loopv(anims)
- {
- formatstring(num, "%d", anims[i]);
- if(i > 0) buf.add(' ');
- buf.put(num, strlen(num));
- }
- buf.add('\0');
- result(buf.getbuf());
+ vector<int> anims;
+ findanims(name, anims);
+ vector<char> buf;
+ string num;
+ loopv(anims)
+ {
+ formatstring(num, "%d", anims[i]);
+ if(i > 0) buf.add(' ');
+ buf.put(num, strlen(num));
+ }
+ buf.add('\0');
+ result(buf.getbuf());
});
void loadskin(const char *dir, const char *altdir, Texture *&skin, Texture *&masks) // model skin sharing
{
#define ifnoload(tex, path) if((tex = textureload(path, 0, true, false))==notexture)
#define tryload(tex, prefix, cmd, name) \
- ifnoload(tex, makerelpath(mdir, name ".jpg", prefix, cmd)) \
- { \
- ifnoload(tex, makerelpath(mdir, name ".png", prefix, cmd)) \
- { \
- ifnoload(tex, makerelpath(maltdir, name ".jpg", prefix, cmd)) \
- { \
- ifnoload(tex, makerelpath(maltdir, name ".png", prefix, cmd)) return; \
- } \
- } \
- }
-
- defformatstring(mdir, "packages/models/%s", dir);
- defformatstring(maltdir, "packages/models/%s", altdir);
- masks = notexture;
- tryload(skin, NULL, NULL, "skin");
- tryload(masks, NULL, NULL, "masks");
+ ifnoload(tex, makerelpath(mdir, name ".jpg", prefix, cmd)) \
+ { \
+ ifnoload(tex, makerelpath(mdir, name ".png", prefix, cmd)) \
+ { \
+ ifnoload(tex, makerelpath(maltdir, name ".jpg", prefix, cmd)) \
+ { \
+ ifnoload(tex, makerelpath(maltdir, name ".png", prefix, cmd)) return; \
+ } \
+ } \
+ }
+
+ defformatstring(mdir, "packages/models/%s", dir);
+ defformatstring(maltdir, "packages/models/%s", altdir);
+ masks = notexture;
+ tryload(skin, NULL, NULL, "skin");
+ tryload(masks, NULL, NULL, "masks");
}
// convenient function that covers the usual anims for players/monsters/npcs
@@ -1033,86 +1033,86 @@ VAR(testpitch, -90, 0, 90);
void renderclient(dynent *d, const char *mdlname, modelattach *attachments, int hold, int attack, int attackdelay, int lastaction, int lastpain, float fade, bool ragdoll)
{
- int anim = hold ? hold : ANIM_IDLE|ANIM_LOOP;
- float yaw = testanims && d==player ? 0 : d->yaw+90,
- pitch = testpitch && d==player ? testpitch : d->pitch;
- vec o = d->feetpos();
- int basetime = 0;
- if(animoverride) anim = (animoverride<0 ? ANIM_ALL : animoverride)|ANIM_LOOP;
- else if(d->state==CS_DEAD)
- {
- anim = ANIM_DYING|ANIM_NOPITCH;
- basetime = lastpain;
- if(ragdoll)
- {
- if(!d->ragdoll || d->ragdoll->millis < basetime)
- {
- DELETEP(d->ragdoll);
- anim |= ANIM_RAGDOLL;
- }
- }
- else if(lastmillis-basetime>1000) anim = ANIM_DEAD|ANIM_LOOP|ANIM_NOPITCH;
- }
- else if(d->state==CS_EDITING || d->state==CS_SPECTATOR) anim = ANIM_EDIT|ANIM_LOOP;
- else if(d->state==CS_LAGGED) anim = ANIM_LAG|ANIM_LOOP;
- else
- {
- if(lastmillis-lastpain < 300)
- {
- anim = ANIM_PAIN;
- basetime = lastpain;
- }
- else if(lastpain < lastaction && (attack < 0 || (d->type != ENT_AI && lastmillis-lastaction < attackdelay)))
- {
- anim = attack < 0 ? -attack : attack;
- basetime = lastaction;
- }
-
- if(d->inwater && d->physstate<=PHYS_FALL) anim |= (((game::allowmove(d) && (d->move || d->strafe)) || d->vel.z+d->falling.z>0 ? ANIM_SWIM : ANIM_SINK)|ANIM_LOOP)<<ANIM_SECONDARY;
- else if(d->timeinair>100) anim |= (ANIM_JUMP|ANIM_END)<<ANIM_SECONDARY;
- else if(game::allowmove(d) && (d->move || d->strafe))
- {
- if(d->move>0) anim |= (ANIM_FORWARD|ANIM_LOOP)<<ANIM_SECONDARY;
- else if(d->strafe)
- {
- if(d->move<0) anim |= ((d->strafe>0 ? ANIM_RIGHT : ANIM_LEFT)|ANIM_REVERSE|ANIM_LOOP)<<ANIM_SECONDARY;
- else anim |= ((d->strafe>0 ? ANIM_LEFT : ANIM_RIGHT)|ANIM_LOOP)<<ANIM_SECONDARY;
- }
- else if(d->move<0) anim |= (ANIM_BACKWARD|ANIM_LOOP)<<ANIM_SECONDARY;
- }
-
- if((anim&ANIM_INDEX)==ANIM_IDLE && (anim>>ANIM_SECONDARY)&ANIM_INDEX) anim >>= ANIM_SECONDARY;
- }
- if(d->ragdoll && (!ragdoll || (anim&ANIM_INDEX)!=ANIM_DYING)) DELETEP(d->ragdoll);
- if(!((anim>>ANIM_SECONDARY)&ANIM_INDEX)) anim |= (ANIM_IDLE|ANIM_LOOP)<<ANIM_SECONDARY;
- int flags = MDL_LIGHT;
- if(d!=player && !(anim&ANIM_RAGDOLL)) flags |= MDL_CULL_VFC | MDL_CULL_OCCLUDED | MDL_CULL_QUERY;
- if(d->type==ENT_PLAYER) flags |= MDL_FULLBRIGHT;
- else flags |= MDL_CULL_DIST;
- if(d->state==CS_LAGGED) fade = min(fade, 0.3f);
- else flags |= MDL_DYNSHADOW;
- if(drawtex == DRAWTEX_MODELPREVIEW) flags &= ~(MDL_LIGHT | MDL_FULLBRIGHT | MDL_CULL_VFC | MDL_CULL_OCCLUDED | MDL_CULL_QUERY | MDL_CULL_DIST | MDL_DYNSHADOW);
- rendermodel(NULL, mdlname, anim, o, yaw, pitch, flags, d, attachments, basetime, 0, fade);
+ int anim = hold ? hold : ANIM_IDLE|ANIM_LOOP;
+ float yaw = testanims && d==player ? 0 : d->yaw+90,
+ pitch = testpitch && d==player ? testpitch : d->pitch;
+ vec o = d->feetpos();
+ int basetime = 0;
+ if(animoverride) anim = (animoverride<0 ? ANIM_ALL : animoverride)|ANIM_LOOP;
+ else if(d->state==CS_DEAD)
+ {
+ anim = ANIM_DYING|ANIM_NOPITCH;
+ basetime = lastpain;
+ if(ragdoll)
+ {
+ if(!d->ragdoll || d->ragdoll->millis < basetime)
+ {
+ DELETEP(d->ragdoll);
+ anim |= ANIM_RAGDOLL;
+ }
+ }
+ else if(lastmillis-basetime>1000) anim = ANIM_DEAD|ANIM_LOOP|ANIM_NOPITCH;
+ }
+ else if(d->state==CS_EDITING || d->state==CS_SPECTATOR) anim = ANIM_EDIT|ANIM_LOOP;
+ else if(d->state==CS_LAGGED) anim = ANIM_LAG|ANIM_LOOP;
+ else
+ {
+ if(lastmillis-lastpain < 300)
+ {
+ anim = ANIM_PAIN;
+ basetime = lastpain;
+ }
+ else if(lastpain < lastaction && (attack < 0 || (d->type != ENT_AI && lastmillis-lastaction < attackdelay)))
+ {
+ anim = attack < 0 ? -attack : attack;
+ basetime = lastaction;
+ }
+
+ if(d->inwater && d->physstate<=PHYS_FALL) anim |= (((game::allowmove(d) && (d->move || d->strafe)) || d->vel.z+d->falling.z>0 ? ANIM_SWIM : ANIM_SINK)|ANIM_LOOP)<<ANIM_SECONDARY;
+ else if(d->timeinair>100) anim |= (ANIM_JUMP|ANIM_END)<<ANIM_SECONDARY;
+ else if(game::allowmove(d) && (d->move || d->strafe))
+ {
+ if(d->move>0) anim |= (ANIM_FORWARD|ANIM_LOOP)<<ANIM_SECONDARY;
+ else if(d->strafe)
+ {
+ if(d->move<0) anim |= ((d->strafe>0 ? ANIM_RIGHT : ANIM_LEFT)|ANIM_REVERSE|ANIM_LOOP)<<ANIM_SECONDARY;
+ else anim |= ((d->strafe>0 ? ANIM_LEFT : ANIM_RIGHT)|ANIM_LOOP)<<ANIM_SECONDARY;
+ }
+ else if(d->move<0) anim |= (ANIM_BACKWARD|ANIM_LOOP)<<ANIM_SECONDARY;
+ }
+
+ if((anim&ANIM_INDEX)==ANIM_IDLE && (anim>>ANIM_SECONDARY)&ANIM_INDEX) anim >>= ANIM_SECONDARY;
+ }
+ if(d->ragdoll && (!ragdoll || (anim&ANIM_INDEX)!=ANIM_DYING)) DELETEP(d->ragdoll);
+ if(!((anim>>ANIM_SECONDARY)&ANIM_INDEX)) anim |= (ANIM_IDLE|ANIM_LOOP)<<ANIM_SECONDARY;
+ int flags = MDL_LIGHT;
+ if(d!=player && !(anim&ANIM_RAGDOLL)) flags |= MDL_CULL_VFC | MDL_CULL_OCCLUDED | MDL_CULL_QUERY;
+ if(d->type==ENT_PLAYER) flags |= MDL_FULLBRIGHT;
+ else flags |= MDL_CULL_DIST;
+ if(d->state==CS_LAGGED) fade = min(fade, 0.3f);
+ else flags |= MDL_DYNSHADOW;
+ if(drawtex == DRAWTEX_MODELPREVIEW) flags &= ~(MDL_LIGHT | MDL_FULLBRIGHT | MDL_CULL_VFC | MDL_CULL_OCCLUDED | MDL_CULL_QUERY | MDL_CULL_DIST | MDL_DYNSHADOW);
+ rendermodel(NULL, mdlname, anim, o, yaw, pitch, flags, d, attachments, basetime, 0, fade);
}
void setbbfrommodel(dynent *d, const char *mdl)
{
- model *m = loadmodel(mdl);
- if(!m) return;
- vec center, radius;
- m->collisionbox(center, radius);
- if(d->type==ENT_INANIMATE && !m->ellipsecollide)
- d->collidetype = COLLIDE_OBB;
- d->xradius = radius.x + fabs(center.x);
- d->yradius = radius.y + fabs(center.y);
- d->radius = d->collidetype==COLLIDE_OBB ? sqrtf(d->xradius*d->xradius + d->yradius*d->yradius) : max(d->xradius, d->yradius);
- d->eyeheight = (center.z-radius.z) + radius.z*2*m->eyeheight;
- d->aboveeye = radius.z*2*(1.0f-m->eyeheight);
- if (d->aboveeye + d->eyeheight <= 0.5f)
- {
- float zrad = (0.5f - (d->aboveeye + d->eyeheight)) / 2;
- d->aboveeye += zrad;
- d->eyeheight += zrad;
- }
+ model *m = loadmodel(mdl);
+ if(!m) return;
+ vec center, radius;
+ m->collisionbox(center, radius);
+ if(d->type==ENT_INANIMATE && !m->ellipsecollide)
+ d->collidetype = COLLIDE_OBB;
+ d->xradius = radius.x + fabs(center.x);
+ d->yradius = radius.y + fabs(center.y);
+ d->radius = d->collidetype==COLLIDE_OBB ? sqrtf(d->xradius*d->xradius + d->yradius*d->yradius) : max(d->xradius, d->yradius);
+ d->eyeheight = (center.z-radius.z) + radius.z*2*m->eyeheight;
+ d->aboveeye = radius.z*2*(1.0f-m->eyeheight);
+ if (d->aboveeye + d->eyeheight <= 0.5f)
+ {
+ float zrad = (0.5f - (d->aboveeye + d->eyeheight)) / 2;
+ d->aboveeye += zrad;
+ d->eyeheight += zrad;
+ }
}
diff --git a/src/engine/renderparticles.cpp b/src/engine/renderparticles.cpp
index c6994bf..accc204 100644
--- a/src/engine/renderparticles.cpp
+++ b/src/engine/renderparticles.cpp
@@ -15,54 +15,51 @@ static bool canemit = false, regenemitters = false, canstep = false;
static bool canemitparticles()
{
- if(reflecting || refracting) return false;
- return canemit || emitoffset;
+ if(reflecting || refracting) return false;
+ return canemit || emitoffset;
}
VARP(showparticles, 0, 1, 1);
VAR(cullparticles, 0, 1, 1);
VAR(replayparticles, 0, 1, 1);
VARN(seedparticles, seedmillis, 0, 3000, 10000);
-VAR(dbgpcull, 0, 0, 1);
-VAR(dbgpseed, 0, 0, 1);
struct particleemitter
{
- extentity *ent;
- vec bbmin, bbmax;
- vec center;
- float radius;
- ivec cullmin, cullmax;
- int maxfade, lastemit, lastcull;
-
- particleemitter(extentity *ent)
- : ent(ent), bbmin(ent->o), bbmax(ent->o), maxfade(-1), lastemit(0), lastcull(0)
- {}
-
- void finalize()
- {
- center = vec(bbmin).add(bbmax).mul(0.5f);
- radius = bbmin.dist(bbmax)/2;
- cullmin = ivec(int(floor(bbmin.x)), int(floor(bbmin.y)), int(floor(bbmin.z)));
- cullmax = ivec(int(ceil(bbmax.x)), int(ceil(bbmax.y)), int(ceil(bbmax.z)));
- if(dbgpseed) conoutf(CON_DEBUG, "radius: %f, maxfade: %d", radius, maxfade);
- }
-
- void extendbb(const vec &o, float size = 0)
- {
- bbmin.x = min(bbmin.x, o.x - size);
- bbmin.y = min(bbmin.y, o.y - size);
- bbmin.z = min(bbmin.z, o.z - size);
- bbmax.x = max(bbmax.x, o.x + size);
- bbmax.y = max(bbmax.y, o.y + size);
- bbmax.z = max(bbmax.z, o.z + size);
- }
-
- void extendbb(float z, float size = 0)
- {
- bbmin.z = min(bbmin.z, z - size);
- bbmax.z = max(bbmax.z, z + size);
- }
+ extentity *ent;
+ vec bbmin, bbmax;
+ vec center;
+ float radius;
+ ivec cullmin, cullmax;
+ int maxfade, lastemit, lastcull;
+
+ particleemitter(extentity *ent)
+ : ent(ent), bbmin(ent->o), bbmax(ent->o), maxfade(-1), lastemit(0), lastcull(0)
+ {}
+
+ void finalize()
+ {
+ center = vec(bbmin).add(bbmax).mul(0.5f);
+ radius = bbmin.dist(bbmax)/2;
+ cullmin = ivec(int(floor(bbmin.x)), int(floor(bbmin.y)), int(floor(bbmin.z)));
+ cullmax = ivec(int(ceil(bbmax.x)), int(ceil(bbmax.y)), int(ceil(bbmax.z)));
+ }
+
+ void extendbb(const vec &o, float size = 0)
+ {
+ bbmin.x = min(bbmin.x, o.x - size);
+ bbmin.y = min(bbmin.y, o.y - size);
+ bbmin.z = min(bbmin.z, o.z - size);
+ bbmax.x = max(bbmax.x, o.x + size);
+ bbmax.y = max(bbmax.y, o.y + size);
+ bbmax.z = max(bbmax.z, o.z + size);
+ }
+
+ void extendbb(float z, float size = 0)
+ {
+ bbmin.z = min(bbmin.z, z - size);
+ bbmax.z = max(bbmax.z, z + size);
+ }
};
static vector<particleemitter> emitters;
@@ -70,80 +67,80 @@ static particleemitter *seedemitter = NULL;
void clearparticleemitters()
{
- emitters.setsize(0);
- regenemitters = true;
+ emitters.setsize(0);
+ regenemitters = true;
}
void addparticleemitters()
{
- emitters.setsize(0);
- const vector<extentity *> &ents = entities::getents();
- loopv(ents)
- {
- extentity &e = *ents[i];
- if(e.type != ET_PARTICLES) continue;
- emitters.add(particleemitter(&e));
- }
- regenemitters = false;
+ emitters.setsize(0);
+ const vector<extentity *> &ents = entities::getents();
+ loopv(ents)
+ {
+ extentity &e = *ents[i];
+ if(e.type != ET_PARTICLES) continue;
+ emitters.add(particleemitter(&e));
+ }
+ regenemitters = false;
}
enum
{
- PT_PART = 0,
- PT_TAPE,
- PT_TRAIL,
- PT_TEXT,
- PT_TEXTICON,
- PT_METER,
- PT_METERVS,
- PT_FIREBALL,
- PT_LIGHTNING,
- PT_FLARE,
-
- PT_MOD = 1<<8,
- PT_RND4 = 1<<9,
- PT_LERP = 1<<10, // use very sparingly - order of blending issues
- PT_TRACK = 1<<11,
- PT_GLARE = 1<<12,
- PT_SOFT = 1<<13,
- PT_HFLIP = 1<<14,
- PT_VFLIP = 1<<15,
- PT_ROT = 1<<16,
- PT_CULL = 1<<17,
- PT_FEW = 1<<18,
- PT_ICON = 1<<19,
- PT_NOTEX = 1<<20,
- PT_SHADER = 1<<21,
- PT_FLIP = PT_HFLIP | PT_VFLIP | PT_ROT
+ PT_PART = 0,
+ PT_TAPE,
+ PT_TRAIL,
+ PT_TEXT,
+ PT_TEXTICON,
+ PT_METER,
+ PT_METERVS,
+ PT_FIREBALL,
+ PT_LIGHTNING,
+ PT_FLARE,
+
+ PT_MOD = 1<<8,
+ PT_RND4 = 1<<9,
+ PT_LERP = 1<<10, // use very sparingly - order of blending issues
+ PT_TRACK = 1<<11,
+ PT_GLARE = 1<<12,
+ PT_SOFT = 1<<13,
+ PT_HFLIP = 1<<14,
+ PT_VFLIP = 1<<15,
+ PT_ROT = 1<<16,
+ PT_CULL = 1<<17,
+ PT_FEW = 1<<18,
+ PT_ICON = 1<<19,
+ PT_NOTEX = 1<<20,
+ PT_SHADER = 1<<21,
+ PT_FLIP = PT_HFLIP | PT_VFLIP | PT_ROT
};
const char *partnames[] = { "part", "tape", "trail", "text", "texticon", "meter", "metervs", "fireball", "lightning", "flare" };
struct particle
{
- vec o, d;
- int gravity, fade, millis;
- bvec color;
- uchar flags;
- float size;
- union
- {
- const char *text;
- float val;
- physent *owner;
- struct
- {
- uchar color2[3];
- uchar progress;
- };
- };
+ vec o, d;
+ int gravity, fade, millis;
+ bvec color;
+ uchar flags;
+ float size;
+ union
+ {
+ const char *text;
+ float val;
+ physent *owner;
+ struct
+ {
+ uchar color2[3];
+ uchar progress;
+ };
+ };
};
struct partvert
{
- vec pos;
- bvec4 color;
- vec2 tc;
+ vec pos;
+ bvec4 color;
+ vec2 tc;
};
#define COLLIDERADIUS 8.0f
@@ -151,381 +148,381 @@ struct partvert
struct partrenderer
{
- Texture *tex;
- const char *texname;
- int texclamp;
- uint type;
- int collide;
- string info;
-
- partrenderer(const char *texname, int texclamp, int type, int collide = 0)
- : tex(NULL), texname(texname), texclamp(texclamp), type(type), collide(collide)
- {
- }
- partrenderer(int type, int collide = 0)
- : tex(NULL), texname(NULL), texclamp(0), type(type), collide(collide)
- {
- }
- virtual ~partrenderer()
- {
- }
-
- virtual void init(int n) { }
- virtual void reset() = 0;
- virtual void resettracked(physent *owner) { }
- virtual particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity = 0) = 0;
- virtual int adddepthfx(vec &bbmin, vec &bbmax) { return 0; }
- virtual void update() { }
- virtual void render() = 0;
- virtual bool haswork() = 0;
- virtual void cleanup() {}
-
- virtual void seedemitter(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int gravity)
- {
- }
-
- //blend = 0 => remove it
- void calc(particle *p, int &blend, int &ts, vec &o, vec &d, bool step = true)
- {
- o = p->o;
- d = p->d;
- if(type&PT_TRACK && p->owner) game::particletrack(p->owner, o, d);
- if(p->fade <= 5)
- {
- ts = 1;
- blend = 255;
- }
- else
- {
- ts = lastmillis-p->millis;
- blend = max(255 - (ts<<8)/p->fade, 0);
- if(p->gravity)
- {
- if(ts > p->fade) ts = p->fade;
- float t = ts;
- o.add(vec(d).mul(t/5000.0f));
- o.z -= t*t/(2.0f * 5000.0f * p->gravity);
- }
- if(collide && o.z < p->val && step)
- {
- if(collide >= 0)
- {
- vec surface;
- float floorz = rayfloor(vec(o.x, o.y, p->val), surface, RAY_CLIPMAT, COLLIDERADIUS);
- float collidez = floorz<0 ? o.z-COLLIDERADIUS : p->val - floorz;
- if(o.z >= collidez+COLLIDEERROR)
- p->val = collidez+COLLIDEERROR;
- else
- {
- adddecal(collide, vec(o.x, o.y, collidez), vec(p->o).sub(o).normalize(), 2*p->size, p->color, type&PT_RND4 ? (p->flags>>5)&3 : 0);
- blend = 0;
- }
- }
- else blend = 0;
- }
- }
- }
+ Texture *tex;
+ const char *texname;
+ int texclamp;
+ uint type;
+ int collide;
+ string info;
+
+ partrenderer(const char *texname, int texclamp, int type, int collide = 0)
+ : tex(NULL), texname(texname), texclamp(texclamp), type(type), collide(collide)
+ {
+ }
+ partrenderer(int type, int collide = 0)
+ : tex(NULL), texname(NULL), texclamp(0), type(type), collide(collide)
+ {
+ }
+ virtual ~partrenderer()
+ {
+ }
+
+ virtual void init(int n) { }
+ virtual void reset() = 0;
+ virtual void resettracked(physent *owner) { }
+ virtual particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity = 0) = 0;
+ virtual int adddepthfx(vec &bbmin, vec &bbmax) { return 0; }
+ virtual void update() { }
+ virtual void render() = 0;
+ virtual bool haswork() = 0;
+ virtual void cleanup() {}
+
+ virtual void seedemitter(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int gravity)
+ {
+ }
+
+ //blend = 0 => remove it
+ void calc(particle *p, int &blend, int &ts, vec &o, vec &d, bool step = true)
+ {
+ o = p->o;
+ d = p->d;
+ if(type&PT_TRACK && p->owner) game::particletrack(p->owner, o, d);
+ if(p->fade <= 5)
+ {
+ ts = 1;
+ blend = 255;
+ }
+ else
+ {
+ ts = lastmillis-p->millis;
+ blend = max(255 - (ts<<8)/p->fade, 0);
+ if(p->gravity)
+ {
+ if(ts > p->fade) ts = p->fade;
+ float t = ts;
+ o.add(vec(d).mul(t/5000.0f));
+ o.z -= t*t/(2.0f * 5000.0f * p->gravity);
+ }
+ if(collide && o.z < p->val && step)
+ {
+ if(collide >= 0)
+ {
+ vec surface;
+ float floorz = rayfloor(vec(o.x, o.y, p->val), surface, RAY_CLIPMAT, COLLIDERADIUS);
+ float collidez = floorz<0 ? o.z-COLLIDERADIUS : p->val - floorz;
+ if(o.z >= collidez+COLLIDEERROR)
+ p->val = collidez+COLLIDEERROR;
+ else
+ {
+ adddecal(collide, vec(o.x, o.y, collidez), vec(p->o).sub(o).normalize(), 2*p->size, p->color, type&PT_RND4 ? (p->flags>>5)&3 : 0);
+ blend = 0;
+ }
+ }
+ else blend = 0;
+ }
+ }
+ }
};
struct listparticle : particle
{
- listparticle *next;
+ listparticle *next;
};
VARP(outlinemeters, 0, 0, 1);
struct listrenderer : partrenderer
{
- static listparticle *parempty;
- listparticle *list;
-
- listrenderer(const char *texname, int texclamp, int type, int collide = 0)
- : partrenderer(texname, texclamp, type, collide), list(NULL)
- {
- }
- listrenderer(int type, int collide = 0)
- : partrenderer(type, collide), list(NULL)
- {
- }
-
- virtual ~listrenderer()
- {
- }
-
- virtual void killpart(listparticle *p)
- {
- }
-
- void reset()
- {
- if(!list) return;
- listparticle *p = list;
- for(;;)
- {
- killpart(p);
- if(p->next) p = p->next;
- else break;
- }
- p->next = parempty;
- parempty = list;
- list = NULL;
- }
-
- void resettracked(physent *owner)
- {
- if(!(type&PT_TRACK)) return;
- for(listparticle **prev = &list, *cur = list; cur; cur = *prev)
- {
- if(!owner || cur->owner==owner)
- {
- *prev = cur->next;
- cur->next = parempty;
- parempty = cur;
- }
- else prev = &cur->next;
- }
- }
-
- particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity)
- {
- if(!parempty)
- {
- listparticle *ps = new listparticle[256];
- loopi(255) ps[i].next = &ps[i+1];
- ps[255].next = parempty;
- parempty = ps;
- }
- listparticle *p = parempty;
- parempty = p->next;
- p->next = list;
- list = p;
- p->o = o;
- p->d = d;
- p->gravity = gravity;
- p->fade = fade;
- p->millis = lastmillis + emitoffset;
- p->color = bvec(color>>16, (color>>8)&0xFF, color&0xFF);
- p->size = size;
- p->owner = NULL;
- p->flags = 0;
- return p;
- }
-
- int count()
- {
- int num = 0;
- listparticle *lp;
- for(lp = list; lp; lp = lp->next) num++;
- return num;
- }
-
- bool haswork()
- {
- return (list != NULL);
- }
-
- virtual void startrender() = 0;
- virtual void endrender() = 0;
- virtual void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts) = 0;
-
- void render()
- {
- startrender();
- if(texname)
- {
- if(!tex) tex = textureload(texname, texclamp);
- glBindTexture(GL_TEXTURE_2D, tex->id);
- }
-
- for(listparticle **prev = &list, *p = list; p; p = *prev)
- {
- vec o, d;
- int blend, ts;
- calc(p, blend, ts, o, d, canstep);
- if(blend > 0)
- {
- renderpart(p, o, d, blend, ts);
-
- if(p->fade > 5 || !canstep)
- {
- prev = &p->next;
- continue;
- }
- }
- //remove
- *prev = p->next;
- p->next = parempty;
- killpart(p);
- parempty = p;
- }
-
- endrender();
- }
+ static listparticle *parempty;
+ listparticle *list;
+
+ listrenderer(const char *texname, int texclamp, int type, int collide = 0)
+ : partrenderer(texname, texclamp, type, collide), list(NULL)
+ {
+ }
+ listrenderer(int type, int collide = 0)
+ : partrenderer(type, collide), list(NULL)
+ {
+ }
+
+ virtual ~listrenderer()
+ {
+ }
+
+ virtual void killpart(listparticle *p)
+ {
+ }
+
+ void reset()
+ {
+ if(!list) return;
+ listparticle *p = list;
+ for(;;)
+ {
+ killpart(p);
+ if(p->next) p = p->next;
+ else break;
+ }
+ p->next = parempty;
+ parempty = list;
+ list = NULL;
+ }
+
+ void resettracked(physent *owner)
+ {
+ if(!(type&PT_TRACK)) return;
+ for(listparticle **prev = &list, *cur = list; cur; cur = *prev)
+ {
+ if(!owner || cur->owner==owner)
+ {
+ *prev = cur->next;
+ cur->next = parempty;
+ parempty = cur;
+ }
+ else prev = &cur->next;
+ }
+ }
+
+ particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity)
+ {
+ if(!parempty)
+ {
+ listparticle *ps = new listparticle[256];
+ loopi(255) ps[i].next = &ps[i+1];
+ ps[255].next = parempty;
+ parempty = ps;
+ }
+ listparticle *p = parempty;
+ parempty = p->next;
+ p->next = list;
+ list = p;
+ p->o = o;
+ p->d = d;
+ p->gravity = gravity;
+ p->fade = fade;
+ p->millis = lastmillis + emitoffset;
+ p->color = bvec(color>>16, (color>>8)&0xFF, color&0xFF);
+ p->size = size;
+ p->owner = NULL;
+ p->flags = 0;
+ return p;
+ }
+
+ int count()
+ {
+ int num = 0;
+ listparticle *lp;
+ for(lp = list; lp; lp = lp->next) num++;
+ return num;
+ }
+
+ bool haswork()
+ {
+ return (list != NULL);
+ }
+
+ virtual void startrender() = 0;
+ virtual void endrender() = 0;
+ virtual void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts) = 0;
+
+ void render()
+ {
+ startrender();
+ if(texname)
+ {
+ if(!tex) tex = textureload(texname, texclamp);
+ glBindTexture(GL_TEXTURE_2D, tex->id);
+ }
+
+ for(listparticle **prev = &list, *p = list; p; p = *prev)
+ {
+ vec o, d;
+ int blend, ts;
+ calc(p, blend, ts, o, d, canstep);
+ if(blend > 0)
+ {
+ renderpart(p, o, d, blend, ts);
+
+ if(p->fade > 5 || !canstep)
+ {
+ prev = &p->next;
+ continue;
+ }
+ }
+ //remove
+ *prev = p->next;
+ p->next = parempty;
+ killpart(p);
+ parempty = p;
+ }
+
+ endrender();
+ }
};
listparticle *listrenderer::parempty = NULL;
struct meterrenderer : listrenderer
{
- meterrenderer(int type)
- : listrenderer(type|PT_NOTEX|PT_LERP)
- {}
-
- void startrender()
- {
- glDisable(GL_BLEND);
- gle::defvertex();
- }
-
- void endrender()
- {
- glEnable(GL_BLEND);
- }
-
- void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts)
- {
- int basetype = type&0xFF;
- float scale = FONTH*p->size/80.0f, right = 8, left = p->progress/100.0f*right;
- matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o);
- m.scale(scale);
- m.translate(-right/2.0f, 0, 0);
-
- if(outlinemeters)
- {
- gle::colorf(0, 0.8f, 0);
- gle::begin(GL_TRIANGLE_STRIP);
- loopk(10)
- {
- const vec2 &sc = sincos360[k*(180/(10-1))];
- float c = (0.5f + 0.1f)*sc.y, s = 0.5f - (0.5f + 0.1f)*sc.x;
- gle::attrib(m.transform(vec2(-c, s)));
- gle::attrib(m.transform(vec2(right + c, s)));
- }
- gle::end();
- }
-
- if(basetype==PT_METERVS) gle::colorub(p->color2[0], p->color2[1], p->color2[2]);
- else gle::colorf(0, 0, 0);
- gle::begin(GL_TRIANGLE_STRIP);
- loopk(10)
- {
- const vec2 &sc = sincos360[k*(180/(10-1))];
- float c = 0.5f*sc.y, s = 0.5f - 0.5f*sc.x;
- gle::attrib(m.transform(vec2(left + c, s)));
- gle::attrib(m.transform(vec2(right + c, s)));
- }
- gle::end();
-
- if(outlinemeters)
- {
- gle::colorf(0, 0.8f, 0);
- gle::begin(GL_TRIANGLE_FAN);
- loopk(10)
- {
- const vec2 &sc = sincos360[k*(180/(10-1))];
- float c = (0.5f + 0.1f)*sc.y, s = 0.5f - (0.5f + 0.1f)*sc.x;
- gle::attrib(m.transform(vec2(left + c, s)));
- }
- gle::end();
- }
-
- gle::color(p->color);
- gle::begin(GL_TRIANGLE_STRIP);
- loopk(10)
- {
- const vec2 &sc = sincos360[k*(180/(10-1))];
- float c = 0.5f*sc.y, s = 0.5f - 0.5f*sc.x;
- gle::attrib(m.transform(vec2(-c, s)));
- gle::attrib(m.transform(vec2(left + c, s)));
- }
- gle::end();
- }
+ meterrenderer(int type)
+ : listrenderer(type|PT_NOTEX|PT_LERP)
+ {}
+
+ void startrender()
+ {
+ glDisable(GL_BLEND);
+ gle::defvertex();
+ }
+
+ void endrender()
+ {
+ glEnable(GL_BLEND);
+ }
+
+ void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts)
+ {
+ int basetype = type&0xFF;
+ float scale = FONTH*p->size/80.0f, right = 8, left = p->progress/100.0f*right;
+ matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o);
+ m.scale(scale);
+ m.translate(-right/2.0f, 0, 0);
+
+ if(outlinemeters)
+ {
+ gle::colorf(0, 0.8f, 0);
+ gle::begin(GL_TRIANGLE_STRIP);
+ loopk(10)
+ {
+ const vec2 &sc = sincos360[k*(180/(10-1))];
+ float c = (0.5f + 0.1f)*sc.y, s = 0.5f - (0.5f + 0.1f)*sc.x;
+ gle::attrib(m.transform(vec2(-c, s)));
+ gle::attrib(m.transform(vec2(right + c, s)));
+ }
+ gle::end();
+ }
+
+ if(basetype==PT_METERVS) gle::colorub(p->color2[0], p->color2[1], p->color2[2]);
+ else gle::colorf(0, 0, 0);
+ gle::begin(GL_TRIANGLE_STRIP);
+ loopk(10)
+ {
+ const vec2 &sc = sincos360[k*(180/(10-1))];
+ float c = 0.5f*sc.y, s = 0.5f - 0.5f*sc.x;
+ gle::attrib(m.transform(vec2(left + c, s)));
+ gle::attrib(m.transform(vec2(right + c, s)));
+ }
+ gle::end();
+
+ if(outlinemeters)
+ {
+ gle::colorf(0, 0.8f, 0);
+ gle::begin(GL_TRIANGLE_FAN);
+ loopk(10)
+ {
+ const vec2 &sc = sincos360[k*(180/(10-1))];
+ float c = (0.5f + 0.1f)*sc.y, s = 0.5f - (0.5f + 0.1f)*sc.x;
+ gle::attrib(m.transform(vec2(left + c, s)));
+ }
+ gle::end();
+ }
+
+ gle::color(p->color);
+ gle::begin(GL_TRIANGLE_STRIP);
+ loopk(10)
+ {
+ const vec2 &sc = sincos360[k*(180/(10-1))];
+ float c = 0.5f*sc.y, s = 0.5f - 0.5f*sc.x;
+ gle::attrib(m.transform(vec2(-c, s)));
+ gle::attrib(m.transform(vec2(left + c, s)));
+ }
+ gle::end();
+ }
};
static meterrenderer meters(PT_METER), metervs(PT_METERVS);
struct textrenderer : listrenderer
{
- textrenderer(int type)
- : listrenderer(type)
- {}
-
- void startrender()
- {
- }
-
- void endrender()
- {
- }
-
- void killpart(listparticle *p)
- {
- if(p->text && p->flags&1) delete[] p->text;
- }
-
- void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts)
- {
- float scale = p->size/80.0f, xoff = -(text_width(p->text) + ((p->flags>>1)&7)*FONTH)/2, yoff = 0;
-
- matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o);
- m.scale(scale);
- m.translate(xoff, yoff, 50);
-
- textmatrix = &m;
- draw_text(p->text, 0, 0, p->color.r, p->color.g, p->color.b, blend);
- textmatrix = NULL;
- }
+ textrenderer(int type)
+ : listrenderer(type)
+ {}
+
+ void startrender()
+ {
+ }
+
+ void endrender()
+ {
+ }
+
+ void killpart(listparticle *p)
+ {
+ if(p->text && p->flags&1) delete[] p->text;
+ }
+
+ void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts)
+ {
+ float scale = p->size/80.0f, xoff = -(text_width(p->text) + ((p->flags>>1)&7)*FONTH)/2, yoff = 0;
+
+ matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o);
+ m.scale(scale);
+ m.translate(xoff, yoff, 50);
+
+ textmatrix = &m;
+ draw_text(p->text, 0, 0, p->color.r, p->color.g, p->color.b, blend);
+ textmatrix = NULL;
+ }
};
static textrenderer texts(PT_TEXT|PT_LERP);
struct texticonrenderer : listrenderer
{
- texticonrenderer(const char *texname, int type)
- : listrenderer(texname, 3, type)
- {}
-
- void startrender()
- {
- gle::defvertex();
- gle::deftexcoord0();
- gle::defcolor(4, GL_UNSIGNED_BYTE);
- gle::begin(GL_QUADS);
- }
-
- void endrender()
- {
- gle::end();
- }
-
- void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts)
- {
- float scale = p->size/80.0f, xoff = p->val, yoff = 0;
-
- matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o);
- m.scale(scale);
- m.translate(xoff, yoff, 50);
-
- float tx = 0.25f*(p->flags&3), ty = 0.25f*((p->flags>>2)&3);
-
- gle::attrib(m.transform(vec2(0, 0)));
- gle::attrib(tx, ty);
- gle::attrib(p->color, blend);
- gle::attrib(m.transform(vec2(FONTH, 0)));
- gle::attrib(tx + 0.25f, ty);
- gle::attrib(p->color, blend);
- gle::attrib(m.transform(vec2(FONTH, FONTH)));
- gle::attrib(tx + 0.25f, ty + 0.25f);
- gle::attrib(p->color, blend);
- gle::attrib(m.transform(vec2(0, FONTH)));
- gle::attrib(tx, ty + 0.25f);
- gle::attrib(p->color, blend);
- }
+ texticonrenderer(const char *texname, int type)
+ : listrenderer(texname, 3, type)
+ {}
+
+ void startrender()
+ {
+ gle::defvertex();
+ gle::deftexcoord0();
+ gle::defcolor(4, GL_UNSIGNED_BYTE);
+ gle::begin(GL_QUADS);
+ }
+
+ void endrender()
+ {
+ gle::end();
+ }
+
+ void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts)
+ {
+ float scale = p->size/80.0f, xoff = p->val, yoff = 0;
+
+ matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o);
+ m.scale(scale);
+ m.translate(xoff, yoff, 50);
+
+ float tx = 0.25f*(p->flags&3), ty = 0.25f*((p->flags>>2)&3);
+
+ gle::attrib(m.transform(vec2(0, 0)));
+ gle::attrib(tx, ty);
+ gle::attrib(p->color, blend);
+ gle::attrib(m.transform(vec2(FONTH, 0)));
+ gle::attrib(tx + 0.25f, ty);
+ gle::attrib(p->color, blend);
+ gle::attrib(m.transform(vec2(FONTH, FONTH)));
+ gle::attrib(tx + 0.25f, ty + 0.25f);
+ gle::attrib(p->color, blend);
+ gle::attrib(m.transform(vec2(0, FONTH)));
+ gle::attrib(tx, ty + 0.25f);
+ gle::attrib(p->color, blend);
+ }
};
static texticonrenderer texticons("packages/hud/items.png", PT_TEXTICON|PT_LERP);
template<int T>
static inline void modifyblend(const vec &o, int &blend)
{
- blend = min(blend<<2, 255);
+ blend = min(blend<<2, 255);
}
template<>
@@ -536,306 +533,306 @@ inline void modifyblend<PT_TAPE>(const vec &o, int &blend)
template<int T>
static inline void genpos(const vec &o, const vec &d, float size, int grav, int ts, partvert *vs)
{
- vec udir = vec(camup).sub(camright).mul(size);
- vec vdir = vec(camup).add(camright).mul(size);
- vs[0].pos = vec(o.x + udir.x, o.y + udir.y, o.z + udir.z);
- vs[1].pos = vec(o.x + vdir.x, o.y + vdir.y, o.z + vdir.z);
- vs[2].pos = vec(o.x - udir.x, o.y - udir.y, o.z - udir.z);
- vs[3].pos = vec(o.x - vdir.x, o.y - vdir.y, o.z - vdir.z);
+ vec udir = vec(camup).sub(camright).mul(size);
+ vec vdir = vec(camup).add(camright).mul(size);
+ vs[0].pos = vec(o.x + udir.x, o.y + udir.y, o.z + udir.z);
+ vs[1].pos = vec(o.x + vdir.x, o.y + vdir.y, o.z + vdir.z);
+ vs[2].pos = vec(o.x - udir.x, o.y - udir.y, o.z - udir.z);
+ vs[3].pos = vec(o.x - vdir.x, o.y - vdir.y, o.z - vdir.z);
}
template<>
inline void genpos<PT_TAPE>(const vec &o, const vec &d, float size, int ts, int grav, partvert *vs)
{
- vec dir1 = d, dir2 = d, c;
- dir1.sub(o);
- dir2.sub(camera1->o);
- c.cross(dir2, dir1).normalize().mul(size);
- vs[0].pos = vec(d.x-c.x, d.y-c.y, d.z-c.z);
- vs[1].pos = vec(o.x-c.x, o.y-c.y, o.z-c.z);
- vs[2].pos = vec(o.x+c.x, o.y+c.y, o.z+c.z);
- vs[3].pos = vec(d.x+c.x, d.y+c.y, d.z+c.z);
+ vec dir1 = d, dir2 = d, c;
+ dir1.sub(o);
+ dir2.sub(camera1->o);
+ c.cross(dir2, dir1).normalize().mul(size);
+ vs[0].pos = vec(d.x-c.x, d.y-c.y, d.z-c.z);
+ vs[1].pos = vec(o.x-c.x, o.y-c.y, o.z-c.z);
+ vs[2].pos = vec(o.x+c.x, o.y+c.y, o.z+c.z);
+ vs[3].pos = vec(d.x+c.x, d.y+c.y, d.z+c.z);
}
template<>
inline void genpos<PT_TRAIL>(const vec &o, const vec &d, float size, int ts, int grav, partvert *vs)
{
- vec e = d;
- if(grav) e.z -= float(ts)/grav;
- e.div(-75.0f).add(o);
- genpos<PT_TAPE>(o, e, size, ts, grav, vs);
+ vec e = d;
+ if(grav) e.z -= float(ts)/grav;
+ e.div(-75.0f).add(o);
+ genpos<PT_TAPE>(o, e, size, ts, grav, vs);
}
template<int T>
static inline void genrotpos(const vec &o, const vec &d, float size, int grav, int ts, partvert *vs, int rot)
{
- genpos<T>(o, d, size, grav, ts, vs);
+ genpos<T>(o, d, size, grav, ts, vs);
}
#define ROTCOEFFS(n) { \
- vec(-1, 1, 0).rotate_around_z(n*2*M_PI/32.0f), \
- vec( 1, 1, 0).rotate_around_z(n*2*M_PI/32.0f), \
- vec( 1, -1, 0).rotate_around_z(n*2*M_PI/32.0f), \
- vec(-1, -1, 0).rotate_around_z(n*2*M_PI/32.0f) \
+ vec(-1, 1, 0).rotate_around_z(n*2*M_PI/32.0f), \
+ vec( 1, 1, 0).rotate_around_z(n*2*M_PI/32.0f), \
+ vec( 1, -1, 0).rotate_around_z(n*2*M_PI/32.0f), \
+ vec(-1, -1, 0).rotate_around_z(n*2*M_PI/32.0f) \
}
static const vec rotcoeffs[32][4] =
{
- ROTCOEFFS(0), ROTCOEFFS(1), ROTCOEFFS(2), ROTCOEFFS(3), ROTCOEFFS(4), ROTCOEFFS(5), ROTCOEFFS(6), ROTCOEFFS(7),
- ROTCOEFFS(8), ROTCOEFFS(9), ROTCOEFFS(10), ROTCOEFFS(11), ROTCOEFFS(12), ROTCOEFFS(13), ROTCOEFFS(14), ROTCOEFFS(15),
- ROTCOEFFS(16), ROTCOEFFS(17), ROTCOEFFS(18), ROTCOEFFS(19), ROTCOEFFS(20), ROTCOEFFS(21), ROTCOEFFS(22), ROTCOEFFS(7),
- ROTCOEFFS(24), ROTCOEFFS(25), ROTCOEFFS(26), ROTCOEFFS(27), ROTCOEFFS(28), ROTCOEFFS(29), ROTCOEFFS(30), ROTCOEFFS(31),
+ ROTCOEFFS(0), ROTCOEFFS(1), ROTCOEFFS(2), ROTCOEFFS(3), ROTCOEFFS(4), ROTCOEFFS(5), ROTCOEFFS(6), ROTCOEFFS(7),
+ ROTCOEFFS(8), ROTCOEFFS(9), ROTCOEFFS(10), ROTCOEFFS(11), ROTCOEFFS(12), ROTCOEFFS(13), ROTCOEFFS(14), ROTCOEFFS(15),
+ ROTCOEFFS(16), ROTCOEFFS(17), ROTCOEFFS(18), ROTCOEFFS(19), ROTCOEFFS(20), ROTCOEFFS(21), ROTCOEFFS(22), ROTCOEFFS(7),
+ ROTCOEFFS(24), ROTCOEFFS(25), ROTCOEFFS(26), ROTCOEFFS(27), ROTCOEFFS(28), ROTCOEFFS(29), ROTCOEFFS(30), ROTCOEFFS(31),
};
template<>
inline void genrotpos<PT_PART>(const vec &o, const vec &d, float size, int grav, int ts, partvert *vs, int rot)
{
- const vec *coeffs = rotcoeffs[rot];
- (vs[0].pos = o).add(vec(camright).mul(coeffs[0].x*size)).add(vec(camup).mul(coeffs[0].y*size));
- (vs[1].pos = o).add(vec(camright).mul(coeffs[1].x*size)).add(vec(camup).mul(coeffs[1].y*size));
- (vs[2].pos = o).add(vec(camright).mul(coeffs[2].x*size)).add(vec(camup).mul(coeffs[2].y*size));
- (vs[3].pos = o).add(vec(camright).mul(coeffs[3].x*size)).add(vec(camup).mul(coeffs[3].y*size));
+ const vec *coeffs = rotcoeffs[rot];
+ (vs[0].pos = o).add(vec(camright).mul(coeffs[0].x*size)).add(vec(camup).mul(coeffs[0].y*size));
+ (vs[1].pos = o).add(vec(camright).mul(coeffs[1].x*size)).add(vec(camup).mul(coeffs[1].y*size));
+ (vs[2].pos = o).add(vec(camright).mul(coeffs[2].x*size)).add(vec(camup).mul(coeffs[2].y*size));
+ (vs[3].pos = o).add(vec(camright).mul(coeffs[3].x*size)).add(vec(camup).mul(coeffs[3].y*size));
}
template<int T>
static inline void seedpos(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int grav)
{
- if(grav)
- {
- vec end(o);
- float t = fade;
- end.add(vec(d).mul(t/5000.0f));
- end.z -= t*t/(2.0f * 5000.0f * grav);
- pe.extendbb(end, size);
-
- float tpeak = d.z*grav;
- if(tpeak > 0 && tpeak < fade) pe.extendbb(o.z + 1.5f*d.z*tpeak/5000.0f, size);
- }
+ if(grav)
+ {
+ vec end(o);
+ float t = fade;
+ end.add(vec(d).mul(t/5000.0f));
+ end.z -= t*t/(2.0f * 5000.0f * grav);
+ pe.extendbb(end, size);
+
+ float tpeak = d.z*grav;
+ if(tpeak > 0 && tpeak < fade) pe.extendbb(o.z + 1.5f*d.z*tpeak/5000.0f, size);
+ }
}
template<>
inline void seedpos<PT_TAPE>(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int grav)
{
- pe.extendbb(d, size);
+ pe.extendbb(d, size);
}
template<>
inline void seedpos<PT_TRAIL>(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int grav)
{
- vec e = d;
- if(grav) e.z -= float(fade)/grav;
- e.div(-75.0f).add(o);
- pe.extendbb(e, size);
+ vec e = d;
+ if(grav) e.z -= float(fade)/grav;
+ e.div(-75.0f).add(o);
+ pe.extendbb(e, size);
}
template<int T>
struct varenderer : partrenderer
{
- partvert *verts;
- particle *parts;
- int maxparts, numparts, lastupdate, rndmask;
- GLuint vbo;
-
- varenderer(const char *texname, int type, int collide = 0)
- : partrenderer(texname, 3, type, collide),
- verts(NULL), parts(NULL), maxparts(0), numparts(0), lastupdate(-1), rndmask(0), vbo(0)
- {
- if(type & PT_HFLIP) rndmask |= 0x01;
- if(type & PT_VFLIP) rndmask |= 0x02;
- if(type & PT_ROT) rndmask |= 0x1F<<2;
- if(type & PT_RND4) rndmask |= 0x03<<5;
- }
-
- void cleanup()
- {
- if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; }
- }
-
- void init(int n)
- {
- DELETEA(parts);
- DELETEA(verts);
- parts = new particle[n];
- verts = new partvert[n*4];
- maxparts = n;
- numparts = 0;
- lastupdate = -1;
- }
-
- void reset()
- {
- numparts = 0;
- lastupdate = -1;
- }
-
- void resettracked(physent *owner)
- {
- if(!(type&PT_TRACK)) return;
- loopi(numparts)
- {
- particle *p = parts+i;
- if(!owner || (p->owner == owner)) p->fade = -1;
- }
- lastupdate = -1;
- }
-
- int count()
- {
- return numparts;
- }
-
- bool haswork()
- {
- return (numparts > 0);
- }
-
- particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity)
- {
- particle *p = parts + (numparts < maxparts ? numparts++ : rnd(maxparts)); //next free slot, or kill a random kitten
- p->o = o;
- p->d = d;
- p->gravity = gravity;
- p->fade = fade;
- p->millis = lastmillis + emitoffset;
- p->color = bvec(color>>16, (color>>8)&0xFF, color&0xFF);
- p->size = size;
- p->owner = NULL;
- p->flags = 0x80 | (rndmask ? rnd(0x80) & rndmask : 0);
- lastupdate = -1;
- return p;
- }
-
- void seedemitter(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int gravity)
- {
- pe.maxfade = max(pe.maxfade, fade);
- size *= SQRT2;
- pe.extendbb(o, size);
-
- seedpos<T>(pe, o, d, fade, size, gravity);
- if(!gravity) return;
-
- vec end(o);
- float t = fade;
- end.add(vec(d).mul(t/5000.0f));
- end.z -= t*t/(2.0f * 5000.0f * gravity);
- pe.extendbb(end, size);
-
- float tpeak = d.z*gravity;
- if(tpeak > 0 && tpeak < fade) pe.extendbb(o.z + 1.5f*d.z*tpeak/5000.0f, size);
- }
-
- void genverts(particle *p, partvert *vs, bool regen)
- {
- vec o, d;
- int blend, ts;
-
- calc(p, blend, ts, o, d);
- if(blend <= 1 || p->fade <= 5) p->fade = -1; //mark to remove on next pass (i.e. after render)
-
- modifyblend<T>(o, blend);
-
- if(regen)
- {
- p->flags &= ~0x80;
-
- #define SETTEXCOORDS(u1c, u2c, v1c, v2c, body) \
- { \
- float u1 = u1c, u2 = u2c, v1 = v1c, v2 = v2c; \
- body; \
- vs[0].tc = vec2(u1, v1); \
- vs[1].tc = vec2(u2, v1); \
- vs[2].tc = vec2(u2, v2); \
- vs[3].tc = vec2(u1, v2); \
- }
- if(type&PT_RND4)
- {
- float tx = 0.5f*((p->flags>>5)&1), ty = 0.5f*((p->flags>>6)&1);
- SETTEXCOORDS(tx, tx + 0.5f, ty, ty + 0.5f,
- {
- if(p->flags&0x01) swap(u1, u2);
- if(p->flags&0x02) swap(v1, v2);
- });
- }
- else if(type&PT_ICON)
- {
- float tx = 0.25f*(p->flags&3), ty = 0.25f*((p->flags>>2)&3);
- SETTEXCOORDS(tx, tx + 0.25f, ty, ty + 0.25f, {});
- }
- else SETTEXCOORDS(0, 1, 0, 1, {});
-
- #define SETCOLOR(r, g, b, a) \
- do { \
- bvec4 col(r, g, b, a); \
- loopi(4) vs[i].color = col; \
- } while(0)
- #define SETMODCOLOR SETCOLOR((p->color.r*blend)>>8, (p->color.g*blend)>>8, (p->color.b*blend)>>8, 255)
- if(type&PT_MOD) SETMODCOLOR;
- else SETCOLOR(p->color.r, p->color.g, p->color.b, blend);
- }
- else if(type&PT_MOD) SETMODCOLOR;
- else loopi(4) vs[i].color.a = blend;
-
- if(type&PT_ROT) genrotpos<T>(o, d, p->size, ts, p->gravity, vs, (p->flags>>2)&0x1F);
- else genpos<T>(o, d, p->size, ts, p->gravity, vs);
- }
-
- void genverts()
- {
- loopi(numparts)
- {
- particle *p = &parts[i];
- partvert *vs = &verts[i*4];
- if(p->fade < 0)
- {
- do
- {
- --numparts;
- if(numparts <= i) return;
- }
- while(parts[numparts].fade < 0);
- *p = parts[numparts];
- genverts(p, vs, true);
- }
- else genverts(p, vs, (p->flags&0x80)!=0);
- }
- }
-
- void update()
- {
- if(lastmillis == lastupdate && vbo) return;
- lastupdate = lastmillis;
-
- genverts();
-
- if(!vbo) glGenBuffers_(1, &vbo);
- gle::bindvbo(vbo);
- glBufferData_(GL_ARRAY_BUFFER, maxparts*4*sizeof(partvert), NULL, GL_STREAM_DRAW);
- glBufferSubData_(GL_ARRAY_BUFFER, 0, numparts*4*sizeof(partvert), verts);
- gle::clearvbo();
- }
-
- void render()
- {
- if(!tex) tex = textureload(texname, texclamp);
- glBindTexture(GL_TEXTURE_2D, tex->id);
-
- gle::bindvbo(vbo);
- const partvert *ptr = 0;
- gle::vertexpointer(sizeof(partvert), ptr->pos.v);
- gle::texcoord0pointer(sizeof(partvert), ptr->tc.v);
- gle::colorpointer(sizeof(partvert), ptr->color.v);
- gle::enablevertex();
- gle::enabletexcoord0();
- gle::enablecolor();
- gle::enablequads();
-
- gle::drawquads(0, numparts);
-
- gle::disablequads();
- gle::disablevertex();
- gle::disabletexcoord0();
- gle::disablecolor();
- gle::clearvbo();
- }
+ partvert *verts;
+ particle *parts;
+ int maxparts, numparts, lastupdate, rndmask;
+ GLuint vbo;
+
+ varenderer(const char *texname, int type, int collide = 0)
+ : partrenderer(texname, 3, type, collide),
+ verts(NULL), parts(NULL), maxparts(0), numparts(0), lastupdate(-1), rndmask(0), vbo(0)
+ {
+ if(type & PT_HFLIP) rndmask |= 0x01;
+ if(type & PT_VFLIP) rndmask |= 0x02;
+ if(type & PT_ROT) rndmask |= 0x1F<<2;
+ if(type & PT_RND4) rndmask |= 0x03<<5;
+ }
+
+ void cleanup()
+ {
+ if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; }
+ }
+
+ void init(int n)
+ {
+ DELETEA(parts);
+ DELETEA(verts);
+ parts = new particle[n];
+ verts = new partvert[n*4];
+ maxparts = n;
+ numparts = 0;
+ lastupdate = -1;
+ }
+
+ void reset()
+ {
+ numparts = 0;
+ lastupdate = -1;
+ }
+
+ void resettracked(physent *owner)
+ {
+ if(!(type&PT_TRACK)) return;
+ loopi(numparts)
+ {
+ particle *p = parts+i;
+ if(!owner || (p->owner == owner)) p->fade = -1;
+ }
+ lastupdate = -1;
+ }
+
+ int count()
+ {
+ return numparts;
+ }
+
+ bool haswork()
+ {
+ return (numparts > 0);
+ }
+
+ particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity)
+ {
+ particle *p = parts + (numparts < maxparts ? numparts++ : rnd(maxparts)); //next free slot, or kill a random kitten
+ p->o = o;
+ p->d = d;
+ p->gravity = gravity;
+ p->fade = fade;
+ p->millis = lastmillis + emitoffset;
+ p->color = bvec(color>>16, (color>>8)&0xFF, color&0xFF);
+ p->size = size;
+ p->owner = NULL;
+ p->flags = 0x80 | (rndmask ? rnd(0x80) & rndmask : 0);
+ lastupdate = -1;
+ return p;
+ }
+
+ void seedemitter(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int gravity)
+ {
+ pe.maxfade = max(pe.maxfade, fade);
+ size *= SQRT2;
+ pe.extendbb(o, size);
+
+ seedpos<T>(pe, o, d, fade, size, gravity);
+ if(!gravity) return;
+
+ vec end(o);
+ float t = fade;
+ end.add(vec(d).mul(t/5000.0f));
+ end.z -= t*t/(2.0f * 5000.0f * gravity);
+ pe.extendbb(end, size);
+
+ float tpeak = d.z*gravity;
+ if(tpeak > 0 && tpeak < fade) pe.extendbb(o.z + 1.5f*d.z*tpeak/5000.0f, size);
+ }
+
+ void genverts(particle *p, partvert *vs, bool regen)
+ {
+ vec o, d;
+ int blend, ts;
+
+ calc(p, blend, ts, o, d);
+ if(blend <= 1 || p->fade <= 5) p->fade = -1; //mark to remove on next pass (i.e. after render)
+
+ modifyblend<T>(o, blend);
+
+ if(regen)
+ {
+ p->flags &= ~0x80;
+
+ #define SETTEXCOORDS(u1c, u2c, v1c, v2c, body) \
+ { \
+ float u1 = u1c, u2 = u2c, v1 = v1c, v2 = v2c; \
+ body; \
+ vs[0].tc = vec2(u1, v1); \
+ vs[1].tc = vec2(u2, v1); \
+ vs[2].tc = vec2(u2, v2); \
+ vs[3].tc = vec2(u1, v2); \
+ }
+ if(type&PT_RND4)
+ {
+ float tx = 0.5f*((p->flags>>5)&1), ty = 0.5f*((p->flags>>6)&1);
+ SETTEXCOORDS(tx, tx + 0.5f, ty, ty + 0.5f,
+ {
+ if(p->flags&0x01) swap(u1, u2);
+ if(p->flags&0x02) swap(v1, v2);
+ });
+ }
+ else if(type&PT_ICON)
+ {
+ float tx = 0.25f*(p->flags&3), ty = 0.25f*((p->flags>>2)&3);
+ SETTEXCOORDS(tx, tx + 0.25f, ty, ty + 0.25f, {});
+ }
+ else SETTEXCOORDS(0, 1, 0, 1, {});
+
+ #define SETCOLOR(r, g, b, a) \
+ do { \
+ bvec4 col(r, g, b, a); \
+ loopi(4) vs[i].color = col; \
+ } while(0)
+ #define SETMODCOLOR SETCOLOR((p->color.r*blend)>>8, (p->color.g*blend)>>8, (p->color.b*blend)>>8, 255)
+ if(type&PT_MOD) SETMODCOLOR;
+ else SETCOLOR(p->color.r, p->color.g, p->color.b, blend);
+ }
+ else if(type&PT_MOD) SETMODCOLOR;
+ else loopi(4) vs[i].color.a = blend;
+
+ if(type&PT_ROT) genrotpos<T>(o, d, p->size, ts, p->gravity, vs, (p->flags>>2)&0x1F);
+ else genpos<T>(o, d, p->size, ts, p->gravity, vs);
+ }
+
+ void genverts()
+ {
+ loopi(numparts)
+ {
+ particle *p = &parts[i];
+ partvert *vs = &verts[i*4];
+ if(p->fade < 0)
+ {
+ do
+ {
+ --numparts;
+ if(numparts <= i) return;
+ }
+ while(parts[numparts].fade < 0);
+ *p = parts[numparts];
+ genverts(p, vs, true);
+ }
+ else genverts(p, vs, (p->flags&0x80)!=0);
+ }
+ }
+
+ void update()
+ {
+ if(lastmillis == lastupdate && vbo) return;
+ lastupdate = lastmillis;
+
+ genverts();
+
+ if(!vbo) glGenBuffers_(1, &vbo);
+ gle::bindvbo(vbo);
+ glBufferData_(GL_ARRAY_BUFFER, maxparts*4*sizeof(partvert), NULL, GL_STREAM_DRAW);
+ glBufferSubData_(GL_ARRAY_BUFFER, 0, numparts*4*sizeof(partvert), verts);
+ gle::clearvbo();
+ }
+
+ void render()
+ {
+ if(!tex) tex = textureload(texname, texclamp);
+ glBindTexture(GL_TEXTURE_2D, tex->id);
+
+ gle::bindvbo(vbo);
+ const partvert *ptr = 0;
+ gle::vertexpointer(sizeof(partvert), ptr->pos.v);
+ gle::texcoord0pointer(sizeof(partvert), ptr->tc.v);
+ gle::colorpointer(sizeof(partvert), ptr->color.v);
+ gle::enablevertex();
+ gle::enabletexcoord0();
+ gle::enablecolor();
+ gle::enablequads();
+
+ gle::drawquads(0, numparts);
+
+ gle::disablequads();
+ gle::disablevertex();
+ gle::disabletexcoord0();
+ gle::disablecolor();
+ gle::clearvbo();
+ }
};
typedef varenderer<PT_PART> quadrenderer;
typedef varenderer<PT_TAPE> taperenderer;
@@ -847,92 +844,92 @@ typedef varenderer<PT_TRAIL> trailrenderer;
struct softquadrenderer : quadrenderer
{
- softquadrenderer(const char *texname, int type, int collide = 0)
- : quadrenderer(texname, type|PT_SOFT, collide)
- {
- }
-
- int adddepthfx(vec &bbmin, vec &bbmax)
- {
- if(!depthfxtex.highprecision() && !depthfxtex.emulatehighprecision()) return 0;
- int numsoft = 0;
- loopi(numparts)
- {
- particle &p = parts[i];
- float radius = p.size*SQRT2;
- vec o, d;
- int blend, ts;
- calc(&p, blend, ts, o, d, false);
- if(!isfoggedsphere(radius, p.o) && (depthfxscissor!=2 || depthfxtex.addscissorbox(p.o, radius)))
- {
- numsoft++;
- loopk(3)
- {
- bbmin[k] = min(bbmin[k], o[k] - radius);
- bbmax[k] = max(bbmax[k], o[k] + radius);
- }
- }
- }
- return numsoft;
- }
+ softquadrenderer(const char *texname, int type, int collide = 0)
+ : quadrenderer(texname, type|PT_SOFT, collide)
+ {
+ }
+
+ int adddepthfx(vec &bbmin, vec &bbmax)
+ {
+ if(!depthfxtex.highprecision() && !depthfxtex.emulatehighprecision()) return 0;
+ int numsoft = 0;
+ loopi(numparts)
+ {
+ particle &p = parts[i];
+ float radius = p.size*SQRT2;
+ vec o, d;
+ int blend, ts;
+ calc(&p, blend, ts, o, d, false);
+ if(!isfoggedsphere(radius, p.o) && (depthfxscissor!=2 || depthfxtex.addscissorbox(p.o, radius)))
+ {
+ numsoft++;
+ loopk(3)
+ {
+ bbmin[k] = min(bbmin[k], o[k] - radius);
+ bbmax[k] = max(bbmax[k], o[k] + radius);
+ }
+ }
+ }
+ return numsoft;
+ }
};
static partrenderer *parts[] =
{
- new quadrenderer("<grey>packages/particles/blood.png", PT_PART|PT_FLIP|PT_MOD|PT_RND4, DECAL_BLOOD), // blood spats (note: rgb is inverted)
- new trailrenderer("packages/particles/base.png", PT_TRAIL|PT_LERP), // water, entity
- new quadrenderer("<grey>packages/particles/smoke.png", PT_PART|PT_FLIP|PT_LERP), // smoke
- new quadrenderer("<grey>packages/particles/steam.png", PT_PART|PT_FLIP), // steam
- new quadrenderer("<grey>packages/particles/flames.png", PT_PART|PT_HFLIP|PT_RND4|PT_GLARE), // flame on - no flipping please, they have orientation
- new quadrenderer("packages/particles/ball1.png", PT_PART|PT_FEW|PT_GLARE), // fireball1
- new quadrenderer("packages/particles/ball2.png", PT_PART|PT_FEW|PT_GLARE), // fireball2
- new quadrenderer("packages/particles/ball3.png", PT_PART|PT_FEW|PT_GLARE), // fireball3
- new taperenderer("packages/particles/flare.png", PT_TAPE|PT_GLARE), // streak
- &lightnings, // lightning
- &fireballs, // explosion fireball
- &bluefireballs, // bluish explosion fireball
- new quadrenderer("packages/particles/spark.png", PT_PART|PT_FLIP|PT_GLARE), // sparks
- new quadrenderer("packages/particles/base.png", PT_PART|PT_FLIP|PT_GLARE), // edit mode entities
- new quadrenderer("<grey>packages/particles/snow.png", PT_PART|PT_FLIP|PT_RND4, -1), // colliding snow
- new quadrenderer("packages/particles/muzzleflash1.png", PT_PART|PT_FEW|PT_FLIP|PT_GLARE|PT_TRACK), // muzzle flash
- new quadrenderer("packages/particles/muzzleflash2.png", PT_PART|PT_FEW|PT_FLIP|PT_GLARE|PT_TRACK), // muzzle flash
- new quadrenderer("packages/particles/muzzleflash3.png", PT_PART|PT_FEW|PT_FLIP|PT_GLARE|PT_TRACK), // muzzle flash
- new quadrenderer("packages/hud/items.png", PT_PART|PT_FEW|PT_ICON), // hud icon
- new quadrenderer("<colorify:1/1/1>packages/hud/items.png", PT_PART|PT_FEW|PT_ICON), // grey hud icon
- &texts, // text
- &texticons, // text icons
- &meters, // meter
- &metervs, // meter vs.
+ new quadrenderer("<grey>packages/particles/blood.png", PT_PART|PT_FLIP|PT_MOD|PT_RND4, DECAL_BLOOD), // blood spats (note: rgb is inverted)
+ new trailrenderer("packages/particles/base.png", PT_TRAIL|PT_LERP), // water, entity
+ new quadrenderer("<grey>packages/particles/smoke.png", PT_PART|PT_FLIP|PT_LERP), // smoke
+ new quadrenderer("<grey>packages/particles/steam.png", PT_PART|PT_FLIP), // steam
+ new quadrenderer("<grey>packages/particles/flames.png", PT_PART|PT_HFLIP|PT_RND4|PT_GLARE), // flame on - no flipping please, they have orientation
+ new quadrenderer("packages/particles/ball1.png", PT_PART|PT_FEW|PT_GLARE), // fireball1
+ new quadrenderer("packages/particles/ball2.png", PT_PART|PT_FEW|PT_GLARE), // fireball2
+ new quadrenderer("packages/particles/ball3.png", PT_PART|PT_FEW|PT_GLARE), // fireball3
+ new taperenderer("packages/particles/flare.png", PT_TAPE|PT_GLARE), // streak
+ &lightnings, // lightning
+ &fireballs, // explosion fireball
+ &bluefireballs, // bluish explosion fireball
+ new quadrenderer("packages/particles/spark.png", PT_PART|PT_FLIP|PT_GLARE), // sparks
+ new quadrenderer("packages/particles/base.png", PT_PART|PT_FLIP|PT_GLARE), // edit mode entities
+ new quadrenderer("<grey>packages/particles/snow.png", PT_PART|PT_FLIP|PT_RND4, -1), // colliding snow
+ new quadrenderer("packages/particles/muzzleflash1.png", PT_PART|PT_FEW|PT_FLIP|PT_GLARE|PT_TRACK), // muzzle flash
+ new quadrenderer("packages/particles/muzzleflash2.png", PT_PART|PT_FEW|PT_FLIP|PT_GLARE|PT_TRACK), // muzzle flash
+ new quadrenderer("packages/particles/muzzleflash3.png", PT_PART|PT_FEW|PT_FLIP|PT_GLARE|PT_TRACK), // muzzle flash
+ new quadrenderer("packages/hud/items.png", PT_PART|PT_FEW|PT_ICON), // hud icon
+ new quadrenderer("<colorify:1/1/1>packages/hud/items.png", PT_PART|PT_FEW|PT_ICON), // grey hud icon
+ &texts, // text
+ &texticons, // text icons
+ &meters, // meter
+ &metervs, // meter vs.
};
void finddepthfxranges()
{
- depthfxmin = vec(1e16f, 1e16f, 1e16f);
- depthfxmax = vec(0, 0, 0);
- numdepthfxranges = fireballs.finddepthfxranges(depthfxowners, depthfxranges, 0, MAXDFXRANGES, depthfxmin, depthfxmax);
- numdepthfxranges = bluefireballs.finddepthfxranges(depthfxowners, depthfxranges, numdepthfxranges, MAXDFXRANGES, depthfxmin, depthfxmax);
- loopk(3)
- {
- depthfxmin[k] -= depthfxmargin;
- depthfxmax[k] += depthfxmargin;
- }
- if(depthfxparts)
- {
- loopi(sizeof(parts)/sizeof(parts[0]))
- {
- partrenderer *p = parts[i];
- if(p->type&PT_SOFT && p->adddepthfx(depthfxmin, depthfxmax))
- {
- if(!numdepthfxranges)
- {
- numdepthfxranges = 1;
- depthfxowners[0] = NULL;
- depthfxranges[0] = 0;
- }
- }
- }
- }
- if(depthfxscissor<2 && numdepthfxranges>0) depthfxtex.addscissorbox(depthfxmin, depthfxmax);
+ depthfxmin = vec(1e16f, 1e16f, 1e16f);
+ depthfxmax = vec(0, 0, 0);
+ numdepthfxranges = fireballs.finddepthfxranges(depthfxowners, depthfxranges, 0, MAXDFXRANGES, depthfxmin, depthfxmax);
+ numdepthfxranges = bluefireballs.finddepthfxranges(depthfxowners, depthfxranges, numdepthfxranges, MAXDFXRANGES, depthfxmin, depthfxmax);
+ loopk(3)
+ {
+ depthfxmin[k] -= depthfxmargin;
+ depthfxmax[k] += depthfxmargin;
+ }
+ if(depthfxparts)
+ {
+ loopi(sizeof(parts)/sizeof(parts[0]))
+ {
+ partrenderer *p = parts[i];
+ if(p->type&PT_SOFT && p->adddepthfx(depthfxmin, depthfxmax))
+ {
+ if(!numdepthfxranges)
+ {
+ numdepthfxranges = 1;
+ depthfxowners[0] = NULL;
+ depthfxranges[0] = 0;
+ }
+ }
+ }
+ }
+ if(depthfxscissor<2 && numdepthfxranges>0) depthfxtex.addscissorbox(depthfxmin, depthfxmax);
}
VARFP(maxparticles, 10, 4000, 40000, initparticles());
@@ -940,185 +937,185 @@ VARFP(fewparticles, 10, 100, 40000, initparticles());
void initparticles()
{
- if(!particleshader) particleshader = lookupshaderbyname("particle");
- if(!particlenotextureshader) particlenotextureshader = lookupshaderbyname("particlenotexture");
- loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->init(parts[i]->type&PT_FEW ? min(fewparticles, maxparticles) : maxparticles);
+ if(!particleshader) particleshader = lookupshaderbyname("particle");
+ if(!particlenotextureshader) particlenotextureshader = lookupshaderbyname("particlenotexture");
+ loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->init(parts[i]->type&PT_FEW ? min(fewparticles, maxparticles) : maxparticles);
}
void clearparticles()
{
- loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->reset();
- clearparticleemitters();
+ loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->reset();
+ clearparticleemitters();
}
void cleanupparticles()
{
- loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->cleanup();
+ loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->cleanup();
}
void removetrackedparticles(physent *owner)
{
- loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->resettracked(owner);
+ loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->resettracked(owner);
}
VARP(particleglare, 0, 2, 100);
void renderparticles(bool mainpass)
{
- canstep = mainpass;
-
- if(glaring && !particleglare) return;
-
- loopi(sizeof(parts)/sizeof(parts[0]))
- {
- if(glaring && !(parts[i]->type&PT_GLARE)) continue;
- parts[i]->update();
- }
-
- bool rendered = false;
- uint lastflags = PT_LERP|PT_SHADER,
- flagmask = PT_LERP|PT_MOD|PT_SHADER|PT_NOTEX;
-
- if(binddepthfxtex()) flagmask |= PT_SOFT;
-
- loopi(sizeof(parts)/sizeof(parts[0]))
- {
- partrenderer *p = parts[i];
- if(glaring && !(p->type&PT_GLARE)) continue;
- if(!p->haswork()) continue;
-
- if(!rendered)
- {
- rendered = true;
- glDepthMask(GL_FALSE);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- if(glaring) GLOBALPARAMF(colorscale, particleglare, particleglare, particleglare, 1);
- else GLOBALPARAMF(colorscale, 1, 1, 1, 1);
- }
-
- uint flags = p->type & flagmask, changedbits = (flags ^ lastflags);
- if(changedbits)
- {
- if(changedbits&PT_LERP)
- {
- if(flags&PT_LERP) resetfogcolor();
- else zerofogcolor();
- }
- if(changedbits&(PT_LERP|PT_MOD))
- {
- if(flags&PT_LERP) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- else if(flags&PT_MOD) glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
- else glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- }
- if(!(flags&PT_SHADER))
- {
- if(changedbits&(PT_SOFT|PT_SHADER|PT_NOTEX|PT_LERP))
- {
- if(flags&PT_SOFT)
- {
- if(!depthfxtex.highprecision()) SETSHADER(particlesoft8);
- else SETSHADER(particlesoft);
-
- binddepthfxparams(depthfxpartblend);
- }
- else if(flags&PT_NOTEX) particlenotextureshader->set();
- else particleshader->set();
- }
- }
- lastflags = flags;
- }
- p->render();
- }
-
- if(rendered)
- {
- if(lastflags&(PT_LERP|PT_MOD)) glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- if(!(lastflags&PT_LERP)) resetfogcolor();
- glDisable(GL_BLEND);
- glDepthMask(GL_TRUE);
- }
+ canstep = mainpass;
+
+ if(glaring && !particleglare) return;
+
+ loopi(sizeof(parts)/sizeof(parts[0]))
+ {
+ if(glaring && !(parts[i]->type&PT_GLARE)) continue;
+ parts[i]->update();
+ }
+
+ bool rendered = false;
+ uint lastflags = PT_LERP|PT_SHADER,
+ flagmask = PT_LERP|PT_MOD|PT_SHADER|PT_NOTEX;
+
+ if(binddepthfxtex()) flagmask |= PT_SOFT;
+
+ loopi(sizeof(parts)/sizeof(parts[0]))
+ {
+ partrenderer *p = parts[i];
+ if(glaring && !(p->type&PT_GLARE)) continue;
+ if(!p->haswork()) continue;
+
+ if(!rendered)
+ {
+ rendered = true;
+ glDepthMask(GL_FALSE);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ if(glaring) GLOBALPARAMF(colorscale, particleglare, particleglare, particleglare, 1);
+ else GLOBALPARAMF(colorscale, 1, 1, 1, 1);
+ }
+
+ uint flags = p->type & flagmask, changedbits = (flags ^ lastflags);
+ if(changedbits)
+ {
+ if(changedbits&PT_LERP)
+ {
+ if(flags&PT_LERP) resetfogcolor();
+ else zerofogcolor();
+ }
+ if(changedbits&(PT_LERP|PT_MOD))
+ {
+ if(flags&PT_LERP) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ else if(flags&PT_MOD) glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
+ else glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ }
+ if(!(flags&PT_SHADER))
+ {
+ if(changedbits&(PT_SOFT|PT_SHADER|PT_NOTEX|PT_LERP))
+ {
+ if(flags&PT_SOFT)
+ {
+ if(!depthfxtex.highprecision()) SETSHADER(particlesoft8);
+ else SETSHADER(particlesoft);
+
+ binddepthfxparams(depthfxpartblend);
+ }
+ else if(flags&PT_NOTEX) particlenotextureshader->set();
+ else particleshader->set();
+ }
+ }
+ lastflags = flags;
+ }
+ p->render();
+ }
+
+ if(rendered)
+ {
+ if(lastflags&(PT_LERP|PT_MOD)) glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ if(!(lastflags&PT_LERP)) resetfogcolor();
+ glDisable(GL_BLEND);
+ glDepthMask(GL_TRUE);
+ }
}
static int addedparticles = 0;
static inline particle *newparticle(const vec &o, const vec &d, int fade, int type, int color, float size, int gravity = 0)
{
- static particle dummy;
- if(seedemitter)
- {
- parts[type]->seedemitter(*seedemitter, o, d, fade, size, gravity);
- return &dummy;
- }
- if(fade + emitoffset < 0) return &dummy;
- addedparticles++;
- return parts[type]->addpart(o, d, fade, color, size, gravity);
+ static particle dummy;
+ if(seedemitter)
+ {
+ parts[type]->seedemitter(*seedemitter, o, d, fade, size, gravity);
+ return &dummy;
+ }
+ if(fade + emitoffset < 0) return &dummy;
+ addedparticles++;
+ return parts[type]->addpart(o, d, fade, color, size, gravity);
}
VARP(maxparticledistance, 256, 1024, 4096);
static void splash(int type, int color, int radius, int num, int fade, const vec &p, float size, int gravity)
{
- if(camera1->o.dist(p) > maxparticledistance && !seedemitter) return;
- float collidez = parts[type]->collide ? p.z - raycube(p, vec(0, 0, -1), COLLIDERADIUS, RAY_CLIPMAT) + (parts[type]->collide >= 0 ? COLLIDEERROR : 0) : -1;
- int fmin = 1;
- int fmax = fade*3;
- loopi(num)
- {
- int x, y, z;
- do
- {
- x = rnd(radius*2)-radius;
- y = rnd(radius*2)-radius;
- z = rnd(radius*2)-radius;
- }
- while(x*x+y*y+z*z>radius*radius);
- vec tmp = vec((float)x, (float)y, (float)z);
- int f = (num < 10) ? (fmin + rnd(fmax)) : (fmax - (i*(fmax-fmin))/(num-1)); //help deallocater by using fade distribution rather than random
- newparticle(p, tmp, f, type, color, size, gravity)->val = collidez;
- }
+ if(camera1->o.dist(p) > maxparticledistance && !seedemitter) return;
+ float collidez = parts[type]->collide ? p.z - raycube(p, vec(0, 0, -1), COLLIDERADIUS, RAY_CLIPMAT) + (parts[type]->collide >= 0 ? COLLIDEERROR : 0) : -1;
+ int fmin = 1;
+ int fmax = fade*3;
+ loopi(num)
+ {
+ int x, y, z;
+ do
+ {
+ x = rnd(radius*2)-radius;
+ y = rnd(radius*2)-radius;
+ z = rnd(radius*2)-radius;
+ }
+ while(x*x+y*y+z*z>radius*radius);
+ vec tmp = vec((float)x, (float)y, (float)z);
+ int f = (num < 10) ? (fmin + rnd(fmax)) : (fmax - (i*(fmax-fmin))/(num-1)); //help deallocater by using fade distribution rather than random
+ newparticle(p, tmp, f, type, color, size, gravity)->val = collidez;
+ }
}
static void regularsplash(int type, int color, int radius, int num, int fade, const vec &p, float size, int gravity, int delay = 0)
{
- if(!canemitparticles() || (delay > 0 && rnd(delay) != 0)) return;
- splash(type, color, radius, num, fade, p, size, gravity);
+ if(!canemitparticles() || (delay > 0 && rnd(delay) != 0)) return;
+ splash(type, color, radius, num, fade, p, size, gravity);
}
bool canaddparticles()
{
- return !renderedgame && !shadowmapping && !minimized;
+ return !renderedgame && !shadowmapping && !minimized;
}
void regular_particle_splash(int type, int num, int fade, const vec &p, int color, float size, int radius, int gravity, int delay)
{
- if(!canaddparticles()) return;
- regularsplash(type, color, radius, num, fade, p, size, gravity, delay);
+ if(!canaddparticles()) return;
+ regularsplash(type, color, radius, num, fade, p, size, gravity, delay);
}
void particle_splash(int type, int num, int fade, const vec &p, int color, float size, int radius, int gravity)
{
- if(!canaddparticles()) return;
- splash(type, color, radius, num, fade, p, size, gravity);
+ if(!canaddparticles()) return;
+ splash(type, color, radius, num, fade, p, size, gravity);
}
VARP(maxtrail, 1, 500, 10000);
void particle_trail(int type, int fade, const vec &s, const vec &e, int color, float size, int gravity)
{
- if(!canaddparticles()) return;
- vec v;
- float d = e.dist(s, v);
- int steps = clamp(int(d*2), 1, maxtrail);
- v.div(steps);
- vec p = s;
- loopi(steps)
- {
- p.add(v);
- vec tmp = vec(float(rnd(11)-5), float(rnd(11)-5), float(rnd(11)-5));
- newparticle(p, tmp, rnd(fade)+fade, type, color, size, gravity);
- }
+ if(!canaddparticles()) return;
+ vec v;
+ float d = e.dist(s, v);
+ int steps = clamp(int(d*2), 1, maxtrail);
+ v.div(steps);
+ vec p = s;
+ loopi(steps)
+ {
+ p.add(v);
+ vec tmp = vec(float(rnd(11)-5), float(rnd(11)-5), float(rnd(11)-5));
+ newparticle(p, tmp, rnd(fade)+fade, type, color, size, gravity);
+ }
}
VARP(particletext, 0, 1, 1);
@@ -1126,74 +1123,74 @@ VARP(maxparticletextdistance, 0, 128, 10000);
void particle_text(const vec &s, const char *t, int type, int fade, int color, float size, int gravity, int icons)
{
- if(!canaddparticles()) return;
- if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return;
- particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity);
- p->text = t;
- p->flags = icons<<1;
+ if(!canaddparticles()) return;
+ if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return;
+ particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity);
+ p->text = t;
+ p->flags = icons<<1;
}
void particle_textcopy(const vec &s, const char *t, int type, int fade, int color, float size, int gravity)
{
- if(!canaddparticles()) return;
- if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return;
- particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity);
- p->text = newstring(t);
- p->flags = 1;
+ if(!canaddparticles()) return;
+ if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return;
+ particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity);
+ p->text = newstring(t);
+ p->flags = 1;
}
void particle_texticon(const vec &s, int ix, int iy, float offset, int type, int fade, int color, float size, int gravity)
{
- if(!canaddparticles()) return;
- if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return;
- particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity);
- p->flags |= ix | (iy<<2);
- p->val = offset;
+ if(!canaddparticles()) return;
+ if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return;
+ particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity);
+ p->flags |= ix | (iy<<2);
+ p->val = offset;
}
void particle_icon(const vec &s, int ix, int iy, int type, int fade, int color, float size, int gravity)
{
- if(!canaddparticles()) return;
- particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity);
- p->flags |= ix | (iy<<2);
+ if(!canaddparticles()) return;
+ particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity);
+ p->flags |= ix | (iy<<2);
}
void particle_meter(const vec &s, float val, int type, int fade, int color, int color2, float size)
{
- if(!canaddparticles()) return;
- particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size);
- p->color2[0] = color2>>16;
- p->color2[1] = (color2>>8)&0xFF;
- p->color2[2] = color2&0xFF;
- p->progress = clamp(int(val*100), 0, 100);
+ if(!canaddparticles()) return;
+ particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size);
+ p->color2[0] = color2>>16;
+ p->color2[1] = (color2>>8)&0xFF;
+ p->color2[2] = color2&0xFF;
+ p->progress = clamp(int(val*100), 0, 100);
}
void particle_flare(const vec &p, const vec &dest, int fade, int type, int color, float size, physent *owner)
{
- if(!canaddparticles()) return;
- newparticle(p, dest, fade, type, color, size)->owner = owner;
+ if(!canaddparticles()) return;
+ newparticle(p, dest, fade, type, color, size)->owner = owner;
}
void particle_fireball(const vec &dest, float maxsize, int type, int fade, int color, float size)
{
- if(!canaddparticles()) return;
- float growth = maxsize - size;
- if(fade < 0) fade = int(growth*20);
- newparticle(dest, vec(0, 0, 1), fade, type, color, size)->val = growth;
+ if(!canaddparticles()) return;
+ float growth = maxsize - size;
+ if(fade < 0) fade = int(growth*20);
+ newparticle(dest, vec(0, 0, 1), fade, type, color, size)->val = growth;
}
//dir = 0..6 where 0=up
static inline vec offsetvec(vec o, int dir, int dist)
{
- vec v = vec(o);
- v[(2+dir)%3] += (dir>2)?(-dist):dist;
- return v;
+ vec v = vec(o);
+ v[(2+dir)%3] += (dir>2)?(-dist):dist;
+ return v;
}
//converts a 16bit color to 24bit
static inline int colorfromattr(int attr)
{
- return (((attr&0xF)<<4) | ((attr&0xF0)<<8) | ((attr&0xF00)<<12)) + 0x0F0F0F;
+ return (((attr&0xF)<<4) | ((attr&0xF0)<<8) | ((attr&0xF00)<<12)) + 0x0F0F0F;
}
/* Experiments in shapes...
@@ -1209,300 +1206,299 @@ static inline int colorfromattr(int attr)
*/
void regularshape(int type, int radius, int color, int dir, int num, int fade, const vec &p, float size, int gravity, int vel = 200)
{
- if(!canemitparticles()) return;
-
- int basetype = parts[type]->type&0xFF;
- bool flare = (basetype == PT_TAPE) || (basetype == PT_LIGHTNING),
- inv = (dir&0x20)!=0, taper = (dir&0x40)!=0 && !seedemitter;
- dir &= 0x1F;
- loopi(num)
- {
- vec to, from;
- if(dir < 12)
- {
- const vec2 &sc = sincos360[rnd(360)];
- to[dir%3] = sc.y*radius;
- to[(dir+1)%3] = sc.x*radius;
- to[(dir+2)%3] = 0.0;
- to.add(p);
- if(dir < 3) //circle
- from = p;
- else if(dir < 6) //cylinder
- {
- from = to;
- to[(dir+2)%3] += radius;
- from[(dir+2)%3] -= radius;
- }
- else //cone
- {
- from = p;
- to[(dir+2)%3] += (dir < 9)?radius:(-radius);
- }
- }
- else if(dir < 15) //plane
- {
- to[dir%3] = float(rnd(radius<<4)-(radius<<3))/8.0;
- to[(dir+1)%3] = float(rnd(radius<<4)-(radius<<3))/8.0;
- to[(dir+2)%3] = radius;
- to.add(p);
- from = to;
- from[(dir+2)%3] -= 2*radius;
- }
- else if(dir < 21) //line
- {
- if(dir < 18)
- {
- to[dir%3] = float(rnd(radius<<4)-(radius<<3))/8.0;
- to[(dir+1)%3] = 0.0;
- }
- else
- {
- to[dir%3] = 0.0;
- to[(dir+1)%3] = float(rnd(radius<<4)-(radius<<3))/8.0;
- }
- to[(dir+2)%3] = 0.0;
- to.add(p);
- from = to;
- to[(dir+2)%3] += radius;
- }
- else if(dir < 24) //sphere
- {
- to = vec(2*M_PI*float(rnd(1000))/1000.0, M_PI*float(rnd(1000)-500)/1000.0).mul(radius);
- to.add(p);
- from = p;
- }
- else if(dir < 27) // flat plane
- {
- to[dir%3] = float(rndscale(2*radius)-radius);
- to[(dir+1)%3] = float(rndscale(2*radius)-radius);
- to[(dir+2)%3] = 0.0;
- to.add(p);
- from = to;
- }
- else from = to = p;
-
- if(inv) swap(from, to);
-
- if(taper)
- {
- float dist = clamp(from.dist2(camera1->o)/maxparticledistance, 0.0f, 1.0f);
- if(dist > 0.2f)
- {
- dist = 1 - (dist - 0.2f)/0.8f;
- if(rnd(0x10000) > dist*dist*0xFFFF) continue;
- }
- }
-
- if(flare)
- newparticle(from, to, rnd(fade*3)+1, type, color, size, gravity);
- else
- {
- vec d = vec(to).sub(from).rescale(vel); //velocity
- particle *n = newparticle(from, d, rnd(fade*3)+1, type, color, size, gravity);
- if(parts[type]->collide)
- n->val = from.z - raycube(from, vec(0, 0, -1), parts[type]->collide >= 0 ? COLLIDERADIUS : max(from.z, 0.0f), RAY_CLIPMAT) + (parts[type]->collide >= 0 ? COLLIDEERROR : 0);
- }
- }
+ if(!canemitparticles()) return;
+
+ int basetype = parts[type]->type&0xFF;
+ bool flare = (basetype == PT_TAPE) || (basetype == PT_LIGHTNING),
+ inv = (dir&0x20)!=0, taper = (dir&0x40)!=0 && !seedemitter;
+ dir &= 0x1F;
+ loopi(num)
+ {
+ vec to, from;
+ if(dir < 12)
+ {
+ const vec2 &sc = sincos360[rnd(360)];
+ to[dir%3] = sc.y*radius;
+ to[(dir+1)%3] = sc.x*radius;
+ to[(dir+2)%3] = 0.0;
+ to.add(p);
+ if(dir < 3) //circle
+ from = p;
+ else if(dir < 6) //cylinder
+ {
+ from = to;
+ to[(dir+2)%3] += radius;
+ from[(dir+2)%3] -= radius;
+ }
+ else //cone
+ {
+ from = p;
+ to[(dir+2)%3] += (dir < 9)?radius:(-radius);
+ }
+ }
+ else if(dir < 15) //plane
+ {
+ to[dir%3] = float(rnd(radius<<4)-(radius<<3))/8.0;
+ to[(dir+1)%3] = float(rnd(radius<<4)-(radius<<3))/8.0;
+ to[(dir+2)%3] = radius;
+ to.add(p);
+ from = to;
+ from[(dir+2)%3] -= 2*radius;
+ }
+ else if(dir < 21) //line
+ {
+ if(dir < 18)
+ {
+ to[dir%3] = float(rnd(radius<<4)-(radius<<3))/8.0;
+ to[(dir+1)%3] = 0.0;
+ }
+ else
+ {
+ to[dir%3] = 0.0;
+ to[(dir+1)%3] = float(rnd(radius<<4)-(radius<<3))/8.0;
+ }
+ to[(dir+2)%3] = 0.0;
+ to.add(p);
+ from = to;
+ to[(dir+2)%3] += radius;
+ }
+ else if(dir < 24) //sphere
+ {
+ to = vec(2*M_PI*float(rnd(1000))/1000.0, M_PI*float(rnd(1000)-500)/1000.0).mul(radius);
+ to.add(p);
+ from = p;
+ }
+ else if(dir < 27) // flat plane
+ {
+ to[dir%3] = float(rndscale(2*radius)-radius);
+ to[(dir+1)%3] = float(rndscale(2*radius)-radius);
+ to[(dir+2)%3] = 0.0;
+ to.add(p);
+ from = to;
+ }
+ else from = to = p;
+
+ if(inv) swap(from, to);
+
+ if(taper)
+ {
+ float dist = clamp(from.dist2(camera1->o)/maxparticledistance, 0.0f, 1.0f);
+ if(dist > 0.2f)
+ {
+ dist = 1 - (dist - 0.2f)/0.8f;
+ if(rnd(0x10000) > dist*dist*0xFFFF) continue;
+ }
+ }
+
+ if(flare)
+ newparticle(from, to, rnd(fade*3)+1, type, color, size, gravity);
+ else
+ {
+ vec d = vec(to).sub(from).rescale(vel); //velocity
+ particle *n = newparticle(from, d, rnd(fade*3)+1, type, color, size, gravity);
+ if(parts[type]->collide)
+ n->val = from.z - raycube(from, vec(0, 0, -1), parts[type]->collide >= 0 ? COLLIDERADIUS : max(from.z, 0.0f), RAY_CLIPMAT) + (parts[type]->collide >= 0 ? COLLIDEERROR : 0);
+ }
+ }
}
static void regularflame(int type, const vec &p, float radius, float height, int color, int density = 3, float scale = 2.0f, float speed = 200.0f, float fade = 600.0f, int gravity = -15)
{
- if(!canemitparticles()) return;
-
- float size = scale * min(radius, height);
- vec v(0, 0, min(1.0f, height)*speed);
- loopi(density)
- {
- vec s = p;
- s.x += rndscale(radius*2.0f)-radius;
- s.y += rndscale(radius*2.0f)-radius;
- newparticle(s, v, rnd(max(int(fade*height), 1))+1, type, color, size, gravity);
- }
+ if(!canemitparticles()) return;
+
+ float size = scale * min(radius, height);
+ vec v(0, 0, min(1.0f, height)*speed);
+ loopi(density)
+ {
+ vec s = p;
+ s.x += rndscale(radius*2.0f)-radius;
+ s.y += rndscale(radius*2.0f)-radius;
+ newparticle(s, v, rnd(max(int(fade*height), 1))+1, type, color, size, gravity);
+ }
}
void regular_particle_flame(int type, const vec &p, float radius, float height, int color, int density, float scale, float speed, float fade, int gravity)
{
- if(!canaddparticles()) return;
- regularflame(type, p, radius, height, color, density, scale, speed, fade, gravity);
+ if(!canaddparticles()) return;
+ regularflame(type, p, radius, height, color, density, scale, speed, fade, gravity);
}
static void makeparticles(entity &e)
{
- switch(e.attr1)
- {
- case 0: //fire and smoke - <radius> <height> <rgb> - 0 values default to compat for old maps
- {
- //regularsplash(PART_FIREBALL1, 0xFFC8C8, 150, 1, 40, e.o, 4.8f);
- //regularsplash(PART_SMOKE, 0x897661, 50, 1, 200, vec(e.o.x, e.o.y, e.o.z+3.0f), 2.4f, -20, 3);
- float radius = e.attr2 ? float(e.attr2)/100.0f : 1.5f,
- height = e.attr3 ? float(e.attr3)/100.0f : radius/3;
- regularflame(PART_FLAME, e.o, radius, height, e.attr4 ? colorfromattr(e.attr4) : 0x903020, 3, 2.0f);
- regularflame(PART_SMOKE, vec(e.o.x, e.o.y, e.o.z + 4.0f*min(radius, height)), radius, height, 0x303020, 1, 4.0f, 100.0f, 2000.0f, -20);
- break;
- }
- case 1: //steam vent - <dir>
- regularsplash(PART_STEAM, 0x897661, 50, 1, 200, offsetvec(e.o, e.attr2, rnd(10)), 2.4f, -20);
- break;
- case 2: //water fountain - <dir>
- {
- int color;
- if(e.attr3 > 0) color = colorfromattr(e.attr3);
- else
- {
- int mat = MAT_WATER + clamp(-e.attr3, 0, 3);
- const bvec &wfcol = getwaterfallcolor(mat);
- color = (int(wfcol[0])<<16) | (int(wfcol[1])<<8) | int(wfcol[2]);
- if(!color)
- {
- const bvec &wcol = getwatercolor(mat);
- color = (int(wcol[0])<<16) | (int(wcol[1])<<8) | int(wcol[2]);
- }
- }
- regularsplash(PART_WATER, color, 150, 4, 200, offsetvec(e.o, e.attr2, rnd(10)), 0.6f, 2);
- break;
- }
- case 3: //fire ball - <size> <rgb>
- newparticle(e.o, vec(0, 0, 1), 1, PART_EXPLOSION, colorfromattr(e.attr3), 4.0f)->val = 1+e.attr2;
- break;
- case 4: //tape - <dir> <length> <rgb>
- case 7: //lightning
- case 9: //steam
- case 10: //water
- case 13: //snow
- {
- static const int typemap[] = { PART_STREAK, -1, -1, PART_LIGHTNING, -1, PART_STEAM, PART_WATER, -1, -1, PART_SNOW };
- static const float sizemap[] = { 0.28f, 0.0f, 0.0f, 1.0f, 0.0f, 2.4f, 0.60f, 0.0f, 0.0f, 0.5f };
- static const int gravmap[] = { 0, 0, 0, 0, 0, -20, 2, 0, 0, 20 };
- int type = typemap[e.attr1-4];
- float size = sizemap[e.attr1-4];
- int gravity = gravmap[e.attr1-4];
- if(e.attr2 >= 256) regularshape(type, max(1+e.attr3, 1), colorfromattr(e.attr4), e.attr2-256, 5, e.attr5 > 0 ? min(int(e.attr5), 10000) : 200, e.o, size, gravity);
- else newparticle(e.o, offsetvec(e.o, e.attr2, max(1+e.attr3, 0)), 1, type, colorfromattr(e.attr4), size, gravity);
- break;
- }
- case 5: //meter, metervs - <percent> <rgb> <rgb2>
- case 6:
- {
- particle *p = newparticle(e.o, vec(0, 0, 1), 1, e.attr1==5 ? PART_METER : PART_METER_VS, colorfromattr(e.attr3), 2.0f);
- int color2 = colorfromattr(e.attr4);
- p->color2[0] = color2>>16;
- p->color2[1] = (color2>>8)&0xFF;
- p->color2[2] = color2&0xFF;
- p->progress = clamp(int(e.attr2), 0, 100);
- break;
- }
- case 11: // flame <radius> <height> <rgb> - radius=100, height=100 is the classic size
- regularflame(PART_FLAME, e.o, float(e.attr2)/100.0f, float(e.attr3)/100.0f, colorfromattr(e.attr4), 3, 2.0f);
- break;
- case 12: // smoke plume <radius> <height> <rgb>
- regularflame(PART_SMOKE, e.o, float(e.attr2)/100.0f, float(e.attr3)/100.0f, colorfromattr(e.attr4), 1, 4.0f, 100.0f, 2000.0f, -20);
- break;
- case 32: //lens flares - plain/sparkle/sun/sparklesun <red> <green> <blue>
- case 33:
- case 34:
- case 35:
- break;
- default:
- if(!editmode)
- {
- defformatstring(ds, "particles %d?", e.attr1);
- particle_textcopy(e.o, ds, PART_TEXT, 1, 0x6496FF, 2.0f);
- }
- break;
- }
+ switch(e.attr1)
+ {
+ case 0: //fire and smoke - <radius> <height> <rgb> - 0 values default to compat for old maps
+ {
+ //regularsplash(PART_FIREBALL1, 0xFFC8C8, 150, 1, 40, e.o, 4.8f);
+ //regularsplash(PART_SMOKE, 0x897661, 50, 1, 200, vec(e.o.x, e.o.y, e.o.z+3.0f), 2.4f, -20, 3);
+ float radius = e.attr2 ? float(e.attr2)/100.0f : 1.5f,
+ height = e.attr3 ? float(e.attr3)/100.0f : radius/3;
+ regularflame(PART_FLAME, e.o, radius, height, e.attr4 ? colorfromattr(e.attr4) : 0x903020, 3, 2.0f);
+ regularflame(PART_SMOKE, vec(e.o.x, e.o.y, e.o.z + 4.0f*min(radius, height)), radius, height, 0x303020, 1, 4.0f, 100.0f, 2000.0f, -20);
+ break;
+ }
+ case 1: //steam vent - <dir>
+ regularsplash(PART_STEAM, 0x897661, 50, 1, 200, offsetvec(e.o, e.attr2, rnd(10)), 2.4f, -20);
+ break;
+ case 2: //water fountain - <dir>
+ {
+ int color;
+ if(e.attr3 > 0) color = colorfromattr(e.attr3);
+ else
+ {
+ int mat = MAT_WATER + clamp(-e.attr3, 0, 3);
+ const bvec &wfcol = getwaterfallcolor(mat);
+ color = (int(wfcol[0])<<16) | (int(wfcol[1])<<8) | int(wfcol[2]);
+ if(!color)
+ {
+ const bvec &wcol = getwatercolor(mat);
+ color = (int(wcol[0])<<16) | (int(wcol[1])<<8) | int(wcol[2]);
+ }
+ }
+ regularsplash(PART_WATER, color, 150, 4, 200, offsetvec(e.o, e.attr2, rnd(10)), 0.6f, 2);
+ break;
+ }
+ case 3: //fire ball - <size> <rgb>
+ newparticle(e.o, vec(0, 0, 1), 1, PART_EXPLOSION, colorfromattr(e.attr3), 4.0f)->val = 1+e.attr2;
+ break;
+ case 4: //tape - <dir> <length> <rgb>
+ case 7: //lightning
+ case 9: //steam
+ case 10: //water
+ case 13: //snow
+ {
+ static const int typemap[] = { PART_STREAK, -1, -1, PART_LIGHTNING, -1, PART_STEAM, PART_WATER, -1, -1, PART_SNOW };
+ static const float sizemap[] = { 0.28f, 0.0f, 0.0f, 1.0f, 0.0f, 2.4f, 0.60f, 0.0f, 0.0f, 0.5f };
+ static const int gravmap[] = { 0, 0, 0, 0, 0, -20, 2, 0, 0, 20 };
+ int type = typemap[e.attr1-4];
+ float size = sizemap[e.attr1-4];
+ int gravity = gravmap[e.attr1-4];
+ if(e.attr2 >= 256) regularshape(type, max(1+e.attr3, 1), colorfromattr(e.attr4), e.attr2-256, 5, e.attr5 > 0 ? min(int(e.attr5), 10000) : 200, e.o, size, gravity);
+ else newparticle(e.o, offsetvec(e.o, e.attr2, max(1+e.attr3, 0)), 1, type, colorfromattr(e.attr4), size, gravity);
+ break;
+ }
+ case 5: //meter, metervs - <percent> <rgb> <rgb2>
+ case 6:
+ {
+ particle *p = newparticle(e.o, vec(0, 0, 1), 1, e.attr1==5 ? PART_METER : PART_METER_VS, colorfromattr(e.attr3), 2.0f);
+ int color2 = colorfromattr(e.attr4);
+ p->color2[0] = color2>>16;
+ p->color2[1] = (color2>>8)&0xFF;
+ p->color2[2] = color2&0xFF;
+ p->progress = clamp(int(e.attr2), 0, 100);
+ break;
+ }
+ case 11: // flame <radius> <height> <rgb> - radius=100, height=100 is the classic size
+ regularflame(PART_FLAME, e.o, float(e.attr2)/100.0f, float(e.attr3)/100.0f, colorfromattr(e.attr4), 3, 2.0f);
+ break;
+ case 12: // smoke plume <radius> <height> <rgb>
+ regularflame(PART_SMOKE, e.o, float(e.attr2)/100.0f, float(e.attr3)/100.0f, colorfromattr(e.attr4), 1, 4.0f, 100.0f, 2000.0f, -20);
+ break;
+ case 32: //lens flares - plain/sparkle/sun/sparklesun <red> <green> <blue>
+ case 33:
+ case 34:
+ case 35:
+ break;
+ default:
+ if(!editmode)
+ {
+ defformatstring(ds, "particles %d?", e.attr1);
+ particle_textcopy(e.o, ds, PART_TEXT, 1, 0x6496FF, 2.0f);
+ }
+ break;
+ }
}
bool printparticles(extentity &e, char *buf, int len)
{
- switch(e.attr1)
- {
- case 0: case 4: case 7: case 8: case 9: case 10: case 11: case 12: case 13:
- nformatstring(buf, len, "%s %d %d %d 0x%.3hX %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5);
- return true;
- case 3:
- nformatstring(buf, len, "%s %d %d 0x%.3hX %d %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5);
- return true;
- case 5: case 6:
- nformatstring(buf, len, "%s %d %d 0x%.3hX 0x%.3hX %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5);
- return true;
- }
- return false;
+ switch(e.attr1)
+ {
+ case 0: case 4: case 7: case 8: case 9: case 10: case 11: case 12: case 13:
+ nformatstring(buf, len, "%s %d %d %d 0x%.3hX %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5);
+ return true;
+ case 3:
+ nformatstring(buf, len, "%s %d %d 0x%.3hX %d %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5);
+ return true;
+ case 5: case 6:
+ nformatstring(buf, len, "%s %d %d 0x%.3hX 0x%.3hX %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5);
+ return true;
+ }
+ return false;
}
void seedparticles()
{
- renderprogress(0, "seeding particles");
- addparticleemitters();
- canemit = true;
- loopv(emitters)
- {
- particleemitter &pe = emitters[i];
- extentity &e = *pe.ent;
- seedemitter = &pe;
- for(int millis = 0; millis < seedmillis; millis += min(emitmillis, seedmillis/10))
- makeparticles(e);
- seedemitter = NULL;
- pe.lastemit = -seedmillis;
- pe.finalize();
- }
+ renderprogress(0, "seeding particles");
+ addparticleemitters();
+ canemit = true;
+ loopv(emitters)
+ {
+ particleemitter &pe = emitters[i];
+ extentity &e = *pe.ent;
+ seedemitter = &pe;
+ for(int millis = 0; millis < seedmillis; millis += min(emitmillis, seedmillis/10))
+ makeparticles(e);
+ seedemitter = NULL;
+ pe.lastemit = -seedmillis;
+ pe.finalize();
+ }
}
void updateparticles()
{
- if(regenemitters) addparticleemitters();
-
- if(minimized) { canemit = false; return; }
-
- if(lastmillis - lastemitframe >= emitmillis)
- {
- canemit = true;
- lastemitframe = lastmillis - (lastmillis%emitmillis);
- }
- else canemit = false;
-
- if(!editmode || showparticles)
- {
- int emitted = 0, replayed = 0;
- addedparticles = 0;
- loopv(emitters)
- {
- particleemitter &pe = emitters[i];
- extentity &e = *pe.ent;
- if(e.o.dist(camera1->o) > maxparticledistance) { pe.lastemit = lastmillis; continue; }
- if(cullparticles && pe.maxfade >= 0)
- {
- if(isfoggedsphere(pe.radius, pe.center)) { pe.lastcull = lastmillis; continue; }
- }
- makeparticles(e);
- emitted++;
- if(replayparticles && pe.maxfade > 5 && pe.lastcull > pe.lastemit)
- {
- for(emitoffset = max(pe.lastemit + emitmillis - lastmillis, -pe.maxfade); emitoffset < 0; emitoffset += emitmillis)
- {
- makeparticles(e);
- replayed++;
- }
- emitoffset = 0;
- }
- pe.lastemit = lastmillis;
- }
- if(dbgpcull && (canemit || replayed) && addedparticles) conoutf(CON_DEBUG, "%d emitters, %d particles", emitted, addedparticles);
- }
- if(editmode) // show sparkly thingies for map entities in edit mode
- {
- const vector<extentity *> &ents = entities::getents();
- // note: order matters in this case as particles of the same type are drawn in the reverse order that they are added
- loopv(entgroup)
- {
- entity &e = *ents[entgroup[i]];
- particle_textcopy(e.o, entname(e), PART_TEXT, 1, 0xFF4B19, 2.0f);
- }
- loopv(ents)
- {
- entity &e = *ents[i];
- if(e.type==ET_EMPTY) continue;
- particle_textcopy(e.o, entname(e), PART_TEXT, 1, 0x1EC850, 2.0f);
- regular_particle_splash(PART_EDIT, 2, 40, e.o, 0x3232FF, 0.32f*particlesize/100.0f);
- }
- }
+ if(regenemitters) addparticleemitters();
+
+ if(minimized) { canemit = false; return; }
+
+ if(lastmillis - lastemitframe >= emitmillis)
+ {
+ canemit = true;
+ lastemitframe = lastmillis - (lastmillis%emitmillis);
+ }
+ else canemit = false;
+
+ if(!editmode || showparticles)
+ {
+ int emitted = 0, replayed = 0;
+ addedparticles = 0;
+ loopv(emitters)
+ {
+ particleemitter &pe = emitters[i];
+ extentity &e = *pe.ent;
+ if(e.o.dist(camera1->o) > maxparticledistance) { pe.lastemit = lastmillis; continue; }
+ if(cullparticles && pe.maxfade >= 0)
+ {
+ if(isfoggedsphere(pe.radius, pe.center)) { pe.lastcull = lastmillis; continue; }
+ }
+ makeparticles(e);
+ emitted++;
+ if(replayparticles && pe.maxfade > 5 && pe.lastcull > pe.lastemit)
+ {
+ for(emitoffset = max(pe.lastemit + emitmillis - lastmillis, -pe.maxfade); emitoffset < 0; emitoffset += emitmillis)
+ {
+ makeparticles(e);
+ replayed++;
+ }
+ emitoffset = 0;
+ }
+ pe.lastemit = lastmillis;
+ }
+ }
+ if(editmode) // show sparkly thingies for map entities in edit mode
+ {
+ const vector<extentity *> &ents = entities::getents();
+ // note: order matters in this case as particles of the same type are drawn in the reverse order that they are added
+ loopv(entgroup)
+ {
+ entity &e = *ents[entgroup[i]];
+ particle_textcopy(e.o, entname(e), PART_TEXT, 1, 0xFF4B19, 2.0f);
+ }
+ loopv(ents)
+ {
+ entity &e = *ents[i];
+ if(e.type==ET_EMPTY) continue;
+ particle_textcopy(e.o, entname(e), PART_TEXT, 1, 0x1EC850, 2.0f);
+ regular_particle_splash(PART_EDIT, 2, 40, e.o, 0x3232FF, 0.32f*particlesize/100.0f);
+ }
+ }
}
diff --git a/src/engine/rendersky.cpp b/src/engine/rendersky.cpp
index 32ca947..458befb 100644
--- a/src/engine/rendersky.cpp
+++ b/src/engine/rendersky.cpp
@@ -4,55 +4,55 @@ Texture *sky[6] = { 0, 0, 0, 0, 0, 0 }, *clouds[6] = { 0, 0, 0, 0, 0, 0 };
void loadsky(const char *basename, Texture *texs[6])
{
- const char *wildcard = strchr(basename, '*');
- loopi(6)
- {
- const char *side = cubemapsides[i].name;
- string name;
- copystring(name, makerelpath("packages", basename));
- if(wildcard)
- {
- char *chop = strchr(name, '*');
- if(chop) { *chop = '\0'; concatstring(name, side); concatstring(name, wildcard+1); }
- texs[i] = textureload(name, 3, true, false);
- }
- else
- {
- defformatstring(ext, "_%s.jpg", side);
- concatstring(name, ext);
- if((texs[i] = textureload(name, 3, true, false))==notexture)
- {
- strcpy(name+strlen(name)-3, "png");
- texs[i] = textureload(name, 3, true, false);
- }
- }
- if(texs[i]==notexture) conoutf(CON_ERROR, "could not load side %s of sky texture %s", side, basename);
- }
+ const char *wildcard = strchr(basename, '*');
+ loopi(6)
+ {
+ const char *side = cubemapsides[i].name;
+ string name;
+ copystring(name, makerelpath("packages", basename));
+ if(wildcard)
+ {
+ char *chop = strchr(name, '*');
+ if(chop) { *chop = '\0'; concatstring(name, side); concatstring(name, wildcard+1); }
+ texs[i] = textureload(name, 3, true, false);
+ }
+ else
+ {
+ defformatstring(ext, "_%s.jpg", side);
+ concatstring(name, ext);
+ if((texs[i] = textureload(name, 3, true, false))==notexture)
+ {
+ strcpy(name+strlen(name)-3, "png");
+ texs[i] = textureload(name, 3, true, false);
+ }
+ }
+ if(texs[i]==notexture) conoutf(CON_ERROR, "could not load side %s of sky texture %s", side, basename);
+ }
}
Texture *cloudoverlay = NULL;
Texture *loadskyoverlay(const char *basename)
{
- const char *ext = strrchr(basename, '.');
- string name;
- copystring(name, makerelpath("packages", basename));
- Texture *t = notexture;
- if(ext) t = textureload(name, 0, true, false);
- else
- {
- concatstring(name, ".jpg");
- if((t = textureload(name, 0, true, false)) == notexture)
- {
- strcpy(name+strlen(name)-3, "png");
- t = textureload(name, 0, true, false);
- }
- }
- if(t==notexture) conoutf(CON_ERROR, "could not load sky overlay texture %s", basename);
- return t;
+ const char *ext = strrchr(basename, '.');
+ string name;
+ copystring(name, makerelpath("packages", basename));
+ Texture *t = notexture;
+ if(ext) t = textureload(name, 0, true, false);
+ else
+ {
+ concatstring(name, ".jpg");
+ if((t = textureload(name, 0, true, false)) == notexture)
+ {
+ strcpy(name+strlen(name)-3, "png");
+ t = textureload(name, 0, true, false);
+ }
+ }
+ if(t==notexture) conoutf(CON_ERROR, "could not load sky overlay texture %s", basename);
+ return t;
}
-SVARFR(skybox, "", { if(skybox[0]) loadsky(skybox, sky); });
+SVARFR(skybox, "", { if(skybox[0]) loadsky(skybox, sky); });
HVARR(skyboxcolour, 0, 0xFFFFFF, 0xFFFFFF);
FVARR(spinsky, -720, 0, 720);
VARR(yawsky, 0, 0, 360);
@@ -77,101 +77,101 @@ VARR(cloudsubdiv, 4, 16, 64);
HVARR(cloudcolour, 0, 0xFFFFFF, 0xFFFFFF);
void drawenvboxface(float s0, float t0, int x0, int y0, int z0,
- float s1, float t1, int x1, int y1, int z1,
- float s2, float t2, int x2, int y2, int z2,
- float s3, float t3, int x3, int y3, int z3,
- Texture *tex)
+ float s1, float t1, int x1, int y1, int z1,
+ float s2, float t2, int x2, int y2, int z2,
+ float s3, float t3, int x3, int y3, int z3,
+ Texture *tex)
{
- glBindTexture(GL_TEXTURE_2D, (tex ? tex : notexture)->id);
- gle::begin(GL_TRIANGLE_STRIP);
- gle::attribf(x3, y3, z3); gle::attribf(s3, t3);
- gle::attribf(x2, y2, z2); gle::attribf(s2, t2);
- gle::attribf(x0, y0, z0); gle::attribf(s0, t0);
- gle::attribf(x1, y1, z1); gle::attribf(s1, t1);
- xtraverts += gle::end();
+ glBindTexture(GL_TEXTURE_2D, (tex ? tex : notexture)->id);
+ gle::begin(GL_TRIANGLE_STRIP);
+ gle::attribf(x3, y3, z3); gle::attribf(s3, t3);
+ gle::attribf(x2, y2, z2); gle::attribf(s2, t2);
+ gle::attribf(x0, y0, z0); gle::attribf(s0, t0);
+ gle::attribf(x1, y1, z1); gle::attribf(s1, t1);
+ xtraverts += gle::end();
}
void drawenvbox(int w, float z1clip = 0.0f, float z2clip = 1.0f, int faces = 0x3F, Texture **sky = NULL)
{
- if(z1clip >= z2clip) return;
-
- float v1 = 1-z1clip, v2 = 1-z2clip;
- int z1 = int(ceil(2*w*(z1clip-0.5f))), z2 = int(ceil(2*w*(z2clip-0.5f)));
-
- gle::defvertex();
- gle::deftexcoord0();
-
- if(faces&0x01)
- drawenvboxface(0.0f, v2, -w, -w, z2,
- 1.0f, v2, -w, w, z2,
- 1.0f, v1, -w, w, z1,
- 0.0f, v1, -w, -w, z1, sky[0]);
-
- if(faces&0x02)
- drawenvboxface(1.0f, v1, w, -w, z1,
- 0.0f, v1, w, w, z1,
- 0.0f, v2, w, w, z2,
- 1.0f, v2, w, -w, z2, sky[1]);
-
- if(faces&0x04)
- drawenvboxface(1.0f, v1, -w, -w, z1,
- 0.0f, v1, w, -w, z1,
- 0.0f, v2, w, -w, z2,
- 1.0f, v2, -w, -w, z2, sky[2]);
-
- if(faces&0x08)
- drawenvboxface(1.0f, v1, w, w, z1,
- 0.0f, v1, -w, w, z1,
- 0.0f, v2, -w, w, z2,
- 1.0f, v2, w, w, z2, sky[3]);
-
- if(z1clip <= 0 && faces&0x10)
- drawenvboxface(0.0f, 1.0f, -w, w, -w,
- 0.0f, 0.0f, w, w, -w,
- 1.0f, 0.0f, w, -w, -w,
- 1.0f, 1.0f, -w, -w, -w, sky[4]);
-
- if(z2clip >= 1 && faces&0x20)
- drawenvboxface(0.0f, 1.0f, w, w, w,
- 0.0f, 0.0f, -w, w, w,
- 1.0f, 0.0f, -w, -w, w,
- 1.0f, 1.0f, w, -w, w, sky[5]);
+ if(z1clip >= z2clip) return;
+
+ float v1 = 1-z1clip, v2 = 1-z2clip;
+ int z1 = int(ceil(2*w*(z1clip-0.5f))), z2 = int(ceil(2*w*(z2clip-0.5f)));
+
+ gle::defvertex();
+ gle::deftexcoord0();
+
+ if(faces&0x01)
+ drawenvboxface(0.0f, v2, -w, -w, z2,
+ 1.0f, v2, -w, w, z2,
+ 1.0f, v1, -w, w, z1,
+ 0.0f, v1, -w, -w, z1, sky[0]);
+
+ if(faces&0x02)
+ drawenvboxface(1.0f, v1, w, -w, z1,
+ 0.0f, v1, w, w, z1,
+ 0.0f, v2, w, w, z2,
+ 1.0f, v2, w, -w, z2, sky[1]);
+
+ if(faces&0x04)
+ drawenvboxface(1.0f, v1, -w, -w, z1,
+ 0.0f, v1, w, -w, z1,
+ 0.0f, v2, w, -w, z2,
+ 1.0f, v2, -w, -w, z2, sky[2]);
+
+ if(faces&0x08)
+ drawenvboxface(1.0f, v1, w, w, z1,
+ 0.0f, v1, -w, w, z1,
+ 0.0f, v2, -w, w, z2,
+ 1.0f, v2, w, w, z2, sky[3]);
+
+ if(z1clip <= 0 && faces&0x10)
+ drawenvboxface(0.0f, 1.0f, -w, w, -w,
+ 0.0f, 0.0f, w, w, -w,
+ 1.0f, 0.0f, w, -w, -w,
+ 1.0f, 1.0f, -w, -w, -w, sky[4]);
+
+ if(z2clip >= 1 && faces&0x20)
+ drawenvboxface(0.0f, 1.0f, w, w, w,
+ 0.0f, 0.0f, -w, w, w,
+ 1.0f, 0.0f, -w, -w, w,
+ 1.0f, 1.0f, w, -w, w, sky[5]);
}
void drawenvoverlay(int w, Texture *overlay = NULL, float tx = 0, float ty = 0)
{
- float z = w*cloudheight, tsz = 0.5f*(1-cloudfade)/cloudscale, psz = w*(1-cloudfade);
- glBindTexture(GL_TEXTURE_2D, overlay ? overlay->id : notexture->id);
- vec color = vec::hexcolor(cloudcolour);
- gle::color(color, cloudalpha);
- gle::defvertex();
- gle::deftexcoord0();
- gle::begin(GL_TRIANGLE_FAN);
- loopi(cloudsubdiv+1)
- {
- vec p(1, 1, 0);
- p.rotate_around_z((-2.0f*M_PI*i)/cloudsubdiv);
- gle::attribf(p.x*psz, p.y*psz, z);
- gle::attribf(tx + p.x*tsz, ty + p.y*tsz);
- }
- xtraverts += gle::end();
- float tsz2 = 0.5f/cloudscale;
- gle::defvertex();
- gle::deftexcoord0();
- gle::defcolor(4);
- gle::begin(GL_TRIANGLE_STRIP);
- loopi(cloudsubdiv+1)
- {
- vec p(1, 1, 0);
- p.rotate_around_z((-2.0f*M_PI*i)/cloudsubdiv);
- gle::attribf(p.x*psz, p.y*psz, z);
- gle::attribf(tx + p.x*tsz, ty + p.y*tsz);
- gle::attrib(color, cloudalpha);
- gle::attribf(p.x*w, p.y*w, z);
- gle::attribf(tx + p.x*tsz2, ty + p.y*tsz2);
- gle::attrib(color, 0.0f);
- }
- xtraverts += gle::end();
+ float z = w*cloudheight, tsz = 0.5f*(1-cloudfade)/cloudscale, psz = w*(1-cloudfade);
+ glBindTexture(GL_TEXTURE_2D, overlay ? overlay->id : notexture->id);
+ vec color = vec::hexcolor(cloudcolour);
+ gle::color(color, cloudalpha);
+ gle::defvertex();
+ gle::deftexcoord0();
+ gle::begin(GL_TRIANGLE_FAN);
+ loopi(cloudsubdiv+1)
+ {
+ vec p(1, 1, 0);
+ p.rotate_around_z((-2.0f*M_PI*i)/cloudsubdiv);
+ gle::attribf(p.x*psz, p.y*psz, z);
+ gle::attribf(tx + p.x*tsz, ty + p.y*tsz);
+ }
+ xtraverts += gle::end();
+ float tsz2 = 0.5f/cloudscale;
+ gle::defvertex();
+ gle::deftexcoord0();
+ gle::defcolor(4);
+ gle::begin(GL_TRIANGLE_STRIP);
+ loopi(cloudsubdiv+1)
+ {
+ vec p(1, 1, 0);
+ p.rotate_around_z((-2.0f*M_PI*i)/cloudsubdiv);
+ gle::attribf(p.x*psz, p.y*psz, z);
+ gle::attribf(tx + p.x*tsz, ty + p.y*tsz);
+ gle::attrib(color, cloudalpha);
+ gle::attribf(p.x*w, p.y*w, z);
+ gle::attribf(tx + p.x*tsz2, ty + p.y*tsz2);
+ gle::attrib(color, 0.0f);
+ }
+ xtraverts += gle::end();
}
FVARR(fogdomeheight, -1, -0.5f, 1);
@@ -182,207 +182,207 @@ FVARR(fogdomeclip, 0, 1, 1);
bvec fogdomecolor(0, 0, 0);
HVARFR(fogdomecolour, 0, 0, 0xFFFFFF,
{
- fogdomecolor = bvec((fogdomecolour>>16)&0xFF, (fogdomecolour>>8)&0xFF, fogdomecolour&0xFF);
+ fogdomecolor = bvec((fogdomecolour>>16)&0xFF, (fogdomecolour>>8)&0xFF, fogdomecolour&0xFF);
});
VARR(fogdomeclouds, 0, 1, 1);
namespace fogdome
{
- struct vert
- {
- vec pos;
- bvec4 color;
-
- vert() {}
- vert(const vec &pos, const bvec &fcolor, float alpha) : pos(pos), color(fcolor, uchar(alpha*255))
- {
- }
- vert(const vert &v0, const vert &v1) : pos(vec(v0.pos).add(v1.pos).normalize()), color(v0.color)
- {
- if(v0.pos.z != v1.pos.z) color.a += uchar((v1.color.a - v0.color.a) * (pos.z - v0.pos.z) / (v1.pos.z - v0.pos.z));
- }
- } *verts = NULL;
- GLushort *indices = NULL;
- int numverts = 0, numindices = 0, capindices = 0;
- GLuint vbuf = 0, ebuf = 0;
- bvec lastcolor(0, 0, 0);
- float lastminalpha = 0, lastmaxalpha = 0, lastcapsize = -1, lastclipz = 1;
-
- void subdivide(int depth, int face);
-
- void genface(int depth, int i1, int i2, int i3)
- {
- int face = numindices; numindices += 3;
- indices[face] = i3;
- indices[face+1] = i2;
- indices[face+2] = i1;
- subdivide(depth, face);
- }
-
- void subdivide(int depth, int face)
- {
- if(depth-- <= 0) return;
- int idx[6];
- loopi(3) idx[i] = indices[face+2-i];
- loopi(3)
- {
- int curvert = numverts++;
- verts[curvert] = vert(verts[idx[i]], verts[idx[(i+1)%3]]); //push on to unit sphere
- idx[3+i] = curvert;
- indices[face+2-i] = curvert;
- }
- subdivide(depth, face);
- loopi(3) genface(depth, idx[i], idx[3+i], idx[3+(i+2)%3]);
- }
-
- int sortcap(GLushort x, GLushort y)
- {
- const vec &xv = verts[x].pos, &yv = verts[y].pos;
- return xv.y < 0 ? yv.y >= 0 || xv.x < yv.x : yv.y >= 0 && xv.x > yv.x;
- }
-
- void init(const bvec &color, float minalpha = 0.0f, float maxalpha = 1.0f, float capsize = -1, float clipz = 1, int hres = 16, int depth = 2)
- {
- const int tris = hres << (2*depth);
- numverts = numindices = capindices = 0;
- verts = new vert[tris+1 + (capsize >= 0 ? 1 : 0)];
- indices = new GLushort[(tris + (capsize >= 0 ? hres<<depth : 0))*3];
- if(clipz >= 1)
- {
- verts[numverts++] = vert(vec(0.0f, 0.0f, 1.0f), color, minalpha); //build initial 'hres' sided pyramid
- loopi(hres) verts[numverts++] = vert(vec(sincos360[(360*i)/hres], 0.0f), color, maxalpha);
- loopi(hres) genface(depth, 0, i+1, 1+(i+1)%hres);
- }
- else if(clipz <= 0)
- {
- loopi(hres<<depth) verts[numverts++] = vert(vec(sincos360[(360*i)/(hres<<depth)], 0.0f), color, maxalpha);
- }
- else
- {
- float clipxy = sqrtf(1 - clipz*clipz);
- const vec2 &scm = sincos360[180/hres];
- loopi(hres)
- {
- const vec2 &sc = sincos360[(360*i)/hres];
- verts[numverts++] = vert(vec(sc.x*clipxy, sc.y*clipxy, clipz), color, minalpha);
- verts[numverts++] = vert(vec(sc.x, sc.y, 0.0f), color, maxalpha);
- verts[numverts++] = vert(vec(sc.x*scm.x - sc.y*scm.y, sc.y*scm.x + sc.x*scm.y, 0.0f), color, maxalpha);
- }
- loopi(hres)
- {
- genface(depth-1, 3*i, 3*i+1, 3*i+2);
- genface(depth-1, 3*i, 3*i+2, 3*((i+1)%hres));
- genface(depth-1, 3*i+2, 3*((i+1)%hres)+1, 3*((i+1)%hres));
- }
- }
-
- if(capsize >= 0)
- {
- GLushort *cap = &indices[numindices];
- int capverts = 0;
- loopi(numverts) if(!verts[i].pos.z) cap[capverts++] = i;
- verts[numverts++] = vert(vec(0.0f, 0.0f, -capsize), color, maxalpha);
- quicksort(cap, capverts, sortcap);
- loopi(capverts)
- {
- int n = capverts-1-i;
- cap[n*3] = cap[n];
- cap[n*3+1] = cap[(n+1)%capverts];
- cap[n*3+2] = numverts-1;
- capindices += 3;
- }
- }
-
- if(!vbuf) glGenBuffers_(1, &vbuf);
- gle::bindvbo(vbuf);
- glBufferData_(GL_ARRAY_BUFFER, numverts*sizeof(vert), verts, GL_STATIC_DRAW);
- DELETEA(verts);
-
- if(!ebuf) glGenBuffers_(1, &ebuf);
- gle::bindebo(ebuf);
- glBufferData_(GL_ELEMENT_ARRAY_BUFFER, (numindices + capindices)*sizeof(GLushort), indices, GL_STATIC_DRAW);
- DELETEA(indices);
- }
-
- void cleanup()
- {
- numverts = numindices = 0;
- if(vbuf) { glDeleteBuffers_(1, &vbuf); vbuf = 0; }
- if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; }
- }
-
- void draw()
- {
- float capsize = fogdomecap && fogdomeheight < 1 ? (1 + fogdomeheight) / (1 - fogdomeheight) : -1;
- bvec color = fogdomecolour ? fogdomecolor : fogcolor;
- if(!numverts || lastcolor != color || lastminalpha != fogdomemin || lastmaxalpha != fogdomemax || lastcapsize != capsize || lastclipz != fogdomeclip)
- {
- init(color, min(fogdomemin, fogdomemax), fogdomemax, capsize, fogdomeclip);
- lastcolor = color;
- lastminalpha = fogdomemin;
- lastmaxalpha = fogdomemax;
- lastcapsize = capsize;
- lastclipz = fogdomeclip;
- }
-
- gle::bindvbo(vbuf);
- gle::bindebo(ebuf);
-
- gle::vertexpointer(sizeof(vert), &verts->pos);
- gle::colorpointer(sizeof(vert), &verts->color);
- gle::enablevertex();
- gle::enablecolor();
-
- glDrawRangeElements_(GL_TRIANGLES, 0, numverts-1, numindices + fogdomecap*capindices, GL_UNSIGNED_SHORT, indices);
- xtraverts += numverts;
- glde++;
-
- gle::disablevertex();
- gle::disablecolor();
-
- gle::clearvbo();
- gle::clearebo();
- }
+ struct vert
+ {
+ vec pos;
+ bvec4 color;
+
+ vert() {}
+ vert(const vec &pos, const bvec &fcolor, float alpha) : pos(pos), color(fcolor, uchar(alpha*255))
+ {
+ }
+ vert(const vert &v0, const vert &v1) : pos(vec(v0.pos).add(v1.pos).normalize()), color(v0.color)
+ {
+ if(v0.pos.z != v1.pos.z) color.a += uchar((v1.color.a - v0.color.a) * (pos.z - v0.pos.z) / (v1.pos.z - v0.pos.z));
+ }
+ } *verts = NULL;
+ GLushort *indices = NULL;
+ int numverts = 0, numindices = 0, capindices = 0;
+ GLuint vbuf = 0, ebuf = 0;
+ bvec lastcolor(0, 0, 0);
+ float lastminalpha = 0, lastmaxalpha = 0, lastcapsize = -1, lastclipz = 1;
+
+ void subdivide(int depth, int face);
+
+ void genface(int depth, int i1, int i2, int i3)
+ {
+ int face = numindices; numindices += 3;
+ indices[face] = i3;
+ indices[face+1] = i2;
+ indices[face+2] = i1;
+ subdivide(depth, face);
+ }
+
+ void subdivide(int depth, int face)
+ {
+ if(depth-- <= 0) return;
+ int idx[6];
+ loopi(3) idx[i] = indices[face+2-i];
+ loopi(3)
+ {
+ int curvert = numverts++;
+ verts[curvert] = vert(verts[idx[i]], verts[idx[(i+1)%3]]); //push on to unit sphere
+ idx[3+i] = curvert;
+ indices[face+2-i] = curvert;
+ }
+ subdivide(depth, face);
+ loopi(3) genface(depth, idx[i], idx[3+i], idx[3+(i+2)%3]);
+ }
+
+ int sortcap(GLushort x, GLushort y)
+ {
+ const vec &xv = verts[x].pos, &yv = verts[y].pos;
+ return xv.y < 0 ? yv.y >= 0 || xv.x < yv.x : yv.y >= 0 && xv.x > yv.x;
+ }
+
+ void init(const bvec &color, float minalpha = 0.0f, float maxalpha = 1.0f, float capsize = -1, float clipz = 1, int hres = 16, int depth = 2)
+ {
+ const int tris = hres << (2*depth);
+ numverts = numindices = capindices = 0;
+ verts = new vert[tris+1 + (capsize >= 0 ? 1 : 0)];
+ indices = new GLushort[(tris + (capsize >= 0 ? hres<<depth : 0))*3];
+ if(clipz >= 1)
+ {
+ verts[numverts++] = vert(vec(0.0f, 0.0f, 1.0f), color, minalpha); //build initial 'hres' sided pyramid
+ loopi(hres) verts[numverts++] = vert(vec(sincos360[(360*i)/hres], 0.0f), color, maxalpha);
+ loopi(hres) genface(depth, 0, i+1, 1+(i+1)%hres);
+ }
+ else if(clipz <= 0)
+ {
+ loopi(hres<<depth) verts[numverts++] = vert(vec(sincos360[(360*i)/(hres<<depth)], 0.0f), color, maxalpha);
+ }
+ else
+ {
+ float clipxy = sqrtf(1 - clipz*clipz);
+ const vec2 &scm = sincos360[180/hres];
+ loopi(hres)
+ {
+ const vec2 &sc = sincos360[(360*i)/hres];
+ verts[numverts++] = vert(vec(sc.x*clipxy, sc.y*clipxy, clipz), color, minalpha);
+ verts[numverts++] = vert(vec(sc.x, sc.y, 0.0f), color, maxalpha);
+ verts[numverts++] = vert(vec(sc.x*scm.x - sc.y*scm.y, sc.y*scm.x + sc.x*scm.y, 0.0f), color, maxalpha);
+ }
+ loopi(hres)
+ {
+ genface(depth-1, 3*i, 3*i+1, 3*i+2);
+ genface(depth-1, 3*i, 3*i+2, 3*((i+1)%hres));
+ genface(depth-1, 3*i+2, 3*((i+1)%hres)+1, 3*((i+1)%hres));
+ }
+ }
+
+ if(capsize >= 0)
+ {
+ GLushort *cap = &indices[numindices];
+ int capverts = 0;
+ loopi(numverts) if(!verts[i].pos.z) cap[capverts++] = i;
+ verts[numverts++] = vert(vec(0.0f, 0.0f, -capsize), color, maxalpha);
+ quicksort(cap, capverts, sortcap);
+ loopi(capverts)
+ {
+ int n = capverts-1-i;
+ cap[n*3] = cap[n];
+ cap[n*3+1] = cap[(n+1)%capverts];
+ cap[n*3+2] = numverts-1;
+ capindices += 3;
+ }
+ }
+
+ if(!vbuf) glGenBuffers_(1, &vbuf);
+ gle::bindvbo(vbuf);
+ glBufferData_(GL_ARRAY_BUFFER, numverts*sizeof(vert), verts, GL_STATIC_DRAW);
+ DELETEA(verts);
+
+ if(!ebuf) glGenBuffers_(1, &ebuf);
+ gle::bindebo(ebuf);
+ glBufferData_(GL_ELEMENT_ARRAY_BUFFER, (numindices + capindices)*sizeof(GLushort), indices, GL_STATIC_DRAW);
+ DELETEA(indices);
+ }
+
+ void cleanup()
+ {
+ numverts = numindices = 0;
+ if(vbuf) { glDeleteBuffers_(1, &vbuf); vbuf = 0; }
+ if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; }
+ }
+
+ void draw()
+ {
+ float capsize = fogdomecap && fogdomeheight < 1 ? (1 + fogdomeheight) / (1 - fogdomeheight) : -1;
+ bvec color = fogdomecolour ? fogdomecolor : fogcolor;
+ if(!numverts || lastcolor != color || lastminalpha != fogdomemin || lastmaxalpha != fogdomemax || lastcapsize != capsize || lastclipz != fogdomeclip)
+ {
+ init(color, min(fogdomemin, fogdomemax), fogdomemax, capsize, fogdomeclip);
+ lastcolor = color;
+ lastminalpha = fogdomemin;
+ lastmaxalpha = fogdomemax;
+ lastcapsize = capsize;
+ lastclipz = fogdomeclip;
+ }
+
+ gle::bindvbo(vbuf);
+ gle::bindebo(ebuf);
+
+ gle::vertexpointer(sizeof(vert), &verts->pos);
+ gle::colorpointer(sizeof(vert), &verts->color);
+ gle::enablevertex();
+ gle::enablecolor();
+
+ glDrawRangeElements_(GL_TRIANGLES, 0, numverts-1, numindices + fogdomecap*capindices, GL_UNSIGNED_SHORT, indices);
+ xtraverts += numverts;
+ glde++;
+
+ gle::disablevertex();
+ gle::disablecolor();
+
+ gle::clearvbo();
+ gle::clearebo();
+ }
}
static void drawfogdome(int farplane)
{
- SETSHADER(skyfog);
+ SETSHADER(skyfog);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- matrix4 skymatrix = cammatrix, skyprojmatrix;
- skymatrix.settranslation(vec(cammatrix.c).mul(farplane*fogdomeheight*0.5f));
- skymatrix.scale(farplane/2, farplane/2, farplane*(0.5f - fogdomeheight*0.5f));
- skyprojmatrix.mul(projmatrix, skymatrix);
- LOCALPARAM(skymatrix, skyprojmatrix);
+ matrix4 skymatrix = cammatrix, skyprojmatrix;
+ skymatrix.settranslation(vec(cammatrix.c).mul(farplane*fogdomeheight*0.5f));
+ skymatrix.scale(farplane/2, farplane/2, farplane*(0.5f - fogdomeheight*0.5f));
+ skyprojmatrix.mul(projmatrix, skymatrix);
+ LOCALPARAM(skymatrix, skyprojmatrix);
- fogdome::draw();
+ fogdome::draw();
- glDisable(GL_BLEND);
+ glDisable(GL_BLEND);
}
void cleanupsky()
{
- fogdome::cleanup();
+ fogdome::cleanup();
}
extern int atmo;
void preloadatmoshaders(bool force = false)
{
- static bool needatmo = false;
- if(force) needatmo = true;
- if(!atmo || !needatmo) return;
+ static bool needatmo = false;
+ if(force) needatmo = true;
+ if(!atmo || !needatmo) return;
- useshaderbyname("atmosphere");
- useshaderbyname("atmosphereglare");
+ useshaderbyname("atmosphere");
+ useshaderbyname("atmosphereglare");
}
void setupsky()
{
- preloadatmoshaders(true);
+ preloadatmoshaders(true);
}
VARFR(atmo, 0, 0, 1, preloadatmoshaders());
@@ -392,15 +392,15 @@ FVARR(atmobright, 0, 1, 16);
bvec atmosunlightcolor(0, 0, 0);
HVARFR(atmosunlight, 0, 0, 0xFFFFFF,
{
- if(atmosunlight <= 255) atmosunlight |= (atmosunlight<<8) | (atmosunlight<<16);
- atmosunlightcolor = bvec((atmosunlight>>16)&0xFF, (atmosunlight>>8)&0xFF, atmosunlight&0xFF);
+ if(atmosunlight <= 255) atmosunlight |= (atmosunlight<<8) | (atmosunlight<<16);
+ atmosunlightcolor = bvec((atmosunlight>>16)&0xFF, (atmosunlight>>8)&0xFF, atmosunlight&0xFF);
});
FVARR(atmosunlightscale, 0, 1, 16);
bvec atmosundiskcolor(0, 0, 0);
HVARFR(atmosundisk, 0, 0, 0xFFFFFF,
{
- if(atmosundisk <= 255) atmosundisk |= (atmosundisk<<8) | (atmosundisk<<16);
- atmosundiskcolor = bvec((atmosundisk>>16)&0xFF, (atmosundisk>>8)&0xFF, atmosundisk&0xFF);
+ if(atmosundisk <= 255) atmosundisk |= (atmosundisk<<8) | (atmosundisk<<16);
+ atmosundiskcolor = bvec((atmosundisk>>16)&0xFF, (atmosundisk>>8)&0xFF, atmosundisk&0xFF);
});
FVARR(atmosundisksize, 0, 12, 90);
FVARR(atmosundiskcorona, 0, 0.4f, 1);
@@ -412,344 +412,344 @@ FVARR(atmoalpha, 0, 1, 1);
static void drawatmosphere(int w, float z1clip = 0.0f, float z2clip = 1.0f, int faces = 0x3F)
{
- if(z1clip >= z2clip) return;
-
- if(glaring) SETSHADER(atmosphereglare);
- else SETSHADER(atmosphere);
-
- matrix4 skymatrix = cammatrix, skyprojmatrix;
- skymatrix.settranslation(0, 0, 0);
- skyprojmatrix.mul(projmatrix, skymatrix);
- LOCALPARAM(skymatrix, skyprojmatrix);
-
- // optical depth scales for 3 different shells of atmosphere - air, haze, ozone
- const float earthradius = 6371e3f, earthairheight = 8.4e3f, earthhazeheight = 1.25e3f, earthozoneheight = 50e3f;
- float planetradius = earthradius*atmoplanetsize;
- vec atmoshells = vec(earthairheight, earthhazeheight, earthozoneheight).mul(atmoheight).add(planetradius).square().sub(planetradius*planetradius);
- LOCALPARAM(opticaldepthparams, vec4(atmoshells, planetradius));
-
- // Henyey-Greenstein approximation, 1/(4pi) * (1 - g^2)/(1 + g^2 - 2gcos)]^1.5
- // Hoffman-Preetham variation uses (1-g)^2 instead of 1-g^2 which avoids excessive glare
- // clamp values near 0 angle to avoid spotlight artifact inside sundisk
- float gm = max(0.95f - 0.2f*atmohaze, 0.65f), miescale = pow((1-gm)*(1-gm)/(4*M_PI), -2.0f/3.0f);
- LOCALPARAMF(mieparams, miescale*(1 + gm*gm), miescale*-2*gm, 1 - (1 - cosf(0.5f*atmosundisksize*(1 - atmosundiskcorona)*RAD)));
-
- static const vec lambda(680e-9f, 550e-9f, 450e-9f),
- k(0.686f, 0.678f, 0.666f),
- ozone(3.426f, 8.298f, 0.356f);
- vec betar = vec(lambda).square().square().recip().mul(1.241e-30f/M_LN2 * atmodensity),
- betam = vec(lambda).recip().square().mul(k).mul(9.072e-17f/M_LN2 * atmohaze),
- betao = vec(ozone).mul(1.5e-7f/M_LN2 * atmoozone);
- LOCALPARAM(betarayleigh, betar);
- LOCALPARAM(betamie, betam);
- LOCALPARAM(betaozone, betao);
-
- // extinction in direction of sun
- float sunoffset = sunlightdir.z*planetradius;
- vec sundepth = vec(atmoshells).add(sunoffset*sunoffset).sqrt().sub(sunoffset);
- vec sunweight = vec(betar).mul(sundepth.x).madd(betam, sundepth.y).madd(betao, sundepth.z - sundepth.x);
- vec sunextinction = vec(sunweight).neg().exp2();
- vec suncolor = atmosunlight ? atmosunlightcolor.tocolor().mul(atmosunlightscale) : sunlightcolor.tocolor().mul(sunlightscale);
- // assume sunlight color is gamma encoded, so decode to linear light, then apply extinction
- vec sunscale = vec(suncolor).square().mul(atmobright * 16).mul(sunextinction);
- float maxsunweight = max(max(sunweight.x, sunweight.y), sunweight.z);
- if(maxsunweight > 127) sunweight.mul(127/maxsunweight);
- sunweight.add(1e-4f);
- LOCALPARAM(sunweight, sunweight);
- LOCALPARAM(sunlight, vec4(sunscale, atmoalpha));
- LOCALPARAM(sundir, sunlightdir);
-
- // invert extinction at zenith to get an approximation of how bright the sun disk should be
- vec zenithdepth = vec(atmoshells).add(planetradius*planetradius).sqrt().sub(planetradius);
- vec zenithweight = vec(betar).mul(zenithdepth.x).madd(betam, zenithdepth.y).madd(betao, zenithdepth.z - zenithdepth.x);
- vec zenithextinction = vec(zenithweight).sub(sunweight).exp2();
- vec diskcolor = (atmosundisk ? atmosundiskcolor.tocolor() : suncolor).square().mul(zenithextinction).mul(atmosundiskbright * (glaring ? 1 : 1.5f)).min(1);
- LOCALPARAM(sundiskcolor, diskcolor);
-
- // convert from view cosine into mu^2 for limb darkening, where mu = sqrt(1 - sin^2) and sin^2 = 1 - cos^2, thus mu^2 = 1 - (1 - cos^2*scale)
- // convert corona offset into scale for mu^2, where sin = (1-corona) and thus mu^2 = 1 - (1-corona^2)
- float sundiskscale = sinf(0.5f*atmosundisksize*RAD);
- float coronamu = 1 - (1-atmosundiskcorona)*(1-atmosundiskcorona);
- if(sundiskscale > 0) LOCALPARAMF(sundiskparams, 1.0f/(sundiskscale*sundiskscale), 1.0f/max(coronamu, 1e-3f));
- else LOCALPARAMF(sundiskparams, 0, 0);
-
- float z1 = 2*w*(z1clip-0.5f), z2 = ceil(2*w*(z2clip-0.5f));
-
- gle::defvertex();
-
- if(glaring)
- {
- if(sundiskscale > 0 && sunlightdir.z*w + sundiskscale > z1 && sunlightdir.z*w - sundiskscale < z2)
- {
- gle::begin(GL_TRIANGLE_FAN);
- vec spoke;
- spoke.orthogonal(sunlightdir);
- spoke.rescale(2*sundiskscale);
- loopi(4) gle::attrib(vec(spoke).rotate(-2*M_PI*i/4.0f, sunlightdir).add(sunlightdir).mul(w));
- xtraverts += gle::end();
- }
- return;
- }
-
- gle::begin(GL_QUADS);
-
- if(faces&0x01)
- {
- gle::attribf(-w, -w, z1);
- gle::attribf(-w, w, z1);
- gle::attribf(-w, w, z2);
- gle::attribf(-w, -w, z2);
- }
-
- if(faces&0x02)
- {
- gle::attribf(w, -w, z2);
- gle::attribf(w, w, z2);
- gle::attribf(w, w, z1);
- gle::attribf(w, -w, z1);
- }
-
- if(faces&0x04)
- {
- gle::attribf(-w, -w, z2);
- gle::attribf( w, -w, z2);
- gle::attribf( w, -w, z1);
- gle::attribf(-w, -w, z1);
- }
-
- if(faces&0x08)
- {
- gle::attribf( w, w, z2);
- gle::attribf(-w, w, z2);
- gle::attribf(-w, w, z1);
- gle::attribf( w, w, z1);
- }
-
- if(z1clip <= 0 && faces&0x10)
- {
- gle::attribf(-w, -w, -w);
- gle::attribf( w, -w, -w);
- gle::attribf( w, w, -w);
- gle::attribf(-w, w, -w);
- }
-
- if(z2clip >= 1 && faces&0x20)
- {
- gle::attribf( w, -w, w);
- gle::attribf(-w, -w, w);
- gle::attribf(-w, w, w);
- gle::attribf( w, w, w);
- }
-
- xtraverts += gle::end();
+ if(z1clip >= z2clip) return;
+
+ if(glaring) SETSHADER(atmosphereglare);
+ else SETSHADER(atmosphere);
+
+ matrix4 skymatrix = cammatrix, skyprojmatrix;
+ skymatrix.settranslation(0, 0, 0);
+ skyprojmatrix.mul(projmatrix, skymatrix);
+ LOCALPARAM(skymatrix, skyprojmatrix);
+
+ // optical depth scales for 3 different shells of atmosphere - air, haze, ozone
+ const float earthradius = 6371e3f, earthairheight = 8.4e3f, earthhazeheight = 1.25e3f, earthozoneheight = 50e3f;
+ float planetradius = earthradius*atmoplanetsize;
+ vec atmoshells = vec(earthairheight, earthhazeheight, earthozoneheight).mul(atmoheight).add(planetradius).square().sub(planetradius*planetradius);
+ LOCALPARAM(opticaldepthparams, vec4(atmoshells, planetradius));
+
+ // Henyey-Greenstein approximation, 1/(4pi) * (1 - g^2)/(1 + g^2 - 2gcos)]^1.5
+ // Hoffman-Preetham variation uses (1-g)^2 instead of 1-g^2 which avoids excessive glare
+ // clamp values near 0 angle to avoid spotlight artifact inside sundisk
+ float gm = max(0.95f - 0.2f*atmohaze, 0.65f), miescale = pow((1-gm)*(1-gm)/(4*M_PI), -2.0f/3.0f);
+ LOCALPARAMF(mieparams, miescale*(1 + gm*gm), miescale*-2*gm, 1 - (1 - cosf(0.5f*atmosundisksize*(1 - atmosundiskcorona)*RAD)));
+
+ static const vec lambda(680e-9f, 550e-9f, 450e-9f),
+ k(0.686f, 0.678f, 0.666f),
+ ozone(3.426f, 8.298f, 0.356f);
+ vec betar = vec(lambda).square().square().recip().mul(1.241e-30f/M_LN2 * atmodensity),
+ betam = vec(lambda).recip().square().mul(k).mul(9.072e-17f/M_LN2 * atmohaze),
+ betao = vec(ozone).mul(1.5e-7f/M_LN2 * atmoozone);
+ LOCALPARAM(betarayleigh, betar);
+ LOCALPARAM(betamie, betam);
+ LOCALPARAM(betaozone, betao);
+
+ // extinction in direction of sun
+ float sunoffset = sunlightdir.z*planetradius;
+ vec sundepth = vec(atmoshells).add(sunoffset*sunoffset).sqrt().sub(sunoffset);
+ vec sunweight = vec(betar).mul(sundepth.x).madd(betam, sundepth.y).madd(betao, sundepth.z - sundepth.x);
+ vec sunextinction = vec(sunweight).neg().exp2();
+ vec suncolor = atmosunlight ? atmosunlightcolor.tocolor().mul(atmosunlightscale) : sunlightcolor.tocolor().mul(sunlightscale);
+ // assume sunlight color is gamma encoded, so decode to linear light, then apply extinction
+ vec sunscale = vec(suncolor).square().mul(atmobright * 16).mul(sunextinction);
+ float maxsunweight = max(max(sunweight.x, sunweight.y), sunweight.z);
+ if(maxsunweight > 127) sunweight.mul(127/maxsunweight);
+ sunweight.add(1e-4f);
+ LOCALPARAM(sunweight, sunweight);
+ LOCALPARAM(sunlight, vec4(sunscale, atmoalpha));
+ LOCALPARAM(sundir, sunlightdir);
+
+ // invert extinction at zenith to get an approximation of how bright the sun disk should be
+ vec zenithdepth = vec(atmoshells).add(planetradius*planetradius).sqrt().sub(planetradius);
+ vec zenithweight = vec(betar).mul(zenithdepth.x).madd(betam, zenithdepth.y).madd(betao, zenithdepth.z - zenithdepth.x);
+ vec zenithextinction = vec(zenithweight).sub(sunweight).exp2();
+ vec diskcolor = (atmosundisk ? atmosundiskcolor.tocolor() : suncolor).square().mul(zenithextinction).mul(atmosundiskbright * (glaring ? 1 : 1.5f)).min(1);
+ LOCALPARAM(sundiskcolor, diskcolor);
+
+ // convert from view cosine into mu^2 for limb darkening, where mu = sqrt(1 - sin^2) and sin^2 = 1 - cos^2, thus mu^2 = 1 - (1 - cos^2*scale)
+ // convert corona offset into scale for mu^2, where sin = (1-corona) and thus mu^2 = 1 - (1-corona^2)
+ float sundiskscale = sinf(0.5f*atmosundisksize*RAD);
+ float coronamu = 1 - (1-atmosundiskcorona)*(1-atmosundiskcorona);
+ if(sundiskscale > 0) LOCALPARAMF(sundiskparams, 1.0f/(sundiskscale*sundiskscale), 1.0f/max(coronamu, 1e-3f));
+ else LOCALPARAMF(sundiskparams, 0, 0);
+
+ float z1 = 2*w*(z1clip-0.5f), z2 = ceil(2*w*(z2clip-0.5f));
+
+ gle::defvertex();
+
+ if(glaring)
+ {
+ if(sundiskscale > 0 && sunlightdir.z*w + sundiskscale > z1 && sunlightdir.z*w - sundiskscale < z2)
+ {
+ gle::begin(GL_TRIANGLE_FAN);
+ vec spoke;
+ spoke.orthogonal(sunlightdir);
+ spoke.rescale(2*sundiskscale);
+ loopi(4) gle::attrib(vec(spoke).rotate(-2*M_PI*i/4.0f, sunlightdir).add(sunlightdir).mul(w));
+ xtraverts += gle::end();
+ }
+ return;
+ }
+
+ gle::begin(GL_QUADS);
+
+ if(faces&0x01)
+ {
+ gle::attribf(-w, -w, z1);
+ gle::attribf(-w, w, z1);
+ gle::attribf(-w, w, z2);
+ gle::attribf(-w, -w, z2);
+ }
+
+ if(faces&0x02)
+ {
+ gle::attribf(w, -w, z2);
+ gle::attribf(w, w, z2);
+ gle::attribf(w, w, z1);
+ gle::attribf(w, -w, z1);
+ }
+
+ if(faces&0x04)
+ {
+ gle::attribf(-w, -w, z2);
+ gle::attribf( w, -w, z2);
+ gle::attribf( w, -w, z1);
+ gle::attribf(-w, -w, z1);
+ }
+
+ if(faces&0x08)
+ {
+ gle::attribf( w, w, z2);
+ gle::attribf(-w, w, z2);
+ gle::attribf(-w, w, z1);
+ gle::attribf( w, w, z1);
+ }
+
+ if(z1clip <= 0 && faces&0x10)
+ {
+ gle::attribf(-w, -w, -w);
+ gle::attribf( w, -w, -w);
+ gle::attribf( w, w, -w);
+ gle::attribf(-w, w, -w);
+ }
+
+ if(z2clip >= 1 && faces&0x20)
+ {
+ gle::attribf( w, -w, w);
+ gle::attribf(-w, -w, w);
+ gle::attribf(-w, w, w);
+ gle::attribf( w, w, w);
+ }
+
+ xtraverts += gle::end();
}
VARP(sparklyfix, 0, 0, 1);
-VAR(showsky, 0, 1, 1);
+VAR(showsky, 0, 1, 1);
VAR(clipsky, 0, 1, 1);
bool drawskylimits(bool explicitonly)
{
- nocolorshader->set();
+ nocolorshader->set();
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
- bool rendered = rendersky(explicitonly);
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ bool rendered = rendersky(explicitonly);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- return rendered;
+ return rendered;
}
void drawskyoutline()
{
- notextureshader->set();
-
- glDepthMask(GL_FALSE);
- extern int wireframe;
- if(!wireframe)
- {
- enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- }
- gle::colorf(0.5f, 0.0f, 0.5f);
- rendersky(true);
- if(!wireframe)
- {
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
- }
- glDepthMask(GL_TRUE);
+ notextureshader->set();
+
+ glDepthMask(GL_FALSE);
+ extern int wireframe;
+ if(!wireframe)
+ {
+ enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ }
+ gle::colorf(0.5f, 0.0f, 0.5f);
+ rendersky(true);
+ if(!wireframe)
+ {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
+ }
+ glDepthMask(GL_TRUE);
}
VAR(clampsky, 0, 1, 1);
static int yawskyfaces(int faces, int yaw, float spin = 0)
{
- if(spin || yaw%90) return faces&0x0F ? faces | 0x0F : faces;
- static const int faceidxs[3][4] =
- {
- { 3, 2, 0, 1 },
- { 1, 0, 3, 2 },
- { 2, 3, 1, 0 }
- };
- yaw /= 90;
- if(yaw < 1 || yaw > 3) return faces;
- const int *idxs = faceidxs[yaw - 1];
- return (faces & ~0x0F) | (((faces>>idxs[0])&1)<<0) | (((faces>>idxs[1])&1)<<1) | (((faces>>idxs[2])&1)<<2) | (((faces>>idxs[3])&1)<<3);
+ if(spin || yaw%90) return faces&0x0F ? faces | 0x0F : faces;
+ static const int faceidxs[3][4] =
+ {
+ { 3, 2, 0, 1 },
+ { 1, 0, 3, 2 },
+ { 2, 3, 1, 0 }
+ };
+ yaw /= 90;
+ if(yaw < 1 || yaw > 3) return faces;
+ const int *idxs = faceidxs[yaw - 1];
+ return (faces & ~0x0F) | (((faces>>idxs[0])&1)<<0) | (((faces>>idxs[1])&1)<<1) | (((faces>>idxs[2])&1)<<2) | (((faces>>idxs[3])&1)<<3);
}
void drawskybox(int farplane, bool limited, bool force)
{
- extern int renderedskyfaces, renderedskyclip; // , renderedsky, renderedexplicitsky;
- bool alwaysrender = editmode || !insideworld(camera1->o) || reflecting || force,
- explicitonly = false;
- if(limited)
- {
- explicitonly = alwaysrender || !sparklyfix || refracting;
- if(!drawskylimits(explicitonly) && !alwaysrender) return;
- extern int ati_skybox_bug;
- if(!alwaysrender && !renderedskyfaces && !ati_skybox_bug) explicitonly = false;
- }
- else if(!alwaysrender)
- {
- renderedskyfaces = 0;
- renderedskyclip = INT_MAX;
- for(vtxarray *va = visibleva; va; va = va->next)
- {
- if(va->occluded >= OCCLUDE_BB && va->skyfaces&0x80) continue;
- renderedskyfaces |= va->skyfaces&0x3F;
- if(!(va->skyfaces&0x1F) || camera1->o.z < va->skyclip) renderedskyclip = min(renderedskyclip, va->skyclip);
- else renderedskyclip = 0;
- }
- if(!renderedskyfaces) return;
- }
-
- if(alwaysrender)
- {
- renderedskyfaces = 0x3F;
- renderedskyclip = 0;
- }
-
- float skyclip = clipsky ? max(renderedskyclip-1, 0) : 0, topclip = 1;
- if(reflectz<worldsize)
- {
- if(refracting<0) topclip = 0.5f + 0.5f*(reflectz-camera1->o.z)/float(worldsize);
- else if(reflectz>skyclip) skyclip = reflectz;
- }
- if(skyclip) skyclip = 0.5f + 0.5f*(skyclip-camera1->o.z)/float(worldsize);
-
- if(limited)
- {
- if(explicitonly) glDisable(GL_DEPTH_TEST);
- else glDepthFunc(GL_GEQUAL);
- }
- else glDepthFunc(GL_LEQUAL);
-
- glDepthMask(GL_FALSE);
-
- if(clampsky) glDepthRange(1, 1);
-
- if(!atmo || (skybox[0] && atmoalpha < 1))
- {
- if(glaring) SETSHADER(skyboxglare);
- else SETSHADER(skybox);
-
- gle::color(vec::hexcolor(skyboxcolour));
-
- matrix4 skymatrix = cammatrix, skyprojmatrix;
- skymatrix.settranslation(0, 0, 0);
- skymatrix.rotate_around_z((spinsky*lastmillis/1000.0f+yawsky)*-RAD);
- skyprojmatrix.mul(projmatrix, skymatrix);
- LOCALPARAM(skymatrix, skyprojmatrix);
-
- drawenvbox(farplane/2, skyclip, topclip, yawskyfaces(renderedskyfaces, yawsky, spinsky), sky);
- }
-
- if(atmo)
- {
- if(atmoalpha < 1)
- {
- if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
-
- drawatmosphere(farplane/2, skyclip, topclip, renderedskyfaces);
-
- if(atmoalpha < 1) glDisable(GL_BLEND);
- }
-
- if(!glaring)
- {
- if(fogdomemax && !fogdomeclouds)
- {
- if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
- drawfogdome(farplane);
- }
-
- if(cloudbox[0])
- {
- SETSHADER(skybox);
-
- if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- gle::color(vec::hexcolor(cloudboxcolour), cloudboxalpha);
-
- matrix4 skymatrix = cammatrix, skyprojmatrix;
- skymatrix.settranslation(0, 0, 0);
- skymatrix.rotate_around_z((spinclouds*lastmillis/1000.0f+yawclouds)*-RAD);
- skyprojmatrix.mul(projmatrix, skymatrix);
- LOCALPARAM(skymatrix, skyprojmatrix);
-
- drawenvbox(farplane/2, skyclip ? skyclip : cloudclip, topclip, yawskyfaces(renderedskyfaces, yawclouds, spinclouds), clouds);
-
- glDisable(GL_BLEND);
- }
-
- if(cloudlayer[0] && cloudheight && renderedskyfaces&(cloudheight < 0 ? 0x1F : 0x2F))
- {
- SETSHADER(skybox);
-
- if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
-
- glDisable(GL_CULL_FACE);
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- matrix4 skymatrix = cammatrix, skyprojmatrix;
- skymatrix.settranslation(0, 0, 0);
- skymatrix.rotate_around_z((spincloudlayer*lastmillis/1000.0f+yawcloudlayer)*-RAD);
- skyprojmatrix.mul(projmatrix, skymatrix);
- LOCALPARAM(skymatrix, skyprojmatrix);
+ extern int renderedskyfaces, renderedskyclip; // , renderedsky, renderedexplicitsky;
+ bool alwaysrender = editmode || !insideworld(camera1->o) || reflecting || force,
+ explicitonly = false;
+ if(limited)
+ {
+ explicitonly = alwaysrender || !sparklyfix || refracting;
+ if(!drawskylimits(explicitonly) && !alwaysrender) return;
+ extern int ati_skybox_bug;
+ if(!alwaysrender && !renderedskyfaces && !ati_skybox_bug) explicitonly = false;
+ }
+ else if(!alwaysrender)
+ {
+ renderedskyfaces = 0;
+ renderedskyclip = INT_MAX;
+ for(vtxarray *va = visibleva; va; va = va->next)
+ {
+ if(va->occluded >= OCCLUDE_BB && va->skyfaces&0x80) continue;
+ renderedskyfaces |= va->skyfaces&0x3F;
+ if(!(va->skyfaces&0x1F) || camera1->o.z < va->skyclip) renderedskyclip = min(renderedskyclip, va->skyclip);
+ else renderedskyclip = 0;
+ }
+ if(!renderedskyfaces) return;
+ }
+
+ if(alwaysrender)
+ {
+ renderedskyfaces = 0x3F;
+ renderedskyclip = 0;
+ }
+
+ float skyclip = clipsky ? max(renderedskyclip-1, 0) : 0, topclip = 1;
+ if(reflectz<worldsize)
+ {
+ if(refracting<0) topclip = 0.5f + 0.5f*(reflectz-camera1->o.z)/float(worldsize);
+ else if(reflectz>skyclip) skyclip = reflectz;
+ }
+ if(skyclip) skyclip = 0.5f + 0.5f*(skyclip-camera1->o.z)/float(worldsize);
+
+ if(limited)
+ {
+ if(explicitonly) glDisable(GL_DEPTH_TEST);
+ else glDepthFunc(GL_GEQUAL);
+ }
+ else glDepthFunc(GL_LEQUAL);
+
+ glDepthMask(GL_FALSE);
+
+ if(clampsky) glDepthRange(1, 1);
+
+ if(!atmo || (skybox[0] && atmoalpha < 1))
+ {
+ if(glaring) SETSHADER(skyboxglare);
+ else SETSHADER(skybox);
+
+ gle::color(vec::hexcolor(skyboxcolour));
+
+ matrix4 skymatrix = cammatrix, skyprojmatrix;
+ skymatrix.settranslation(0, 0, 0);
+ skymatrix.rotate_around_z((spinsky*lastmillis/1000.0f+yawsky)*-RAD);
+ skyprojmatrix.mul(projmatrix, skymatrix);
+ LOCALPARAM(skymatrix, skyprojmatrix);
+
+ drawenvbox(farplane/2, skyclip, topclip, yawskyfaces(renderedskyfaces, yawsky, spinsky), sky);
+ }
+
+ if(atmo)
+ {
+ if(atmoalpha < 1)
+ {
+ if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ drawatmosphere(farplane/2, skyclip, topclip, renderedskyfaces);
+
+ if(atmoalpha < 1) glDisable(GL_BLEND);
+ }
+
+ if(!glaring)
+ {
+ if(fogdomemax && !fogdomeclouds)
+ {
+ if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+ drawfogdome(farplane);
+ }
+
+ if(cloudbox[0])
+ {
+ SETSHADER(skybox);
+
+ if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ gle::color(vec::hexcolor(cloudboxcolour), cloudboxalpha);
+
+ matrix4 skymatrix = cammatrix, skyprojmatrix;
+ skymatrix.settranslation(0, 0, 0);
+ skymatrix.rotate_around_z((spinclouds*lastmillis/1000.0f+yawclouds)*-RAD);
+ skyprojmatrix.mul(projmatrix, skymatrix);
+ LOCALPARAM(skymatrix, skyprojmatrix);
+
+ drawenvbox(farplane/2, skyclip ? skyclip : cloudclip, topclip, yawskyfaces(renderedskyfaces, yawclouds, spinclouds), clouds);
+
+ glDisable(GL_BLEND);
+ }
+
+ if(cloudlayer[0] && cloudheight && renderedskyfaces&(cloudheight < 0 ? 0x1F : 0x2F))
+ {
+ SETSHADER(skybox);
+
+ if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+
+ glDisable(GL_CULL_FACE);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ matrix4 skymatrix = cammatrix, skyprojmatrix;
+ skymatrix.settranslation(0, 0, 0);
+ skymatrix.rotate_around_z((spincloudlayer*lastmillis/1000.0f+yawcloudlayer)*-RAD);
+ skyprojmatrix.mul(projmatrix, skymatrix);
+ LOCALPARAM(skymatrix, skyprojmatrix);
- drawenvoverlay(farplane/2, cloudoverlay, cloudoffsetx + cloudscrollx * lastmillis/1000.0f, cloudoffsety + cloudscrolly * lastmillis/1000.0f);
+ drawenvoverlay(farplane/2, cloudoverlay, cloudoffsetx + cloudscrollx * lastmillis/1000.0f, cloudoffsety + cloudscrolly * lastmillis/1000.0f);
- glDisable(GL_BLEND);
+ glDisable(GL_BLEND);
- glEnable(GL_CULL_FACE);
- }
+ glEnable(GL_CULL_FACE);
+ }
- if(fogdomemax && fogdomeclouds)
- {
- if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
- drawfogdome(farplane);
- }
- }
+ if(fogdomemax && fogdomeclouds)
+ {
+ if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+ drawfogdome(farplane);
+ }
+ }
- if(clampsky) glDepthRange(0, 1);
+ if(clampsky) glDepthRange(0, 1);
- glDepthMask(GL_TRUE);
+ glDepthMask(GL_TRUE);
- if(limited)
- {
- if(explicitonly) glEnable(GL_DEPTH_TEST);
- else glDepthFunc(GL_LESS);
- if(!reflecting && !refracting && !drawtex && editmode && showsky) drawskyoutline();
- }
- else glDepthFunc(GL_LESS);
+ if(limited)
+ {
+ if(explicitonly) glEnable(GL_DEPTH_TEST);
+ else glDepthFunc(GL_LESS);
+ if(!reflecting && !refracting && !drawtex && editmode && showsky) drawskyoutline();
+ }
+ else glDepthFunc(GL_LESS);
}
VARNR(skytexture, useskytexture, 0, 1, 1);
@@ -759,16 +759,16 @@ double skyarea = 0;
bool limitsky()
{
- return (explicitsky && (useskytexture || editmode)) || (sparklyfix && skyarea / (double(worldsize)*double(worldsize)*6) < 0.9);
+ return (explicitsky && (useskytexture || editmode)) || (sparklyfix && skyarea / (double(worldsize)*double(worldsize)*6) < 0.9);
}
bool shouldrenderskyenvmap()
{
- return atmo != 0;
+ return atmo != 0;
}
bool shouldclearskyboxglare()
{
- return atmo && (!skybox[0] || atmoalpha >= 1);
+ return atmo && (!skybox[0] || atmoalpha >= 1);
}
diff --git a/src/engine/rendertarget.h b/src/engine/rendertarget.h
index f1e13e6..a22298b 100644
--- a/src/engine/rendertarget.h
+++ b/src/engine/rendertarget.h
@@ -2,390 +2,390 @@ extern int rtsharefb, rtscissor, blurtile;
struct rendertarget
{
- int texw, texh, vieww, viewh;
- GLenum colorfmt, depthfmt;
- GLuint rendertex, renderfb, renderdb, blurtex, blurfb, blurdb;
- int blursize, blurysize;
- float blursigma;
- float blurweights[MAXBLURRADIUS+1], bluroffsets[MAXBLURRADIUS+1];
- float bluryweights[MAXBLURRADIUS+1], bluryoffsets[MAXBLURRADIUS+1];
-
- float scissorx1, scissory1, scissorx2, scissory2;
+ int texw, texh, vieww, viewh;
+ GLenum colorfmt, depthfmt;
+ GLuint rendertex, renderfb, renderdb, blurtex, blurfb, blurdb;
+ int blursize, blurysize;
+ float blursigma;
+ float blurweights[MAXBLURRADIUS+1], bluroffsets[MAXBLURRADIUS+1];
+ float bluryweights[MAXBLURRADIUS+1], bluryoffsets[MAXBLURRADIUS+1];
+
+ float scissorx1, scissory1, scissorx2, scissory2;
#define BLURTILES 32
#define BLURTILEMASK (0xFFFFFFFFU>>(32-BLURTILES))
- uint blurtiles[BLURTILES+1];
-
- bool initialized;
-
- rendertarget() : texw(0), texh(0), vieww(0), viewh(0), colorfmt(GL_FALSE), depthfmt(GL_FALSE), rendertex(0), renderfb(0), renderdb(0), blurtex(0), blurfb(0), blurdb(0), blursize(0), blurysize(0), blursigma(0), initialized(false)
- {
- }
-
- virtual ~rendertarget() {}
-
- virtual GLenum attachment() const
- {
- return GL_COLOR_ATTACHMENT0;
- }
-
- virtual const GLenum *colorformats() const
- {
- static const GLenum colorfmts[] = { GL_RGB, GL_RGB8, GL_FALSE };
- return colorfmts;
- }
-
- virtual const GLenum *depthformats() const
- {
- static const GLenum depthfmts[] = { GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_FALSE };
- return depthfmts;
- }
-
- virtual bool depthtest() const { return true; }
-
- void cleanup(bool fullclean = false)
- {
- if(renderfb) { glDeleteFramebuffers_(1, &renderfb); renderfb = 0; }
- if(renderdb) { glDeleteRenderbuffers_(1, &renderdb); renderdb = 0; }
- if(rendertex) { glDeleteTextures(1, &rendertex); rendertex = 0; }
- texw = texh = 0;
- cleanupblur();
-
- if(fullclean) colorfmt = depthfmt = GL_FALSE;
- }
-
- void cleanupblur()
- {
- if(blurfb) { glDeleteFramebuffers_(1, &blurfb); blurfb = 0; }
- if(blurtex) { glDeleteTextures(1, &blurtex); blurtex = 0; }
- if(blurdb) { glDeleteRenderbuffers_(1, &blurdb); blurdb = 0; }
- blursize = blurysize = 0;
- blursigma = 0.0f;
- }
-
- void setupblur()
- {
- if(!blurtex) glGenTextures(1, &blurtex);
- createtexture(blurtex, texw, texh, NULL, 3, 1, colorfmt);
-
- if(!swaptexs() || rtsharefb) return;
- if(!blurfb) glGenFramebuffers_(1, &blurfb);
- glBindFramebuffer_(GL_FRAMEBUFFER, blurfb);
- glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, blurtex, 0);
- if(depthtest())
- {
- if(!blurdb) glGenRenderbuffers_(1, &blurdb);
- glGenRenderbuffers_(1, &blurdb);
- glBindRenderbuffer_(GL_RENDERBUFFER, blurdb);
- glRenderbufferStorage_(GL_RENDERBUFFER, depthfmt, texw, texh);
- glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, blurdb);
- }
- glBindFramebuffer_(GL_FRAMEBUFFER, 0);
- }
-
- void setup(int w, int h)
- {
- if(!renderfb) glGenFramebuffers_(1, &renderfb);
- glBindFramebuffer_(GL_FRAMEBUFFER, renderfb);
- if(!rendertex) glGenTextures(1, &rendertex);
-
- GLenum attach = attachment();
- if(attach == GL_DEPTH_ATTACHMENT)
- {
- glDrawBuffer(GL_NONE);
- glReadBuffer(GL_NONE);
- }
-
- const GLenum *colorfmts = colorformats();
- int find = 0;
- do
- {
- createtexture(rendertex, w, h, NULL, 3, filter() ? 1 : 0, colorfmt ? colorfmt : colorfmts[find]);
- glFramebufferTexture2D_(GL_FRAMEBUFFER, attach, GL_TEXTURE_2D, rendertex, 0);
- if(glCheckFramebufferStatus_(GL_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE) break;
- }
- while(!colorfmt && colorfmts[++find]);
- if(!colorfmt) colorfmt = colorfmts[find];
-
- if(attach != GL_DEPTH_ATTACHMENT && depthtest())
- {
- if(!renderdb) { glGenRenderbuffers_(1, &renderdb); depthfmt = GL_FALSE; }
- if(!depthfmt) glBindRenderbuffer_(GL_RENDERBUFFER, renderdb);
- const GLenum *depthfmts = depthformats();
- find = 0;
- do
- {
- if(!depthfmt) glRenderbufferStorage_(GL_RENDERBUFFER, depthfmts[find], w, h);
- glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderdb);
- if(glCheckFramebufferStatus_(GL_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE) break;
- }
- while(!depthfmt && depthfmts[++find]);
- if(!depthfmt) depthfmt = depthfmts[find];
- }
-
- glBindFramebuffer_(GL_FRAMEBUFFER, 0);
-
- texw = w;
- texh = h;
- initialized = false;
- }
-
- bool addblurtiles(float x1, float y1, float x2, float y2, float blurmargin = 0)
- {
- if(x1 >= 1 || y1 >= 1 || x2 <= -1 || y2 <= -1) return false;
-
- scissorx1 = min(scissorx1, max(x1, -1.0f));
- scissory1 = min(scissory1, max(y1, -1.0f));
- scissorx2 = max(scissorx2, min(x2, 1.0f));
- scissory2 = max(scissory2, min(y2, 1.0f));
-
- float blurerror = 2.0f*float(2*blursize + blurmargin);
- int tx1 = max(0, min(BLURTILES - 1, int((x1-blurerror/vieww + 1)/2 * BLURTILES))),
- ty1 = max(0, min(BLURTILES - 1, int((y1-blurerror/viewh + 1)/2 * BLURTILES))),
- tx2 = max(0, min(BLURTILES - 1, int((x2+blurerror/vieww + 1)/2 * BLURTILES))),
- ty2 = max(0, min(BLURTILES - 1, int((y2+blurerror/viewh + 1)/2 * BLURTILES)));
-
- uint mask = (BLURTILEMASK>>(BLURTILES - (tx2+1))) & (BLURTILEMASK<<tx1);
- for(int y = ty1; y <= ty2; y++) blurtiles[y] |= mask;
- return true;
- }
-
- bool checkblurtiles(float x1, float y1, float x2, float y2, float blurmargin = 0)
- {
- float blurerror = 2.0f*float(2*blursize + blurmargin);
- if(x2+blurerror/vieww < scissorx1 || y2+blurerror/viewh < scissory1 ||
- x1-blurerror/vieww > scissorx2 || y1-blurerror/viewh > scissory2)
- return false;
-
- if(!blurtile) return true;
-
- int tx1 = max(0, min(BLURTILES - 1, int((x1 + 1)/2 * BLURTILES))),
- ty1 = max(0, min(BLURTILES - 1, int((y1 + 1)/2 * BLURTILES))),
- tx2 = max(0, min(BLURTILES - 1, int((x2 + 1)/2 * BLURTILES))),
- ty2 = max(0, min(BLURTILES - 1, int((y2 + 1)/2 * BLURTILES)));
-
- uint mask = (BLURTILEMASK>>(BLURTILES - (tx2+1))) & (BLURTILEMASK<<tx1);
- for(int y = ty1; y <= ty2; y++) if(blurtiles[y] & mask) return true;
-
- return false;
- }
-
- void rendertiles()
- {
- float wscale = vieww/float(texw), hscale = viewh/float(texh);
- if(blurtile && scissorx1 < scissorx2 && scissory1 < scissory2)
- {
- uint tiles[sizeof(blurtiles)/sizeof(uint)];
- memcpy(tiles, blurtiles, sizeof(blurtiles));
-
- LOCALPARAMF(screentexcoord0, wscale*0.5f, hscale*0.5f, wscale*0.5f, hscale*0.5f);
- gle::defvertex(2);
- gle::begin(GL_QUADS);
- float tsz = 1.0f/BLURTILES;
- loop(y, BLURTILES+1)
- {
- uint mask = tiles[y];
- int x = 0;
- while(mask)
- {
- while(!(mask&0xFF)) { mask >>= 8; x += 8; }
- while(!(mask&1)) { mask >>= 1; x++; }
- int xstart = x;
- do { mask >>= 1; x++; } while(mask&1);
- uint strip = (BLURTILEMASK>>(BLURTILES - x)) & (BLURTILEMASK<<xstart);
- int yend = y;
- do { tiles[yend] &= ~strip; yend++; } while((tiles[yend] & strip) == strip);
- float tx = xstart*tsz,
- ty = y*tsz,
- tw = (x-xstart)*tsz,
- th = (yend-y)*tsz,
- vx = 2*tx - 1, vy = 2*ty - 1, vw = tw*2, vh = th*2;
- gle::attribf(vx, vy);
- gle::attribf(vx+vw, vy);
- gle::attribf(vx+vw, vy+vh);
- gle::attribf(vx, vy+vh);
- }
- }
- gle::end();
- }
- else
- {
- screenquad(wscale, hscale);
- }
- }
-
- void blur(int wantsblursize, float wantsblursigma, int wantsblurysize, int x, int y, int w, int h, bool scissor)
- {
- if(!blurtex) setupblur();
- if(blursize!=wantsblursize || blurysize != wantsblurysize || (wantsblursize && blursigma!=wantsblursigma))
- {
- setupblurkernel(wantsblursize, wantsblursigma, blurweights, bluroffsets);
- if(wantsblurysize != wantsblursize) setupblurkernel(wantsblurysize, wantsblursigma, bluryweights, bluryoffsets);
- blursize = wantsblursize;
- blursigma = wantsblursigma;
- blurysize = wantsblurysize;
- }
-
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_CULL_FACE);
-
- if(scissor)
- {
- glScissor(x, y, w, h);
- glEnable(GL_SCISSOR_TEST);
- }
-
- loopi(2)
- {
- if(i && blurysize != blursize) setblurshader(i, texh, blurysize, bluryweights, bluryoffsets);
- else setblurshader(i, i ? texh : texw, blursize, blurweights, bluroffsets);
-
- if(!swaptexs() || rtsharefb) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, i ? rendertex : blurtex, 0);
- else glBindFramebuffer_(GL_FRAMEBUFFER, i ? renderfb : blurfb);
- glBindTexture(GL_TEXTURE_2D, i ? blurtex : rendertex);
-
- rendertiles();
- }
-
- if(scissor) glDisable(GL_SCISSOR_TEST);
-
- glEnable(GL_DEPTH_TEST);
- glEnable(GL_CULL_FACE);
- }
-
- virtual bool swaptexs() const { return false; }
-
- virtual bool dorender() { return true; }
-
- virtual bool shouldrender() { return true; }
-
- virtual void doblur(int blursize, float blursigma, int blurysize)
- {
- int sx, sy, sw, sh;
- bool scissoring = rtscissor && scissorblur(sx, sy, sw, sh) && sw > 0 && sh > 0;
- if(!scissoring) { sx = sy = 0; sw = vieww; sh = viewh; }
- blur(blursize, blursigma, blurysize, sx, sy, sw, sh, scissoring);
- }
-
- virtual bool scissorrender(int &x, int &y, int &w, int &h)
- {
- if(scissorx1 >= scissorx2 || scissory1 >= scissory2)
- {
- if(vieww < texw || viewh < texh)
- {
- x = y = 0;
- w = vieww;
- h = viewh;
- return true;
- }
- return false;
- }
- x = max(int(floor((scissorx1+1)/2*vieww)) - 2*blursize, 0);
- y = max(int(floor((scissory1+1)/2*viewh)) - 2*blursize, 0);
- w = min(int(ceil((scissorx2+1)/2*vieww)) + 2*blursize, vieww) - x;
- h = min(int(ceil((scissory2+1)/2*viewh)) + 2*blursize, viewh) - y;
- return true;
- }
-
- virtual bool scissorblur(int &x, int &y, int &w, int &h)
- {
- if(scissorx1 >= scissorx2 || scissory1 >= scissory2)
- {
- if(vieww < texw || viewh < texh)
- {
- x = y = 0;
- w = vieww;
- h = viewh;
- return true;
- }
- return false;
- }
- x = max(int(floor((scissorx1+1)/2*vieww)), 0);
- y = max(int(floor((scissory1+1)/2*viewh)), 0);
- w = min(int(ceil((scissorx2+1)/2*vieww)), vieww) - x;
- h = min(int(ceil((scissory2+1)/2*viewh)), viewh) - y;
- return true;
- }
-
- virtual void doclear() {}
-
- virtual bool screenrect() const { return false; }
- virtual bool filter() const { return true; }
-
- void render(int w, int h, int blursize = 0, float blursigma = 0, int blurysize = 0)
- {
- w = min(w, hwtexsize);
- h = min(h, hwtexsize);
- if(screenrect())
- {
- if(w > screenw) w = screenw;
- if(h > screenh) h = screenh;
- }
- vieww = w;
- viewh = h;
- if(w!=texw || h!=texh || (swaptexs() && !rtsharefb ? !blurfb : blurfb)) cleanup();
-
- if(!filter())
- {
- if(blurtex) cleanupblur();
- blursize = blurysize = 0;
- }
-
- if(!rendertex) setup(w, h);
-
- scissorx2 = scissory2 = -1;
- scissorx1 = scissory1 = 1;
- memset(blurtiles, 0, sizeof(blurtiles));
-
- if(!shouldrender()) return;
-
- if(blursize && !blurtex) setupblur();
- if(swaptexs() && blursize)
- {
- swap(rendertex, blurtex);
- if(!rtsharefb)
- {
- swap(renderfb, blurfb);
- swap(renderdb, blurdb);
- }
- }
- glBindFramebuffer_(GL_FRAMEBUFFER, renderfb);
- if(swaptexs() && blursize && rtsharefb)
- glFramebufferTexture2D_(GL_FRAMEBUFFER, attachment(), GL_TEXTURE_2D, rendertex, 0);
- glViewport(0, 0, vieww, viewh);
-
- doclear();
-
- int sx, sy, sw, sh;
- bool scissoring = rtscissor && scissorrender(sx, sy, sw, sh) && sw > 0 && sh > 0;
- if(scissoring)
- {
- glScissor(sx, sy, sw, sh);
- glEnable(GL_SCISSOR_TEST);
- }
- else
- {
- sx = sy = 0;
- sw = vieww;
- sh = viewh;
- }
-
- if(!depthtest()) glDisable(GL_DEPTH_TEST);
-
- bool succeeded = dorender();
-
- if(!depthtest()) glEnable(GL_DEPTH_TEST);
-
- if(scissoring) glDisable(GL_SCISSOR_TEST);
-
- if(succeeded)
- {
- initialized = true;
-
- if(blursize) doblur(blursize, blursigma, blurysize ? blurysize : blursize);
- }
-
- glBindFramebuffer_(GL_FRAMEBUFFER, 0);
- glViewport(0, 0, screenw, screenh);
- }
+ uint blurtiles[BLURTILES+1];
+
+ bool initialized;
+
+ rendertarget() : texw(0), texh(0), vieww(0), viewh(0), colorfmt(GL_FALSE), depthfmt(GL_FALSE), rendertex(0), renderfb(0), renderdb(0), blurtex(0), blurfb(0), blurdb(0), blursize(0), blurysize(0), blursigma(0), initialized(false)
+ {
+ }
+
+ virtual ~rendertarget() {}
+
+ virtual GLenum attachment() const
+ {
+ return GL_COLOR_ATTACHMENT0;
+ }
+
+ virtual const GLenum *colorformats() const
+ {
+ static const GLenum colorfmts[] = { GL_RGB, GL_RGB8, GL_FALSE };
+ return colorfmts;
+ }
+
+ virtual const GLenum *depthformats() const
+ {
+ static const GLenum depthfmts[] = { GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_FALSE };
+ return depthfmts;
+ }
+
+ virtual bool depthtest() const { return true; }
+
+ void cleanup(bool fullclean = false)
+ {
+ if(renderfb) { glDeleteFramebuffers_(1, &renderfb); renderfb = 0; }
+ if(renderdb) { glDeleteRenderbuffers_(1, &renderdb); renderdb = 0; }
+ if(rendertex) { glDeleteTextures(1, &rendertex); rendertex = 0; }
+ texw = texh = 0;
+ cleanupblur();
+
+ if(fullclean) colorfmt = depthfmt = GL_FALSE;
+ }
+
+ void cleanupblur()
+ {
+ if(blurfb) { glDeleteFramebuffers_(1, &blurfb); blurfb = 0; }
+ if(blurtex) { glDeleteTextures(1, &blurtex); blurtex = 0; }
+ if(blurdb) { glDeleteRenderbuffers_(1, &blurdb); blurdb = 0; }
+ blursize = blurysize = 0;
+ blursigma = 0.0f;
+ }
+
+ void setupblur()
+ {
+ if(!blurtex) glGenTextures(1, &blurtex);
+ createtexture(blurtex, texw, texh, NULL, 3, 1, colorfmt);
+
+ if(!swaptexs() || rtsharefb) return;
+ if(!blurfb) glGenFramebuffers_(1, &blurfb);
+ glBindFramebuffer_(GL_FRAMEBUFFER, blurfb);
+ glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, blurtex, 0);
+ if(depthtest())
+ {
+ if(!blurdb) glGenRenderbuffers_(1, &blurdb);
+ glGenRenderbuffers_(1, &blurdb);
+ glBindRenderbuffer_(GL_RENDERBUFFER, blurdb);
+ glRenderbufferStorage_(GL_RENDERBUFFER, depthfmt, texw, texh);
+ glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, blurdb);
+ }
+ glBindFramebuffer_(GL_FRAMEBUFFER, 0);
+ }
+
+ void setup(int w, int h)
+ {
+ if(!renderfb) glGenFramebuffers_(1, &renderfb);
+ glBindFramebuffer_(GL_FRAMEBUFFER, renderfb);
+ if(!rendertex) glGenTextures(1, &rendertex);
+
+ GLenum attach = attachment();
+ if(attach == GL_DEPTH_ATTACHMENT)
+ {
+ glDrawBuffer(GL_NONE);
+ glReadBuffer(GL_NONE);
+ }
+
+ const GLenum *colorfmts = colorformats();
+ int find = 0;
+ do
+ {
+ createtexture(rendertex, w, h, NULL, 3, filter() ? 1 : 0, colorfmt ? colorfmt : colorfmts[find]);
+ glFramebufferTexture2D_(GL_FRAMEBUFFER, attach, GL_TEXTURE_2D, rendertex, 0);
+ if(glCheckFramebufferStatus_(GL_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE) break;
+ }
+ while(!colorfmt && colorfmts[++find]);
+ if(!colorfmt) colorfmt = colorfmts[find];
+
+ if(attach != GL_DEPTH_ATTACHMENT && depthtest())
+ {
+ if(!renderdb) { glGenRenderbuffers_(1, &renderdb); depthfmt = GL_FALSE; }
+ if(!depthfmt) glBindRenderbuffer_(GL_RENDERBUFFER, renderdb);
+ const GLenum *depthfmts = depthformats();
+ find = 0;
+ do
+ {
+ if(!depthfmt) glRenderbufferStorage_(GL_RENDERBUFFER, depthfmts[find], w, h);
+ glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderdb);
+ if(glCheckFramebufferStatus_(GL_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE) break;
+ }
+ while(!depthfmt && depthfmts[++find]);
+ if(!depthfmt) depthfmt = depthfmts[find];
+ }
+
+ glBindFramebuffer_(GL_FRAMEBUFFER, 0);
+
+ texw = w;
+ texh = h;
+ initialized = false;
+ }
+
+ bool addblurtiles(float x1, float y1, float x2, float y2, float blurmargin = 0)
+ {
+ if(x1 >= 1 || y1 >= 1 || x2 <= -1 || y2 <= -1) return false;
+
+ scissorx1 = min(scissorx1, max(x1, -1.0f));
+ scissory1 = min(scissory1, max(y1, -1.0f));
+ scissorx2 = max(scissorx2, min(x2, 1.0f));
+ scissory2 = max(scissory2, min(y2, 1.0f));
+
+ float blurerror = 2.0f*float(2*blursize + blurmargin);
+ int tx1 = max(0, min(BLURTILES - 1, int((x1-blurerror/vieww + 1)/2 * BLURTILES))),
+ ty1 = max(0, min(BLURTILES - 1, int((y1-blurerror/viewh + 1)/2 * BLURTILES))),
+ tx2 = max(0, min(BLURTILES - 1, int((x2+blurerror/vieww + 1)/2 * BLURTILES))),
+ ty2 = max(0, min(BLURTILES - 1, int((y2+blurerror/viewh + 1)/2 * BLURTILES)));
+
+ uint mask = (BLURTILEMASK>>(BLURTILES - (tx2+1))) & (BLURTILEMASK<<tx1);
+ for(int y = ty1; y <= ty2; y++) blurtiles[y] |= mask;
+ return true;
+ }
+
+ bool checkblurtiles(float x1, float y1, float x2, float y2, float blurmargin = 0)
+ {
+ float blurerror = 2.0f*float(2*blursize + blurmargin);
+ if(x2+blurerror/vieww < scissorx1 || y2+blurerror/viewh < scissory1 ||
+ x1-blurerror/vieww > scissorx2 || y1-blurerror/viewh > scissory2)
+ return false;
+
+ if(!blurtile) return true;
+
+ int tx1 = max(0, min(BLURTILES - 1, int((x1 + 1)/2 * BLURTILES))),
+ ty1 = max(0, min(BLURTILES - 1, int((y1 + 1)/2 * BLURTILES))),
+ tx2 = max(0, min(BLURTILES - 1, int((x2 + 1)/2 * BLURTILES))),
+ ty2 = max(0, min(BLURTILES - 1, int((y2 + 1)/2 * BLURTILES)));
+
+ uint mask = (BLURTILEMASK>>(BLURTILES - (tx2+1))) & (BLURTILEMASK<<tx1);
+ for(int y = ty1; y <= ty2; y++) if(blurtiles[y] & mask) return true;
+
+ return false;
+ }
+
+ void rendertiles()
+ {
+ float wscale = vieww/float(texw), hscale = viewh/float(texh);
+ if(blurtile && scissorx1 < scissorx2 && scissory1 < scissory2)
+ {
+ uint tiles[sizeof(blurtiles)/sizeof(uint)];
+ memcpy(tiles, blurtiles, sizeof(blurtiles));
+
+ LOCALPARAMF(screentexcoord0, wscale*0.5f, hscale*0.5f, wscale*0.5f, hscale*0.5f);
+ gle::defvertex(2);
+ gle::begin(GL_QUADS);
+ float tsz = 1.0f/BLURTILES;
+ loop(y, BLURTILES+1)
+ {
+ uint mask = tiles[y];
+ int x = 0;
+ while(mask)
+ {
+ while(!(mask&0xFF)) { mask >>= 8; x += 8; }
+ while(!(mask&1)) { mask >>= 1; x++; }
+ int xstart = x;
+ do { mask >>= 1; x++; } while(mask&1);
+ uint strip = (BLURTILEMASK>>(BLURTILES - x)) & (BLURTILEMASK<<xstart);
+ int yend = y;
+ do { tiles[yend] &= ~strip; yend++; } while((tiles[yend] & strip) == strip);
+ float tx = xstart*tsz,
+ ty = y*tsz,
+ tw = (x-xstart)*tsz,
+ th = (yend-y)*tsz,
+ vx = 2*tx - 1, vy = 2*ty - 1, vw = tw*2, vh = th*2;
+ gle::attribf(vx, vy);
+ gle::attribf(vx+vw, vy);
+ gle::attribf(vx+vw, vy+vh);
+ gle::attribf(vx, vy+vh);
+ }
+ }
+ gle::end();
+ }
+ else
+ {
+ screenquad(wscale, hscale);
+ }
+ }
+
+ void blur(int wantsblursize, float wantsblursigma, int wantsblurysize, int x, int y, int w, int h, bool scissor)
+ {
+ if(!blurtex) setupblur();
+ if(blursize!=wantsblursize || blurysize != wantsblurysize || (wantsblursize && blursigma!=wantsblursigma))
+ {
+ setupblurkernel(wantsblursize, wantsblursigma, blurweights, bluroffsets);
+ if(wantsblurysize != wantsblursize) setupblurkernel(wantsblurysize, wantsblursigma, bluryweights, bluryoffsets);
+ blursize = wantsblursize;
+ blursigma = wantsblursigma;
+ blurysize = wantsblurysize;
+ }
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+
+ if(scissor)
+ {
+ glScissor(x, y, w, h);
+ glEnable(GL_SCISSOR_TEST);
+ }
+
+ loopi(2)
+ {
+ if(i && blurysize != blursize) setblurshader(i, texh, blurysize, bluryweights, bluryoffsets);
+ else setblurshader(i, i ? texh : texw, blursize, blurweights, bluroffsets);
+
+ if(!swaptexs() || rtsharefb) glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, i ? rendertex : blurtex, 0);
+ else glBindFramebuffer_(GL_FRAMEBUFFER, i ? renderfb : blurfb);
+ glBindTexture(GL_TEXTURE_2D, i ? blurtex : rendertex);
+
+ rendertiles();
+ }
+
+ if(scissor) glDisable(GL_SCISSOR_TEST);
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ }
+
+ virtual bool swaptexs() const { return false; }
+
+ virtual bool dorender() { return true; }
+
+ virtual bool shouldrender() { return true; }
+
+ virtual void doblur(int blursize, float blursigma, int blurysize)
+ {
+ int sx, sy, sw, sh;
+ bool scissoring = rtscissor && scissorblur(sx, sy, sw, sh) && sw > 0 && sh > 0;
+ if(!scissoring) { sx = sy = 0; sw = vieww; sh = viewh; }
+ blur(blursize, blursigma, blurysize, sx, sy, sw, sh, scissoring);
+ }
+
+ virtual bool scissorrender(int &x, int &y, int &w, int &h)
+ {
+ if(scissorx1 >= scissorx2 || scissory1 >= scissory2)
+ {
+ if(vieww < texw || viewh < texh)
+ {
+ x = y = 0;
+ w = vieww;
+ h = viewh;
+ return true;
+ }
+ return false;
+ }
+ x = max(int(floor((scissorx1+1)/2*vieww)) - 2*blursize, 0);
+ y = max(int(floor((scissory1+1)/2*viewh)) - 2*blursize, 0);
+ w = min(int(ceil((scissorx2+1)/2*vieww)) + 2*blursize, vieww) - x;
+ h = min(int(ceil((scissory2+1)/2*viewh)) + 2*blursize, viewh) - y;
+ return true;
+ }
+
+ virtual bool scissorblur(int &x, int &y, int &w, int &h)
+ {
+ if(scissorx1 >= scissorx2 || scissory1 >= scissory2)
+ {
+ if(vieww < texw || viewh < texh)
+ {
+ x = y = 0;
+ w = vieww;
+ h = viewh;
+ return true;
+ }
+ return false;
+ }
+ x = max(int(floor((scissorx1+1)/2*vieww)), 0);
+ y = max(int(floor((scissory1+1)/2*viewh)), 0);
+ w = min(int(ceil((scissorx2+1)/2*vieww)), vieww) - x;
+ h = min(int(ceil((scissory2+1)/2*viewh)), viewh) - y;
+ return true;
+ }
+
+ virtual void doclear() {}
+
+ virtual bool screenrect() const { return false; }
+ virtual bool filter() const { return true; }
+
+ void render(int w, int h, int blursize = 0, float blursigma = 0, int blurysize = 0)
+ {
+ w = min(w, hwtexsize);
+ h = min(h, hwtexsize);
+ if(screenrect())
+ {
+ if(w > screenw) w = screenw;
+ if(h > screenh) h = screenh;
+ }
+ vieww = w;
+ viewh = h;
+ if(w!=texw || h!=texh || (swaptexs() && !rtsharefb ? !blurfb : blurfb)) cleanup();
+
+ if(!filter())
+ {
+ if(blurtex) cleanupblur();
+ blursize = blurysize = 0;
+ }
+
+ if(!rendertex) setup(w, h);
+
+ scissorx2 = scissory2 = -1;
+ scissorx1 = scissory1 = 1;
+ memset(blurtiles, 0, sizeof(blurtiles));
+
+ if(!shouldrender()) return;
+
+ if(blursize && !blurtex) setupblur();
+ if(swaptexs() && blursize)
+ {
+ swap(rendertex, blurtex);
+ if(!rtsharefb)
+ {
+ swap(renderfb, blurfb);
+ swap(renderdb, blurdb);
+ }
+ }
+ glBindFramebuffer_(GL_FRAMEBUFFER, renderfb);
+ if(swaptexs() && blursize && rtsharefb)
+ glFramebufferTexture2D_(GL_FRAMEBUFFER, attachment(), GL_TEXTURE_2D, rendertex, 0);
+ glViewport(0, 0, vieww, viewh);
+
+ doclear();
+
+ int sx, sy, sw, sh;
+ bool scissoring = rtscissor && scissorrender(sx, sy, sw, sh) && sw > 0 && sh > 0;
+ if(scissoring)
+ {
+ glScissor(sx, sy, sw, sh);
+ glEnable(GL_SCISSOR_TEST);
+ }
+ else
+ {
+ sx = sy = 0;
+ sw = vieww;
+ sh = viewh;
+ }
+
+ if(!depthtest()) glDisable(GL_DEPTH_TEST);
+
+ bool succeeded = dorender();
+
+ if(!depthtest()) glEnable(GL_DEPTH_TEST);
+
+ if(scissoring) glDisable(GL_SCISSOR_TEST);
+
+ if(succeeded)
+ {
+ initialized = true;
+
+ if(blursize) doblur(blursize, blursigma, blurysize ? blurysize : blursize);
+ }
+
+ glBindFramebuffer_(GL_FRAMEBUFFER, 0);
+ glViewport(0, 0, screenw, screenh);
+ }
};
diff --git a/src/engine/rendertext.cpp b/src/engine/rendertext.cpp
index 44ad136..925e1a8 100644
--- a/src/engine/rendertext.cpp
+++ b/src/engine/rendertext.cpp
@@ -9,67 +9,67 @@ int curfonttex = 0;
void newfont(char *name, char *tex, int *defaultw, int *defaulth)
{
- font *f = &fonts[name];
- if(!f->name) f->name = newstring(name);
- f->texs.shrink(0);
- f->texs.add(textureload(tex));
- f->chars.shrink(0);
- f->charoffset = '!';
- f->defaultw = *defaultw;
- f->defaulth = *defaulth;
- f->scale = f->defaulth;
-
- fontdef = f;
- fontdeftex = 0;
+ font *f = &fonts[name];
+ if(!f->name) f->name = newstring(name);
+ f->texs.shrink(0);
+ f->texs.add(textureload(tex));
+ f->chars.shrink(0);
+ f->charoffset = '!';
+ f->defaultw = *defaultw;
+ f->defaulth = *defaulth;
+ f->scale = f->defaulth;
+
+ fontdef = f;
+ fontdeftex = 0;
}
void fontoffset(char *c)
{
- if(!fontdef) return;
-
- fontdef->charoffset = c[0];
+ if(!fontdef) return;
+
+ fontdef->charoffset = c[0];
}
void fontscale(int *scale)
{
- if(!fontdef) return;
+ if(!fontdef) return;
- fontdef->scale = *scale > 0 ? *scale : fontdef->defaulth;
+ fontdef->scale = *scale > 0 ? *scale : fontdef->defaulth;
}
void fonttex(char *s)
{
- if(!fontdef) return;
+ if(!fontdef) return;
- Texture *t = textureload(s);
- loopv(fontdef->texs) if(fontdef->texs[i] == t) { fontdeftex = i; return; }
- fontdeftex = fontdef->texs.length();
- fontdef->texs.add(t);
+ Texture *t = textureload(s);
+ loopv(fontdef->texs) if(fontdef->texs[i] == t) { fontdeftex = i; return; }
+ fontdeftex = fontdef->texs.length();
+ fontdef->texs.add(t);
}
void fontchar(int *x, int *y, int *w, int *h, int *offsetx, int *offsety, int *advance)
{
- if(!fontdef) return;
-
- font::charinfo &c = fontdef->chars.add();
- c.x = *x;
- c.y = *y;
- c.w = *w ? *w : fontdef->defaultw;
- c.h = *h ? *h : fontdef->defaulth;
- c.offsetx = *offsetx;
- c.offsety = *offsety;
- c.advance = *advance ? *advance : c.offsetx + c.w;
- c.tex = fontdeftex;
+ if(!fontdef) return;
+
+ font::charinfo &c = fontdef->chars.add();
+ c.x = *x;
+ c.y = *y;
+ c.w = *w ? *w : fontdef->defaultw;
+ c.h = *h ? *h : fontdef->defaulth;
+ c.offsetx = *offsetx;
+ c.offsety = *offsety;
+ c.advance = *advance ? *advance : c.offsetx + c.w;
+ c.tex = fontdeftex;
}
void fontskip(int *n)
{
- if(!fontdef) return;
- loopi(max(*n, 1))
- {
- font::charinfo &c = fontdef->chars.add();
- c.x = c.y = c.w = c.h = c.offsetx = c.offsety = c.advance = c.tex = 0;
- }
+ if(!fontdef) return;
+ loopi(max(*n, 1))
+ {
+ font::charinfo &c = fontdef->chars.add();
+ c.x = c.y = c.w = c.h = c.offsetx = c.offsety = c.advance = c.tex = 0;
+ }
}
COMMANDN(font, newfont, "ssii");
@@ -81,67 +81,67 @@ COMMAND(fontskip, "i");
void fontalias(const char *dst, const char *src)
{
- font *s = fonts.access(src);
- if(!s) return;
- font *d = &fonts[dst];
- if(!d->name) d->name = newstring(dst);
- d->texs = s->texs;
- d->chars = s->chars;
- d->charoffset = s->charoffset;
- d->defaultw = s->defaultw;
- d->defaulth = s->defaulth;
- d->scale = s->scale;
-
- fontdef = d;
- fontdeftex = d->texs.length()-1;
+ font *s = fonts.access(src);
+ if(!s) return;
+ font *d = &fonts[dst];
+ if(!d->name) d->name = newstring(dst);
+ d->texs = s->texs;
+ d->chars = s->chars;
+ d->charoffset = s->charoffset;
+ d->defaultw = s->defaultw;
+ d->defaulth = s->defaulth;
+ d->scale = s->scale;
+
+ fontdef = d;
+ fontdeftex = d->texs.length()-1;
}
COMMAND(fontalias, "ss");
bool setfont(const char *name)
{
- font *f = fonts.access(name);
- if(!f) return false;
- curfont = f;
- return true;
+ font *f = fonts.access(name);
+ if(!f) return false;
+ curfont = f;
+ return true;
}
static vector<font *> fontstack;
void pushfont()
{
- fontstack.add(curfont);
+ fontstack.add(curfont);
}
bool popfont()
{
- if(fontstack.empty()) return false;
- curfont = fontstack.pop();
- return true;
+ if(fontstack.empty()) return false;
+ curfont = fontstack.pop();
+ return true;
}
void gettextres(int &w, int &h)
{
- if(w < MINRESW || h < MINRESH)
- {
- if(MINRESW > w*MINRESH/h)
- {
- h = h*MINRESW/w;
- w = MINRESW;
- }
- else
- {
- w = w*MINRESH/h;
- h = MINRESH;
- }
- }
+ if(w < MINRESW || h < MINRESH)
+ {
+ if(MINRESW > w*MINRESH/h)
+ {
+ h = h*MINRESW/w;
+ w = MINRESW;
+ }
+ else
+ {
+ w = w*MINRESH/h;
+ h = MINRESH;
+ }
+ }
}
-float text_widthf(const char *str)
+float text_widthf(const char *str)
{
- float width, height;
- text_boundsf(str, width, height);
- return width;
+ float width, height;
+ text_boundsf(str, width, height);
+ return width;
}
#define FONTTAB (4*FONTW)
@@ -149,244 +149,244 @@ float text_widthf(const char *str)
void tabify(const char *str, int *numtabs)
{
- int tw = max(*numtabs, 0)*FONTTAB-1, tabs = 0;
- for(float w = text_widthf(str); w <= tw; w = TEXTTAB(w)) ++tabs;
- int len = strlen(str);
- char *tstr = newstring(len + tabs);
- memcpy(tstr, str, len);
- memset(&tstr[len], '\t', tabs);
- tstr[len+tabs] = '\0';
- stringret(tstr);
+ int tw = max(*numtabs, 0)*FONTTAB-1, tabs = 0;
+ for(float w = text_widthf(str); w <= tw; w = TEXTTAB(w)) ++tabs;
+ int len = strlen(str);
+ char *tstr = newstring(len + tabs);
+ memcpy(tstr, str, len);
+ memset(&tstr[len], '\t', tabs);
+ tstr[len+tabs] = '\0';
+ stringret(tstr);
}
COMMAND(tabify, "si");
-
+
void draw_textf(const char *fstr, int left, int top, ...)
{
- defvformatstring(str, top, fstr);
- draw_text(str, left, top);
+ defvformatstring(str, top, fstr);
+ draw_text(str, left, top);
}
const matrix4x3 *textmatrix = NULL;
static float draw_char(Texture *&tex, int c, float x, float y, float scale)
{
- font::charinfo &info = curfont->chars[c-curfont->charoffset];
- if(tex != curfont->texs[info.tex])
- {
- xtraverts += gle::end();
- tex = curfont->texs[info.tex];
- glBindTexture(GL_TEXTURE_2D, tex->id);
- }
-
- float x1 = x + scale*info.offsetx,
- y1 = y + scale*info.offsety,
- x2 = x + scale*(info.offsetx + info.w),
- y2 = y + scale*(info.offsety + info.h),
- tx1 = info.x / float(tex->xs),
- ty1 = info.y / float(tex->ys),
- tx2 = (info.x + info.w) / float(tex->xs),
- ty2 = (info.y + info.h) / float(tex->ys);
-
- if(textmatrix)
- {
- gle::attrib(textmatrix->transform(vec2(x1, y1))); gle::attribf(tx1, ty1);
- gle::attrib(textmatrix->transform(vec2(x2, y1))); gle::attribf(tx2, ty1);
- gle::attrib(textmatrix->transform(vec2(x2, y2))); gle::attribf(tx2, ty2);
- gle::attrib(textmatrix->transform(vec2(x1, y2))); gle::attribf(tx1, ty2);
- }
- else
- {
- gle::attribf(x1, y1); gle::attribf(tx1, ty1);
- gle::attribf(x2, y1); gle::attribf(tx2, ty1);
- gle::attribf(x2, y2); gle::attribf(tx2, ty2);
- gle::attribf(x1, y2); gle::attribf(tx1, ty2);
- }
-
- return scale*info.advance;
+ font::charinfo &info = curfont->chars[c-curfont->charoffset];
+ if(tex != curfont->texs[info.tex])
+ {
+ xtraverts += gle::end();
+ tex = curfont->texs[info.tex];
+ glBindTexture(GL_TEXTURE_2D, tex->id);
+ }
+
+ float x1 = x + scale*info.offsetx,
+ y1 = y + scale*info.offsety,
+ x2 = x + scale*(info.offsetx + info.w),
+ y2 = y + scale*(info.offsety + info.h),
+ tx1 = info.x / float(tex->xs),
+ ty1 = info.y / float(tex->ys),
+ tx2 = (info.x + info.w) / float(tex->xs),
+ ty2 = (info.y + info.h) / float(tex->ys);
+
+ if(textmatrix)
+ {
+ gle::attrib(textmatrix->transform(vec2(x1, y1))); gle::attribf(tx1, ty1);
+ gle::attrib(textmatrix->transform(vec2(x2, y1))); gle::attribf(tx2, ty1);
+ gle::attrib(textmatrix->transform(vec2(x2, y2))); gle::attribf(tx2, ty2);
+ gle::attrib(textmatrix->transform(vec2(x1, y2))); gle::attribf(tx1, ty2);
+ }
+ else
+ {
+ gle::attribf(x1, y1); gle::attribf(tx1, ty1);
+ gle::attribf(x2, y1); gle::attribf(tx2, ty1);
+ gle::attribf(x2, y2); gle::attribf(tx2, ty2);
+ gle::attribf(x1, y2); gle::attribf(tx1, ty2);
+ }
+
+ return scale*info.advance;
}
//stack[sp] is current color index
-static void text_color(char c, char *stack, int size, int &sp, bvec color, int a)
+static void text_color(char c, char *stack, int size, int &sp, bvec color, int a)
{
- if(c=='s') // save color
- {
- c = stack[sp];
- if(sp<size-1) stack[++sp] = c;
- }
- else
- {
- xtraverts += gle::end();
- if(c=='r') { if(sp > 0) --sp; c = stack[sp]; } // restore color
- else stack[sp] = c;
- switch(c)
- {
- case '0': color = bvec( 64, 255, 128); break; // green: player talk
- case '1': color = bvec( 96, 160, 255); break; // blue: "echo" command
- case '2': color = bvec(255, 192, 64); break; // yellow: gameplay messages
- case '3': color = bvec(255, 64, 64); break; // red: important errors
- case '4': color = bvec(128, 128, 128); break; // gray
- case '5': color = bvec(192, 64, 192); break; // magenta
- case '6': color = bvec(255, 128, 0); break; // orange
- case '7': color = bvec(255, 255, 255); break; // white
- case '8': color = bvec( 96, 240, 255); break; // cyan
- // provided color: everything else
- }
- gle::color(color, a);
- }
+ if(c=='s') // save color
+ {
+ c = stack[sp];
+ if(sp<size-1) stack[++sp] = c;
+ }
+ else
+ {
+ xtraverts += gle::end();
+ if(c=='r') { if(sp > 0) --sp; c = stack[sp]; } // restore color
+ else stack[sp] = c;
+ switch(c)
+ {
+ case '0': color = bvec( 64, 255, 128); break; // green: player talk
+ case '1': color = bvec( 96, 160, 255); break; // blue: "echo" command
+ case '2': color = bvec(255, 192, 64); break; // yellow: gameplay messages
+ case '3': color = bvec(255, 64, 64); break; // red: important errors
+ case '4': color = bvec(128, 128, 128); break; // gray
+ case '5': color = bvec(192, 64, 192); break; // magenta
+ case '6': color = bvec(255, 128, 0); break; // orange
+ case '7': color = bvec(255, 255, 255); break; // white
+ case '8': color = bvec( 96, 240, 255); break; // cyan
+ // provided color: everything else
+ }
+ gle::color(color, a);
+ }
}
#define TEXTSKELETON \
- float y = 0, x = 0, scale = curfont->scale/float(curfont->defaulth);\
- int i;\
- for(i = 0; str[i]; i++)\
- {\
- TEXTINDEX(i)\
- int c = uchar(str[i]);\
- if(c=='\t') { x = TEXTTAB(x); TEXTWHITE(i) }\
- else if(c==' ') { x += scale*curfont->defaultw; TEXTWHITE(i) }\
- else if(c=='\n') { TEXTLINE(i) x = 0; y += FONTH; }\
- else if(c=='\f') { if(str[i+1]) { i++; TEXTCOLOR(i) }}\
- else if(curfont->chars.inrange(c-curfont->charoffset))\
- {\
- float cw = scale*curfont->chars[c-curfont->charoffset].advance;\
- if(cw <= 0) continue;\
- if(maxwidth != -1)\
- {\
- int j = i;\
- float w = cw;\
- for(; str[i+1]; i++)\
- {\
- int c = uchar(str[i+1]);\
- if(c=='\f') { if(str[i+2]) i++; continue; }\
- if(i-j > 16) break;\
- if(!curfont->chars.inrange(c-curfont->charoffset)) break;\
- float cw = scale*curfont->chars[c-curfont->charoffset].advance;\
- if(cw <= 0 || w + cw > maxwidth) break;\
- w += cw;\
- }\
- if(x + w > maxwidth && j!=0) { TEXTLINE(j-1) x = 0; y += FONTH; }\
- TEXTWORD\
- }\
- else\
- { TEXTCHAR(i) }\
- }\
- }
+ float y = 0, x = 0, scale = curfont->scale/float(curfont->defaulth);\
+ int i;\
+ for(i = 0; str[i]; i++)\
+ {\
+ TEXTINDEX(i)\
+ int c = uchar(str[i]);\
+ if(c=='\t') { x = TEXTTAB(x); TEXTWHITE(i) }\
+ else if(c==' ') { x += scale*curfont->defaultw; TEXTWHITE(i) }\
+ else if(c=='\n') { TEXTLINE(i) x = 0; y += FONTH; }\
+ else if(c=='\f') { if(str[i+1]) { i++; TEXTCOLOR(i) }}\
+ else if(curfont->chars.inrange(c-curfont->charoffset))\
+ {\
+ float cw = scale*curfont->chars[c-curfont->charoffset].advance;\
+ if(cw <= 0) continue;\
+ if(maxwidth != -1)\
+ {\
+ int j = i;\
+ float w = cw;\
+ for(; str[i+1]; i++)\
+ {\
+ int c = uchar(str[i+1]);\
+ if(c=='\f') { if(str[i+2]) i++; continue; }\
+ if(i-j > 16) break;\
+ if(!curfont->chars.inrange(c-curfont->charoffset)) break;\
+ float cw = scale*curfont->chars[c-curfont->charoffset].advance;\
+ if(cw <= 0 || w + cw > maxwidth) break;\
+ w += cw;\
+ }\
+ if(x + w > maxwidth && j!=0) { TEXTLINE(j-1) x = 0; y += FONTH; }\
+ TEXTWORD\
+ }\
+ else\
+ { TEXTCHAR(i) }\
+ }\
+ }
//all the chars are guaranteed to be either drawable or color commands
#define TEXTWORDSKELETON \
- for(; j <= i; j++)\
- {\
- TEXTINDEX(j)\
- int c = uchar(str[j]);\
- if(c=='\f') { if(str[j+1]) { j++; TEXTCOLOR(j) }}\
- else { float cw = scale*curfont->chars[c-curfont->charoffset].advance; TEXTCHAR(j) }\
- }
+ for(; j <= i; j++)\
+ {\
+ TEXTINDEX(j)\
+ int c = uchar(str[j]);\
+ if(c=='\f') { if(str[j+1]) { j++; TEXTCOLOR(j) }}\
+ else { float cw = scale*curfont->chars[c-curfont->charoffset].advance; TEXTCHAR(j) }\
+ }
#define TEXTEND(cursor) if(cursor >= i) { do { TEXTINDEX(cursor); } while(0); }
int text_visible(const char *str, float hitx, float hity, int maxwidth)
{
- #define TEXTINDEX(idx)
- #define TEXTWHITE(idx) if(y+FONTH > hity && x >= hitx) return idx;
- #define TEXTLINE(idx) if(y+FONTH > hity) return idx;
- #define TEXTCOLOR(idx)
- #define TEXTCHAR(idx) x += cw; TEXTWHITE(idx)
- #define TEXTWORD TEXTWORDSKELETON
- TEXTSKELETON
- #undef TEXTINDEX
- #undef TEXTWHITE
- #undef TEXTLINE
- #undef TEXTCOLOR
- #undef TEXTCHAR
- #undef TEXTWORD
- return i;
+ #define TEXTINDEX(idx)
+ #define TEXTWHITE(idx) if(y+FONTH > hity && x >= hitx) return idx;
+ #define TEXTLINE(idx) if(y+FONTH > hity) return idx;
+ #define TEXTCOLOR(idx)
+ #define TEXTCHAR(idx) x += cw; TEXTWHITE(idx)
+ #define TEXTWORD TEXTWORDSKELETON
+ TEXTSKELETON
+ #undef TEXTINDEX
+ #undef TEXTWHITE
+ #undef TEXTLINE
+ #undef TEXTCOLOR
+ #undef TEXTCHAR
+ #undef TEXTWORD
+ return i;
}
//inverse of text_visible
-void text_posf(const char *str, int cursor, float &cx, float &cy, int maxwidth)
+void text_posf(const char *str, int cursor, float &cx, float &cy, int maxwidth)
{
- #define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; break; }
- #define TEXTWHITE(idx)
- #define TEXTLINE(idx)
- #define TEXTCOLOR(idx)
- #define TEXTCHAR(idx) x += cw;
- #define TEXTWORD TEXTWORDSKELETON if(i >= cursor) break;
- cx = cy = 0;
- TEXTSKELETON
- TEXTEND(cursor)
- #undef TEXTINDEX
- #undef TEXTWHITE
- #undef TEXTLINE
- #undef TEXTCOLOR
- #undef TEXTCHAR
- #undef TEXTWORD
+ #define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; break; }
+ #define TEXTWHITE(idx)
+ #define TEXTLINE(idx)
+ #define TEXTCOLOR(idx)
+ #define TEXTCHAR(idx) x += cw;
+ #define TEXTWORD TEXTWORDSKELETON if(i >= cursor) break;
+ cx = cy = 0;
+ TEXTSKELETON
+ TEXTEND(cursor)
+ #undef TEXTINDEX
+ #undef TEXTWHITE
+ #undef TEXTLINE
+ #undef TEXTCOLOR
+ #undef TEXTCHAR
+ #undef TEXTWORD
}
void text_boundsf(const char *str, float &width, float &height, int maxwidth)
{
- #define TEXTINDEX(idx)
- #define TEXTWHITE(idx)
- #define TEXTLINE(idx) if(x > width) width = x;
- #define TEXTCOLOR(idx)
- #define TEXTCHAR(idx) x += cw;
- #define TEXTWORD x += w;
- width = 0;
- TEXTSKELETON
- height = y + FONTH;
- TEXTLINE(_)
- #undef TEXTINDEX
- #undef TEXTWHITE
- #undef TEXTLINE
- #undef TEXTCOLOR
- #undef TEXTCHAR
- #undef TEXTWORD
+ #define TEXTINDEX(idx)
+ #define TEXTWHITE(idx)
+ #define TEXTLINE(idx) if(x > width) width = x;
+ #define TEXTCOLOR(idx)
+ #define TEXTCHAR(idx) x += cw;
+ #define TEXTWORD x += w;
+ width = 0;
+ TEXTSKELETON
+ height = y + FONTH;
+ TEXTLINE(_)
+ #undef TEXTINDEX
+ #undef TEXTWHITE
+ #undef TEXTLINE
+ #undef TEXTCOLOR
+ #undef TEXTCHAR
+ #undef TEXTWORD
}
-void draw_text(const char *str, int left, int top, int r, int g, int b, int a, int cursor, int maxwidth)
+void draw_text(const char *str, int left, int top, int r, int g, int b, int a, int cursor, int maxwidth)
{
- #define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; }
- #define TEXTWHITE(idx)
- #define TEXTLINE(idx)
- #define TEXTCOLOR(idx) if(usecolor) text_color(str[idx], colorstack, sizeof(colorstack), colorpos, color, a);
- #define TEXTCHAR(idx) draw_char(tex, c, left+x, top+y, scale); x += cw;
- #define TEXTWORD TEXTWORDSKELETON
- char colorstack[10];
- colorstack[0] = 'c'; //indicate user color
- bvec color(r, g, b);
- int colorpos = 0;
- float cx = -FONTW, cy = 0;
- bool usecolor = true;
- if(a < 0) { usecolor = false; a = -a; }
- Texture *tex = curfont->texs[0];
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glBindTexture(GL_TEXTURE_2D, tex->id);
- gle::color(color, a);
- gle::defvertex(textmatrix ? 3 : 2);
- gle::deftexcoord0();
- gle::begin(GL_QUADS);
- TEXTSKELETON
- TEXTEND(cursor)
- xtraverts += gle::end();
- if(cursor >= 0 && (totalmillis/250)&1)
- {
- gle::color(color, a);
- if(maxwidth != -1 && cx >= maxwidth) { cx = 0; cy += FONTH; }
- draw_char(tex, '_', left+cx, top+cy, scale);
- xtraverts += gle::end();
- }
- #undef TEXTINDEX
- #undef TEXTWHITE
- #undef TEXTLINE
- #undef TEXTCOLOR
- #undef TEXTCHAR
- #undef TEXTWORD
+ #define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; }
+ #define TEXTWHITE(idx)
+ #define TEXTLINE(idx)
+ #define TEXTCOLOR(idx) if(usecolor) text_color(str[idx], colorstack, sizeof(colorstack), colorpos, color, a);
+ #define TEXTCHAR(idx) draw_char(tex, c, left+x, top+y, scale); x += cw;
+ #define TEXTWORD TEXTWORDSKELETON
+ char colorstack[10];
+ colorstack[0] = 'c'; //indicate user color
+ bvec color(r, g, b);
+ int colorpos = 0;
+ float cx = -FONTW, cy = 0;
+ bool usecolor = true;
+ if(a < 0) { usecolor = false; a = -a; }
+ Texture *tex = curfont->texs[0];
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glBindTexture(GL_TEXTURE_2D, tex->id);
+ gle::color(color, a);
+ gle::defvertex(textmatrix ? 3 : 2);
+ gle::deftexcoord0();
+ gle::begin(GL_QUADS);
+ TEXTSKELETON
+ TEXTEND(cursor)
+ xtraverts += gle::end();
+ if(cursor >= 0 && (totalmillis/250)&1)
+ {
+ gle::color(color, a);
+ if(maxwidth != -1 && cx >= maxwidth) { cx = 0; cy += FONTH; }
+ draw_char(tex, '_', left+cx, top+cy, scale);
+ xtraverts += gle::end();
+ }
+ #undef TEXTINDEX
+ #undef TEXTWHITE
+ #undef TEXTLINE
+ #undef TEXTCOLOR
+ #undef TEXTCHAR
+ #undef TEXTWORD
}
void reloadfonts()
{
- enumerate(fonts, font, f,
- loopv(f.texs) if(!reloadtexture(*f.texs[i])) fatal("failed to reload font texture");
- );
+ enumerate(fonts, font, f,
+ loopv(f.texs) if(!reloadtexture(*f.texs[i])) fatal("failed to reload font texture");
+ );
}
diff --git a/src/engine/renderva.cpp b/src/engine/renderva.cpp
index cef5ac5..dc3dca1 100644
--- a/src/engine/renderva.cpp
+++ b/src/engine/renderva.cpp
@@ -4,13 +4,13 @@
static inline void drawtris(GLsizei numindices, const GLvoid *indices, ushort minvert, ushort maxvert)
{
- glDrawRangeElements_(GL_TRIANGLES, minvert, maxvert, numindices, GL_UNSIGNED_SHORT, indices);
- glde++;
+ glDrawRangeElements_(GL_TRIANGLES, minvert, maxvert, numindices, GL_UNSIGNED_SHORT, indices);
+ glde++;
}
static inline void drawvatris(vtxarray *va, GLsizei numindices, const GLvoid *indices)
{
- drawtris(numindices, indices, va->minvert, va->maxvert);
+ drawtris(numindices, indices, va->minvert, va->maxvert);
}
///////// view frustrum culling ///////////////////////
@@ -23,65 +23,65 @@ vtxarray *visibleva;
bool isfoggedsphere(float rad, const vec &cv)
{
- loopi(4) if(vfcP[i].dist(cv) < -rad) return true;
- float dist = vfcP[4].dist(cv);
- return dist < -rad || dist > vfcDfog + rad;
+ loopi(4) if(vfcP[i].dist(cv) < -rad) return true;
+ float dist = vfcP[4].dist(cv);
+ return dist < -rad || dist > vfcDfog + rad;
}
int isvisiblesphere(float rad, const vec &cv)
{
- int v = VFC_FULL_VISIBLE;
- float dist;
+ int v = VFC_FULL_VISIBLE;
+ float dist;
- loopi(5)
- {
- dist = vfcP[i].dist(cv);
- if(dist < -rad) return VFC_NOT_VISIBLE;
- if(dist < rad) v = VFC_PART_VISIBLE;
- }
+ loopi(5)
+ {
+ dist = vfcP[i].dist(cv);
+ if(dist < -rad) return VFC_NOT_VISIBLE;
+ if(dist < rad) v = VFC_PART_VISIBLE;
+ }
- dist -= vfcDfog;
- if(dist > rad) return VFC_FOGGED; //VFC_NOT_VISIBLE; // culling when fog is closer than size of world results in HOM
- if(dist > -rad) v = VFC_PART_VISIBLE;
+ dist -= vfcDfog;
+ if(dist > rad) return VFC_FOGGED; //VFC_NOT_VISIBLE; // culling when fog is closer than size of world results in HOM
+ if(dist > -rad) v = VFC_PART_VISIBLE;
- return v;
+ return v;
}
static inline int ishiddencube(const ivec &o, int size)
{
- loopi(5) if(o.dist(vfcP[i]) < -vfcDfar[i]*size) return true;
- return false;
+ loopi(5) if(o.dist(vfcP[i]) < -vfcDfar[i]*size) return true;
+ return false;
}
static inline int isfoggedcube(const ivec &o, int size)
{
- loopi(4) if(o.dist(vfcP[i]) < -vfcDfar[i]*size) return true;
- float dist = o.dist(vfcP[4]);
- return dist < -vfcDfar[4]*size || dist > vfcDfog - vfcDnear[4]*size;
+ loopi(4) if(o.dist(vfcP[i]) < -vfcDfar[i]*size) return true;
+ float dist = o.dist(vfcP[4]);
+ return dist < -vfcDfar[4]*size || dist > vfcDfog - vfcDnear[4]*size;
}
int isvisiblecube(const ivec &o, int size)
{
- int v = VFC_FULL_VISIBLE;
- float dist;
+ int v = VFC_FULL_VISIBLE;
+ float dist;
- loopi(5)
- {
- dist = o.dist(vfcP[i]);
- if(dist < -vfcDfar[i]*size) return VFC_NOT_VISIBLE;
- if(dist < -vfcDnear[i]*size) v = VFC_PART_VISIBLE;
- }
+ loopi(5)
+ {
+ dist = o.dist(vfcP[i]);
+ if(dist < -vfcDfar[i]*size) return VFC_NOT_VISIBLE;
+ if(dist < -vfcDnear[i]*size) v = VFC_PART_VISIBLE;
+ }
- dist -= vfcDfog;
- if(dist > -vfcDnear[4]*size) return VFC_FOGGED;
- if(dist > -vfcDfar[4]*size) v = VFC_PART_VISIBLE;
+ dist -= vfcDfog;
+ if(dist > -vfcDnear[4]*size) return VFC_FOGGED;
+ if(dist > -vfcDfar[4]*size) v = VFC_PART_VISIBLE;
- return v;
+ return v;
}
float vadist(vtxarray *va, const vec &p)
{
- return p.dist_to_bb(va->bbmin, va->bbmax);
+ return p.dist_to_bb(va->bbmin, va->bbmax);
}
#define VASORTSIZE 64
@@ -90,128 +90,128 @@ static vtxarray *vasort[VASORTSIZE];
void addvisibleva(vtxarray *va)
{
- float dist = vadist(va, camera1->o);
- va->distance = int(dist); /*cv.dist(camera1->o) - va->size*SQRT3/2*/
+ float dist = vadist(va, camera1->o);
+ va->distance = int(dist); /*cv.dist(camera1->o) - va->size*SQRT3/2*/
- int hash = clamp(int(dist*VASORTSIZE/worldsize), 0, VASORTSIZE-1);
- vtxarray **prev = &vasort[hash], *cur = vasort[hash];
+ int hash = clamp(int(dist*VASORTSIZE/worldsize), 0, VASORTSIZE-1);
+ vtxarray **prev = &vasort[hash], *cur = vasort[hash];
- while(cur && va->distance >= cur->distance)
- {
- prev = &cur->next;
- cur = cur->next;
- }
+ while(cur && va->distance >= cur->distance)
+ {
+ prev = &cur->next;
+ cur = cur->next;
+ }
- va->next = *prev;
- *prev = va;
+ va->next = *prev;
+ *prev = va;
}
void sortvisiblevas()
{
- visibleva = NULL;
- vtxarray **last = &visibleva;
- loopi(VASORTSIZE) if(vasort[i])
- {
- vtxarray *va = vasort[i];
- *last = va;
- while(va->next) va = va->next;
- last = &va->next;
- }
+ visibleva = NULL;
+ vtxarray **last = &visibleva;
+ loopi(VASORTSIZE) if(vasort[i])
+ {
+ vtxarray *va = vasort[i];
+ *last = va;
+ while(va->next) va = va->next;
+ last = &va->next;
+ }
}
void findvisiblevas(vector<vtxarray *> &vas, bool resetocclude = false)
{
- loopv(vas)
- {
- vtxarray &v = *vas[i];
- int prevvfc = resetocclude ? (int) VFC_NOT_VISIBLE : (int) v.curvfc;
- v.curvfc = isvisiblecube(v.o, v.size);
- if(v.curvfc!=VFC_NOT_VISIBLE)
- {
- addvisibleva(&v);
- if(v.children.length()) findvisiblevas(v.children, prevvfc>=VFC_NOT_VISIBLE);
- if(prevvfc>=VFC_NOT_VISIBLE)
- {
- v.occluded = !v.texs ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
- v.query = NULL;
- }
- }
- }
+ loopv(vas)
+ {
+ vtxarray &v = *vas[i];
+ int prevvfc = resetocclude ? (int) VFC_NOT_VISIBLE : (int) v.curvfc;
+ v.curvfc = isvisiblecube(v.o, v.size);
+ if(v.curvfc!=VFC_NOT_VISIBLE)
+ {
+ addvisibleva(&v);
+ if(v.children.length()) findvisiblevas(v.children, prevvfc>=VFC_NOT_VISIBLE);
+ if(prevvfc>=VFC_NOT_VISIBLE)
+ {
+ v.occluded = !v.texs ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
+ v.query = NULL;
+ }
+ }
+ }
}
void calcvfcD()
{
- loopi(5)
- {
- plane &p = vfcP[i];
- vfcDnear[i] = vfcDfar[i] = 0;
- loopk(3) if(p[k] > 0) vfcDfar[i] += p[k];
- else vfcDnear[i] += p[k];
- }
+ loopi(5)
+ {
+ plane &p = vfcP[i];
+ vfcDnear[i] = vfcDfar[i] = 0;
+ loopk(3) if(p[k] > 0) vfcDfar[i] += p[k];
+ else vfcDnear[i] += p[k];
+ }
}
void setvfcP(float z, const vec &bbmin, const vec &bbmax)
{
- vec4 px = camprojmatrix.rowx(), py = camprojmatrix.rowy(), pz = camprojmatrix.rowz(), pw = camprojmatrix.roww();
- vfcP[0] = plane(vec4(pw).mul(-bbmin.x).add(px)).normalize(); // left plane
- vfcP[1] = plane(vec4(pw).mul(bbmax.x).sub(px)).normalize(); // right plane
- vfcP[2] = plane(vec4(pw).mul(-bbmin.y).add(py)).normalize(); // bottom plane
- vfcP[3] = plane(vec4(pw).mul(bbmax.y).sub(py)).normalize(); // top plane
- vfcP[4] = plane(vec4(pw).add(pz)).normalize(); // near/far planes
- if(z >= 0) loopi(5) vfcP[i].reflectz(z);
+ vec4 px = camprojmatrix.rowx(), py = camprojmatrix.rowy(), pz = camprojmatrix.rowz(), pw = camprojmatrix.roww();
+ vfcP[0] = plane(vec4(pw).mul(-bbmin.x).add(px)).normalize(); // left plane
+ vfcP[1] = plane(vec4(pw).mul(bbmax.x).sub(px)).normalize(); // right plane
+ vfcP[2] = plane(vec4(pw).mul(-bbmin.y).add(py)).normalize(); // bottom plane
+ vfcP[3] = plane(vec4(pw).mul(bbmax.y).sub(py)).normalize(); // top plane
+ vfcP[4] = plane(vec4(pw).add(pz)).normalize(); // near/far planes
+ if(z >= 0) loopi(5) vfcP[i].reflectz(z);
- vfcDfog = fog;
- calcvfcD();
+ vfcDfog = fog;
+ calcvfcD();
}
plane oldvfcP[5];
void savevfcP()
{
- memcpy(oldvfcP, vfcP, sizeof(vfcP));
+ memcpy(oldvfcP, vfcP, sizeof(vfcP));
}
void restorevfcP()
{
- memcpy(vfcP, oldvfcP, sizeof(vfcP));
- calcvfcD();
+ memcpy(vfcP, oldvfcP, sizeof(vfcP));
+ calcvfcD();
}
void visiblecubes(bool cull)
{
- memclear(vasort);
-
- if(cull)
- {
- setvfcP();
- findvisiblevas(varoot);
- sortvisiblevas();
- }
- else
- {
- memclear(vfcP);
- vfcDfog = 1000000;
- memclear(vfcDnear);
- memclear(vfcDfar);
- visibleva = NULL;
- loopv(valist)
- {
- vtxarray *va = valist[i];
- va->distance = 0;
- va->curvfc = VFC_FULL_VISIBLE;
- va->occluded = !va->texs ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
- va->query = NULL;
- va->next = visibleva;
- visibleva = va;
- }
- }
+ memclear(vasort);
+
+ if(cull)
+ {
+ setvfcP();
+ findvisiblevas(varoot);
+ sortvisiblevas();
+ }
+ else
+ {
+ memclear(vfcP);
+ vfcDfog = 1000000;
+ memclear(vfcDnear);
+ memclear(vfcDfar);
+ visibleva = NULL;
+ loopv(valist)
+ {
+ vtxarray *va = valist[i];
+ va->distance = 0;
+ va->curvfc = VFC_FULL_VISIBLE;
+ va->occluded = !va->texs ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
+ va->query = NULL;
+ va->next = visibleva;
+ visibleva = va;
+ }
+ }
}
static inline bool insideva(const vtxarray *va, const vec &v, int margin = 2)
{
- int size = va->size + margin;
- return v.x>=va->o.x-margin && v.y>=va->o.y-margin && v.z>=va->o.z-margin &&
- v.x<=va->o.x+size && v.y<=va->o.y+size && v.z<=va->o.z+size;
+ int size = va->size + margin;
+ return v.x>=va->o.x-margin && v.y>=va->o.y-margin && v.z>=va->o.z-margin &&
+ v.x<=va->o.x+size && v.y<=va->o.y+size && v.z<=va->o.z+size;
}
///////// occlusion queries /////////////
@@ -221,37 +221,37 @@ static inline bool insideva(const vtxarray *va, const vec &v, int margin = 2)
struct queryframe
{
- int cur, max;
- occludequery queries[MAXQUERY];
-
- queryframe() : cur(0), max(0) {}
-
- void flip() { loopi(cur) queries[i].owner = NULL; cur = 0; }
-
- occludequery *newquery(void *owner)
- {
- if(cur >= max)
- {
- if(max >= MAXQUERY) return NULL;
- glGenQueries_(1, &queries[max++].id);
- }
- occludequery *query = &queries[cur++];
- query->owner = owner;
- query->fragments = -1;
- return query;
- }
-
- void reset() { loopi(max) queries[i].owner = NULL; }
-
- void cleanup()
- {
- loopi(max)
- {
- glDeleteQueries_(1, &queries[i].id);
- queries[i].owner = NULL;
- }
- cur = max = 0;
- }
+ int cur, max;
+ occludequery queries[MAXQUERY];
+
+ queryframe() : cur(0), max(0) {}
+
+ void flip() { loopi(cur) queries[i].owner = NULL; cur = 0; }
+
+ occludequery *newquery(void *owner)
+ {
+ if(cur >= max)
+ {
+ if(max >= MAXQUERY) return NULL;
+ glGenQueries_(1, &queries[max++].id);
+ }
+ occludequery *query = &queries[cur++];
+ query->owner = owner;
+ query->fragments = -1;
+ return query;
+ }
+
+ void reset() { loopi(max) queries[i].owner = NULL; }
+
+ void cleanup()
+ {
+ loopi(max)
+ {
+ glDeleteQueries_(1, &queries[i].id);
+ queries[i].owner = NULL;
+ }
+ cur = max = 0;
+ }
};
static queryframe queryframes[MAXQUERYFRAMES];
@@ -259,28 +259,28 @@ static uint flipquery = 0;
int getnumqueries()
{
- return queryframes[flipquery].cur;
+ return queryframes[flipquery].cur;
}
void flipqueries()
{
- flipquery = (flipquery + 1) % MAXQUERYFRAMES;
- queryframes[flipquery].flip();
+ flipquery = (flipquery + 1) % MAXQUERYFRAMES;
+ queryframes[flipquery].flip();
}
occludequery *newquery(void *owner)
{
- return queryframes[flipquery].newquery(owner);
+ return queryframes[flipquery].newquery(owner);
}
void resetqueries()
{
- loopi(MAXQUERYFRAMES) queryframes[i].reset();
+ loopi(MAXQUERYFRAMES) queryframes[i].reset();
}
void clearqueries()
{
- loopi(MAXQUERYFRAMES) queryframes[i].cleanup();
+ loopi(MAXQUERYFRAMES) queryframes[i].cleanup();
}
VAR(oqfrags, 0, 8, 64);
@@ -288,107 +288,107 @@ VAR(oqwait, 0, 1, 1);
void startquery(occludequery *query)
{
- glBeginQuery_(GL_SAMPLES_PASSED, query->id);
+ glBeginQuery_(GL_SAMPLES_PASSED, query->id);
}
void endquery(occludequery *query)
{
- glEndQuery_(GL_SAMPLES_PASSED);
+ glEndQuery_(GL_SAMPLES_PASSED);
}
bool checkquery(occludequery *query, bool nowait)
{
- GLuint fragments;
- if(query->fragments >= 0) fragments = query->fragments;
- else
- {
- if(nowait || !oqwait)
- {
- GLint avail;
- glGetQueryObjectiv_(query->id, GL_QUERY_RESULT_AVAILABLE, &avail);
- if(!avail) return false;
- }
- glGetQueryObjectuiv_(query->id, GL_QUERY_RESULT, &fragments);
- query->fragments = fragments;
- }
- return fragments < uint(oqfrags);
+ GLuint fragments;
+ if(query->fragments >= 0) fragments = query->fragments;
+ else
+ {
+ if(nowait || !oqwait)
+ {
+ GLint avail;
+ glGetQueryObjectiv_(query->id, GL_QUERY_RESULT_AVAILABLE, &avail);
+ if(!avail) return false;
+ }
+ glGetQueryObjectuiv_(query->id, GL_QUERY_RESULT, &fragments);
+ query->fragments = fragments;
+ }
+ return fragments < uint(oqfrags);
}
static GLuint bbvbo = 0, bbebo = 0;
static void setupbb()
{
- if(!bbvbo)
- {
- glGenBuffers_(1, &bbvbo);
- gle::bindvbo(bbvbo);
- vec verts[8];
- loopi(8) verts[i] = vec(i&1, (i>>1)&1, (i>>2)&1);
- glBufferData_(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
- gle::clearvbo();
- }
- if(!bbebo)
- {
- glGenBuffers_(1, &bbebo);
- gle::bindebo(bbebo);
- GLushort tris[3*2*6];
- #define GENFACEORIENT(orient, v0, v1, v2, v3) do { \
- int offset = orient*3*2; \
- tris[offset + 0] = v0; \
- tris[offset + 1] = v1; \
- tris[offset + 2] = v2; \
- tris[offset + 3] = v0; \
- tris[offset + 4] = v2; \
- tris[offset + 5] = v3; \
- } while(0);
- #define GENFACEVERT(orient, vert, ox,oy,oz, rx,ry,rz) (ox | oy | oz)
- GENFACEVERTS(0, 1, 0, 2, 0, 4, , , , , , )
- #undef GENFACEORIENT
- #undef GENFACEVERT
- glBufferData_(GL_ELEMENT_ARRAY_BUFFER, sizeof(tris), tris, GL_STATIC_DRAW);
- gle::clearebo();
- }
+ if(!bbvbo)
+ {
+ glGenBuffers_(1, &bbvbo);
+ gle::bindvbo(bbvbo);
+ vec verts[8];
+ loopi(8) verts[i] = vec(i&1, (i>>1)&1, (i>>2)&1);
+ glBufferData_(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
+ gle::clearvbo();
+ }
+ if(!bbebo)
+ {
+ glGenBuffers_(1, &bbebo);
+ gle::bindebo(bbebo);
+ GLushort tris[3*2*6];
+ #define GENFACEORIENT(orient, v0, v1, v2, v3) do { \
+ int offset = orient*3*2; \
+ tris[offset + 0] = v0; \
+ tris[offset + 1] = v1; \
+ tris[offset + 2] = v2; \
+ tris[offset + 3] = v0; \
+ tris[offset + 4] = v2; \
+ tris[offset + 5] = v3; \
+ } while(0);
+ #define GENFACEVERT(orient, vert, ox,oy,oz, rx,ry,rz) (ox | oy | oz)
+ GENFACEVERTS(0, 1, 0, 2, 0, 4, , , , , , )
+ #undef GENFACEORIENT
+ #undef GENFACEVERT
+ glBufferData_(GL_ELEMENT_ARRAY_BUFFER, sizeof(tris), tris, GL_STATIC_DRAW);
+ gle::clearebo();
+ }
}
static void cleanupbb()
{
- if(bbvbo) { glDeleteBuffers_(1, &bbvbo); bbvbo = 0; }
- if(bbebo) { glDeleteBuffers_(1, &bbebo); bbebo = 0; }
+ if(bbvbo) { glDeleteBuffers_(1, &bbvbo); bbvbo = 0; }
+ if(bbebo) { glDeleteBuffers_(1, &bbebo); bbebo = 0; }
}
void startbb(bool mask)
{
- setupbb();
- gle::bindvbo(bbvbo);
- gle::bindebo(bbebo);
- gle::vertexpointer(sizeof(vec), (const vec *)0);
- gle::enablevertex();
- SETSHADER(bbquery);
- if(mask)
- {
- glDepthMask(GL_FALSE);
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
- }
+ setupbb();
+ gle::bindvbo(bbvbo);
+ gle::bindebo(bbebo);
+ gle::vertexpointer(sizeof(vec), (const vec *)0);
+ gle::enablevertex();
+ SETSHADER(bbquery);
+ if(mask)
+ {
+ glDepthMask(GL_FALSE);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ }
}
void endbb(bool mask)
{
- gle::disablevertex();
- gle::clearvbo();
- gle::clearebo();
- if(mask)
- {
- glDepthMask(GL_TRUE);
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- }
+ gle::disablevertex();
+ gle::clearvbo();
+ gle::clearebo();
+ if(mask)
+ {
+ glDepthMask(GL_TRUE);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ }
}
void drawbb(const ivec &bo, const ivec &br)
{
- LOCALPARAMF(bborigin, bo.x, bo.y, bo.z);
- LOCALPARAMF(bbsize, br.x, br.y, br.z);
- glDrawRangeElements_(GL_TRIANGLES, 0, 8-1, 3*2*6, GL_UNSIGNED_SHORT, (ushort *)0);
- xtraverts += 8;
+ LOCALPARAMF(bborigin, bo.x, bo.y, bo.z);
+ LOCALPARAMF(bbsize, br.x, br.y, br.z);
+ glDrawRangeElements_(GL_TRIANGLES, 0, 8-1, 3*2*6, GL_UNSIGNED_SHORT, (ushort *)0);
+ xtraverts += 8;
}
extern int octaentsize;
@@ -397,214 +397,214 @@ static octaentities *visiblemms, **lastvisiblemms;
static inline bool insideoe(const octaentities *oe, const vec &v, int margin = 1)
{
- return v.x>=oe->bbmin.x-margin && v.y>=oe->bbmin.y-margin && v.z>=oe->bbmin.z-margin &&
- v.x<=oe->bbmax.x+margin && v.y<=oe->bbmax.y+margin && v.z<=oe->bbmax.z+margin;
+ return v.x>=oe->bbmin.x-margin && v.y>=oe->bbmin.y-margin && v.z>=oe->bbmin.z-margin &&
+ v.x<=oe->bbmax.x+margin && v.y<=oe->bbmax.y+margin && v.z<=oe->bbmax.z+margin;
}
void findvisiblemms(const vector<extentity *> &ents, bool doquery)
{
- visiblemms = NULL;
- lastvisiblemms = &visiblemms;
- for(vtxarray *va = visibleva; va; va = va->next)
- {
- if(va->mapmodels.empty() || va->curvfc >= VFC_FOGGED || va->occluded >= OCCLUDE_BB) continue;
- loopv(va->mapmodels)
- {
- octaentities *oe = va->mapmodels[i];
- if(isfoggedcube(oe->o, oe->size)) continue;
-
- bool occluded = doquery && oe->query && oe->query->owner == oe && checkquery(oe->query);
- if(occluded)
- {
- oe->distance = -1;
-
- oe->next = NULL;
- *lastvisiblemms = oe;
- lastvisiblemms = &oe->next;
- }
- else
- {
- int visible = 0;
- loopv(oe->mapmodels)
- {
- extentity &e = *ents[oe->mapmodels[i]];
- if(e.flags&EF_NOVIS) continue;
- e.flags |= EF_RENDER;
- ++visible;
- }
- if(!visible) continue;
-
- oe->distance = int(camera1->o.dist_to_bb(oe->o, oe->size));
-
- octaentities **prev = &visiblemms, *cur = visiblemms;
- while(cur && cur->distance >= 0 && oe->distance > cur->distance)
- {
- prev = &cur->next;
- cur = cur->next;
- }
-
- if(*prev == NULL) lastvisiblemms = &oe->next;
- oe->next = *prev;
- *prev = oe;
- }
- }
- }
+ visiblemms = NULL;
+ lastvisiblemms = &visiblemms;
+ for(vtxarray *va = visibleva; va; va = va->next)
+ {
+ if(va->mapmodels.empty() || va->curvfc >= VFC_FOGGED || va->occluded >= OCCLUDE_BB) continue;
+ loopv(va->mapmodels)
+ {
+ octaentities *oe = va->mapmodels[i];
+ if(isfoggedcube(oe->o, oe->size)) continue;
+
+ bool occluded = doquery && oe->query && oe->query->owner == oe && checkquery(oe->query);
+ if(occluded)
+ {
+ oe->distance = -1;
+
+ oe->next = NULL;
+ *lastvisiblemms = oe;
+ lastvisiblemms = &oe->next;
+ }
+ else
+ {
+ int visible = 0;
+ loopv(oe->mapmodels)
+ {
+ extentity &e = *ents[oe->mapmodels[i]];
+ if(e.flags&EF_NOVIS) continue;
+ e.flags |= EF_RENDER;
+ ++visible;
+ }
+ if(!visible) continue;
+
+ oe->distance = int(camera1->o.dist_to_bb(oe->o, oe->size));
+
+ octaentities **prev = &visiblemms, *cur = visiblemms;
+ while(cur && cur->distance >= 0 && oe->distance > cur->distance)
+ {
+ prev = &cur->next;
+ cur = cur->next;
+ }
+
+ if(*prev == NULL) lastvisiblemms = &oe->next;
+ oe->next = *prev;
+ *prev = oe;
+ }
+ }
+ }
}
VAR(oqmm, 0, 4, 8);
void rendermapmodel(extentity &e)
{
- int anim = ANIM_MAPMODEL|ANIM_LOOP, basetime = 0;
- mapmodelinfo *mmi = getmminfo(e.attr2);
- if(mmi) rendermodel(&e.light, mmi->name, anim, e.o, e.attr1, 0, MDL_CULL_VFC | MDL_CULL_DIST | MDL_DYNLIGHT, NULL, NULL, basetime);
+ int anim = ANIM_MAPMODEL|ANIM_LOOP, basetime = 0;
+ mapmodelinfo *mmi = getmminfo(e.attr2);
+ if(mmi) rendermodel(&e.light, mmi->name, anim, e.o, e.attr1, 0, MDL_CULL_VFC | MDL_CULL_DIST | MDL_DYNLIGHT, NULL, NULL, basetime);
}
vtxarray *reflectedva;
void renderreflectedmapmodels()
{
- const vector<extentity *> &ents = entities::getents();
-
- octaentities *mms = visiblemms;
- if(reflecting)
- {
- octaentities **lastmms = &mms;
- for(vtxarray *va = reflectedva; va; va = va->rnext)
- {
- if(va->mapmodels.empty() || va->distance > reflectdist) continue;
- loopv(va->mapmodels)
- {
- octaentities *oe = va->mapmodels[i];
- *lastmms = oe;
- lastmms = &oe->rnext;
- }
- }
- *lastmms = NULL;
- }
- for(octaentities *oe = mms; oe; oe = reflecting ? oe->rnext : oe->next) if(reflecting || oe->distance >= 0)
- {
- if(reflecting || refracting>0 ? oe->bbmax.z <= reflectz : oe->bbmin.z >= reflectz) continue;
- if(isfoggedcube(oe->o, oe->size)) continue;
- loopv(oe->mapmodels)
- {
- extentity &e = *ents[oe->mapmodels[i]];
- if(e.flags&(EF_NOVIS | EF_RENDER)) continue;
- e.flags |= EF_RENDER;
- }
- }
- if(mms)
- {
- startmodelbatches();
- for(octaentities *oe = mms; oe; oe = reflecting ? oe->rnext : oe->next)
- {
- loopv(oe->mapmodels)
- {
- extentity &e = *ents[oe->mapmodels[i]];
- if(!(e.flags&EF_RENDER)) continue;
- rendermapmodel(e);
- e.flags &= ~EF_RENDER;
- }
- }
- endmodelbatches();
- }
+ const vector<extentity *> &ents = entities::getents();
+
+ octaentities *mms = visiblemms;
+ if(reflecting)
+ {
+ octaentities **lastmms = &mms;
+ for(vtxarray *va = reflectedva; va; va = va->rnext)
+ {
+ if(va->mapmodels.empty() || va->distance > reflectdist) continue;
+ loopv(va->mapmodels)
+ {
+ octaentities *oe = va->mapmodels[i];
+ *lastmms = oe;
+ lastmms = &oe->rnext;
+ }
+ }
+ *lastmms = NULL;
+ }
+ for(octaentities *oe = mms; oe; oe = reflecting ? oe->rnext : oe->next) if(reflecting || oe->distance >= 0)
+ {
+ if(reflecting || refracting>0 ? oe->bbmax.z <= reflectz : oe->bbmin.z >= reflectz) continue;
+ if(isfoggedcube(oe->o, oe->size)) continue;
+ loopv(oe->mapmodels)
+ {
+ extentity &e = *ents[oe->mapmodels[i]];
+ if(e.flags&(EF_NOVIS | EF_RENDER)) continue;
+ e.flags |= EF_RENDER;
+ }
+ }
+ if(mms)
+ {
+ startmodelbatches();
+ for(octaentities *oe = mms; oe; oe = reflecting ? oe->rnext : oe->next)
+ {
+ loopv(oe->mapmodels)
+ {
+ extentity &e = *ents[oe->mapmodels[i]];
+ if(!(e.flags&EF_RENDER)) continue;
+ rendermapmodel(e);
+ e.flags &= ~EF_RENDER;
+ }
+ }
+ endmodelbatches();
+ }
}
void rendermapmodels()
{
- static int skipoq = 0;
- bool doquery = !drawtex && oqfrags && oqmm;
- const vector<extentity *> &ents = entities::getents();
- findvisiblemms(ents, doquery);
-
- startmodelbatches();
- for(octaentities *oe = visiblemms; oe; oe = oe->next) if(oe->distance>=0)
- {
- bool rendered = false;
- loopv(oe->mapmodels)
- {
- extentity &e = *ents[oe->mapmodels[i]];
- if(!(e.flags&EF_RENDER)) continue;
- if(!rendered)
- {
- rendered = true;
- oe->query = doquery && oe->distance>0 && !(++skipoq%oqmm) ? newquery(oe) : NULL;
- if(oe->query) startmodelquery(oe->query);
- }
- rendermapmodel(e);
- e.flags &= ~EF_RENDER;
- }
- if(rendered && oe->query) endmodelquery();
- }
- endmodelbatches();
-
- bool queried = true;
- for(octaentities *oe = visiblemms; oe; oe = oe->next) if(oe->distance<0)
- {
- oe->query = doquery && !insideoe(oe, camera1->o) ? newquery(oe) : NULL;
- if(!oe->query) continue;
- if(queried)
- {
- startbb();
- queried = false;
- }
- startquery(oe->query);
- drawbb(oe->bbmin, ivec(oe->bbmax).sub(oe->bbmin));
- endquery(oe->query);
- }
- if(!queried)
- {
- endbb();
- }
+ static int skipoq = 0;
+ bool doquery = !drawtex && oqfrags && oqmm;
+ const vector<extentity *> &ents = entities::getents();
+ findvisiblemms(ents, doquery);
+
+ startmodelbatches();
+ for(octaentities *oe = visiblemms; oe; oe = oe->next) if(oe->distance>=0)
+ {
+ bool rendered = false;
+ loopv(oe->mapmodels)
+ {
+ extentity &e = *ents[oe->mapmodels[i]];
+ if(!(e.flags&EF_RENDER)) continue;
+ if(!rendered)
+ {
+ rendered = true;
+ oe->query = doquery && oe->distance>0 && !(++skipoq%oqmm) ? newquery(oe) : NULL;
+ if(oe->query) startmodelquery(oe->query);
+ }
+ rendermapmodel(e);
+ e.flags &= ~EF_RENDER;
+ }
+ if(rendered && oe->query) endmodelquery();
+ }
+ endmodelbatches();
+
+ bool queried = true;
+ for(octaentities *oe = visiblemms; oe; oe = oe->next) if(oe->distance<0)
+ {
+ oe->query = doquery && !insideoe(oe, camera1->o) ? newquery(oe) : NULL;
+ if(!oe->query) continue;
+ if(queried)
+ {
+ startbb();
+ queried = false;
+ }
+ startquery(oe->query);
+ drawbb(oe->bbmin, ivec(oe->bbmax).sub(oe->bbmin));
+ endquery(oe->query);
+ }
+ if(!queried)
+ {
+ endbb();
+ }
}
static inline bool bbinsideva(const ivec &bo, const ivec &br, vtxarray *va)
{
- return bo.x >= va->bbmin.x && bo.y >= va->bbmin.y && bo.z >= va->bbmin.z &&
- br.x <= va->bbmax.x && br.y <= va->bbmax.y && br.z <= va->bbmax.z;
+ return bo.x >= va->bbmin.x && bo.y >= va->bbmin.y && bo.z >= va->bbmin.z &&
+ br.x <= va->bbmax.x && br.y <= va->bbmax.y && br.z <= va->bbmax.z;
}
static inline bool bboccluded(const ivec &bo, const ivec &br, cube *c, const ivec &o, int size)
{
- loopoctabox(o, size, bo, br)
- {
- ivec co(i, o, size);
- if(c[i].ext && c[i].ext->va)
- {
- vtxarray *va = c[i].ext->va;
- if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) continue;
- }
- if(c[i].children && bboccluded(bo, br, c[i].children, co, size>>1)) continue;
- return false;
- }
- return true;
+ loopoctabox(o, size, bo, br)
+ {
+ ivec co(i, o, size);
+ if(c[i].ext && c[i].ext->va)
+ {
+ vtxarray *va = c[i].ext->va;
+ if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) continue;
+ }
+ if(c[i].children && bboccluded(bo, br, c[i].children, co, size>>1)) continue;
+ return false;
+ }
+ return true;
}
bool bboccluded(const ivec &bo, const ivec &br)
{
- int diff = (bo.x^br.x) | (bo.y^br.y) | (bo.z^br.z);
- if(diff&~((1<<worldscale)-1)) return false;
- int scale = worldscale-1;
- if(diff&(1<<scale)) return bboccluded(bo, br, worldroot, ivec(0, 0, 0), 1<<scale);
- cube *c = &worldroot[octastep(bo.x, bo.y, bo.z, scale)];
- if(c->ext && c->ext->va)
- {
- vtxarray *va = c->ext->va;
- if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) return true;
- }
- scale--;
- while(c->children && !(diff&(1<<scale)))
- {
- c = &c->children[octastep(bo.x, bo.y, bo.z, scale)];
- if(c->ext && c->ext->va)
- {
- vtxarray *va = c->ext->va;
- if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) return true;
- }
- scale--;
- }
- if(c->children) return bboccluded(bo, br, c->children, ivec(bo).mask(~((2<<scale)-1)), 1<<scale);
- return false;
+ int diff = (bo.x^br.x) | (bo.y^br.y) | (bo.z^br.z);
+ if(diff&~((1<<worldscale)-1)) return false;
+ int scale = worldscale-1;
+ if(diff&(1<<scale)) return bboccluded(bo, br, worldroot, ivec(0, 0, 0), 1<<scale);
+ cube *c = &worldroot[octastep(bo.x, bo.y, bo.z, scale)];
+ if(c->ext && c->ext->va)
+ {
+ vtxarray *va = c->ext->va;
+ if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) return true;
+ }
+ scale--;
+ while(c->children && !(diff&(1<<scale)))
+ {
+ c = &c->children[octastep(bo.x, bo.y, bo.z, scale)];
+ if(c->ext && c->ext->va)
+ {
+ vtxarray *va = c->ext->va;
+ if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) return true;
+ }
+ scale--;
+ }
+ if(c->children) return bboccluded(bo, br, c->children, ivec(bo).mask(~((2<<scale)-1)), 1<<scale);
+ return false;
}
VAR(outline, 0, 0, 1);
@@ -613,213 +613,213 @@ VAR(dtoutline, 0, 1, 1);
void renderoutline()
{
- notextureshader->set();
+ notextureshader->set();
- gle::enablevertex();
+ gle::enablevertex();
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- gle::color(vec::hexcolor(outlinecolour));
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ gle::color(vec::hexcolor(outlinecolour));
- enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
+ enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
- if(!dtoutline) glDisable(GL_DEPTH_TEST);
+ if(!dtoutline) glDisable(GL_DEPTH_TEST);
- vtxarray *prev = NULL;
- for(vtxarray *va = visibleva; va; va = va->next)
- {
- if(va->occluded >= OCCLUDE_BB) continue;
- if(!va->alphaback && !va->alphafront && (!va->texs || va->occluded >= OCCLUDE_GEOM)) continue;
+ vtxarray *prev = NULL;
+ for(vtxarray *va = visibleva; va; va = va->next)
+ {
+ if(va->occluded >= OCCLUDE_BB) continue;
+ if(!va->alphaback && !va->alphafront && (!va->texs || va->occluded >= OCCLUDE_GEOM)) continue;
- if(!prev || va->vbuf != prev->vbuf)
- {
- gle::bindvbo(va->vbuf);
- gle::bindebo(va->ebuf);
- const vertex *ptr = 0;
- gle::vertexpointer(sizeof(vertex), ptr->pos.v);
- }
+ if(!prev || va->vbuf != prev->vbuf)
+ {
+ gle::bindvbo(va->vbuf);
+ gle::bindebo(va->ebuf);
+ const vertex *ptr = 0;
+ gle::vertexpointer(sizeof(vertex), ptr->pos.v);
+ }
- if(va->texs && va->occluded < OCCLUDE_GEOM)
- {
- drawvatris(va, 3*va->tris, va->edata);
- xtravertsva += va->verts;
- }
- if(va->alphatris)
- {
- drawvatris(va, 3*va->alphatris, &va->edata[3*(va->tris + va->blendtris)]);
- xtravertsva += 3*va->alphatris;
- }
+ if(va->texs && va->occluded < OCCLUDE_GEOM)
+ {
+ drawvatris(va, 3*va->tris, va->edata);
+ xtravertsva += va->verts;
+ }
+ if(va->alphatris)
+ {
+ drawvatris(va, 3*va->alphatris, &va->edata[3*(va->tris + va->blendtris)]);
+ xtravertsva += 3*va->alphatris;
+ }
- prev = va;
- }
+ prev = va;
+ }
- if(!dtoutline) glEnable(GL_DEPTH_TEST);
+ if(!dtoutline) glEnable(GL_DEPTH_TEST);
- disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
+ disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- gle::clearvbo();
- gle::clearebo();
- gle::disablevertex();
+ gle::clearvbo();
+ gle::clearebo();
+ gle::disablevertex();
}
HVAR(blendbrushcolor, 0, 0x0000C0, 0xFFFFFF);
void renderblendbrush(GLuint tex, float x, float y, float w, float h)
{
- SETSHADER(blendbrush);
+ SETSHADER(blendbrush);
- gle::enablevertex();
+ gle::enablevertex();
- glDepthFunc(GL_LEQUAL);
+ glDepthFunc(GL_LEQUAL);
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glBindTexture(GL_TEXTURE_2D, tex);
- gle::color(vec::hexcolor(blendbrushcolor), 0.25f);
+ glBindTexture(GL_TEXTURE_2D, tex);
+ gle::color(vec::hexcolor(blendbrushcolor), 0.25f);
- LOCALPARAMF(texgenS, 1.0f/w, 0, 0, -x/w);
- LOCALPARAMF(texgenT, 0, 1.0f/h, 0, -y/h);
+ LOCALPARAMF(texgenS, 1.0f/w, 0, 0, -x/w);
+ LOCALPARAMF(texgenT, 0, 1.0f/h, 0, -y/h);
- vtxarray *prev = NULL;
- for(vtxarray *va = visibleva; va; va = va->next)
- {
- if(!va->texs || va->occluded >= OCCLUDE_GEOM) continue;
- if(va->o.x + va->size <= x || va->o.y + va->size <= y || va->o.x >= x + w || va->o.y >= y + h) continue;
+ vtxarray *prev = NULL;
+ for(vtxarray *va = visibleva; va; va = va->next)
+ {
+ if(!va->texs || va->occluded >= OCCLUDE_GEOM) continue;
+ if(va->o.x + va->size <= x || va->o.y + va->size <= y || va->o.x >= x + w || va->o.y >= y + h) continue;
- if(!prev || va->vbuf != prev->vbuf)
- {
- gle::bindvbo(va->vbuf);
- gle::bindebo(va->ebuf);
- const vertex *ptr = 0;
- gle::vertexpointer(sizeof(vertex), ptr->pos.v);
- }
+ if(!prev || va->vbuf != prev->vbuf)
+ {
+ gle::bindvbo(va->vbuf);
+ gle::bindebo(va->ebuf);
+ const vertex *ptr = 0;
+ gle::vertexpointer(sizeof(vertex), ptr->pos.v);
+ }
- drawvatris(va, 3*va->tris, va->edata);
- xtravertsva += va->verts;
+ drawvatris(va, 3*va->tris, va->edata);
+ xtravertsva += va->verts;
- prev = va;
- }
+ prev = va;
+ }
- glDisable(GL_BLEND);
+ glDisable(GL_BLEND);
- glDepthFunc(GL_LESS);
+ glDepthFunc(GL_LESS);
- gle::clearvbo();
- gle::clearebo();
- gle::disablevertex();
+ gle::clearvbo();
+ gle::clearebo();
+ gle::disablevertex();
}
void rendershadowmapreceivers()
{
- SETSHADER(shadowmapreceiver);
+ SETSHADER(shadowmapreceiver);
- gle::enablevertex();
+ gle::enablevertex();
- glCullFace(GL_FRONT);
- glDepthMask(GL_FALSE);
- glDepthFunc(GL_GREATER);
+ glCullFace(GL_FRONT);
+ glDepthMask(GL_FALSE);
+ glDepthFunc(GL_GREATER);
- extern int ati_minmax_bug;
- if(!ati_minmax_bug) glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
+ extern int ati_minmax_bug;
+ if(!ati_minmax_bug) glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
- glEnable(GL_BLEND);
- glBlendEquation_(GL_MAX);
- glBlendFunc(GL_ONE, GL_ONE);
+ glEnable(GL_BLEND);
+ glBlendEquation_(GL_MAX);
+ glBlendFunc(GL_ONE, GL_ONE);
- vtxarray *prev = NULL;
- for(vtxarray *va = visibleva; va; va = va->next)
- {
- if(!va->texs || va->curvfc >= VFC_FOGGED || !isshadowmapreceiver(va)) continue;
+ vtxarray *prev = NULL;
+ for(vtxarray *va = visibleva; va; va = va->next)
+ {
+ if(!va->texs || va->curvfc >= VFC_FOGGED || !isshadowmapreceiver(va)) continue;
- if(!prev || va->vbuf != prev->vbuf)
- {
- gle::bindvbo(va->vbuf);
- gle::bindebo(va->ebuf);
- const vertex *ptr = 0;
- gle::vertexpointer(sizeof(vertex), ptr->pos.v);
- }
+ if(!prev || va->vbuf != prev->vbuf)
+ {
+ gle::bindvbo(va->vbuf);
+ gle::bindebo(va->ebuf);
+ const vertex *ptr = 0;
+ gle::vertexpointer(sizeof(vertex), ptr->pos.v);
+ }
- drawvatris(va, 3*va->tris, va->edata);
- xtravertsva += va->verts;
+ drawvatris(va, 3*va->tris, va->edata);
+ xtravertsva += va->verts;
- prev = va;
- }
+ prev = va;
+ }
- glDisable(GL_BLEND);
- glBlendEquation_(GL_FUNC_ADD);
+ glDisable(GL_BLEND);
+ glBlendEquation_(GL_FUNC_ADD);
- glCullFace(GL_BACK);
- glDepthMask(GL_TRUE);
- glDepthFunc(GL_LESS);
+ glCullFace(GL_BACK);
+ glDepthMask(GL_TRUE);
+ glDepthFunc(GL_LESS);
- if(!ati_minmax_bug) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ if(!ati_minmax_bug) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- gle::clearvbo();
- gle::clearebo();
- gle::disablevertex();
+ gle::clearvbo();
+ gle::clearebo();
+ gle::disablevertex();
}
void renderdepthobstacles(const vec &bbmin, const vec &bbmax, float scale, float *ranges, int numranges)
{
- float scales[4] = { 0, 0, 0, 0 }, offsets[4] = { 0, 0, 0, 0 };
- if(numranges < 0)
- {
- SETSHADER(depthfxsplitworld);
-
- loopi(-numranges)
- {
- if(!i) scales[i] = 1.0f/scale;
- else scales[i] = scales[i-1]*256;
- }
- }
- else
- {
- SETSHADER(depthfxworld);
-
- if(!numranges) loopi(4) scales[i] = 1.0f/scale;
- else loopi(numranges)
- {
- scales[i] = 1.0f/scale;
- offsets[i] = -ranges[i]/scale;
- }
- }
- LOCALPARAMF(depthscale, scales[0], scales[1], scales[2], scales[3]);
- LOCALPARAMF(depthoffsets, offsets[0], offsets[1], offsets[2], offsets[3]);
-
- gle::enablevertex();
-
- vtxarray *prev = NULL;
- for(vtxarray *va = visibleva; va; va = va->next)
- {
- if(!va->texs || va->occluded >= OCCLUDE_GEOM ||
- va->o.x > bbmax.x || va->o.y > bbmax.y || va->o.z > bbmax.z ||
- va->o.x + va->size < bbmin.x || va->o.y + va->size < bbmin.y || va->o.z + va->size < bbmin.z)
- continue;
-
- if(!prev || va->vbuf != prev->vbuf)
- {
- gle::bindvbo(va->vbuf);
- gle::bindebo(va->ebuf);
- const vertex *ptr = 0;
- gle::vertexpointer(sizeof(vertex), ptr->pos.v);
- }
-
- drawvatris(va, 3*va->tris, va->edata);
- xtravertsva += va->verts;
- if(va->alphatris > 0)
- {
- drawvatris(va, 3*va->alphatris, va->edata + 3*(va->tris + va->blendtris));
- xtravertsva += 3*va->alphatris;
- }
-
- prev = va;
- }
-
- gle::clearvbo();
- gle::clearebo();
- gle::disablevertex();
+ float scales[4] = { 0, 0, 0, 0 }, offsets[4] = { 0, 0, 0, 0 };
+ if(numranges < 0)
+ {
+ SETSHADER(depthfxsplitworld);
+
+ loopi(-numranges)
+ {
+ if(!i) scales[i] = 1.0f/scale;
+ else scales[i] = scales[i-1]*256;
+ }
+ }
+ else
+ {
+ SETSHADER(depthfxworld);
+
+ if(!numranges) loopi(4) scales[i] = 1.0f/scale;
+ else loopi(numranges)
+ {
+ scales[i] = 1.0f/scale;
+ offsets[i] = -ranges[i]/scale;
+ }
+ }
+ LOCALPARAMF(depthscale, scales[0], scales[1], scales[2], scales[3]);
+ LOCALPARAMF(depthoffsets, offsets[0], offsets[1], offsets[2], offsets[3]);
+
+ gle::enablevertex();
+
+ vtxarray *prev = NULL;
+ for(vtxarray *va = visibleva; va; va = va->next)
+ {
+ if(!va->texs || va->occluded >= OCCLUDE_GEOM ||
+ va->o.x > bbmax.x || va->o.y > bbmax.y || va->o.z > bbmax.z ||
+ va->o.x + va->size < bbmin.x || va->o.y + va->size < bbmin.y || va->o.z + va->size < bbmin.z)
+ continue;
+
+ if(!prev || va->vbuf != prev->vbuf)
+ {
+ gle::bindvbo(va->vbuf);
+ gle::bindebo(va->ebuf);
+ const vertex *ptr = 0;
+ gle::vertexpointer(sizeof(vertex), ptr->pos.v);
+ }
+
+ drawvatris(va, 3*va->tris, va->edata);
+ xtravertsva += va->verts;
+ if(va->alphatris > 0)
+ {
+ drawvatris(va, 3*va->alphatris, va->edata + 3*(va->tris + va->blendtris));
+ xtravertsva += 3*va->alphatris;
+ }
+
+ prev = va;
+ }
+
+ gle::clearvbo();
+ gle::clearebo();
+ gle::disablevertex();
}
VAR(oqdist, 0, 256, 1024);
@@ -828,101 +828,101 @@ VAR(envpass, 0, 1, 1);
struct renderstate
{
- bool colormask, depthmask, blending;
- int alphaing;
- GLuint vbuf;
- bool vattribs, vquery;
- vec colorscale, lightcolor;
- float alphascale;
- GLuint textures[8];
- Slot *slot, *texgenslot;
- VSlot *vslot, *texgenvslot;
- vec2 texgenscroll;
- int texgendim;
- int visibledynlights;
- uint dynlightmask;
-
- renderstate() : colormask(true), depthmask(true), blending(false), alphaing(0), vbuf(0), vattribs(false), vquery(false), colorscale(1, 1, 1), alphascale(0), slot(NULL), texgenslot(NULL), vslot(NULL), texgenvslot(NULL), texgenscroll(0, 0), texgendim(-1), visibledynlights(0), dynlightmask(0)
- {
- loopk(8) textures[k] = 0;
- }
+ bool colormask, depthmask, blending;
+ int alphaing;
+ GLuint vbuf;
+ bool vattribs, vquery;
+ vec colorscale, lightcolor;
+ float alphascale;
+ GLuint textures[8];
+ Slot *slot, *texgenslot;
+ VSlot *vslot, *texgenvslot;
+ vec2 texgenscroll;
+ int texgendim;
+ int visibledynlights;
+ uint dynlightmask;
+
+ renderstate() : colormask(true), depthmask(true), blending(false), alphaing(0), vbuf(0), vattribs(false), vquery(false), colorscale(1, 1, 1), alphascale(0), slot(NULL), texgenslot(NULL), vslot(NULL), texgenvslot(NULL), texgenscroll(0, 0), texgendim(-1), visibledynlights(0), dynlightmask(0)
+ {
+ loopk(8) textures[k] = 0;
+ }
};
static inline void disablevbuf(renderstate &cur)
{
- gle::clearvbo();
- gle::clearebo();
- cur.vbuf = 0;
+ gle::clearvbo();
+ gle::clearebo();
+ cur.vbuf = 0;
}
static inline void enablevquery(renderstate &cur)
{
- if(cur.colormask) { cur.colormask = false; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); }
- if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
- startbb(false);
- cur.vquery = true;
+ if(cur.colormask) { cur.colormask = false; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); }
+ if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
+ startbb(false);
+ cur.vquery = true;
}
static inline void disablevquery(renderstate &cur)
{
- endbb(false);
- cur.vquery = false;
+ endbb(false);
+ cur.vquery = false;
}
static void renderquery(renderstate &cur, occludequery *query, vtxarray *va, bool full = true)
{
- if(!cur.vquery) enablevquery(cur);
+ if(!cur.vquery) enablevquery(cur);
- startquery(query);
+ startquery(query);
- if(full) drawbb(ivec(va->bbmin).sub(1), ivec(va->bbmax).sub(va->bbmin).add(2));
- else drawbb(va->geommin, ivec(va->geommax).sub(va->geommin));
+ if(full) drawbb(ivec(va->bbmin).sub(1), ivec(va->bbmax).sub(va->bbmin).add(2));
+ else drawbb(va->geommin, ivec(va->geommax).sub(va->geommin));
- endquery(query);
+ endquery(query);
}
enum
{
- RENDERPASS_LIGHTMAP = 0,
- RENDERPASS_Z,
- RENDERPASS_CAUSTICS,
- RENDERPASS_FOG,
- RENDERPASS_LIGHTMAP_BLEND
+ RENDERPASS_LIGHTMAP = 0,
+ RENDERPASS_Z,
+ RENDERPASS_CAUSTICS,
+ RENDERPASS_FOG,
+ RENDERPASS_LIGHTMAP_BLEND
};
struct geombatch
{
- const elementset &es;
- VSlot &vslot;
- ushort *edata;
- vtxarray *va;
- int next, batch;
-
- geombatch(const elementset &es, ushort *edata, vtxarray *va)
- : es(es), vslot(lookupvslot(es.texture)), edata(edata), va(va),
- next(-1), batch(-1)
- {}
-
- int compare(const geombatch &b) const
- {
- if(va->vbuf < b.va->vbuf) return -1;
- if(va->vbuf > b.va->vbuf) return 1;
- if(va->dynlightmask < b.va->dynlightmask) return -1;
- if(va->dynlightmask > b.va->dynlightmask) return 1;
- if(vslot.slot->shader < b.vslot.slot->shader) return -1;
- if(vslot.slot->shader > b.vslot.slot->shader) return 1;
- if(vslot.slot->params.length() < b.vslot.slot->params.length()) return -1;
- if(vslot.slot->params.length() > b.vslot.slot->params.length()) return 1;
- if(es.texture < b.es.texture) return -1;
- if(es.texture > b.es.texture) return 1;
- if(es.lmid < b.es.lmid) return -1;
- if(es.lmid > b.es.lmid) return 1;
- if(es.envmap < b.es.envmap) return -1;
- if(es.envmap > b.es.envmap) return 1;
- if(es.dim < b.es.dim) return -1;
- if(es.dim > b.es.dim) return 1;
- return 0;
- }
+ const elementset &es;
+ VSlot &vslot;
+ ushort *edata;
+ vtxarray *va;
+ int next, batch;
+
+ geombatch(const elementset &es, ushort *edata, vtxarray *va)
+ : es(es), vslot(lookupvslot(es.texture)), edata(edata), va(va),
+ next(-1), batch(-1)
+ {}
+
+ int compare(const geombatch &b) const
+ {
+ if(va->vbuf < b.va->vbuf) return -1;
+ if(va->vbuf > b.va->vbuf) return 1;
+ if(va->dynlightmask < b.va->dynlightmask) return -1;
+ if(va->dynlightmask > b.va->dynlightmask) return 1;
+ if(vslot.slot->shader < b.vslot.slot->shader) return -1;
+ if(vslot.slot->shader > b.vslot.slot->shader) return 1;
+ if(vslot.slot->params.length() < b.vslot.slot->params.length()) return -1;
+ if(vslot.slot->params.length() > b.vslot.slot->params.length()) return 1;
+ if(es.texture < b.es.texture) return -1;
+ if(es.texture > b.es.texture) return 1;
+ if(es.lmid < b.es.lmid) return -1;
+ if(es.lmid > b.es.lmid) return 1;
+ if(es.envmap < b.es.envmap) return -1;
+ if(es.envmap > b.es.envmap) return 1;
+ if(es.dim < b.es.dim) return -1;
+ if(es.dim > b.es.dim) return 1;
+ return 0;
+ }
};
static vector<geombatch> geombatches;
@@ -930,486 +930,486 @@ static int firstbatch = -1, numbatches = 0;
static void mergetexs(renderstate &cur, vtxarray *va, elementset *texs = NULL, int numtexs = 0, ushort *edata = NULL)
{
- if(!texs)
- {
- texs = va->eslist;
- numtexs = va->texs;
- edata = va->edata;
- if(cur.alphaing)
- {
- texs += va->texs + va->blends;
- edata += 3*(va->tris + va->blendtris);
- numtexs = va->alphaback;
- if(cur.alphaing > 1) numtexs += va->alphafront;
- }
- }
-
- if(firstbatch < 0)
- {
- firstbatch = geombatches.length();
- numbatches = numtexs;
- loopi(numtexs-1)
- {
- geombatches.add(geombatch(texs[i], edata, va)).next = i+1;
- edata += texs[i].length[1];
- }
- geombatches.add(geombatch(texs[numtexs-1], edata, va));
- return;
- }
-
- int prevbatch = -1, curbatch = firstbatch, curtex = 0;
- do
- {
- geombatch &b = geombatches.add(geombatch(texs[curtex], edata, va));
- edata += texs[curtex].length[1];
- int dir = -1;
- while(curbatch >= 0)
- {
- dir = b.compare(geombatches[curbatch]);
- if(dir <= 0) break;
- prevbatch = curbatch;
- curbatch = geombatches[curbatch].next;
- }
- if(!dir)
- {
- int last = curbatch, next;
- for(;;)
- {
- next = geombatches[last].batch;
- if(next < 0) break;
- last = next;
- }
- if(last==curbatch)
- {
- b.batch = curbatch;
- b.next = geombatches[curbatch].next;
- if(prevbatch < 0) firstbatch = geombatches.length()-1;
- else geombatches[prevbatch].next = geombatches.length()-1;
- curbatch = geombatches.length()-1;
- }
- else
- {
- b.batch = next;
- geombatches[last].batch = geombatches.length()-1;
- }
- }
- else
- {
- numbatches++;
- b.next = curbatch;
- if(prevbatch < 0) firstbatch = geombatches.length()-1;
- else geombatches[prevbatch].next = geombatches.length()-1;
- prevbatch = geombatches.length()-1;
- }
- }
- while(++curtex < numtexs);
+ if(!texs)
+ {
+ texs = va->eslist;
+ numtexs = va->texs;
+ edata = va->edata;
+ if(cur.alphaing)
+ {
+ texs += va->texs + va->blends;
+ edata += 3*(va->tris + va->blendtris);
+ numtexs = va->alphaback;
+ if(cur.alphaing > 1) numtexs += va->alphafront;
+ }
+ }
+
+ if(firstbatch < 0)
+ {
+ firstbatch = geombatches.length();
+ numbatches = numtexs;
+ loopi(numtexs-1)
+ {
+ geombatches.add(geombatch(texs[i], edata, va)).next = i+1;
+ edata += texs[i].length[1];
+ }
+ geombatches.add(geombatch(texs[numtexs-1], edata, va));
+ return;
+ }
+
+ int prevbatch = -1, curbatch = firstbatch, curtex = 0;
+ do
+ {
+ geombatch &b = geombatches.add(geombatch(texs[curtex], edata, va));
+ edata += texs[curtex].length[1];
+ int dir = -1;
+ while(curbatch >= 0)
+ {
+ dir = b.compare(geombatches[curbatch]);
+ if(dir <= 0) break;
+ prevbatch = curbatch;
+ curbatch = geombatches[curbatch].next;
+ }
+ if(!dir)
+ {
+ int last = curbatch, next;
+ for(;;)
+ {
+ next = geombatches[last].batch;
+ if(next < 0) break;
+ last = next;
+ }
+ if(last==curbatch)
+ {
+ b.batch = curbatch;
+ b.next = geombatches[curbatch].next;
+ if(prevbatch < 0) firstbatch = geombatches.length()-1;
+ else geombatches[prevbatch].next = geombatches.length()-1;
+ curbatch = geombatches.length()-1;
+ }
+ else
+ {
+ b.batch = next;
+ geombatches[last].batch = geombatches.length()-1;
+ }
+ }
+ else
+ {
+ numbatches++;
+ b.next = curbatch;
+ if(prevbatch < 0) firstbatch = geombatches.length()-1;
+ else geombatches[prevbatch].next = geombatches.length()-1;
+ prevbatch = geombatches.length()-1;
+ }
+ }
+ while(++curtex < numtexs);
}
static inline void enablevattribs(renderstate &cur, bool all = true)
{
- gle::enablevertex();
- if(all)
- {
- gle::enabletexcoord0();
- gle::enabletexcoord1();
- gle::enablenormal();
- gle::enabletangent();
- }
- cur.vattribs = true;
+ gle::enablevertex();
+ if(all)
+ {
+ gle::enabletexcoord0();
+ gle::enabletexcoord1();
+ gle::enablenormal();
+ gle::enabletangent();
+ }
+ cur.vattribs = true;
}
static inline void disablevattribs(renderstate &cur, bool all = true)
{
- gle::disablevertex();
- if(all)
- {
- gle::disabletexcoord0();
- gle::disabletexcoord1();
- gle::disablenormal();
- gle::disabletangent();
- }
- cur.vattribs = false;
+ gle::disablevertex();
+ if(all)
+ {
+ gle::disabletexcoord0();
+ gle::disabletexcoord1();
+ gle::disablenormal();
+ gle::disabletangent();
+ }
+ cur.vattribs = false;
}
static void changevbuf(renderstate &cur, int pass, vtxarray *va)
{
- gle::bindvbo(va->vbuf);
- gle::bindebo(va->ebuf);
- cur.vbuf = va->vbuf;
+ gle::bindvbo(va->vbuf);
+ gle::bindebo(va->ebuf);
+ cur.vbuf = va->vbuf;
- vertex *vdata = (vertex *)0;
- gle::vertexpointer(sizeof(vertex), vdata->pos.v);
+ vertex *vdata = (vertex *)0;
+ gle::vertexpointer(sizeof(vertex), vdata->pos.v);
- if(pass==RENDERPASS_LIGHTMAP)
- {
- gle::normalpointer(sizeof(vertex), vdata->norm.v, GL_BYTE);
- gle::texcoord0pointer(sizeof(vertex), vdata->tc.v);
- gle::texcoord1pointer(sizeof(vertex), vdata->lm.v, GL_SHORT);
- gle::tangentpointer(sizeof(vertex), vdata->tangent.v, GL_BYTE);
- }
+ if(pass==RENDERPASS_LIGHTMAP)
+ {
+ gle::normalpointer(sizeof(vertex), vdata->norm.v, GL_BYTE);
+ gle::texcoord0pointer(sizeof(vertex), vdata->tc.v);
+ gle::texcoord1pointer(sizeof(vertex), vdata->lm.v, GL_SHORT);
+ gle::tangentpointer(sizeof(vertex), vdata->tangent.v, GL_BYTE);
+ }
}
static void changebatchtmus(renderstate &cur, int pass, geombatch &b)
{
- bool changed = false;
- extern bool brightengeom;
- extern int fullbright;
- int lmid = brightengeom && (b.es.lmid < LMID_RESERVED || (fullbright && editmode)) ? (int) LMID_BRIGHT : (int) b.es.lmid;
- if(cur.textures[1]!=lightmaptexs[lmid].id)
- {
- glActiveTexture_(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, cur.textures[1] = lightmaptexs[lmid].id);
- changed = true;
- }
- int tmu = 2;
- if(b.vslot.slot->shader->type&SHADER_NORMALSLMS)
- {
- if(cur.textures[tmu]!=lightmaptexs[lmid+1].id)
- {
- glActiveTexture_(GL_TEXTURE0+tmu);
- glBindTexture(GL_TEXTURE_2D, cur.textures[tmu] = lightmaptexs[lmid+1].id);
- changed = true;
- }
- tmu++;
- }
- if(b.vslot.slot->shader->type&SHADER_ENVMAP && b.es.envmap!=EMID_CUSTOM)
- {
- GLuint emtex = lookupenvmap(b.es.envmap);
- if(cur.textures[tmu]!=emtex)
- {
- glActiveTexture_(GL_TEXTURE0+tmu);
- glBindTexture(GL_TEXTURE_CUBE_MAP, cur.textures[tmu] = emtex);
- changed = true;
- }
- }
- if(changed) glActiveTexture_(GL_TEXTURE0);
-
- if(cur.dynlightmask != b.va->dynlightmask)
- {
- cur.visibledynlights = setdynlights(b.va);
- cur.dynlightmask = b.va->dynlightmask;
- }
+ bool changed = false;
+ extern bool brightengeom;
+ extern int fullbright;
+ int lmid = brightengeom && (b.es.lmid < LMID_RESERVED || (fullbright && editmode)) ? (int) LMID_BRIGHT : (int) b.es.lmid;
+ if(cur.textures[1]!=lightmaptexs[lmid].id)
+ {
+ glActiveTexture_(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, cur.textures[1] = lightmaptexs[lmid].id);
+ changed = true;
+ }
+ int tmu = 2;
+ if(b.vslot.slot->shader->type&SHADER_NORMALSLMS)
+ {
+ if(cur.textures[tmu]!=lightmaptexs[lmid+1].id)
+ {
+ glActiveTexture_(GL_TEXTURE0+tmu);
+ glBindTexture(GL_TEXTURE_2D, cur.textures[tmu] = lightmaptexs[lmid+1].id);
+ changed = true;
+ }
+ tmu++;
+ }
+ if(b.vslot.slot->shader->type&SHADER_ENVMAP && b.es.envmap!=EMID_CUSTOM)
+ {
+ GLuint emtex = lookupenvmap(b.es.envmap);
+ if(cur.textures[tmu]!=emtex)
+ {
+ glActiveTexture_(GL_TEXTURE0+tmu);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, cur.textures[tmu] = emtex);
+ changed = true;
+ }
+ }
+ if(changed) glActiveTexture_(GL_TEXTURE0);
+
+ if(cur.dynlightmask != b.va->dynlightmask)
+ {
+ cur.visibledynlights = setdynlights(b.va);
+ cur.dynlightmask = b.va->dynlightmask;
+ }
}
static void changeslottmus(renderstate &cur, int pass, Slot &slot, VSlot &vslot)
{
- if(pass==RENDERPASS_LIGHTMAP)
- {
- GLuint diffusetex = slot.sts.empty() ? notexture->id : slot.sts[0].t->id;
- if(cur.textures[0]!=diffusetex)
- glBindTexture(GL_TEXTURE_2D, cur.textures[0] = diffusetex);
- }
-
- if(cur.alphaing)
- {
- float alpha = cur.alphaing > 1 ? vslot.alphafront : vslot.alphaback;
- if(cur.colorscale != vslot.colorscale || cur.alphascale != alpha)
- {
- cur.colorscale = vslot.colorscale;
- cur.alphascale = alpha;
- GLOBALPARAMF(colorparams, 2*alpha*vslot.colorscale.x, 2*alpha*vslot.colorscale.y, 2*alpha*vslot.colorscale.z, alpha);
- setfogcolor(vec(curfogcolor).mul(alpha));
- }
- }
- else if(cur.colorscale != vslot.colorscale)
- {
- cur.colorscale = vslot.colorscale;
- GLOBALPARAMF(colorparams, 2*vslot.colorscale.x, 2*vslot.colorscale.y, 2*vslot.colorscale.z, 1);
- }
- int tmu = 2, envmaptmu = -1;
- if(slot.shader->type&SHADER_NORMALSLMS) tmu++;
- if(slot.shader->type&SHADER_ENVMAP) envmaptmu = tmu++;
- loopvj(slot.sts)
- {
- Slot::Tex &t = slot.sts[j];
- if(t.type==TEX_DIFFUSE || t.combined>=0) continue;
- if(t.type==TEX_ENVMAP)
- {
- if(envmaptmu>=0 && t.t && cur.textures[envmaptmu]!=t.t->id)
- {
- glActiveTexture_(GL_TEXTURE0+envmaptmu);
- glBindTexture(GL_TEXTURE_CUBE_MAP, cur.textures[envmaptmu] = t.t->id);
- }
- }
- else
- {
- if(cur.textures[tmu]!=t.t->id)
- {
- glActiveTexture_(GL_TEXTURE0+tmu);
- glBindTexture(GL_TEXTURE_2D, cur.textures[tmu] = t.t->id);
- }
- if(++tmu >= 8) break;
- }
- }
- glActiveTexture_(GL_TEXTURE0);
-
- cur.slot = &slot;
- cur.vslot = &vslot;
+ if(pass==RENDERPASS_LIGHTMAP)
+ {
+ GLuint diffusetex = slot.sts.empty() ? notexture->id : slot.sts[0].t->id;
+ if(cur.textures[0]!=diffusetex)
+ glBindTexture(GL_TEXTURE_2D, cur.textures[0] = diffusetex);
+ }
+
+ if(cur.alphaing)
+ {
+ float alpha = cur.alphaing > 1 ? vslot.alphafront : vslot.alphaback;
+ if(cur.colorscale != vslot.colorscale || cur.alphascale != alpha)
+ {
+ cur.colorscale = vslot.colorscale;
+ cur.alphascale = alpha;
+ GLOBALPARAMF(colorparams, 2*alpha*vslot.colorscale.x, 2*alpha*vslot.colorscale.y, 2*alpha*vslot.colorscale.z, alpha);
+ setfogcolor(vec(curfogcolor).mul(alpha));
+ }
+ }
+ else if(cur.colorscale != vslot.colorscale)
+ {
+ cur.colorscale = vslot.colorscale;
+ GLOBALPARAMF(colorparams, 2*vslot.colorscale.x, 2*vslot.colorscale.y, 2*vslot.colorscale.z, 1);
+ }
+ int tmu = 2, envmaptmu = -1;
+ if(slot.shader->type&SHADER_NORMALSLMS) tmu++;
+ if(slot.shader->type&SHADER_ENVMAP) envmaptmu = tmu++;
+ loopvj(slot.sts)
+ {
+ Slot::Tex &t = slot.sts[j];
+ if(t.type==TEX_DIFFUSE || t.combined>=0) continue;
+ if(t.type==TEX_ENVMAP)
+ {
+ if(envmaptmu>=0 && t.t && cur.textures[envmaptmu]!=t.t->id)
+ {
+ glActiveTexture_(GL_TEXTURE0+envmaptmu);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, cur.textures[envmaptmu] = t.t->id);
+ }
+ }
+ else
+ {
+ if(cur.textures[tmu]!=t.t->id)
+ {
+ glActiveTexture_(GL_TEXTURE0+tmu);
+ glBindTexture(GL_TEXTURE_2D, cur.textures[tmu] = t.t->id);
+ }
+ if(++tmu >= 8) break;
+ }
+ }
+ glActiveTexture_(GL_TEXTURE0);
+
+ cur.slot = &slot;
+ cur.vslot = &vslot;
}
static void changeshader(renderstate &cur, Shader *s, Slot &slot, VSlot &vslot, bool shadowed)
{
- if(glaring)
- {
- static Shader *noglareshader = NULL, *noglareblendshader = NULL, *noglarealphashader = NULL;
- Shader *fallback;
- if(cur.blending) { if(!noglareblendshader) noglareblendshader = lookupshaderbyname("noglareblendworld"); fallback = noglareblendshader; }
- else if(cur.alphaing) { if(!noglarealphashader) noglarealphashader = lookupshaderbyname("noglarealphaworld"); fallback = noglarealphashader; }
- else { if(!noglareshader) noglareshader = lookupshaderbyname("noglareworld"); fallback = noglareshader; }
- if(s->hasoption(4)) s->setvariant(cur.visibledynlights, 4, slot, vslot, fallback);
- else s->setvariant(cur.blending ? 1 : 0, 4, slot, vslot, fallback);
- }
- else if(fading && !cur.blending && !cur.alphaing)
- {
- if(shadowed) s->setvariant(cur.visibledynlights, 3, slot, vslot);
- else s->setvariant(cur.visibledynlights, 2, slot, vslot);
- }
- else if(shadowed) s->setvariant(cur.visibledynlights, 1, slot, vslot);
- else if(!cur.visibledynlights) s->set(slot, vslot);
- else s->setvariant(cur.visibledynlights-1, 0, slot, vslot);
+ if(glaring)
+ {
+ static Shader *noglareshader = NULL, *noglareblendshader = NULL, *noglarealphashader = NULL;
+ Shader *fallback;
+ if(cur.blending) { if(!noglareblendshader) noglareblendshader = lookupshaderbyname("noglareblendworld"); fallback = noglareblendshader; }
+ else if(cur.alphaing) { if(!noglarealphashader) noglarealphashader = lookupshaderbyname("noglarealphaworld"); fallback = noglarealphashader; }
+ else { if(!noglareshader) noglareshader = lookupshaderbyname("noglareworld"); fallback = noglareshader; }
+ if(s->hasoption(4)) s->setvariant(cur.visibledynlights, 4, slot, vslot, fallback);
+ else s->setvariant(cur.blending ? 1 : 0, 4, slot, vslot, fallback);
+ }
+ else if(fading && !cur.blending && !cur.alphaing)
+ {
+ if(shadowed) s->setvariant(cur.visibledynlights, 3, slot, vslot);
+ else s->setvariant(cur.visibledynlights, 2, slot, vslot);
+ }
+ else if(shadowed) s->setvariant(cur.visibledynlights, 1, slot, vslot);
+ else if(!cur.visibledynlights) s->set(slot, vslot);
+ else s->setvariant(cur.visibledynlights-1, 0, slot, vslot);
}
static void changetexgen(renderstate &cur, int dim, Slot &slot, VSlot &vslot)
{
- if(cur.texgenslot != &slot || cur.texgenvslot != &vslot)
- {
- Texture *curtex = !cur.texgenslot || cur.texgenslot->sts.empty() ? notexture : cur.texgenslot->sts[0].t,
- *tex = slot.sts.empty() ? notexture : slot.sts[0].t;
- if(!cur.texgenvslot || slot.sts.empty() ||
- (curtex->xs != tex->xs || curtex->ys != tex->ys ||
- cur.texgenvslot->rotation != vslot.rotation || cur.texgenvslot->scale != vslot.scale ||
- cur.texgenvslot->offset != vslot.offset || cur.texgenvslot->scroll != vslot.scroll))
- {
- const texrotation &r = texrotations[vslot.rotation];
- float xs = r.flipx ? -tex->xs : tex->xs,
- ys = r.flipy ? -tex->ys : tex->ys;
- vec2 scroll(vslot.scroll);
- if(r.swapxy) swap(scroll.x, scroll.y);
- scroll.x *= lastmillis*tex->xs/xs;
- scroll.y *= lastmillis*tex->ys/ys;
- if(cur.texgenscroll != scroll)
- {
- cur.texgenscroll = scroll;
- cur.texgendim = -1;
- }
- }
- cur.texgenslot = &slot;
- cur.texgenvslot = &vslot;
- }
-
- if(cur.texgendim == dim) return;
- GLOBALPARAM(texgenscroll, cur.texgenscroll);
- cur.texgendim = dim;
+ if(cur.texgenslot != &slot || cur.texgenvslot != &vslot)
+ {
+ Texture *curtex = !cur.texgenslot || cur.texgenslot->sts.empty() ? notexture : cur.texgenslot->sts[0].t,
+ *tex = slot.sts.empty() ? notexture : slot.sts[0].t;
+ if(!cur.texgenvslot || slot.sts.empty() ||
+ (curtex->xs != tex->xs || curtex->ys != tex->ys ||
+ cur.texgenvslot->rotation != vslot.rotation || cur.texgenvslot->scale != vslot.scale ||
+ cur.texgenvslot->offset != vslot.offset || cur.texgenvslot->scroll != vslot.scroll))
+ {
+ const texrotation &r = texrotations[vslot.rotation];
+ float xs = r.flipx ? -tex->xs : tex->xs,
+ ys = r.flipy ? -tex->ys : tex->ys;
+ vec2 scroll(vslot.scroll);
+ if(r.swapxy) swap(scroll.x, scroll.y);
+ scroll.x *= lastmillis*tex->xs/xs;
+ scroll.y *= lastmillis*tex->ys/ys;
+ if(cur.texgenscroll != scroll)
+ {
+ cur.texgenscroll = scroll;
+ cur.texgendim = -1;
+ }
+ }
+ cur.texgenslot = &slot;
+ cur.texgenvslot = &vslot;
+ }
+
+ if(cur.texgendim == dim) return;
+ GLOBALPARAM(texgenscroll, cur.texgenscroll);
+ cur.texgendim = dim;
}
static void renderbatch(renderstate &cur, int pass, geombatch &b)
{
- geombatch *shadowed = NULL;
- int rendered = -1;
- for(geombatch *curbatch = &b;; curbatch = &geombatches[curbatch->batch])
- {
- ushort len = curbatch->es.length[curbatch->va->shadowed ? 0 : 1];
- if(len)
- {
- if(rendered < 0)
- {
- changeshader(cur, b.vslot.slot->shader, *b.vslot.slot, b.vslot, false);
- rendered = 0;
- gbatches++;
- }
- ushort minvert = curbatch->es.minvert[0], maxvert = curbatch->es.maxvert[0];
- if(!curbatch->va->shadowed) { minvert = min(minvert, curbatch->es.minvert[1]); maxvert = max(maxvert, curbatch->es.maxvert[1]); }
- drawtris(len, curbatch->edata, minvert, maxvert);
- vtris += len/3;
- }
- if(curbatch->es.length[1] > len && !shadowed) shadowed = curbatch;
- if(curbatch->batch < 0) break;
- }
- if(shadowed) for(geombatch *curbatch = shadowed;; curbatch = &geombatches[curbatch->batch])
- {
- if(curbatch->va->shadowed && curbatch->es.length[1] > curbatch->es.length[0])
- {
- if(rendered < 1)
- {
- changeshader(cur, b.vslot.slot->shader, *b.vslot.slot, b.vslot, true);
- rendered = 1;
- gbatches++;
- }
- ushort len = curbatch->es.length[1] - curbatch->es.length[0];
- drawtris(len, curbatch->edata + curbatch->es.length[0], curbatch->es.minvert[1], curbatch->es.maxvert[1]);
- vtris += len/3;
- }
- if(curbatch->batch < 0) break;
- }
+ geombatch *shadowed = NULL;
+ int rendered = -1;
+ for(geombatch *curbatch = &b;; curbatch = &geombatches[curbatch->batch])
+ {
+ ushort len = curbatch->es.length[curbatch->va->shadowed ? 0 : 1];
+ if(len)
+ {
+ if(rendered < 0)
+ {
+ changeshader(cur, b.vslot.slot->shader, *b.vslot.slot, b.vslot, false);
+ rendered = 0;
+ gbatches++;
+ }
+ ushort minvert = curbatch->es.minvert[0], maxvert = curbatch->es.maxvert[0];
+ if(!curbatch->va->shadowed) { minvert = min(minvert, curbatch->es.minvert[1]); maxvert = max(maxvert, curbatch->es.maxvert[1]); }
+ drawtris(len, curbatch->edata, minvert, maxvert);
+ vtris += len/3;
+ }
+ if(curbatch->es.length[1] > len && !shadowed) shadowed = curbatch;
+ if(curbatch->batch < 0) break;
+ }
+ if(shadowed) for(geombatch *curbatch = shadowed;; curbatch = &geombatches[curbatch->batch])
+ {
+ if(curbatch->va->shadowed && curbatch->es.length[1] > curbatch->es.length[0])
+ {
+ if(rendered < 1)
+ {
+ changeshader(cur, b.vslot.slot->shader, *b.vslot.slot, b.vslot, true);
+ rendered = 1;
+ gbatches++;
+ }
+ ushort len = curbatch->es.length[1] - curbatch->es.length[0];
+ drawtris(len, curbatch->edata + curbatch->es.length[0], curbatch->es.minvert[1], curbatch->es.maxvert[1]);
+ vtris += len/3;
+ }
+ if(curbatch->batch < 0) break;
+ }
}
static void resetbatches()
{
- geombatches.setsize(0);
- firstbatch = -1;
- numbatches = 0;
+ geombatches.setsize(0);
+ firstbatch = -1;
+ numbatches = 0;
}
static void renderbatches(renderstate &cur, int pass)
{
- cur.slot = NULL;
- cur.vslot = NULL;
- int curbatch = firstbatch;
- if(curbatch >= 0)
- {
- if(cur.alphaing)
- {
- if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
- }
- else if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
- if(!cur.colormask) { cur.colormask = true; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, cur.alphaing ? GL_FALSE : GL_TRUE); }
- if(!cur.vattribs)
- {
- if(cur.vquery) disablevquery(cur);
- enablevattribs(cur);
- }
- }
- while(curbatch >= 0)
- {
- geombatch &b = geombatches[curbatch];
- curbatch = b.next;
-
- if(cur.vbuf != b.va->vbuf) changevbuf(cur, pass, b.va);
- if(cur.vslot != &b.vslot)
- {
- changeslottmus(cur, pass, *b.vslot.slot, b.vslot);
- if(cur.texgendim != b.es.dim || (cur.texgendim <= 2 && cur.texgenvslot != &b.vslot)) changetexgen(cur, b.es.dim, *b.vslot.slot, b.vslot);
- }
- else if(cur.texgendim != b.es.dim) changetexgen(cur, b.es.dim, *b.vslot.slot, b.vslot);
- if(pass == RENDERPASS_LIGHTMAP) changebatchtmus(cur, pass, b);
-
- renderbatch(cur, pass, b);
- }
-
- resetbatches();
+ cur.slot = NULL;
+ cur.vslot = NULL;
+ int curbatch = firstbatch;
+ if(curbatch >= 0)
+ {
+ if(cur.alphaing)
+ {
+ if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
+ }
+ else if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
+ if(!cur.colormask) { cur.colormask = true; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, cur.alphaing ? GL_FALSE : GL_TRUE); }
+ if(!cur.vattribs)
+ {
+ if(cur.vquery) disablevquery(cur);
+ enablevattribs(cur);
+ }
+ }
+ while(curbatch >= 0)
+ {
+ geombatch &b = geombatches[curbatch];
+ curbatch = b.next;
+
+ if(cur.vbuf != b.va->vbuf) changevbuf(cur, pass, b.va);
+ if(cur.vslot != &b.vslot)
+ {
+ changeslottmus(cur, pass, *b.vslot.slot, b.vslot);
+ if(cur.texgendim != b.es.dim || (cur.texgendim <= 2 && cur.texgenvslot != &b.vslot)) changetexgen(cur, b.es.dim, *b.vslot.slot, b.vslot);
+ }
+ else if(cur.texgendim != b.es.dim) changetexgen(cur, b.es.dim, *b.vslot.slot, b.vslot);
+ if(pass == RENDERPASS_LIGHTMAP) changebatchtmus(cur, pass, b);
+
+ renderbatch(cur, pass, b);
+ }
+
+ resetbatches();
}
void renderzpass(renderstate &cur, vtxarray *va)
{
- if(!cur.vattribs)
- {
- if(cur.vquery) disablevquery(cur);
- enablevattribs(cur, false);
- }
- if(cur.vbuf!=va->vbuf) changevbuf(cur, RENDERPASS_Z, va);
- if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
- if(cur.colormask) { cur.colormask = false; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); }
- int firsttex = 0, numtris = va->tris;
- ushort *edata = va->edata;
- if(cur.alphaing)
- {
- firsttex += va->texs + va->blends;
- edata += 3*(va->tris + va->blendtris);
- numtris = va->alphatris;
- xtravertsva += 3*numtris;
- }
- else xtravertsva += va->verts;
- nocolorshader->set();
- drawvatris(va, 3*numtris, edata);
+ if(!cur.vattribs)
+ {
+ if(cur.vquery) disablevquery(cur);
+ enablevattribs(cur, false);
+ }
+ if(cur.vbuf!=va->vbuf) changevbuf(cur, RENDERPASS_Z, va);
+ if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
+ if(cur.colormask) { cur.colormask = false; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); }
+ int firsttex = 0, numtris = va->tris;
+ ushort *edata = va->edata;
+ if(cur.alphaing)
+ {
+ firsttex += va->texs + va->blends;
+ edata += 3*(va->tris + va->blendtris);
+ numtris = va->alphatris;
+ xtravertsva += 3*numtris;
+ }
+ else xtravertsva += va->verts;
+ nocolorshader->set();
+ drawvatris(va, 3*numtris, edata);
}
vector<vtxarray *> foggedvas;
#define startvaquery(va, flush) \
- do { \
- if(va->query) \
- { \
- flush; \
- startquery(va->query); \
- } \
- } while(0)
+ do { \
+ if(va->query) \
+ { \
+ flush; \
+ startquery(va->query); \
+ } \
+ } while(0)
#define endvaquery(va, flush) \
- do { \
- if(va->query) \
- { \
- flush; \
- endquery(va->query); \
- } \
- } while(0)
+ do { \
+ if(va->query) \
+ { \
+ flush; \
+ endquery(va->query); \
+ } \
+ } while(0)
void renderfoggedvas(renderstate &cur, bool doquery = false)
{
- static Shader *fogshader = NULL;
- if(!fogshader) fogshader = lookupshaderbyname("fogworld");
- if(fading) fogshader->setvariant(0, 2);
- else fogshader->set();
+ static Shader *fogshader = NULL;
+ if(!fogshader) fogshader = lookupshaderbyname("fogworld");
+ if(fading) fogshader->setvariant(0, 2);
+ else fogshader->set();
- if(!cur.vattribs) enablevattribs(cur, false);
+ if(!cur.vattribs) enablevattribs(cur, false);
- loopv(foggedvas)
- {
- vtxarray *va = foggedvas[i];
- if(cur.vbuf!=va->vbuf) changevbuf(cur, RENDERPASS_FOG, va);
+ loopv(foggedvas)
+ {
+ vtxarray *va = foggedvas[i];
+ if(cur.vbuf!=va->vbuf) changevbuf(cur, RENDERPASS_FOG, va);
- if(doquery) startvaquery(va, );
- drawvatris(va, 3*va->tris, va->edata);
- vtris += va->tris;
- if(doquery) endvaquery(va, );
- }
+ if(doquery) startvaquery(va, );
+ drawvatris(va, 3*va->tris, va->edata);
+ vtris += va->tris;
+ if(doquery) endvaquery(va, );
+ }
- foggedvas.setsize(0);
+ foggedvas.setsize(0);
}
VAR(batchgeom, 0, 1, 1);
void renderva(renderstate &cur, vtxarray *va, int pass = RENDERPASS_LIGHTMAP, bool fogpass = false, bool doquery = false)
{
- switch(pass)
- {
- case RENDERPASS_LIGHTMAP:
- if(!cur.alphaing) vverts += va->verts;
- va->shadowed = false;
- va->dynlightmask = 0;
- if(fogpass ? va->geommax.z<=reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED)
- {
- if(!cur.alphaing && !cur.blending) foggedvas.add(va);
- break;
- }
- if(!drawtex && !glaring && !cur.alphaing)
- {
- va->shadowed = isshadowmapreceiver(va);
- calcdynlightmask(va);
- }
- if(doquery) startvaquery(va, { if(geombatches.length()) renderbatches(cur, pass); });
- mergetexs(cur, va);
- if(doquery) endvaquery(va, { if(geombatches.length()) renderbatches(cur, pass); });
- else if(!batchgeom && geombatches.length()) renderbatches(cur, pass);
- break;
-
- case RENDERPASS_LIGHTMAP_BLEND:
- {
- if(doquery) startvaquery(va, { if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP); });
- mergetexs(cur, va, &va->eslist[va->texs], va->blends, va->edata + 3*va->tris);
- if(doquery) endvaquery(va, { if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP); });
- else if(!batchgeom && geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
- break;
- }
-
- case RENDERPASS_FOG:
- if(cur.vbuf!=va->vbuf) changevbuf(cur, pass, va);
- drawvatris(va, 3*va->tris, va->edata);
- xtravertsva += va->verts;
- break;
-
- case RENDERPASS_CAUSTICS:
- if(cur.vbuf!=va->vbuf) changevbuf(cur, pass, va);
- drawvatris(va, 3*va->tris, va->edata);
- xtravertsva += va->verts;
- break;
-
- case RENDERPASS_Z:
- if(doquery) startvaquery(va, );
- renderzpass(cur, va);
- if(doquery) endvaquery(va, );
- break;
- }
+ switch(pass)
+ {
+ case RENDERPASS_LIGHTMAP:
+ if(!cur.alphaing) vverts += va->verts;
+ va->shadowed = false;
+ va->dynlightmask = 0;
+ if(fogpass ? va->geommax.z<=reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED)
+ {
+ if(!cur.alphaing && !cur.blending) foggedvas.add(va);
+ break;
+ }
+ if(!drawtex && !glaring && !cur.alphaing)
+ {
+ va->shadowed = isshadowmapreceiver(va);
+ calcdynlightmask(va);
+ }
+ if(doquery) startvaquery(va, { if(geombatches.length()) renderbatches(cur, pass); });
+ mergetexs(cur, va);
+ if(doquery) endvaquery(va, { if(geombatches.length()) renderbatches(cur, pass); });
+ else if(!batchgeom && geombatches.length()) renderbatches(cur, pass);
+ break;
+
+ case RENDERPASS_LIGHTMAP_BLEND:
+ {
+ if(doquery) startvaquery(va, { if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP); });
+ mergetexs(cur, va, &va->eslist[va->texs], va->blends, va->edata + 3*va->tris);
+ if(doquery) endvaquery(va, { if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP); });
+ else if(!batchgeom && geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
+ break;
+ }
+
+ case RENDERPASS_FOG:
+ if(cur.vbuf!=va->vbuf) changevbuf(cur, pass, va);
+ drawvatris(va, 3*va->tris, va->edata);
+ xtravertsva += va->verts;
+ break;
+
+ case RENDERPASS_CAUSTICS:
+ if(cur.vbuf!=va->vbuf) changevbuf(cur, pass, va);
+ drawvatris(va, 3*va->tris, va->edata);
+ xtravertsva += va->verts;
+ break;
+
+ case RENDERPASS_Z:
+ if(doquery) startvaquery(va, );
+ renderzpass(cur, va);
+ if(doquery) endvaquery(va, );
+ break;
+ }
}
#define NUMCAUSTICS 32
@@ -1418,24 +1418,24 @@ static Texture *caustictex[NUMCAUSTICS] = { NULL };
void loadcaustics(bool force)
{
- static bool needcaustics = false;
- if(force) needcaustics = true;
- if(!caustics || !needcaustics) return;
- useshaderbyname("caustic");
- if(caustictex[0]) return;
- loopi(NUMCAUSTICS)
- {
- defformatstring(name, "<grey><noswizzle>packages/caustics/caust%.2d.png", i);
- caustictex[i] = textureload(name);
- }
+ static bool needcaustics = false;
+ if(force) needcaustics = true;
+ if(!caustics || !needcaustics) return;
+ useshaderbyname("caustic");
+ if(caustictex[0]) return;
+ loopi(NUMCAUSTICS)
+ {
+ defformatstring(name, "<grey><noswizzle>packages/caustics/caust%.2d.png", i);
+ caustictex[i] = textureload(name);
+ }
}
void cleanupva()
{
- clearvas(worldroot);
- clearqueries();
- cleanupbb();
- loopi(NUMCAUSTICS) caustictex[i] = NULL;
+ clearvas(worldroot);
+ clearqueries();
+ cleanupbb();
+ loopi(NUMCAUSTICS) caustictex[i] = NULL;
}
VARR(causticscale, 0, 50, 10000);
@@ -1445,38 +1445,38 @@ VARFP(caustics, 0, 1, 1, loadcaustics());
void setupcaustics(float blend)
{
- if(!caustictex[0]) loadcaustics(true);
+ if(!caustictex[0]) loadcaustics(true);
- vec s = vec(0.011f, 0, 0.0066f).mul(100.0f/causticscale), t = vec(0, 0.011f, 0.0066f).mul(100.0f/causticscale);
- int tex = (lastmillis/causticmillis)%NUMCAUSTICS;
- float frac = float(lastmillis%causticmillis)/causticmillis;
- loopi(2)
- {
- glActiveTexture_(GL_TEXTURE0+i);
- glBindTexture(GL_TEXTURE_2D, caustictex[(tex+i)%NUMCAUSTICS]->id);
- }
- glActiveTexture_(GL_TEXTURE0);
- SETSHADER(caustic);
- LOCALPARAM(texgenS, s);
- LOCALPARAM(texgenT, t);
- blend *= causticcontrast;
- LOCALPARAMF(frameblend, blend*(1-frac), blend*frac, blend, 1 - blend);
+ vec s = vec(0.011f, 0, 0.0066f).mul(100.0f/causticscale), t = vec(0, 0.011f, 0.0066f).mul(100.0f/causticscale);
+ int tex = (lastmillis/causticmillis)%NUMCAUSTICS;
+ float frac = float(lastmillis%causticmillis)/causticmillis;
+ loopi(2)
+ {
+ glActiveTexture_(GL_TEXTURE0+i);
+ glBindTexture(GL_TEXTURE_2D, caustictex[(tex+i)%NUMCAUSTICS]->id);
+ }
+ glActiveTexture_(GL_TEXTURE0);
+ SETSHADER(caustic);
+ LOCALPARAM(texgenS, s);
+ LOCALPARAM(texgenT, t);
+ blend *= causticcontrast;
+ LOCALPARAMF(frameblend, blend*(1-frac), blend*frac, blend, 1 - blend);
}
void setupgeom(renderstate &cur)
{
- GLOBALPARAMF(colorparams, 2, 2, 2, 1);
- GLOBALPARAM(camera, camera1->o);
- GLOBALPARAMF(ambient, ambientcolor.x/255.0f, ambientcolor.y/255.0f, ambientcolor.z/255.0f);
- GLOBALPARAMF(millis, lastmillis/1000.0f);
+ GLOBALPARAMF(colorparams, 2, 2, 2, 1);
+ GLOBALPARAM(camera, camera1->o);
+ GLOBALPARAMF(ambient, ambientcolor.x/255.0f, ambientcolor.y/255.0f, ambientcolor.z/255.0f);
+ GLOBALPARAMF(millis, lastmillis/1000.0f);
- glActiveTexture_(GL_TEXTURE0);
+ glActiveTexture_(GL_TEXTURE0);
}
void cleanupgeom(renderstate &cur)
{
- if(cur.vattribs) disablevattribs(cur);
- if(cur.vbuf) disablevbuf(cur);
+ if(cur.vattribs) disablevattribs(cur);
+ if(cur.vbuf) disablevbuf(cur);
}
#define FIRSTVA (reflecting ? reflectedva : visibleva)
@@ -1484,398 +1484,398 @@ void cleanupgeom(renderstate &cur)
static void rendergeommultipass(renderstate &cur, int pass, bool fogpass)
{
- if(cur.vbuf) disablevbuf(cur);
- if(!cur.vattribs) enablevattribs(cur, false);
- cur.texgendim = -1;
- for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
- {
- if(!va->texs) continue;
- if(refracting)
- {
- if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_GEOM) continue;
- if(ishiddencube(va->o, va->size)) continue;
- }
- else if(reflecting)
- {
- if(va->geommax.z <= reflectz) continue;
- }
- else if(va->occluded >= OCCLUDE_GEOM) continue;
- if(fogpass ? va->geommax.z <= reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED) continue;
- renderva(cur, va, pass, fogpass);
- }
- if(geombatches.length()) renderbatches(cur, pass);
+ if(cur.vbuf) disablevbuf(cur);
+ if(!cur.vattribs) enablevattribs(cur, false);
+ cur.texgendim = -1;
+ for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
+ {
+ if(!va->texs) continue;
+ if(refracting)
+ {
+ if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_GEOM) continue;
+ if(ishiddencube(va->o, va->size)) continue;
+ }
+ else if(reflecting)
+ {
+ if(va->geommax.z <= reflectz) continue;
+ }
+ else if(va->occluded >= OCCLUDE_GEOM) continue;
+ if(fogpass ? va->geommax.z <= reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED) continue;
+ renderva(cur, va, pass, fogpass);
+ }
+ if(geombatches.length()) renderbatches(cur, pass);
}
VAR(oqgeom, 0, 1, 1);
void rendergeom(float causticspass, bool fogpass)
{
- if(causticspass && (!causticscale || !causticmillis)) causticspass = 0;
-
- bool mainpass = !reflecting && !refracting && !drawtex && !glaring,
- doOQ = oqfrags && oqgeom && mainpass,
- doZP = doOQ && zpass,
- doSM = shadowmap && !drawtex && !glaring;
- renderstate cur;
- if(mainpass)
- {
- flipqueries();
- vtris = vverts = 0;
- }
- if(!doZP)
- {
- if(shadowmap && mainpass) rendershadowmap();
- setupgeom(cur);
- if(doSM) pushshadowmap();
- }
-
- finddynlights();
-
- resetbatches();
-
- int blends = 0;
- for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
- {
- if(!va->texs) continue;
- if(refracting)
- {
- if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_GEOM) continue;
- if(ishiddencube(va->o, va->size)) continue;
- }
- else if(reflecting)
- {
- if(va->geommax.z <= reflectz) continue;
- }
- else if(doOQ && (zpass || va->distance > oqdist) && !insideva(va, camera1->o))
- {
- if(va->parent && va->parent->occluded >= OCCLUDE_BB)
- {
- va->query = NULL;
- va->occluded = OCCLUDE_PARENT;
- continue;
- }
- va->occluded = va->query && va->query->owner == va && checkquery(va->query) ? min(va->occluded+1, int(OCCLUDE_BB)) : OCCLUDE_NOTHING;
- va->query = newquery(va);
- if((!va->query && zpass) || !va->occluded)
- va->occluded = OCCLUDE_NOTHING;
- if(va->occluded >= OCCLUDE_GEOM)
- {
- if(va->query)
- {
- if(!zpass && geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
- if(cur.vattribs) disablevattribs(cur, !doZP);
- if(cur.vbuf) disablevbuf(cur);
- renderquery(cur, va->query, va);
- }
- continue;
- }
- }
- else
- {
- va->query = NULL;
- va->occluded = OCCLUDE_NOTHING;
- }
-
- if(!doZP) blends += va->blends;
- renderva(cur, va, doZP ? RENDERPASS_Z : RENDERPASS_LIGHTMAP, fogpass, doOQ);
- }
-
- if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
-
- if(cur.vquery) disablevquery(cur);
- if(cur.vattribs) disablevattribs(cur, !doZP);
- if(cur.vbuf) disablevbuf(cur);
-
- if(!cur.colormask) { cur.colormask = true; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); }
- if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
-
- bool multipassing = false;
-
- if(doZP)
- {
+ if(causticspass && (!causticscale || !causticmillis)) causticspass = 0;
+
+ bool mainpass = !reflecting && !refracting && !drawtex && !glaring,
+ doOQ = oqfrags && oqgeom && mainpass,
+ doZP = doOQ && zpass,
+ doSM = shadowmap && !drawtex && !glaring;
+ renderstate cur;
+ if(mainpass)
+ {
+ flipqueries();
+ vtris = vverts = 0;
+ }
+ if(!doZP)
+ {
+ if(shadowmap && mainpass) rendershadowmap();
+ setupgeom(cur);
+ if(doSM) pushshadowmap();
+ }
+
+ finddynlights();
+
+ resetbatches();
+
+ int blends = 0;
+ for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
+ {
+ if(!va->texs) continue;
+ if(refracting)
+ {
+ if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_GEOM) continue;
+ if(ishiddencube(va->o, va->size)) continue;
+ }
+ else if(reflecting)
+ {
+ if(va->geommax.z <= reflectz) continue;
+ }
+ else if(doOQ && (zpass || va->distance > oqdist) && !insideva(va, camera1->o))
+ {
+ if(va->parent && va->parent->occluded >= OCCLUDE_BB)
+ {
+ va->query = NULL;
+ va->occluded = OCCLUDE_PARENT;
+ continue;
+ }
+ va->occluded = va->query && va->query->owner == va && checkquery(va->query) ? min(va->occluded+1, int(OCCLUDE_BB)) : OCCLUDE_NOTHING;
+ va->query = newquery(va);
+ if((!va->query && zpass) || !va->occluded)
+ va->occluded = OCCLUDE_NOTHING;
+ if(va->occluded >= OCCLUDE_GEOM)
+ {
+ if(va->query)
+ {
+ if(!zpass && geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
+ if(cur.vattribs) disablevattribs(cur, !doZP);
+ if(cur.vbuf) disablevbuf(cur);
+ renderquery(cur, va->query, va);
+ }
+ continue;
+ }
+ }
+ else
+ {
+ va->query = NULL;
+ va->occluded = OCCLUDE_NOTHING;
+ }
+
+ if(!doZP) blends += va->blends;
+ renderva(cur, va, doZP ? RENDERPASS_Z : RENDERPASS_LIGHTMAP, fogpass, doOQ);
+ }
+
+ if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
+
+ if(cur.vquery) disablevquery(cur);
+ if(cur.vattribs) disablevattribs(cur, !doZP);
+ if(cur.vbuf) disablevbuf(cur);
+
+ if(!cur.colormask) { cur.colormask = true; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); }
+ if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
+
+ bool multipassing = false;
+
+ if(doZP)
+ {
glFlush();
- if(shadowmap && mainpass) rendershadowmap();
- setupgeom(cur);
- if(doSM) pushshadowmap();
-
- if(!multipassing) { multipassing = true; glDepthFunc(GL_LEQUAL); }
- cur.texgendim = -1;
-
- for(vtxarray *va = visibleva; va; va = va->next)
- {
- if(!va->texs || va->occluded >= OCCLUDE_GEOM) continue;
- blends += va->blends;
- renderva(cur, va, RENDERPASS_LIGHTMAP, fogpass);
- }
- if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
- for(vtxarray *va = visibleva; va; va = va->next)
- {
- if(!va->texs || va->occluded < OCCLUDE_GEOM) continue;
- else if((va->parent && va->parent->occluded >= OCCLUDE_BB) ||
- (va->query && checkquery(va->query)))
- {
- va->occluded = OCCLUDE_BB;
- continue;
- }
-
- blends += va->blends;
- renderva(cur, va, RENDERPASS_LIGHTMAP, fogpass);
- }
- if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
- }
-
- if(blends)
- {
- if(cur.vbuf) disablevbuf(cur);
-
- if(!multipassing) { multipassing = true; glDepthFunc(GL_LEQUAL); }
- glDepthMask(GL_FALSE);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
-
- cur.texgendim = -1;
- cur.blending = true;
- for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
- {
- if(!va->blends) continue;
- if(refracting)
- {
- if(refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) continue;
- if(ishiddencube(va->o, va->size)) continue;
- if(va->occluded >= OCCLUDE_GEOM) continue;
- }
- else if(reflecting)
- {
- if(va->geommax.z <= reflectz) continue;
- }
- else if(va->occluded >= OCCLUDE_GEOM) continue;
- if(fogpass ? va->geommax.z <= reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED) continue;
- renderva(cur, va, RENDERPASS_LIGHTMAP_BLEND, fogpass);
- }
- if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
- cur.blending = false;
-
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- glDisable(GL_BLEND);
- glDepthMask(GL_TRUE);
- }
-
- if(doSM) popshadowmap();
-
- if(cur.vattribs) disablevattribs(cur);
-
- if(foggedvas.length()) renderfoggedvas(cur, doOQ && !zpass);
-
- if(causticspass)
- {
- if(!multipassing) { multipassing = true; glDepthFunc(GL_LEQUAL); }
- glDepthMask(GL_FALSE);
- glEnable(GL_BLEND);
-
- setupcaustics(causticspass);
- glBlendFunc(GL_ZERO, GL_SRC_COLOR);
- if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
- rendergeommultipass(cur, RENDERPASS_CAUSTICS, fogpass);
- if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-
- glDisable(GL_BLEND);
- glDepthMask(GL_TRUE);
- }
-
- if(multipassing) glDepthFunc(GL_LESS);
-
- cleanupgeom(cur);
+ if(shadowmap && mainpass) rendershadowmap();
+ setupgeom(cur);
+ if(doSM) pushshadowmap();
+
+ if(!multipassing) { multipassing = true; glDepthFunc(GL_LEQUAL); }
+ cur.texgendim = -1;
+
+ for(vtxarray *va = visibleva; va; va = va->next)
+ {
+ if(!va->texs || va->occluded >= OCCLUDE_GEOM) continue;
+ blends += va->blends;
+ renderva(cur, va, RENDERPASS_LIGHTMAP, fogpass);
+ }
+ if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
+ for(vtxarray *va = visibleva; va; va = va->next)
+ {
+ if(!va->texs || va->occluded < OCCLUDE_GEOM) continue;
+ else if((va->parent && va->parent->occluded >= OCCLUDE_BB) ||
+ (va->query && checkquery(va->query)))
+ {
+ va->occluded = OCCLUDE_BB;
+ continue;
+ }
+
+ blends += va->blends;
+ renderva(cur, va, RENDERPASS_LIGHTMAP, fogpass);
+ }
+ if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
+ }
+
+ if(blends)
+ {
+ if(cur.vbuf) disablevbuf(cur);
+
+ if(!multipassing) { multipassing = true; glDepthFunc(GL_LEQUAL); }
+ glDepthMask(GL_FALSE);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+
+ cur.texgendim = -1;
+ cur.blending = true;
+ for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
+ {
+ if(!va->blends) continue;
+ if(refracting)
+ {
+ if(refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) continue;
+ if(ishiddencube(va->o, va->size)) continue;
+ if(va->occluded >= OCCLUDE_GEOM) continue;
+ }
+ else if(reflecting)
+ {
+ if(va->geommax.z <= reflectz) continue;
+ }
+ else if(va->occluded >= OCCLUDE_GEOM) continue;
+ if(fogpass ? va->geommax.z <= reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED) continue;
+ renderva(cur, va, RENDERPASS_LIGHTMAP_BLEND, fogpass);
+ }
+ if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
+ cur.blending = false;
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glDisable(GL_BLEND);
+ glDepthMask(GL_TRUE);
+ }
+
+ if(doSM) popshadowmap();
+
+ if(cur.vattribs) disablevattribs(cur);
+
+ if(foggedvas.length()) renderfoggedvas(cur, doOQ && !zpass);
+
+ if(causticspass)
+ {
+ if(!multipassing) { multipassing = true; glDepthFunc(GL_LEQUAL); }
+ glDepthMask(GL_FALSE);
+ glEnable(GL_BLEND);
+
+ setupcaustics(causticspass);
+ glBlendFunc(GL_ZERO, GL_SRC_COLOR);
+ if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+ rendergeommultipass(cur, RENDERPASS_CAUSTICS, fogpass);
+ if(fading) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+ glDisable(GL_BLEND);
+ glDepthMask(GL_TRUE);
+ }
+
+ if(multipassing) glDepthFunc(GL_LESS);
+
+ cleanupgeom(cur);
}
void renderalphageom(bool fogpass)
{
- static vector<vtxarray *> alphavas;
- alphavas.setsize(0);
- bool hasback = false;
- for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
- {
- if(!va->alphatris) continue;
- if(refracting)
- {
- if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_BB) continue;
- if(ishiddencube(va->o, va->size)) continue;
- }
- else if(reflecting)
- {
- if(va->geommax.z <= reflectz) continue;
- }
- else
- {
- if(va->occluded >= OCCLUDE_BB) continue;
- }
- if(fogpass ? va->geommax.z <= reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED) continue;
- alphavas.add(va);
- if(va->alphabacktris) hasback = true;
- }
- if(alphavas.empty()) return;
-
- resetbatches();
-
- renderstate cur;
- cur.alphaing = 1;
-
- loop(front, 2) if(front || hasback)
- {
- cur.alphaing = front+1;
- if(!front) glCullFace(GL_FRONT);
- cur.vbuf = 0;
- cur.texgendim = -1;
- loopv(alphavas) renderva(cur, alphavas[i], RENDERPASS_Z);
- if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
- cur.colormask = true;
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
-
- if(cur.vattribs) disablevattribs(cur, false);
- if(cur.vbuf) disablevbuf(cur);
-
- setupgeom(cur);
-
- glDepthFunc(GL_LEQUAL);
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- cur.vbuf = 0;
- cur.texgendim = -1;
- cur.colorscale = vec(1, 1, 1);
- cur.alphascale = -1;
- loopv(alphavas) if(front || alphavas[i]->alphabacktris) renderva(cur, alphavas[i], RENDERPASS_LIGHTMAP, fogpass);
- if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
-
- cleanupgeom(cur);
-
- resetfogcolor();
- if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
- glDisable(GL_BLEND);
- glDepthFunc(GL_LESS);
- if(!front) glCullFace(GL_BACK);
- }
-
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, fading ? GL_FALSE : GL_TRUE);
+ static vector<vtxarray *> alphavas;
+ alphavas.setsize(0);
+ bool hasback = false;
+ for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
+ {
+ if(!va->alphatris) continue;
+ if(refracting)
+ {
+ if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_BB) continue;
+ if(ishiddencube(va->o, va->size)) continue;
+ }
+ else if(reflecting)
+ {
+ if(va->geommax.z <= reflectz) continue;
+ }
+ else
+ {
+ if(va->occluded >= OCCLUDE_BB) continue;
+ }
+ if(fogpass ? va->geommax.z <= reflectz-refractfog || !refractfog : va->curvfc==VFC_FOGGED) continue;
+ alphavas.add(va);
+ if(va->alphabacktris) hasback = true;
+ }
+ if(alphavas.empty()) return;
+
+ resetbatches();
+
+ renderstate cur;
+ cur.alphaing = 1;
+
+ loop(front, 2) if(front || hasback)
+ {
+ cur.alphaing = front+1;
+ if(!front) glCullFace(GL_FRONT);
+ cur.vbuf = 0;
+ cur.texgendim = -1;
+ loopv(alphavas) renderva(cur, alphavas[i], RENDERPASS_Z);
+ if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
+ cur.colormask = true;
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+
+ if(cur.vattribs) disablevattribs(cur, false);
+ if(cur.vbuf) disablevbuf(cur);
+
+ setupgeom(cur);
+
+ glDepthFunc(GL_LEQUAL);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ cur.vbuf = 0;
+ cur.texgendim = -1;
+ cur.colorscale = vec(1, 1, 1);
+ cur.alphascale = -1;
+ loopv(alphavas) if(front || alphavas[i]->alphabacktris) renderva(cur, alphavas[i], RENDERPASS_LIGHTMAP, fogpass);
+ if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
+
+ cleanupgeom(cur);
+
+ resetfogcolor();
+ if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
+ glDisable(GL_BLEND);
+ glDepthFunc(GL_LESS);
+ if(!front) glCullFace(GL_BACK);
+ }
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, fading ? GL_FALSE : GL_TRUE);
}
void findreflectedvas(vector<vtxarray *> &vas, int prevvfc = VFC_PART_VISIBLE)
{
- loopv(vas)
- {
- vtxarray *va = vas[i];
- if(prevvfc >= VFC_NOT_VISIBLE) va->curvfc = prevvfc;
- if(va->curvfc == VFC_FOGGED || va->curvfc == PVS_FOGGED || va->o.z+va->size <= reflectz || isfoggedcube(va->o, va->size)) continue;
- bool render = true;
- if(va->curvfc == VFC_FULL_VISIBLE)
- {
- if(va->occluded >= OCCLUDE_BB) continue;
- if(va->occluded >= OCCLUDE_GEOM) render = false;
- }
- else if(va->curvfc == PVS_FULL_VISIBLE) continue;
- if(render)
- {
- if(va->curvfc >= VFC_NOT_VISIBLE) va->distance = (int)vadist(va, camera1->o);
- vtxarray **vprev = &reflectedva, *vcur = reflectedva;
- while(vcur && va->distance > vcur->distance)
- {
- vprev = &vcur->rnext;
- vcur = vcur->rnext;
- }
- va->rnext = *vprev;
- *vprev = va;
- }
- if(va->children.length()) findreflectedvas(va->children, va->curvfc);
- }
+ loopv(vas)
+ {
+ vtxarray *va = vas[i];
+ if(prevvfc >= VFC_NOT_VISIBLE) va->curvfc = prevvfc;
+ if(va->curvfc == VFC_FOGGED || va->curvfc == PVS_FOGGED || va->o.z+va->size <= reflectz || isfoggedcube(va->o, va->size)) continue;
+ bool render = true;
+ if(va->curvfc == VFC_FULL_VISIBLE)
+ {
+ if(va->occluded >= OCCLUDE_BB) continue;
+ if(va->occluded >= OCCLUDE_GEOM) render = false;
+ }
+ else if(va->curvfc == PVS_FULL_VISIBLE) continue;
+ if(render)
+ {
+ if(va->curvfc >= VFC_NOT_VISIBLE) va->distance = (int)vadist(va, camera1->o);
+ vtxarray **vprev = &reflectedva, *vcur = reflectedva;
+ while(vcur && va->distance > vcur->distance)
+ {
+ vprev = &vcur->rnext;
+ vcur = vcur->rnext;
+ }
+ va->rnext = *vprev;
+ *vprev = va;
+ }
+ if(va->children.length()) findreflectedvas(va->children, va->curvfc);
+ }
}
void renderreflectedgeom(bool causticspass, bool fogpass)
{
- if(reflecting)
- {
- reflectedva = NULL;
- findreflectedvas(varoot);
- rendergeom(causticspass ? 1 : 0, fogpass);
- }
- else rendergeom(causticspass ? 1 : 0, fogpass);
+ if(reflecting)
+ {
+ reflectedva = NULL;
+ findreflectedvas(varoot);
+ rendergeom(causticspass ? 1 : 0, fogpass);
+ }
+ else rendergeom(causticspass ? 1 : 0, fogpass);
}
static vtxarray *prevskyva = NULL;
void renderskyva(vtxarray *va, bool explicitonly = false)
{
- if(!prevskyva || va->vbuf != prevskyva->vbuf)
- {
- gle::bindvbo(va->vbuf);
- gle::bindebo(va->skybuf);
- const vertex *ptr = 0;
- gle::vertexpointer(sizeof(vertex), ptr->pos.v);
- if(!prevskyva) gle::enablevertex();
- }
+ if(!prevskyva || va->vbuf != prevskyva->vbuf)
+ {
+ gle::bindvbo(va->vbuf);
+ gle::bindebo(va->skybuf);
+ const vertex *ptr = 0;
+ gle::vertexpointer(sizeof(vertex), ptr->pos.v);
+ if(!prevskyva) gle::enablevertex();
+ }
- drawvatris(va, explicitonly ? va->explicitsky : va->sky+va->explicitsky, explicitonly ? va->skydata+va->sky : va->skydata);
+ drawvatris(va, explicitonly ? va->explicitsky : va->sky+va->explicitsky, explicitonly ? va->skydata+va->sky : va->skydata);
- if(!explicitonly) xtraverts += va->sky/3;
- xtraverts += va->explicitsky/3;
+ if(!explicitonly) xtraverts += va->sky/3;
+ xtraverts += va->explicitsky/3;
- prevskyva = va;
+ prevskyva = va;
}
int renderedsky = 0, renderedexplicitsky = 0, renderedskyfaces = 0, renderedskyclip = INT_MAX;
static inline void updateskystats(vtxarray *va)
{
- renderedsky += va->sky;
- renderedexplicitsky += va->explicitsky;
- renderedskyfaces |= va->skyfaces&0x3F;
- if(!(va->skyfaces&0x1F) || camera1->o.z < va->skyclip) renderedskyclip = min(renderedskyclip, va->skyclip);
- else renderedskyclip = 0;
+ renderedsky += va->sky;
+ renderedexplicitsky += va->explicitsky;
+ renderedskyfaces |= va->skyfaces&0x3F;
+ if(!(va->skyfaces&0x1F) || camera1->o.z < va->skyclip) renderedskyclip = min(renderedskyclip, va->skyclip);
+ else renderedskyclip = 0;
}
void renderreflectedskyvas(vector<vtxarray *> &vas, int prevvfc = VFC_PART_VISIBLE)
{
- loopv(vas)
- {
- vtxarray *va = vas[i];
- if(prevvfc >= VFC_NOT_VISIBLE) va->curvfc = prevvfc;
- if((va->curvfc == VFC_FULL_VISIBLE && va->occluded >= OCCLUDE_BB) || va->curvfc==PVS_FULL_VISIBLE) continue;
- if(va->o.z+va->size <= reflectz || ishiddencube(va->o, va->size)) continue;
- if(va->sky+va->explicitsky)
- {
- updateskystats(va);
- renderskyva(va);
- }
- if(va->children.length()) renderreflectedskyvas(va->children, va->curvfc);
- }
+ loopv(vas)
+ {
+ vtxarray *va = vas[i];
+ if(prevvfc >= VFC_NOT_VISIBLE) va->curvfc = prevvfc;
+ if((va->curvfc == VFC_FULL_VISIBLE && va->occluded >= OCCLUDE_BB) || va->curvfc==PVS_FULL_VISIBLE) continue;
+ if(va->o.z+va->size <= reflectz || ishiddencube(va->o, va->size)) continue;
+ if(va->sky+va->explicitsky)
+ {
+ updateskystats(va);
+ renderskyva(va);
+ }
+ if(va->children.length()) renderreflectedskyvas(va->children, va->curvfc);
+ }
}
bool rendersky(bool explicitonly)
{
- prevskyva = NULL;
- renderedsky = renderedexplicitsky = renderedskyfaces = 0;
- renderedskyclip = INT_MAX;
-
- if(reflecting)
- {
- renderreflectedskyvas(varoot);
- }
- else for(vtxarray *va = visibleva; va; va = va->next)
- {
- if((va->occluded >= OCCLUDE_BB && va->skyfaces&0x80) || !(va->sky+va->explicitsky)) continue;
-
- // count possibly visible sky even if not actually rendered
- updateskystats(va);
- if(explicitonly && !va->explicitsky) continue;
- renderskyva(va, explicitonly);
- }
-
- if(prevskyva)
- {
- gle::disablevertex();
- gle::clearvbo();
- gle::clearebo();
- }
-
- return renderedsky+renderedexplicitsky > 0;
+ prevskyva = NULL;
+ renderedsky = renderedexplicitsky = renderedskyfaces = 0;
+ renderedskyclip = INT_MAX;
+
+ if(reflecting)
+ {
+ renderreflectedskyvas(varoot);
+ }
+ else for(vtxarray *va = visibleva; va; va = va->next)
+ {
+ if((va->occluded >= OCCLUDE_BB && va->skyfaces&0x80) || !(va->sky+va->explicitsky)) continue;
+
+ // count possibly visible sky even if not actually rendered
+ updateskystats(va);
+ if(explicitonly && !va->explicitsky) continue;
+ renderskyva(va, explicitonly);
+ }
+
+ if(prevskyva)
+ {
+ gle::disablevertex();
+ gle::clearvbo();
+ gle::clearebo();
+ }
+
+ return renderedsky+renderedexplicitsky > 0;
}
diff --git a/src/engine/server.cpp b/src/engine/server.cpp
index 9348b67..7e338b3 100644
--- a/src/engine/server.cpp
+++ b/src/engine/server.cpp
@@ -9,73 +9,73 @@ static FILE *logfile = NULL;
void closelogfile()
{
- if(logfile)
- {
- fclose(logfile);
- logfile = NULL;
- }
+ if(logfile)
+ {
+ fclose(logfile);
+ logfile = NULL;
+ }
}
FILE *getlogfile()
{
- return logfile ? logfile : stdout;
+ return logfile ? logfile : stdout;
}
void setlogfile(const char *fname)
{
- closelogfile();
- if(fname && fname[0])
- {
- fname = findfile(fname, "w");
- if(fname) logfile = fopen(fname, "w");
- }
- FILE *f = getlogfile();
- if(f) setvbuf(f, NULL, _IOLBF, BUFSIZ);
+ closelogfile();
+ if(fname && fname[0])
+ {
+ fname = findfile(fname, "w");
+ if(fname) logfile = fopen(fname, "w");
+ }
+ FILE *f = getlogfile();
+ if(f) setvbuf(f, NULL, _IOLBF, BUFSIZ);
}
void logoutf(const char *fmt, ...)
{
- va_list args;
- va_start(args, fmt);
- logoutfv(fmt, args);
- va_end(args);
+ va_list args;
+ va_start(args, fmt);
+ logoutfv(fmt, args);
+ va_end(args);
}
static void writelog(FILE *file, const char *buf)
{
- static uchar ubuf[512];
- size_t len = strlen(buf), carry = 0;
- while(carry < len)
- {
- size_t numu = encodeutf8(ubuf, sizeof(ubuf)-1, &((const uchar *)buf)[carry], len - carry, &carry);
- if(carry >= len) ubuf[numu++] = '\n';
- fwrite(ubuf, 1, numu, file);
- }
+ static uchar ubuf[512];
+ size_t len = strlen(buf), carry = 0;
+ while(carry < len)
+ {
+ size_t numu = encodeutf8(ubuf, sizeof(ubuf)-1, &((const uchar *)buf)[carry], len - carry, &carry);
+ if(carry >= len) ubuf[numu++] = '\n';
+ fwrite(ubuf, 1, numu, file);
+ }
}
static void writelogv(FILE *file, const char *fmt, va_list args)
{
- static char buf[LOGSTRLEN];
- vformatstring(buf, fmt, args, sizeof(buf));
- writelog(file, buf);
+ static char buf[LOGSTRLEN];
+ vformatstring(buf, fmt, args, sizeof(buf));
+ writelog(file, buf);
}
#ifdef STANDALONE
void fatal(const char *fmt, ...)
{
- void cleanupserver();
- cleanupserver();
+ void cleanupserver();
+ cleanupserver();
defvformatstring(msg,fmt,fmt);
if(logfile) logoutf("%s", msg);
- fprintf(stderr, "server error: %s\n", msg);
- closelogfile();
- exit(EXIT_FAILURE);
+ fprintf(stderr, "server error: %s\n", msg);
+ closelogfile();
+ exit(EXIT_FAILURE);
}
void conoutfv(int type, const char *fmt, va_list args)
{
- logoutfv(fmt, args);
+ logoutfv(fmt, args);
}
#endif
@@ -83,13 +83,13 @@ void conoutfv(int type, const char *fmt, va_list args)
enum { ST_EMPTY, ST_LOCAL, ST_TCPIP };
-struct client // server side version of "dynent" type
+struct client // server side version of "dynent" type
{
- int type;
- int num;
- ENetPeer *peer;
- string hostname;
- void *info;
+ int type;
+ int num;
+ ENetPeer *peer;
+ string hostname;
+ void *info;
};
vector<client *> clients;
@@ -105,54 +105,54 @@ bool haslocalclients() { return localclients!=0; }
client &addclient(int type)
{
- client *c = NULL;
- loopv(clients) if(clients[i]->type==ST_EMPTY)
- {
- c = clients[i];
- break;
- }
- if(!c)
- {
- c = new client;
- c->num = clients.length();
- clients.add(c);
- }
- c->info = server::newclientinfo();
- c->type = type;
- switch(type)
- {
- case ST_TCPIP: nonlocalclients++; break;
- case ST_LOCAL: localclients++; break;
- }
- return *c;
+ client *c = NULL;
+ loopv(clients) if(clients[i]->type==ST_EMPTY)
+ {
+ c = clients[i];
+ break;
+ }
+ if(!c)
+ {
+ c = new client;
+ c->num = clients.length();
+ clients.add(c);
+ }
+ c->info = server::newclientinfo();
+ c->type = type;
+ switch(type)
+ {
+ case ST_TCPIP: nonlocalclients++; break;
+ case ST_LOCAL: localclients++; break;
+ }
+ return *c;
}
void delclient(client *c)
{
- if(!c) return;
- switch(c->type)
- {
- case ST_TCPIP: nonlocalclients--; if(c->peer) c->peer->data = NULL; break;
- case ST_LOCAL: localclients--; break;
- case ST_EMPTY: return;
- }
- c->type = ST_EMPTY;
- c->peer = NULL;
- if(c->info)
- {
- server::deleteclientinfo(c->info);
- c->info = NULL;
- }
+ if(!c) return;
+ switch(c->type)
+ {
+ case ST_TCPIP: nonlocalclients--; if(c->peer) c->peer->data = NULL; break;
+ case ST_LOCAL: localclients--; break;
+ case ST_EMPTY: return;
+ }
+ c->type = ST_EMPTY;
+ c->peer = NULL;
+ if(c->info)
+ {
+ server::deleteclientinfo(c->info);
+ c->info = NULL;
+ }
}
void cleanupserver()
{
- if(serverhost) enet_host_destroy(serverhost);
- serverhost = NULL;
+ if(serverhost) enet_host_destroy(serverhost);
+ serverhost = NULL;
- if(pongsock != ENET_SOCKET_NULL) enet_socket_destroy(pongsock);
- if(lansock != ENET_SOCKET_NULL) enet_socket_destroy(lansock);
- pongsock = lansock = ENET_SOCKET_NULL;
+ if(pongsock != ENET_SOCKET_NULL) enet_socket_destroy(pongsock);
+ if(lansock != ENET_SOCKET_NULL) enet_socket_destroy(lansock);
+ pongsock = lansock = ENET_SOCKET_NULL;
}
VARF(maxclients, 0, DEFAULTCLIENTS, MAXCLIENTS, { if(!maxclients) maxclients = DEFAULTCLIENTS; });
@@ -164,181 +164,181 @@ void process(ENetPacket *packet, int sender, int chan);
int getservermtu() { return serverhost ? serverhost->mtu : -1; }
void *getclientinfo(int i) { return !clients.inrange(i) || clients[i]->type==ST_EMPTY ? NULL : clients[i]->info; }
ENetPeer *getclientpeer(int i) { return clients.inrange(i) && clients[i]->type==ST_TCPIP ? clients[i]->peer : NULL; }
-int getnumclients() { return clients.length(); }
-uint getclientip(int n) { return clients.inrange(n) && clients[n]->type==ST_TCPIP ? clients[n]->peer->address.host : 0; }
+int getnumclients() { return clients.length(); }
+uint getclientip(int n) { return clients.inrange(n) && clients[n]->type==ST_TCPIP ? clients[n]->peer->address.host : 0; }
void sendpacket(int n, int chan, ENetPacket *packet, int exclude)
{
- if(n<0)
- {
- server::recordpacket(chan, packet->data, packet->dataLength);
- loopv(clients) if(i!=exclude && server::allowbroadcast(i)) sendpacket(i, chan, packet);
- return;
- }
- switch(clients[n]->type)
- {
- case ST_TCPIP:
- {
- enet_peer_send(clients[n]->peer, chan, packet);
- break;
- }
+ if(n<0)
+ {
+ server::recordpacket(chan, packet->data, packet->dataLength);
+ loopv(clients) if(i!=exclude && server::allowbroadcast(i)) sendpacket(i, chan, packet);
+ return;
+ }
+ switch(clients[n]->type)
+ {
+ case ST_TCPIP:
+ {
+ enet_peer_send(clients[n]->peer, chan, packet);
+ break;
+ }
#ifndef STANDALONE
- case ST_LOCAL:
- localservertoclient(chan, packet);
- break;
+ case ST_LOCAL:
+ localservertoclient(chan, packet);
+ break;
#endif
- }
+ }
}
ENetPacket *sendf(int cn, int chan, const char *format, ...)
{
- int exclude = -1;
- bool reliable = false;
- if(*format=='r') { reliable = true; ++format; }
- packetbuf p(MAXTRANS, reliable ? ENET_PACKET_FLAG_RELIABLE : 0);
- va_list args;
- va_start(args, format);
- while(*format) switch(*format++)
- {
- case 'x':
- exclude = va_arg(args, int);
- break;
-
- case 'v':
- {
- int n = va_arg(args, int);
- int *v = va_arg(args, int *);
- loopi(n) putint(p, v[i]);
- break;
- }
-
- case 'i':
- {
- int n = isdigit(*format) ? *format++-'0' : 1;
- loopi(n) putint(p, va_arg(args, int));
- break;
- }
- case 'f':
- {
- int n = isdigit(*format) ? *format++-'0' : 1;
- loopi(n) putfloat(p, (float)va_arg(args, double));
- break;
- }
- case 's': sendstring(va_arg(args, const char *), p); break;
- case 'm':
- {
- int n = va_arg(args, int);
- p.put(va_arg(args, uchar *), n);
- break;
- }
- }
- va_end(args);
- ENetPacket *packet = p.finalize();
- sendpacket(cn, chan, packet, exclude);
- return packet->referenceCount > 0 ? packet : NULL;
+ int exclude = -1;
+ bool reliable = false;
+ if(*format=='r') { reliable = true; ++format; }
+ packetbuf p(MAXTRANS, reliable ? ENET_PACKET_FLAG_RELIABLE : 0);
+ va_list args;
+ va_start(args, format);
+ while(*format) switch(*format++)
+ {
+ case 'x':
+ exclude = va_arg(args, int);
+ break;
+
+ case 'v':
+ {
+ int n = va_arg(args, int);
+ int *v = va_arg(args, int *);
+ loopi(n) putint(p, v[i]);
+ break;
+ }
+
+ case 'i':
+ {
+ int n = isdigit(*format) ? *format++-'0' : 1;
+ loopi(n) putint(p, va_arg(args, int));
+ break;
+ }
+ case 'f':
+ {
+ int n = isdigit(*format) ? *format++-'0' : 1;
+ loopi(n) putfloat(p, (float)va_arg(args, double));
+ break;
+ }
+ case 's': sendstring(va_arg(args, const char *), p); break;
+ case 'm':
+ {
+ int n = va_arg(args, int);
+ p.put(va_arg(args, uchar *), n);
+ break;
+ }
+ }
+ va_end(args);
+ ENetPacket *packet = p.finalize();
+ sendpacket(cn, chan, packet, exclude);
+ return packet->referenceCount > 0 ? packet : NULL;
}
ENetPacket *sendfile(int cn, int chan, stream *file, const char *format, ...)
{
- if(cn < 0)
- {
+ if(cn < 0)
+ {
#ifdef STANDALONE
- return NULL;
+ return NULL;
#endif
- }
- else if(!clients.inrange(cn)) return NULL;
-
- int len = (int)min(file->size(), stream::offset(INT_MAX));
- if(len <= 0 || len > 16<<20) return NULL;
-
- packetbuf p(MAXTRANS+len, ENET_PACKET_FLAG_RELIABLE);
- va_list args;
- va_start(args, format);
- while(*format) switch(*format++)
- {
- case 'i':
- {
- int n = isdigit(*format) ? *format++-'0' : 1;
- loopi(n) putint(p, va_arg(args, int));
- break;
- }
- case 's': sendstring(va_arg(args, const char *), p); break;
- case 'l': putint(p, len); break;
- }
- va_end(args);
-
- file->seek(0, SEEK_SET);
- file->read(p.subbuf(len).buf, len);
-
- ENetPacket *packet = p.finalize();
- if(cn >= 0) sendpacket(cn, chan, packet, -1);
+ }
+ else if(!clients.inrange(cn)) return NULL;
+
+ int len = (int)min(file->size(), stream::offset(INT_MAX));
+ if(len <= 0 || len > 16<<20) return NULL;
+
+ packetbuf p(MAXTRANS+len, ENET_PACKET_FLAG_RELIABLE);
+ va_list args;
+ va_start(args, format);
+ while(*format) switch(*format++)
+ {
+ case 'i':
+ {
+ int n = isdigit(*format) ? *format++-'0' : 1;
+ loopi(n) putint(p, va_arg(args, int));
+ break;
+ }
+ case 's': sendstring(va_arg(args, const char *), p); break;
+ case 'l': putint(p, len); break;
+ }
+ va_end(args);
+
+ file->seek(0, SEEK_SET);
+ file->read(p.subbuf(len).buf, len);
+
+ ENetPacket *packet = p.finalize();
+ if(cn >= 0) sendpacket(cn, chan, packet, -1);
#ifndef STANDALONE
- else sendclientpacket(packet, chan);
+ else sendclientpacket(packet, chan);
#endif
- return packet->referenceCount > 0 ? packet : NULL;
+ return packet->referenceCount > 0 ? packet : NULL;
}
const char *disconnectreason(int reason)
{
- switch(reason)
- {
- case DISC_EOP: return "end of packet";
- case DISC_LOCAL: return "server is in local mode";
- case DISC_KICK: return "kicked/banned";
- case DISC_MSGERR: return "message error";
- case DISC_IPBAN: return "ip is banned";
- case DISC_PRIVATE: return "server is in private mode";
- case DISC_MAXCLIENTS: return "server FULL";
- case DISC_TIMEOUT: return "connection timed out";
- case DISC_OVERFLOW: return "overflow";
- case DISC_PASSWORD: return "invalid password";
- default: return NULL;
- }
+ switch(reason)
+ {
+ case DISC_EOP: return "end of packet";
+ case DISC_LOCAL: return "server is in local mode";
+ case DISC_KICK: return "kicked/banned";
+ case DISC_MSGERR: return "message error";
+ case DISC_IPBAN: return "ip is banned";
+ case DISC_PRIVATE: return "server is in private mode";
+ case DISC_MAXCLIENTS: return "server FULL";
+ case DISC_TIMEOUT: return "connection timed out";
+ case DISC_OVERFLOW: return "overflow";
+ case DISC_PASSWORD: return "invalid password";
+ default: return NULL;
+ }
}
void disconnect_client(int n, int reason)
{
- if(!clients.inrange(n) || clients[n]->type!=ST_TCPIP) return;
- enet_peer_disconnect(clients[n]->peer, reason);
- server::clientdisconnect(n);
- delclient(clients[n]);
- const char *msg = disconnectreason(reason);
- string s;
- if(msg) formatstring(s, "client (%s) disconnected because: %s", clients[n]->hostname, msg);
- else formatstring(s, "client (%s) disconnected", clients[n]->hostname);
- logoutf("%s", s);
- server::sendservmsg(s);
+ if(!clients.inrange(n) || clients[n]->type!=ST_TCPIP) return;
+ enet_peer_disconnect(clients[n]->peer, reason);
+ server::clientdisconnect(n);
+ delclient(clients[n]);
+ const char *msg = disconnectreason(reason);
+ string s;
+ if(msg) formatstring(s, "client (%s) disconnected because: %s", clients[n]->hostname, msg);
+ else formatstring(s, "client (%s) disconnected", clients[n]->hostname);
+ logoutf("%s", s);
+ server::sendservmsg(s);
}
void kicknonlocalclients(int reason)
{
- loopv(clients) if(clients[i]->type==ST_TCPIP) disconnect_client(i, reason);
+ loopv(clients) if(clients[i]->type==ST_TCPIP) disconnect_client(i, reason);
}
void process(ENetPacket *packet, int sender, int chan) // sender may be -1
{
- packetbuf p(packet);
- server::parsepacket(sender, chan, p);
- if(p.overread()) { disconnect_client(sender, DISC_EOP); return; }
+ packetbuf p(packet);
+ server::parsepacket(sender, chan, p);
+ if(p.overread()) { disconnect_client(sender, DISC_EOP); return; }
}
void localclienttoserver(int chan, ENetPacket *packet)
{
- client *c = NULL;
- loopv(clients) if(clients[i]->type==ST_LOCAL) { c = clients[i]; break; }
- if(c) process(packet, c->num, chan);
+ client *c = NULL;
+ loopv(clients) if(clients[i]->type==ST_LOCAL) { c = clients[i]; break; }
+ if(c) process(packet, c->num, chan);
}
#ifdef STANDALONE
bool resolverwait(const char *name, ENetAddress *address)
{
- return enet_address_set_host(address, name) >= 0;
+ return enet_address_set_host(address, name) >= 0;
}
int connectwithtimeout(ENetSocket sock, const char *hostname, const ENetAddress &remoteaddress)
{
- return enet_socket_connect(sock, &remoteaddress);
+ return enet_socket_connect(sock, &remoteaddress);
}
#endif
@@ -351,21 +351,21 @@ VARN(updatemaster, allowupdatemaster, 0, 1, 1);
void disconnectmaster()
{
- if(mastersock != ENET_SOCKET_NULL)
- {
- server::masterdisconnected();
- enet_socket_destroy(mastersock);
- mastersock = ENET_SOCKET_NULL;
- }
+ if(mastersock != ENET_SOCKET_NULL)
+ {
+ server::masterdisconnected();
+ enet_socket_destroy(mastersock);
+ mastersock = ENET_SOCKET_NULL;
+ }
- masterout.setsize(0);
- masterin.setsize(0);
- masteroutpos = masterinpos = 0;
+ masterout.setsize(0);
+ masterin.setsize(0);
+ masteroutpos = masterinpos = 0;
- masteraddress.host = ENET_HOST_ANY;
- masteraddress.port = ENET_PORT_ANY;
+ masteraddress.host = ENET_HOST_ANY;
+ masteraddress.port = ENET_PORT_ANY;
- lastupdatemaster = masterconnecting = masterconnected = 0;
+ lastupdatemaster = masterconnecting = masterconnected = 0;
}
SVARF(mastername, server::defaultmaster(), disconnectmaster());
@@ -373,199 +373,199 @@ VARF(masterport, 1, server::masterport(), 0xFFFF, disconnectmaster());
ENetSocket connectmaster(bool wait)
{
- if(!mastername[0]) return ENET_SOCKET_NULL;
- if(masteraddress.host == ENET_HOST_ANY)
- {
- if(isdedicatedserver()) logoutf("looking up %s...", mastername);
- masteraddress.port = masterport;
- if(!resolverwait(mastername, &masteraddress)) return ENET_SOCKET_NULL;
- }
- ENetSocket sock = enet_socket_create(ENET_SOCKET_TYPE_STREAM);
- if(sock == ENET_SOCKET_NULL)
- {
- if(isdedicatedserver()) logoutf("could not open master server socket");
- return ENET_SOCKET_NULL;
- }
- if(wait || serveraddress.host == ENET_HOST_ANY || !enet_socket_bind(sock, &serveraddress))
- {
- enet_socket_set_option(sock, ENET_SOCKOPT_NONBLOCK, 1);
- if(wait)
- {
- if(!connectwithtimeout(sock, mastername, masteraddress)) return sock;
- }
- else if(!enet_socket_connect(sock, &masteraddress)) return sock;
- }
- enet_socket_destroy(sock);
- if(isdedicatedserver()) logoutf("could not connect to master server");
- return ENET_SOCKET_NULL;
+ if(!mastername[0]) return ENET_SOCKET_NULL;
+ if(masteraddress.host == ENET_HOST_ANY)
+ {
+ if(isdedicatedserver()) logoutf("looking up %s...", mastername);
+ masteraddress.port = masterport;
+ if(!resolverwait(mastername, &masteraddress)) return ENET_SOCKET_NULL;
+ }
+ ENetSocket sock = enet_socket_create(ENET_SOCKET_TYPE_STREAM);
+ if(sock == ENET_SOCKET_NULL)
+ {
+ if(isdedicatedserver()) logoutf("could not open master server socket");
+ return ENET_SOCKET_NULL;
+ }
+ if(wait || serveraddress.host == ENET_HOST_ANY || !enet_socket_bind(sock, &serveraddress))
+ {
+ enet_socket_set_option(sock, ENET_SOCKOPT_NONBLOCK, 1);
+ if(wait)
+ {
+ if(!connectwithtimeout(sock, mastername, masteraddress)) return sock;
+ }
+ else if(!enet_socket_connect(sock, &masteraddress)) return sock;
+ }
+ enet_socket_destroy(sock);
+ if(isdedicatedserver()) logoutf("could not connect to master server");
+ return ENET_SOCKET_NULL;
}
bool requestmaster(const char *req)
{
- if(mastersock == ENET_SOCKET_NULL)
- {
- mastersock = connectmaster(false);
- if(mastersock == ENET_SOCKET_NULL) return false;
- lastconnectmaster = masterconnecting = totalmillis ? totalmillis : 1;
- }
+ if(mastersock == ENET_SOCKET_NULL)
+ {
+ mastersock = connectmaster(false);
+ if(mastersock == ENET_SOCKET_NULL) return false;
+ lastconnectmaster = masterconnecting = totalmillis ? totalmillis : 1;
+ }
- if(masterout.length() >= 4096) return false;
+ if(masterout.length() >= 4096) return false;
- masterout.put(req, strlen(req));
- return true;
+ masterout.put(req, strlen(req));
+ return true;
}
bool requestmasterf(const char *fmt, ...)
{
- defvformatstring(req, fmt, fmt);
- return requestmaster(req);
+ defvformatstring(req, fmt, fmt);
+ return requestmaster(req);
}
void processmasterinput()
{
- if(masterinpos >= masterin.length()) return;
+ if(masterinpos >= masterin.length()) return;
- char *input = &masterin[masterinpos], *end = (char *)memchr(input, '\n', masterin.length() - masterinpos);
- while(end)
- {
- *end = '\0';
+ char *input = &masterin[masterinpos], *end = (char *)memchr(input, '\n', masterin.length() - masterinpos);
+ while(end)
+ {
+ *end = '\0';
- const char *args = input;
- while(args < end && !iscubespace(*args)) args++;
- int cmdlen = args - input;
- while(args < end && iscubespace(*args)) args++;
+ const char *args = input;
+ while(args < end && !iscubespace(*args)) args++;
+ int cmdlen = args - input;
+ while(args < end && iscubespace(*args)) args++;
- if(matchstring(input, cmdlen, "failreg"))
- conoutf(CON_ERROR, "master server registration failed: %s", args);
- else if(matchstring(input, cmdlen, "succreg"))
- conoutf("master server registration succeeded");
- else server::processmasterinput(input, cmdlen, args);
+ if(matchstring(input, cmdlen, "failreg"))
+ conoutf(CON_ERROR, "master server registration failed: %s", args);
+ else if(matchstring(input, cmdlen, "succreg"))
+ conoutf("master server registration succeeded");
+ else server::processmasterinput(input, cmdlen, args);
- end++;
- masterinpos = end - masterin.getbuf();
- input = end;
- end = (char *)memchr(input, '\n', masterin.length() - masterinpos);
- }
+ end++;
+ masterinpos = end - masterin.getbuf();
+ input = end;
+ end = (char *)memchr(input, '\n', masterin.length() - masterinpos);
+ }
- if(masterinpos >= masterin.length())
- {
- masterin.setsize(0);
- masterinpos = 0;
- }
+ if(masterinpos >= masterin.length())
+ {
+ masterin.setsize(0);
+ masterinpos = 0;
+ }
}
void flushmasteroutput()
{
- if(masterconnecting && totalmillis - masterconnecting >= 60000)
- {
- logoutf("could not connect to master server");
- disconnectmaster();
- }
- if(masterout.empty() || !masterconnected) return;
-
- ENetBuffer buf;
- buf.data = &masterout[masteroutpos];
- buf.dataLength = masterout.length() - masteroutpos;
- int sent = enet_socket_send(mastersock, NULL, &buf, 1);
- if(sent >= 0)
- {
- masteroutpos += sent;
- if(masteroutpos >= masterout.length())
- {
- masterout.setsize(0);
- masteroutpos = 0;
- }
- }
- else disconnectmaster();
+ if(masterconnecting && totalmillis - masterconnecting >= 60000)
+ {
+ logoutf("could not connect to master server");
+ disconnectmaster();
+ }
+ if(masterout.empty() || !masterconnected) return;
+
+ ENetBuffer buf;
+ buf.data = &masterout[masteroutpos];
+ buf.dataLength = masterout.length() - masteroutpos;
+ int sent = enet_socket_send(mastersock, NULL, &buf, 1);
+ if(sent >= 0)
+ {
+ masteroutpos += sent;
+ if(masteroutpos >= masterout.length())
+ {
+ masterout.setsize(0);
+ masteroutpos = 0;
+ }
+ }
+ else disconnectmaster();
}
void flushmasterinput()
{
- if(masterin.length() >= masterin.capacity())
- masterin.reserve(4096);
+ if(masterin.length() >= masterin.capacity())
+ masterin.reserve(4096);
- ENetBuffer buf;
- buf.data = masterin.getbuf() + masterin.length();
- buf.dataLength = masterin.capacity() - masterin.length();
- int recv = enet_socket_receive(mastersock, NULL, &buf, 1);
- if(recv > 0)
- {
- masterin.advance(recv);
- processmasterinput();
- }
- else disconnectmaster();
+ ENetBuffer buf;
+ buf.data = masterin.getbuf() + masterin.length();
+ buf.dataLength = masterin.capacity() - masterin.length();
+ int recv = enet_socket_receive(mastersock, NULL, &buf, 1);
+ if(recv > 0)
+ {
+ masterin.advance(recv);
+ processmasterinput();
+ }
+ else disconnectmaster();
}
static ENetAddress pongaddr;
void sendserverinforeply(ucharbuf &p)
{
- ENetBuffer buf;
- buf.data = p.buf;
- buf.dataLength = p.length();
- enet_socket_send(pongsock, &pongaddr, &buf, 1);
+ ENetBuffer buf;
+ buf.data = p.buf;
+ buf.dataLength = p.length();
+ enet_socket_send(pongsock, &pongaddr, &buf, 1);
}
#define MAXPINGDATA 32
-void checkserversockets() // reply all server info requests
-{
- static ENetSocketSet readset, writeset;
- ENET_SOCKETSET_EMPTY(readset);
- ENET_SOCKETSET_EMPTY(writeset);
- ENetSocket maxsock = pongsock;
- ENET_SOCKETSET_ADD(readset, pongsock);
- if(mastersock != ENET_SOCKET_NULL)
- {
- maxsock = max(maxsock, mastersock);
- ENET_SOCKETSET_ADD(readset, mastersock);
- if(!masterconnected) ENET_SOCKETSET_ADD(writeset, mastersock);
- }
- if(lansock != ENET_SOCKET_NULL)
- {
- maxsock = max(maxsock, lansock);
- ENET_SOCKETSET_ADD(readset, lansock);
- }
- if(enet_socketset_select(maxsock, &readset, &writeset, 0) <= 0) return;
-
- ENetBuffer buf;
- uchar pong[MAXTRANS];
- loopi(2)
- {
- ENetSocket sock = i ? lansock : pongsock;
- if(sock == ENET_SOCKET_NULL || !ENET_SOCKETSET_CHECK(readset, sock)) continue;
-
- buf.data = pong;
- buf.dataLength = sizeof(pong);
- int len = enet_socket_receive(sock, &pongaddr, &buf, 1);
- if(len < 0 || len > MAXPINGDATA) continue;
- ucharbuf req(pong, len), p(pong, sizeof(pong));
- p.len += len;
- server::serverinforeply(req, p);
- }
-
- if(mastersock != ENET_SOCKET_NULL)
- {
- if(!masterconnected)
- {
- if(ENET_SOCKETSET_CHECK(readset, mastersock) || ENET_SOCKETSET_CHECK(writeset, mastersock))
- {
- int error = 0;
- if(enet_socket_get_option(mastersock, ENET_SOCKOPT_ERROR, &error) < 0 || error)
- {
- logoutf("could not connect to master server");
- disconnectmaster();
- }
- else
- {
- masterconnecting = 0;
- masterconnected = totalmillis ? totalmillis : 1;
- server::masterconnected();
- }
- }
- }
- if(mastersock != ENET_SOCKET_NULL && ENET_SOCKETSET_CHECK(readset, mastersock)) flushmasterinput();
- }
+void checkserversockets() // reply all server info requests
+{
+ static ENetSocketSet readset, writeset;
+ ENET_SOCKETSET_EMPTY(readset);
+ ENET_SOCKETSET_EMPTY(writeset);
+ ENetSocket maxsock = pongsock;
+ ENET_SOCKETSET_ADD(readset, pongsock);
+ if(mastersock != ENET_SOCKET_NULL)
+ {
+ maxsock = max(maxsock, mastersock);
+ ENET_SOCKETSET_ADD(readset, mastersock);
+ if(!masterconnected) ENET_SOCKETSET_ADD(writeset, mastersock);
+ }
+ if(lansock != ENET_SOCKET_NULL)
+ {
+ maxsock = max(maxsock, lansock);
+ ENET_SOCKETSET_ADD(readset, lansock);
+ }
+ if(enet_socketset_select(maxsock, &readset, &writeset, 0) <= 0) return;
+
+ ENetBuffer buf;
+ uchar pong[MAXTRANS];
+ loopi(2)
+ {
+ ENetSocket sock = i ? lansock : pongsock;
+ if(sock == ENET_SOCKET_NULL || !ENET_SOCKETSET_CHECK(readset, sock)) continue;
+
+ buf.data = pong;
+ buf.dataLength = sizeof(pong);
+ int len = enet_socket_receive(sock, &pongaddr, &buf, 1);
+ if(len < 0 || len > MAXPINGDATA) continue;
+ ucharbuf req(pong, len), p(pong, sizeof(pong));
+ p.len += len;
+ server::serverinforeply(req, p);
+ }
+
+ if(mastersock != ENET_SOCKET_NULL)
+ {
+ if(!masterconnected)
+ {
+ if(ENET_SOCKETSET_CHECK(readset, mastersock) || ENET_SOCKETSET_CHECK(writeset, mastersock))
+ {
+ int error = 0;
+ if(enet_socket_get_option(mastersock, ENET_SOCKOPT_ERROR, &error) < 0 || error)
+ {
+ logoutf("could not connect to master server");
+ disconnectmaster();
+ }
+ else
+ {
+ masterconnecting = 0;
+ masterconnected = totalmillis ? totalmillis : 1;
+ server::masterconnected();
+ }
+ }
+ }
+ if(mastersock != ENET_SOCKET_NULL && ENET_SOCKETSET_CHECK(readset, mastersock)) flushmasterinput();
+ }
}
VAR(serveruprate, 0, 0, INT_MAX);
@@ -578,143 +578,143 @@ int curtime = 0, lastmillis = 0, elapsedtime = 0, totalmillis = 0;
void updatemasterserver()
{
- if(!masterconnected && lastconnectmaster && totalmillis-lastconnectmaster <= 5*60*1000) return;
- if(mastername[0] && allowupdatemaster) requestmasterf("regserv %d\n", serverport);
- lastupdatemaster = totalmillis ? totalmillis : 1;
+ if(!masterconnected && lastconnectmaster && totalmillis-lastconnectmaster <= 5*60*1000) return;
+ if(mastername[0] && allowupdatemaster) requestmasterf("regserv %d\n", serverport);
+ lastupdatemaster = totalmillis ? totalmillis : 1;
}
uint totalsecs = 0;
void updatetime()
{
- static int lastsec = 0;
- if(totalmillis - lastsec >= 1000)
- {
- int cursecs = (totalmillis - lastsec) / 1000;
- totalsecs += cursecs;
- lastsec += cursecs * 1000;
- }
+ static int lastsec = 0;
+ if(totalmillis - lastsec >= 1000)
+ {
+ int cursecs = (totalmillis - lastsec) / 1000;
+ totalsecs += cursecs;
+ lastsec += cursecs * 1000;
+ }
}
void serverslice(bool dedicated, uint timeout) // main server update, called from main loop in sp, or from below in dedicated server
{
- if(!serverhost)
- {
- server::serverupdate();
- server::sendpackets();
- return;
- }
-
- // below is network only
-
- if(dedicated)
- {
- int millis = (int)enet_time_get();
- elapsedtime = millis - totalmillis;
- static int timeerr = 0;
- int scaledtime = server::scaletime(elapsedtime) + timeerr;
- curtime = scaledtime/100;
- timeerr = scaledtime%100;
- if(server::ispaused()) curtime = 0;
- lastmillis += curtime;
- totalmillis = millis;
- updatetime();
- }
- server::serverupdate();
-
- flushmasteroutput();
- checkserversockets();
-
- if(!lastupdatemaster || totalmillis-lastupdatemaster>60*60*1000) // send alive signal to masterserver every hour of uptime
- updatemasterserver();
-
- if(totalmillis-laststatus>60*1000) // display bandwidth stats, useful for server ops
- {
- laststatus = totalmillis;
- if(nonlocalclients || serverhost->totalSentData || serverhost->totalReceivedData) logoutf("status: %d remote clients, %.1f send, %.1f rec (K/sec)", nonlocalclients, serverhost->totalSentData/60.0f/1024, serverhost->totalReceivedData/60.0f/1024);
- serverhost->totalSentData = serverhost->totalReceivedData = 0;
- }
-
- ENetEvent event;
- bool serviced = false;
- while(!serviced)
- {
- if(enet_host_check_events(serverhost, &event) <= 0)
- {
- if(enet_host_service(serverhost, &event, timeout) <= 0) break;
- serviced = true;
- }
- switch(event.type)
- {
- case ENET_EVENT_TYPE_CONNECT:
- {
- client &c = addclient(ST_TCPIP);
- c.peer = event.peer;
- c.peer->data = &c;
- string hn;
- copystring(c.hostname, (enet_address_get_host_ip(&c.peer->address, hn, sizeof(hn))==0) ? hn : "unknown");
- logoutf("client connected (%s)", c.hostname);
- int reason = server::clientconnect(c.num);
- if(reason) disconnect_client(c.num, reason);
- break;
- }
- case ENET_EVENT_TYPE_RECEIVE:
- {
- client *c = (client *)event.peer->data;
- if(c) process(event.packet, c->num, event.channelID);
- if(event.packet->referenceCount==0) enet_packet_destroy(event.packet);
- break;
- }
- case ENET_EVENT_TYPE_DISCONNECT:
- {
- client *c = (client *)event.peer->data;
- if(!c) break;
- logoutf("disconnected client (%s)", c->hostname);
- server::clientdisconnect(c->num);
- delclient(c);
- break;
- }
- default:
- break;
- }
- }
- if(server::sendpackets()) enet_host_flush(serverhost);
+ if(!serverhost)
+ {
+ server::serverupdate();
+ server::sendpackets();
+ return;
+ }
+
+ // below is network only
+
+ if(dedicated)
+ {
+ int millis = (int)enet_time_get();
+ elapsedtime = millis - totalmillis;
+ static int timeerr = 0;
+ int scaledtime = server::scaletime(elapsedtime) + timeerr;
+ curtime = scaledtime/100;
+ timeerr = scaledtime%100;
+ if(server::ispaused()) curtime = 0;
+ lastmillis += curtime;
+ totalmillis = millis;
+ updatetime();
+ }
+ server::serverupdate();
+
+ flushmasteroutput();
+ checkserversockets();
+
+ if(!lastupdatemaster || totalmillis-lastupdatemaster>60*60*1000) // send alive signal to masterserver every hour of uptime
+ updatemasterserver();
+
+ if(totalmillis-laststatus>60*1000) // display bandwidth stats, useful for server ops
+ {
+ laststatus = totalmillis;
+ if(nonlocalclients || serverhost->totalSentData || serverhost->totalReceivedData) logoutf("status: %d remote clients, %.1f send, %.1f rec (K/sec)", nonlocalclients, serverhost->totalSentData/60.0f/1024, serverhost->totalReceivedData/60.0f/1024);
+ serverhost->totalSentData = serverhost->totalReceivedData = 0;
+ }
+
+ ENetEvent event;
+ bool serviced = false;
+ while(!serviced)
+ {
+ if(enet_host_check_events(serverhost, &event) <= 0)
+ {
+ if(enet_host_service(serverhost, &event, timeout) <= 0) break;
+ serviced = true;
+ }
+ switch(event.type)
+ {
+ case ENET_EVENT_TYPE_CONNECT:
+ {
+ client &c = addclient(ST_TCPIP);
+ c.peer = event.peer;
+ c.peer->data = &c;
+ string hn;
+ copystring(c.hostname, (enet_address_get_host_ip(&c.peer->address, hn, sizeof(hn))==0) ? hn : "unknown");
+ logoutf("client connected (%s)", c.hostname);
+ int reason = server::clientconnect(c.num);
+ if(reason) disconnect_client(c.num, reason);
+ break;
+ }
+ case ENET_EVENT_TYPE_RECEIVE:
+ {
+ client *c = (client *)event.peer->data;
+ if(c) process(event.packet, c->num, event.channelID);
+ if(event.packet->referenceCount==0) enet_packet_destroy(event.packet);
+ break;
+ }
+ case ENET_EVENT_TYPE_DISCONNECT:
+ {
+ client *c = (client *)event.peer->data;
+ if(!c) break;
+ logoutf("disconnected client (%s)", c->hostname);
+ server::clientdisconnect(c->num);
+ delclient(c);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ if(server::sendpackets()) enet_host_flush(serverhost);
}
void flushserver(bool force)
{
- if(server::sendpackets(force) && serverhost) enet_host_flush(serverhost);
+ if(server::sendpackets(force) && serverhost) enet_host_flush(serverhost);
}
#ifndef STANDALONE
void localdisconnect(bool cleanup)
{
- bool disconnected = false;
- loopv(clients) if(clients[i]->type==ST_LOCAL)
- {
- server::localdisconnect(i);
- delclient(clients[i]);
- disconnected = true;
- }
- if(!disconnected) return;
- game::gamedisconnect(cleanup);
- mainmenu = 1;
+ bool disconnected = false;
+ loopv(clients) if(clients[i]->type==ST_LOCAL)
+ {
+ server::localdisconnect(i);
+ delclient(clients[i]);
+ disconnected = true;
+ }
+ if(!disconnected) return;
+ game::gamedisconnect(cleanup);
+ mainmenu = 1;
}
void localconnect()
{
- if(initing) return;
- client &c = addclient(ST_LOCAL);
- copystring(c.hostname, "local");
- game::gameconnect(false);
- server::localconnect(c.num);
+ if(initing) return;
+ client &c = addclient(ST_LOCAL);
+ copystring(c.hostname, "local");
+ game::gameconnect(false);
+ server::localconnect(c.num);
}
#endif
void logoutfv(const char *fmt, va_list args)
{
- FILE *f = getlogfile();
- if(f) writelogv(f, fmt, args);
+ FILE *f = getlogfile();
+ if(f) writelogv(f, fmt, args);
}
static bool dedicatedserver = false;
@@ -723,121 +723,121 @@ bool isdedicatedserver() { return dedicatedserver; }
void rundedicatedserver()
{
- dedicatedserver = true;
- logoutf("dedicated server started, waiting for clients...");
- for(;;) serverslice(true, 5);
- dedicatedserver = false;
+ dedicatedserver = true;
+ logoutf("dedicated server started, waiting for clients...");
+ for(;;) serverslice(true, 5);
+ dedicatedserver = false;
}
bool servererror(bool dedicated, const char *desc)
{
#ifndef STANDALONE
- if(!dedicated)
- {
- conoutf(CON_ERROR, "%s", desc);
- cleanupserver();
- }
- else
+ if(!dedicated)
+ {
+ conoutf(CON_ERROR, "%s", desc);
+ cleanupserver();
+ }
+ else
#endif
- fatal("%s", desc);
- return false;
+ fatal("%s", desc);
+ return false;
}
bool setuplistenserver(bool dedicated)
{
- ENetAddress address = { ENET_HOST_ANY, enet_uint16(serverport <= 0 ? server::serverport() : serverport) };
- if(*serverip)
- {
- if(enet_address_set_host(&address, serverip)<0) conoutf(CON_WARN, "WARNING: server ip not resolved");
- else serveraddress.host = address.host;
- }
- serverhost = enet_host_create(&address, min(maxclients + server::reserveclients(), MAXCLIENTS), server::numchannels(), 0, serveruprate);
- if(!serverhost) return servererror(dedicated, "could not create server host");
- serverhost->duplicatePeers = maxdupclients ? maxdupclients : MAXCLIENTS;
- address.port = server::serverinfoport(serverport > 0 ? serverport : -1);
- pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
- if(pongsock != ENET_SOCKET_NULL && enet_socket_bind(pongsock, &address) < 0)
- {
- enet_socket_destroy(pongsock);
- pongsock = ENET_SOCKET_NULL;
- }
- if(pongsock == ENET_SOCKET_NULL) return servererror(dedicated, "could not create server info socket");
- else enet_socket_set_option(pongsock, ENET_SOCKOPT_NONBLOCK, 1);
- address.port = server::laninfoport();
- lansock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
- if(lansock != ENET_SOCKET_NULL && (enet_socket_set_option(lansock, ENET_SOCKOPT_REUSEADDR, 1) < 0 || enet_socket_bind(lansock, &address) < 0))
- {
- enet_socket_destroy(lansock);
- lansock = ENET_SOCKET_NULL;
- }
- if(lansock == ENET_SOCKET_NULL) conoutf(CON_WARN, "WARNING: could not create LAN server info socket");
- else enet_socket_set_option(lansock, ENET_SOCKOPT_NONBLOCK, 1);
- return true;
+ ENetAddress address = { ENET_HOST_ANY, enet_uint16(serverport <= 0 ? server::serverport() : serverport) };
+ if(*serverip)
+ {
+ if(enet_address_set_host(&address, serverip)<0) conoutf(CON_WARN, "WARNING: server ip not resolved");
+ else serveraddress.host = address.host;
+ }
+ serverhost = enet_host_create(&address, min(maxclients + server::reserveclients(), MAXCLIENTS), server::numchannels(), 0, serveruprate);
+ if(!serverhost) return servererror(dedicated, "could not create server host");
+ serverhost->duplicatePeers = maxdupclients ? maxdupclients : MAXCLIENTS;
+ address.port = server::serverinfoport(serverport > 0 ? serverport : -1);
+ pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
+ if(pongsock != ENET_SOCKET_NULL && enet_socket_bind(pongsock, &address) < 0)
+ {
+ enet_socket_destroy(pongsock);
+ pongsock = ENET_SOCKET_NULL;
+ }
+ if(pongsock == ENET_SOCKET_NULL) return servererror(dedicated, "could not create server info socket");
+ else enet_socket_set_option(pongsock, ENET_SOCKOPT_NONBLOCK, 1);
+ address.port = server::laninfoport();
+ lansock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
+ if(lansock != ENET_SOCKET_NULL && (enet_socket_set_option(lansock, ENET_SOCKOPT_REUSEADDR, 1) < 0 || enet_socket_bind(lansock, &address) < 0))
+ {
+ enet_socket_destroy(lansock);
+ lansock = ENET_SOCKET_NULL;
+ }
+ if(lansock == ENET_SOCKET_NULL) conoutf(CON_WARN, "WARNING: could not create LAN server info socket");
+ else enet_socket_set_option(lansock, ENET_SOCKOPT_NONBLOCK, 1);
+ return true;
}
void initserver(bool listen, bool dedicated)
{
- execfile("server-init.cfg", false);
+ execfile("server-init.cfg", false);
- if(listen) setuplistenserver(dedicated);
+ if(listen) setuplistenserver(dedicated);
- server::serverinit();
+ server::serverinit();
- if(listen)
- {
- dedicatedserver = dedicated;
- updatemasterserver();
- if(dedicated) rundedicatedserver(); // never returns
+ if(listen)
+ {
+ dedicatedserver = dedicated;
+ updatemasterserver();
+ if(dedicated) rundedicatedserver(); // never returns
#ifndef STANDALONE
- else conoutf("listen server started");
+ else conoutf("listen server started");
#endif
- }
+ }
}
#ifndef STANDALONE
void startlistenserver(int *usemaster)
{
- if(serverhost) { conoutf(CON_ERROR, "listen server is already running"); return; }
+ if(serverhost) { conoutf(CON_ERROR, "listen server is already running"); return; }
- allowupdatemaster = *usemaster>0 ? 1 : 0;
+ allowupdatemaster = *usemaster>0 ? 1 : 0;
- if(!setuplistenserver(false)) return;
+ if(!setuplistenserver(false)) return;
- updatemasterserver();
+ updatemasterserver();
- conoutf("listen server started for %d clients%s", maxclients, allowupdatemaster ? " and listed with master server" : "");
+ conoutf("listen server started for %d clients%s", maxclients, allowupdatemaster ? " and listed with master server" : "");
}
COMMAND(startlistenserver, "i");
void stoplistenserver()
{
- if(!serverhost) { conoutf(CON_ERROR, "listen server is not running"); return; }
+ if(!serverhost) { conoutf(CON_ERROR, "listen server is not running"); return; }
- kicknonlocalclients();
- enet_host_flush(serverhost);
- cleanupserver();
+ kicknonlocalclients();
+ enet_host_flush(serverhost);
+ cleanupserver();
- conoutf("listen server stopped");
+ conoutf("listen server stopped");
}
COMMAND(stoplistenserver, "");
#endif
bool serveroption(char *opt)
{
- switch(opt[1])
- {
- case 'u': setvar("serveruprate", atoi(opt+2)); return true;
- case 'c': setvar("maxclients", atoi(opt+2)); return true;
- case 'i': setsvar("serverip", opt+2); return true;
- case 'j': setvar("serverport", atoi(opt+2)); return true;
- case 'm': setsvar("mastername", opt+2); setvar("updatemaster", mastername[0] ? 1 : 0); return true;
+ switch(opt[1])
+ {
+ case 'u': setvar("serveruprate", atoi(opt+2)); return true;
+ case 'c': setvar("maxclients", atoi(opt+2)); return true;
+ case 'i': setsvar("serverip", opt+2); return true;
+ case 'j': setvar("serverport", atoi(opt+2)); return true;
+ case 'm': setsvar("mastername", opt+2); setvar("updatemaster", mastername[0] ? 1 : 0); return true;
#ifdef STANDALONE
- case 'q': logoutf("Using home directory: %s", opt); sethomedir(opt+2); return true;
- case 'k': logoutf("Adding package directory: %s", opt); addpackagedir(opt+2); return true;
- case 'g': logoutf("Setting log file: %s", opt); setlogfile(opt+2); return true;
+ case 'q': logoutf("Using home directory: %s", opt); sethomedir(opt+2); return true;
+ case 'k': logoutf("Adding package directory: %s", opt); addpackagedir(opt+2); return true;
+ case 'g': logoutf("Setting log file: %s", opt); setlogfile(opt+2); return true;
#endif
- default: return false;
- }
+ default: return false;
+ }
}
vector<const char *> gameargs;
@@ -845,13 +845,13 @@ vector<const char *> gameargs;
#ifdef STANDALONE
int main(int argc, char **argv)
{
- setlogfile(NULL);
- if(enet_initialize()<0) fatal("Unable to initialise network module");
- atexit(enet_deinitialize);
- enet_time_set(0);
- for(int i = 1; i<argc; i++) if(argv[i][0]!='-' || !serveroption(argv[i])) gameargs.add(argv[i]);
- game::parseoptions(gameargs);
- initserver(true, true);
- return EXIT_SUCCESS;
+ setlogfile(NULL);
+ if(enet_initialize()<0) fatal("Unable to initialise network module");
+ atexit(enet_deinitialize);
+ enet_time_set(0);
+ for(int i = 1; i<argc; i++) if(argv[i][0]!='-' || !serveroption(argv[i])) gameargs.add(argv[i]);
+ game::parseoptions(gameargs);
+ initserver(true, true);
+ return EXIT_SUCCESS;
}
#endif
diff --git a/src/engine/serverbrowser.cpp b/src/engine/serverbrowser.cpp
index ee36eea..85109bf 100644
--- a/src/engine/serverbrowser.cpp
+++ b/src/engine/serverbrowser.cpp
@@ -2,19 +2,19 @@
#include "engine.h"
-#define PROTOCOL_VERSION 260 // bump when protocol changes, dpulicate
+#define PROTOCOL_VERSION 260 // bump when protocol changes, dpulicate
struct resolverthread
{
- SDL_Thread *thread;
- const char *query;
- int starttime;
+ SDL_Thread *thread;
+ const char *query;
+ int starttime;
};
struct resolverresult
{
- const char *query;
- ENetAddress address;
+ const char *query;
+ ENetAddress address;
};
vector<resolverthread> resolverthreads;
@@ -28,234 +28,234 @@ SDL_cond *querycond, *resultcond;
int resolverloop(void * data)
{
- resolverthread *rt = (resolverthread *)data;
- SDL_LockMutex(resolvermutex);
- SDL_Thread *thread = rt->thread;
- SDL_UnlockMutex(resolvermutex);
- if(!thread || SDL_GetThreadID(thread) != SDL_ThreadID())
- return 0;
- while(thread == rt->thread)
- {
- SDL_LockMutex(resolvermutex);
- while(resolverqueries.empty()) SDL_CondWait(querycond, resolvermutex);
- rt->query = resolverqueries.pop();
- rt->starttime = totalmillis;
- SDL_UnlockMutex(resolvermutex);
-
- ENetAddress address = { ENET_HOST_ANY, ENET_PORT_ANY };
- enet_address_set_host(&address, rt->query);
-
- SDL_LockMutex(resolvermutex);
- if(rt->query && thread == rt->thread)
- {
- resolverresult &rr = resolverresults.add();
- rr.query = rt->query;
- rr.address = address;
- rt->query = NULL;
- rt->starttime = 0;
- SDL_CondSignal(resultcond);
- }
- SDL_UnlockMutex(resolvermutex);
- }
- return 0;
+ resolverthread *rt = (resolverthread *)data;
+ SDL_LockMutex(resolvermutex);
+ SDL_Thread *thread = rt->thread;
+ SDL_UnlockMutex(resolvermutex);
+ if(!thread || SDL_GetThreadID(thread) != SDL_ThreadID())
+ return 0;
+ while(thread == rt->thread)
+ {
+ SDL_LockMutex(resolvermutex);
+ while(resolverqueries.empty()) SDL_CondWait(querycond, resolvermutex);
+ rt->query = resolverqueries.pop();
+ rt->starttime = totalmillis;
+ SDL_UnlockMutex(resolvermutex);
+
+ ENetAddress address = { ENET_HOST_ANY, ENET_PORT_ANY };
+ enet_address_set_host(&address, rt->query);
+
+ SDL_LockMutex(resolvermutex);
+ if(rt->query && thread == rt->thread)
+ {
+ resolverresult &rr = resolverresults.add();
+ rr.query = rt->query;
+ rr.address = address;
+ rt->query = NULL;
+ rt->starttime = 0;
+ SDL_CondSignal(resultcond);
+ }
+ SDL_UnlockMutex(resolvermutex);
+ }
+ return 0;
}
void resolverinit()
{
- resolvermutex = SDL_CreateMutex();
- querycond = SDL_CreateCond();
- resultcond = SDL_CreateCond();
-
- SDL_LockMutex(resolvermutex);
- loopi(RESOLVERTHREADS)
- {
- resolverthread &rt = resolverthreads.add();
- rt.query = NULL;
- rt.starttime = 0;
- rt.thread = SDL_CreateThread(resolverloop, "resolver", &rt);
- }
- SDL_UnlockMutex(resolvermutex);
+ resolvermutex = SDL_CreateMutex();
+ querycond = SDL_CreateCond();
+ resultcond = SDL_CreateCond();
+
+ SDL_LockMutex(resolvermutex);
+ loopi(RESOLVERTHREADS)
+ {
+ resolverthread &rt = resolverthreads.add();
+ rt.query = NULL;
+ rt.starttime = 0;
+ rt.thread = SDL_CreateThread(resolverloop, "resolver", &rt);
+ }
+ SDL_UnlockMutex(resolvermutex);
}
void resolverstop(resolverthread &rt)
{
- SDL_LockMutex(resolvermutex);
- if(rt.query)
- {
+ SDL_LockMutex(resolvermutex);
+ if(rt.query)
+ {
#if SDL_VERSION_ATLEAST(2, 0, 2)
- SDL_DetachThread(rt.thread);
+ SDL_DetachThread(rt.thread);
#endif
- rt.thread = SDL_CreateThread(resolverloop, "resolver", &rt);
- }
- rt.query = NULL;
- rt.starttime = 0;
- SDL_UnlockMutex(resolvermutex);
+ rt.thread = SDL_CreateThread(resolverloop, "resolver", &rt);
+ }
+ rt.query = NULL;
+ rt.starttime = 0;
+ SDL_UnlockMutex(resolvermutex);
}
void resolverclear()
{
- if(resolverthreads.empty()) return;
-
- SDL_LockMutex(resolvermutex);
- resolverqueries.shrink(0);
- resolverresults.shrink(0);
- loopv(resolverthreads)
- {
- resolverthread &rt = resolverthreads[i];
- resolverstop(rt);
- }
- SDL_UnlockMutex(resolvermutex);
+ if(resolverthreads.empty()) return;
+
+ SDL_LockMutex(resolvermutex);
+ resolverqueries.shrink(0);
+ resolverresults.shrink(0);
+ loopv(resolverthreads)
+ {
+ resolverthread &rt = resolverthreads[i];
+ resolverstop(rt);
+ }
+ SDL_UnlockMutex(resolvermutex);
}
void resolverquery(const char *name)
{
- if(resolverthreads.empty()) resolverinit();
+ if(resolverthreads.empty()) resolverinit();
- SDL_LockMutex(resolvermutex);
- resolverqueries.add(name);
- SDL_CondSignal(querycond);
- SDL_UnlockMutex(resolvermutex);
+ SDL_LockMutex(resolvermutex);
+ resolverqueries.add(name);
+ SDL_CondSignal(querycond);
+ SDL_UnlockMutex(resolvermutex);
}
bool resolvercheck(const char **name, ENetAddress *address)
{
- bool resolved = false;
- SDL_LockMutex(resolvermutex);
- if(!resolverresults.empty())
- {
- resolverresult &rr = resolverresults.pop();
- *name = rr.query;
- address->host = rr.address.host;
- resolved = true;
- }
- else loopv(resolverthreads)
- {
- resolverthread &rt = resolverthreads[i];
- if(rt.query && totalmillis - rt.starttime > RESOLVERLIMIT)
- {
- resolverstop(rt);
- *name = rt.query;
- resolved = true;
- }
- }
- SDL_UnlockMutex(resolvermutex);
- return resolved;
+ bool resolved = false;
+ SDL_LockMutex(resolvermutex);
+ if(!resolverresults.empty())
+ {
+ resolverresult &rr = resolverresults.pop();
+ *name = rr.query;
+ address->host = rr.address.host;
+ resolved = true;
+ }
+ else loopv(resolverthreads)
+ {
+ resolverthread &rt = resolverthreads[i];
+ if(rt.query && totalmillis - rt.starttime > RESOLVERLIMIT)
+ {
+ resolverstop(rt);
+ *name = rt.query;
+ resolved = true;
+ }
+ }
+ SDL_UnlockMutex(resolvermutex);
+ return resolved;
}
bool resolverwait(const char *name, ENetAddress *address)
{
- if(resolverthreads.empty()) resolverinit();
-
- defformatstring(text, "resolving %s... (esc to abort)", name);
- renderprogress(0, text);
-
- SDL_LockMutex(resolvermutex);
- resolverqueries.add(name);
- SDL_CondSignal(querycond);
- int starttime = SDL_GetTicks(), timeout = 0;
- bool resolved = false;
- for(;;)
- {
- SDL_CondWaitTimeout(resultcond, resolvermutex, 250);
- loopv(resolverresults) if(resolverresults[i].query == name)
- {
- address->host = resolverresults[i].address.host;
- resolverresults.remove(i);
- resolved = true;
- break;
- }
- if(resolved) break;
-
- timeout = SDL_GetTicks() - starttime;
- renderprogress(min(float(timeout)/RESOLVERLIMIT, 1.0f), text);
- if(interceptkey(SDLK_ESCAPE)) timeout = RESOLVERLIMIT + 1;
- if(timeout > RESOLVERLIMIT) break;
- }
- if(!resolved && timeout > RESOLVERLIMIT)
- {
- loopv(resolverthreads)
- {
- resolverthread &rt = resolverthreads[i];
- if(rt.query == name) { resolverstop(rt); break; }
- }
- }
- SDL_UnlockMutex(resolvermutex);
- return resolved && address->host != ENET_HOST_ANY;
+ if(resolverthreads.empty()) resolverinit();
+
+ defformatstring(text, "resolving %s... (esc to abort)", name);
+ renderprogress(0, text);
+
+ SDL_LockMutex(resolvermutex);
+ resolverqueries.add(name);
+ SDL_CondSignal(querycond);
+ int starttime = SDL_GetTicks(), timeout = 0;
+ bool resolved = false;
+ for(;;)
+ {
+ SDL_CondWaitTimeout(resultcond, resolvermutex, 250);
+ loopv(resolverresults) if(resolverresults[i].query == name)
+ {
+ address->host = resolverresults[i].address.host;
+ resolverresults.remove(i);
+ resolved = true;
+ break;
+ }
+ if(resolved) break;
+
+ timeout = SDL_GetTicks() - starttime;
+ renderprogress(min(float(timeout)/RESOLVERLIMIT, 1.0f), text);
+ if(interceptkey(SDLK_ESCAPE)) timeout = RESOLVERLIMIT + 1;
+ if(timeout > RESOLVERLIMIT) break;
+ }
+ if(!resolved && timeout > RESOLVERLIMIT)
+ {
+ loopv(resolverthreads)
+ {
+ resolverthread &rt = resolverthreads[i];
+ if(rt.query == name) { resolverstop(rt); break; }
+ }
+ }
+ SDL_UnlockMutex(resolvermutex);
+ return resolved && address->host != ENET_HOST_ANY;
}
#define CONNLIMIT 20000
int connectwithtimeout(ENetSocket sock, const char *hostname, const ENetAddress &address)
{
- defformatstring(text, "connecting to %s... (esc to abort)", hostname);
- renderprogress(0, text);
-
- ENetSocketSet readset, writeset;
- if(!enet_socket_connect(sock, &address)) for(int starttime = SDL_GetTicks(), timeout = 0; timeout <= CONNLIMIT;)
- {
- ENET_SOCKETSET_EMPTY(readset);
- ENET_SOCKETSET_EMPTY(writeset);
- ENET_SOCKETSET_ADD(readset, sock);
- ENET_SOCKETSET_ADD(writeset, sock);
- int result = enet_socketset_select(sock, &readset, &writeset, 250);
- if(result < 0) break;
- else if(result > 0)
- {
- if(ENET_SOCKETSET_CHECK(readset, sock) || ENET_SOCKETSET_CHECK(writeset, sock))
- {
- int error = 0;
- if(enet_socket_get_option(sock, ENET_SOCKOPT_ERROR, &error) < 0 || error) break;
- return 0;
- }
- }
- timeout = SDL_GetTicks() - starttime;
- renderprogress(min(float(timeout)/CONNLIMIT, 1.0f), text);
- if(interceptkey(SDLK_ESCAPE)) break;
- }
-
- return -1;
+ defformatstring(text, "connecting to %s... (esc to abort)", hostname);
+ renderprogress(0, text);
+
+ ENetSocketSet readset, writeset;
+ if(!enet_socket_connect(sock, &address)) for(int starttime = SDL_GetTicks(), timeout = 0; timeout <= CONNLIMIT;)
+ {
+ ENET_SOCKETSET_EMPTY(readset);
+ ENET_SOCKETSET_EMPTY(writeset);
+ ENET_SOCKETSET_ADD(readset, sock);
+ ENET_SOCKETSET_ADD(writeset, sock);
+ int result = enet_socketset_select(sock, &readset, &writeset, 250);
+ if(result < 0) break;
+ else if(result > 0)
+ {
+ if(ENET_SOCKETSET_CHECK(readset, sock) || ENET_SOCKETSET_CHECK(writeset, sock))
+ {
+ int error = 0;
+ if(enet_socket_get_option(sock, ENET_SOCKOPT_ERROR, &error) < 0 || error) break;
+ return 0;
+ }
+ }
+ timeout = SDL_GetTicks() - starttime;
+ renderprogress(min(float(timeout)/CONNLIMIT, 1.0f), text);
+ if(interceptkey(SDLK_ESCAPE)) break;
+ }
+
+ return -1;
}
struct pingattempts
{
- enum { MAXATTEMPTS = 2 };
+ enum { MAXATTEMPTS = 2 };
- int offset, attempts[MAXATTEMPTS];
+ int offset, attempts[MAXATTEMPTS];
- pingattempts() : offset(0) { clearattempts(); }
+ pingattempts() : offset(0) { clearattempts(); }
- void clearattempts() { memset(attempts, 0, sizeof(attempts)); }
+ void clearattempts() { memset(attempts, 0, sizeof(attempts)); }
- void setoffset() { offset = 1 + rnd(0xFFFFFF); }
+ void setoffset() { offset = 1 + rnd(0xFFFFFF); }
- int encodeping(int millis)
- {
- millis += offset;
- return millis ? millis : 1;
- }
+ int encodeping(int millis)
+ {
+ millis += offset;
+ return millis ? millis : 1;
+ }
- int decodeping(int val)
- {
- return val - offset;
- }
+ int decodeping(int val)
+ {
+ return val - offset;
+ }
- int addattempt(int millis)
- {
- int val = encodeping(millis);
- loopk(MAXATTEMPTS-1) attempts[k+1] = attempts[k];
- attempts[0] = val;
- return val;
- }
+ int addattempt(int millis)
+ {
+ int val = encodeping(millis);
+ loopk(MAXATTEMPTS-1) attempts[k+1] = attempts[k];
+ attempts[0] = val;
+ return val;
+ }
- bool checkattempt(int val, bool del = true)
- {
- if(val) loopk(MAXATTEMPTS) if(attempts[k] == val)
- {
- if(del) attempts[k] = 0;
- return true;
- }
- return false;
- }
+ bool checkattempt(int val, bool del = true)
+ {
+ if(val) loopk(MAXATTEMPTS) if(attempts[k] == val)
+ {
+ if(del) attempts[k] = 0;
+ return true;
+ }
+ return false;
+ }
};
@@ -263,95 +263,95 @@ enum { UNRESOLVED = 0, RESOLVING, RESOLVED };
struct serverinfo : pingattempts
{
- enum
- {
- WAITING = INT_MAX,
-
- MAXPINGS = 3
- };
-
- string name, map, sdesc;
- int port, numplayers, resolved, ping, lastping, nextping;
- int pings[MAXPINGS];
- vector<int> attr;
- ENetAddress address;
- bool keep;
- const char *password;
-
- serverinfo()
- : port(-1), numplayers(0), resolved(UNRESOLVED), keep(false), password(NULL)
- {
- name[0] = map[0] = sdesc[0] = '\0';
- clearpings();
- setoffset();
- }
-
- ~serverinfo()
- {
- DELETEA(password);
- }
-
- void clearpings()
- {
- ping = WAITING;
- loopk(MAXPINGS) pings[k] = WAITING;
- nextping = 0;
- lastping = -1;
- clearattempts();
- }
-
- void cleanup()
- {
- clearpings();
- attr.setsize(0);
- numplayers = 0;
- }
-
- void reset()
- {
- lastping = -1;
- }
-
- void checkdecay(int decay)
- {
- if(lastping >= 0 && totalmillis - lastping >= decay)
- cleanup();
- if(lastping < 0) lastping = totalmillis;
- }
-
- void calcping()
- {
- int numpings = 0, totalpings = 0;
- loopk(MAXPINGS) if(pings[k] != WAITING) { totalpings += pings[k]; numpings++; }
- ping = numpings ? totalpings/numpings : WAITING;
- }
-
- void addping(int rtt, int millis)
- {
- if(millis >= lastping) lastping = -1;
- pings[nextping] = rtt;
- nextping = (nextping+1)%MAXPINGS;
- calcping();
- }
-
- static bool compare(serverinfo *a, serverinfo *b)
- {
- bool ac = (a->attr.length() != 0) && (a->attr[0]==PROTOCOL_VERSION);
- bool bc = (b->attr.length() != 0) && (b->attr[0]==PROTOCOL_VERSION);
- if(ac > bc) return true;
- if(bc > ac) return false;
- if(a->keep > b->keep) return true;
- if(a->keep < b->keep) return false;
- if(a->numplayers < b->numplayers) return false;
- if(a->numplayers > b->numplayers) return true;
- if(a->ping > b->ping) return false;
- if(a->ping < b->ping) return true;
- int cmp = strcmp(a->name, b->name);
- if(cmp != 0) return cmp < 0;
- if(a->port < b->port) return true;
- if(a->port > b->port) return false;
- return false;
- }
+ enum
+ {
+ WAITING = INT_MAX,
+
+ MAXPINGS = 3
+ };
+
+ string name, map, sdesc;
+ int port, numplayers, resolved, ping, lastping, nextping;
+ int pings[MAXPINGS];
+ vector<int> attr;
+ ENetAddress address;
+ bool keep;
+ const char *password;
+
+ serverinfo()
+ : port(-1), numplayers(0), resolved(UNRESOLVED), keep(false), password(NULL)
+ {
+ name[0] = map[0] = sdesc[0] = '\0';
+ clearpings();
+ setoffset();
+ }
+
+ ~serverinfo()
+ {
+ DELETEA(password);
+ }
+
+ void clearpings()
+ {
+ ping = WAITING;
+ loopk(MAXPINGS) pings[k] = WAITING;
+ nextping = 0;
+ lastping = -1;
+ clearattempts();
+ }
+
+ void cleanup()
+ {
+ clearpings();
+ attr.setsize(0);
+ numplayers = 0;
+ }
+
+ void reset()
+ {
+ lastping = -1;
+ }
+
+ void checkdecay(int decay)
+ {
+ if(lastping >= 0 && totalmillis - lastping >= decay)
+ cleanup();
+ if(lastping < 0) lastping = totalmillis;
+ }
+
+ void calcping()
+ {
+ int numpings = 0, totalpings = 0;
+ loopk(MAXPINGS) if(pings[k] != WAITING) { totalpings += pings[k]; numpings++; }
+ ping = numpings ? totalpings/numpings : WAITING;
+ }
+
+ void addping(int rtt, int millis)
+ {
+ if(millis >= lastping) lastping = -1;
+ pings[nextping] = rtt;
+ nextping = (nextping+1)%MAXPINGS;
+ calcping();
+ }
+
+ static bool compare(serverinfo *a, serverinfo *b)
+ {
+ bool ac = (a->attr.length() != 0) && (a->attr[0]==PROTOCOL_VERSION);
+ bool bc = (b->attr.length() != 0) && (b->attr[0]==PROTOCOL_VERSION);
+ if(ac > bc) return true;
+ if(bc > ac) return false;
+ if(a->keep > b->keep) return true;
+ if(a->keep < b->keep) return false;
+ if(a->numplayers < b->numplayers) return false;
+ if(a->numplayers > b->numplayers) return true;
+ if(a->ping > b->ping) return false;
+ if(a->ping < b->ping) return true;
+ int cmp = strcmp(a->name, b->name);
+ if(cmp != 0) return cmp < 0;
+ if(a->port < b->port) return true;
+ if(a->port > b->port) return false;
+ return false;
+ }
};
vector<serverinfo *> servers;
@@ -360,44 +360,44 @@ int lastinfo = 0;
static serverinfo *newserver(const char *name, int port, uint ip = ENET_HOST_ANY)
{
- serverinfo *si = new serverinfo;
- si->address.host = ip;
- si->address.port = server::serverinfoport(port);
- if(ip!=ENET_HOST_ANY) si->resolved = RESOLVED;
+ serverinfo *si = new serverinfo;
+ si->address.host = ip;
+ si->address.port = server::serverinfoport(port);
+ if(ip!=ENET_HOST_ANY) si->resolved = RESOLVED;
- si->port = port;
- if(name) copystring(si->name, name);
- else if(ip==ENET_HOST_ANY || enet_address_get_host_ip(&si->address, si->name, sizeof(si->name)) < 0)
- {
- delete si;
- return NULL;
+ si->port = port;
+ if(name) copystring(si->name, name);
+ else if(ip==ENET_HOST_ANY || enet_address_get_host_ip(&si->address, si->name, sizeof(si->name)) < 0)
+ {
+ delete si;
+ return NULL;
- }
+ }
- servers.add(si);
+ servers.add(si);
- return si;
+ return si;
}
void addserver(const char *name, int port, const char *password, bool keep)
{
- if(port <= 0) port = server::serverport();
- loopv(servers)
- {
- serverinfo *s = servers[i];
- if(strcmp(s->name, name) || s->port != port) continue;
- if(password && (!s->password || strcmp(s->password, password)))
- {
- DELETEA(s->password);
- s->password = newstring(password);
- }
- if(keep && !s->keep) s->keep = true;
- return;
- }
- serverinfo *s = newserver(name, port);
- if(!s) return;
- if(password) s->password = newstring(password);
- s->keep = keep;
+ if(port <= 0) port = server::serverport();
+ loopv(servers)
+ {
+ serverinfo *s = servers[i];
+ if(strcmp(s->name, name) || s->port != port) continue;
+ if(password && (!s->password || strcmp(s->password, password)))
+ {
+ DELETEA(s->password);
+ s->password = newstring(password);
+ }
+ if(keep && !s->keep) s->keep = true;
+ return;
+ }
+ serverinfo *s = newserver(name, port);
+ if(!s) return;
+ if(password) s->password = newstring(password);
+ s->keep = keep;
}
VARP(searchlan, 0, 0, 1);
@@ -409,134 +409,134 @@ pingattempts lanpings;
template<size_t N> static inline void buildping(ENetBuffer &buf, uchar (&ping)[N], pingattempts &a)
{
- ucharbuf p(ping, N);
- putint(p, a.addattempt(totalmillis));
- buf.data = ping;
- buf.dataLength = p.length();
+ ucharbuf p(ping, N);
+ putint(p, a.addattempt(totalmillis));
+ buf.data = ping;
+ buf.dataLength = p.length();
}
void pingservers()
{
- if(pingsock == ENET_SOCKET_NULL)
- {
- pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
- if(pingsock == ENET_SOCKET_NULL)
- {
- lastinfo = totalmillis;
- return;
- }
- enet_socket_set_option(pingsock, ENET_SOCKOPT_NONBLOCK, 1);
- enet_socket_set_option(pingsock, ENET_SOCKOPT_BROADCAST, 1);
-
- lanpings.setoffset();
- }
-
- ENetBuffer buf;
- uchar ping[MAXTRANS];
-
- static int lastping = 0;
- if(lastping >= servers.length()) lastping = 0;
- loopi(maxservpings ? min(servers.length(), maxservpings) : servers.length())
- {
- serverinfo &si = *servers[lastping];
- if(++lastping >= servers.length()) lastping = 0;
- if(si.address.host == ENET_HOST_ANY) continue;
- buildping(buf, ping, si);
- enet_socket_send(pingsock, &si.address, &buf, 1);
-
- si.checkdecay(servpingdecay);
- }
- if(searchlan)
- {
- ENetAddress address;
- address.host = ENET_HOST_BROADCAST;
- address.port = server::laninfoport();
- buildping(buf, ping, lanpings);
- enet_socket_send(pingsock, &address, &buf, 1);
- }
- lastinfo = totalmillis;
+ if(pingsock == ENET_SOCKET_NULL)
+ {
+ pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
+ if(pingsock == ENET_SOCKET_NULL)
+ {
+ lastinfo = totalmillis;
+ return;
+ }
+ enet_socket_set_option(pingsock, ENET_SOCKOPT_NONBLOCK, 1);
+ enet_socket_set_option(pingsock, ENET_SOCKOPT_BROADCAST, 1);
+
+ lanpings.setoffset();
+ }
+
+ ENetBuffer buf;
+ uchar ping[MAXTRANS];
+
+ static int lastping = 0;
+ if(lastping >= servers.length()) lastping = 0;
+ loopi(maxservpings ? min(servers.length(), maxservpings) : servers.length())
+ {
+ serverinfo &si = *servers[lastping];
+ if(++lastping >= servers.length()) lastping = 0;
+ if(si.address.host == ENET_HOST_ANY) continue;
+ buildping(buf, ping, si);
+ enet_socket_send(pingsock, &si.address, &buf, 1);
+
+ si.checkdecay(servpingdecay);
+ }
+ if(searchlan)
+ {
+ ENetAddress address;
+ address.host = ENET_HOST_BROADCAST;
+ address.port = server::laninfoport();
+ buildping(buf, ping, lanpings);
+ enet_socket_send(pingsock, &address, &buf, 1);
+ }
+ lastinfo = totalmillis;
}
void checkresolver()
{
- int resolving = 0;
- loopv(servers)
- {
- serverinfo &si = *servers[i];
- if(si.resolved == RESOLVED) continue;
- if(si.address.host == ENET_HOST_ANY)
- {
- if(si.resolved == UNRESOLVED) { si.resolved = RESOLVING; resolverquery(si.name); }
- resolving++;
- }
- }
- if(!resolving) return;
-
- const char *name = NULL;
- for(;;)
- {
- ENetAddress addr = { ENET_HOST_ANY, ENET_PORT_ANY };
- if(!resolvercheck(&name, &addr)) break;
- loopv(servers)
- {
- serverinfo &si = *servers[i];
- if(name == si.name)
- {
- si.resolved = RESOLVED;
- si.address.host = addr.host;
- break;
- }
- }
- }
+ int resolving = 0;
+ loopv(servers)
+ {
+ serverinfo &si = *servers[i];
+ if(si.resolved == RESOLVED) continue;
+ if(si.address.host == ENET_HOST_ANY)
+ {
+ if(si.resolved == UNRESOLVED) { si.resolved = RESOLVING; resolverquery(si.name); }
+ resolving++;
+ }
+ }
+ if(!resolving) return;
+
+ const char *name = NULL;
+ for(;;)
+ {
+ ENetAddress addr = { ENET_HOST_ANY, ENET_PORT_ANY };
+ if(!resolvercheck(&name, &addr)) break;
+ loopv(servers)
+ {
+ serverinfo &si = *servers[i];
+ if(name == si.name)
+ {
+ si.resolved = RESOLVED;
+ si.address.host = addr.host;
+ break;
+ }
+ }
+ }
}
static int lastreset = 0;
void checkpings()
{
- if(pingsock==ENET_SOCKET_NULL) return;
- enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
- ENetBuffer buf;
- ENetAddress addr;
- uchar ping[MAXTRANS];
- char text[MAXTRANS];
- buf.data = ping;
- buf.dataLength = sizeof(ping);
- while(enet_socket_wait(pingsock, &events, 0) >= 0 && events)
- {
- int len = enet_socket_receive(pingsock, &addr, &buf, 1);
- if(len <= 0) return;
- ucharbuf p(ping, len);
- int millis = getint(p);
- serverinfo *si = NULL;
- loopv(servers) if(addr.host == servers[i]->address.host && addr.port == servers[i]->address.port) { si = servers[i]; break; }
- if(si)
- {
- if(!si->checkattempt(millis)) continue;
- millis = si->decodeping(millis);
- }
- else if(!searchlan || !lanpings.checkattempt(millis, false)) continue;
- else
- {
- si = newserver(NULL, server::serverport(addr.port), addr.host);
- millis = lanpings.decodeping(millis);
- }
- int rtt = clamp(totalmillis - millis, 0, min(servpingdecay, totalmillis));
- if(millis >= lastreset && rtt < servpingdecay) si->addping(rtt, millis);
- si->numplayers = getint(p);
- int numattr = getint(p);
- si->attr.setsize(0);
- loopj(numattr) { int attr = getint(p); if(p.overread()) break; si->attr.add(attr); }
- getstring(text, p);
- filtertext(si->map, text, false);
- getstring(text, p);
- filtertext(si->sdesc, text, true, true);
- }
+ if(pingsock==ENET_SOCKET_NULL) return;
+ enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
+ ENetBuffer buf;
+ ENetAddress addr;
+ uchar ping[MAXTRANS];
+ char text[MAXTRANS];
+ buf.data = ping;
+ buf.dataLength = sizeof(ping);
+ while(enet_socket_wait(pingsock, &events, 0) >= 0 && events)
+ {
+ int len = enet_socket_receive(pingsock, &addr, &buf, 1);
+ if(len <= 0) return;
+ ucharbuf p(ping, len);
+ int millis = getint(p);
+ serverinfo *si = NULL;
+ loopv(servers) if(addr.host == servers[i]->address.host && addr.port == servers[i]->address.port) { si = servers[i]; break; }
+ if(si)
+ {
+ if(!si->checkattempt(millis)) continue;
+ millis = si->decodeping(millis);
+ }
+ else if(!searchlan || !lanpings.checkattempt(millis, false)) continue;
+ else
+ {
+ si = newserver(NULL, server::serverport(addr.port), addr.host);
+ millis = lanpings.decodeping(millis);
+ }
+ int rtt = clamp(totalmillis - millis, 0, min(servpingdecay, totalmillis));
+ if(millis >= lastreset && rtt < servpingdecay) si->addping(rtt, millis);
+ si->numplayers = getint(p);
+ int numattr = getint(p);
+ si->attr.setsize(0);
+ loopj(numattr) { int attr = getint(p); if(p.overread()) break; si->attr.add(attr); }
+ getstring(text, p);
+ filtertext(si->map, text, false);
+ getstring(text, p);
+ filtertext(si->sdesc, text, true, true);
+ }
}
void sortservers()
{
- servers.sort(serverinfo::compare);
+ servers.sort(serverinfo::compare);
}
COMMAND(sortservers, "");
@@ -545,173 +545,173 @@ VARP(autoupdateservers, 0, 1, 1);
void refreshservers()
{
- static int lastrefresh = 0;
- if(lastrefresh==totalmillis) return;
- if(totalmillis - lastrefresh > 1000)
- {
- loopv(servers) servers[i]->reset();
- lastreset = totalmillis;
- }
- lastrefresh = totalmillis;
-
- checkresolver();
- checkpings();
- if(totalmillis - lastinfo >= servpingrate/(maxservpings ? max(1, (servers.length() + maxservpings - 1) / maxservpings) : 1)) pingservers();
- if(autosortservers) sortservers();
+ static int lastrefresh = 0;
+ if(lastrefresh==totalmillis) return;
+ if(totalmillis - lastrefresh > 1000)
+ {
+ loopv(servers) servers[i]->reset();
+ lastreset = totalmillis;
+ }
+ lastrefresh = totalmillis;
+
+ checkresolver();
+ checkpings();
+ if(totalmillis - lastinfo >= servpingrate/(maxservpings ? max(1, (servers.length() + maxservpings - 1) / maxservpings) : 1)) pingservers();
+ if(autosortservers) sortservers();
}
serverinfo *selectedserver = NULL;
const char *showservers(g3d_gui *cgui, uint *header, int pagemin, int pagemax)
{
- refreshservers();
- if(servers.empty())
- {
- if(header) execute(header);
- return NULL;
- }
- serverinfo *sc = NULL;
- for(int start = 0; start < servers.length();)
- {
- if(start > 0) cgui->tab();
- if(header) execute(header);
- int end = servers.length();
- cgui->pushlist();
- loopi(10)
- {
- if(!game::serverinfostartcolumn(cgui, i)) break;
- for(int j = start; j < end; j++)
- {
- if(!i && j+1 - start >= pagemin && (j+1 - start >= pagemax || cgui->shouldtab())) { end = j; break; }
- serverinfo &si = *servers[j];
- const char *sdesc = si.sdesc;
- if(si.address.host == ENET_HOST_ANY) sdesc = "[unknown host]";
- else if(si.ping == serverinfo::WAITING) sdesc = "[waiting for response]";
- if(game::serverinfoentry(cgui, i, si.name, si.port, sdesc, si.map, sdesc == si.sdesc ? si.ping : -1, si.attr, si.numplayers))
- sc = &si;
- }
- game::serverinfoendcolumn(cgui, i);
- }
- cgui->poplist();
- start = end;
- }
- if(selectedserver || !sc) return NULL;
- selectedserver = sc;
- return "connectselected";
+ refreshservers();
+ if(servers.empty())
+ {
+ if(header) execute(header);
+ return NULL;
+ }
+ serverinfo *sc = NULL;
+ for(int start = 0; start < servers.length();)
+ {
+ if(start > 0) cgui->tab();
+ if(header) execute(header);
+ int end = servers.length();
+ cgui->pushlist();
+ loopi(10)
+ {
+ if(!game::serverinfostartcolumn(cgui, i)) break;
+ for(int j = start; j < end; j++)
+ {
+ if(!i && j+1 - start >= pagemin && (j+1 - start >= pagemax || cgui->shouldtab())) { end = j; break; }
+ serverinfo &si = *servers[j];
+ const char *sdesc = si.sdesc;
+ if(si.address.host == ENET_HOST_ANY) sdesc = "[unknown host]";
+ else if(si.ping == serverinfo::WAITING) sdesc = "[waiting for response]";
+ if(game::serverinfoentry(cgui, i, si.name, si.port, sdesc, si.map, sdesc == si.sdesc ? si.ping : -1, si.attr, si.numplayers))
+ sc = &si;
+ }
+ game::serverinfoendcolumn(cgui, i);
+ }
+ cgui->poplist();
+ start = end;
+ }
+ if(selectedserver || !sc) return NULL;
+ selectedserver = sc;
+ return "connectselected";
}
void connectselected()
{
- if(!selectedserver) return;
- connectserv(selectedserver->name, selectedserver->port, selectedserver->password);
- selectedserver = NULL;
+ if(!selectedserver) return;
+ connectserv(selectedserver->name, selectedserver->port, selectedserver->password);
+ selectedserver = NULL;
}
COMMAND(connectselected, "");
void clearservers(bool full = false)
{
- resolverclear();
- if(full) servers.deletecontents();
- else loopvrev(servers) if(!servers[i]->keep) delete servers.remove(i);
- selectedserver = NULL;
+ resolverclear();
+ if(full) servers.deletecontents();
+ else loopvrev(servers) if(!servers[i]->keep) delete servers.remove(i);
+ selectedserver = NULL;
}
#define RETRIEVELIMIT 20000
void retrieveservers(vector<char> &data)
{
- ENetSocket sock = connectmaster(true);
- if(sock == ENET_SOCKET_NULL) return;
-
- extern char *mastername;
- defformatstring(text, "retrieving servers from %s... (esc to abort)", mastername);
- renderprogress(0, text);
-
- int starttime = SDL_GetTicks(), timeout = 0;
- const char *req = "list\n";
- int reqlen = strlen(req);
- ENetBuffer buf;
- while(reqlen > 0)
- {
- enet_uint32 events = ENET_SOCKET_WAIT_SEND;
- if(enet_socket_wait(sock, &events, 250) >= 0 && events)
- {
- buf.data = (void *)req;
- buf.dataLength = reqlen;
- int sent = enet_socket_send(sock, NULL, &buf, 1);
- if(sent < 0) break;
- req += sent;
- reqlen -= sent;
- if(reqlen <= 0) break;
- }
- timeout = SDL_GetTicks() - starttime;
- renderprogress(min(float(timeout)/RETRIEVELIMIT, 1.0f), text);
- if(interceptkey(SDLK_ESCAPE)) timeout = RETRIEVELIMIT + 1;
- if(timeout > RETRIEVELIMIT) break;
- }
-
- if(reqlen <= 0) for(;;)
- {
- enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
- if(enet_socket_wait(sock, &events, 250) >= 0 && events)
- {
- if(data.length() >= data.capacity()) data.reserve(4096);
- buf.data = data.getbuf() + data.length();
- buf.dataLength = data.capacity() - data.length();
- int recv = enet_socket_receive(sock, NULL, &buf, 1);
- if(recv <= 0) break;
- data.advance(recv);
- }
- timeout = SDL_GetTicks() - starttime;
- renderprogress(min(float(timeout)/RETRIEVELIMIT, 1.0f), text);
- if(interceptkey(SDLK_ESCAPE)) timeout = RETRIEVELIMIT + 1;
- if(timeout > RETRIEVELIMIT) break;
- }
-
- if(data.length()) data.add('\0');
- enet_socket_destroy(sock);
+ ENetSocket sock = connectmaster(true);
+ if(sock == ENET_SOCKET_NULL) return;
+
+ extern char *mastername;
+ defformatstring(text, "retrieving servers from %s... (esc to abort)", mastername);
+ renderprogress(0, text);
+
+ int starttime = SDL_GetTicks(), timeout = 0;
+ const char *req = "list\n";
+ int reqlen = strlen(req);
+ ENetBuffer buf;
+ while(reqlen > 0)
+ {
+ enet_uint32 events = ENET_SOCKET_WAIT_SEND;
+ if(enet_socket_wait(sock, &events, 250) >= 0 && events)
+ {
+ buf.data = (void *)req;
+ buf.dataLength = reqlen;
+ int sent = enet_socket_send(sock, NULL, &buf, 1);
+ if(sent < 0) break;
+ req += sent;
+ reqlen -= sent;
+ if(reqlen <= 0) break;
+ }
+ timeout = SDL_GetTicks() - starttime;
+ renderprogress(min(float(timeout)/RETRIEVELIMIT, 1.0f), text);
+ if(interceptkey(SDLK_ESCAPE)) timeout = RETRIEVELIMIT + 1;
+ if(timeout > RETRIEVELIMIT) break;
+ }
+
+ if(reqlen <= 0) for(;;)
+ {
+ enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
+ if(enet_socket_wait(sock, &events, 250) >= 0 && events)
+ {
+ if(data.length() >= data.capacity()) data.reserve(4096);
+ buf.data = data.getbuf() + data.length();
+ buf.dataLength = data.capacity() - data.length();
+ int recv = enet_socket_receive(sock, NULL, &buf, 1);
+ if(recv <= 0) break;
+ data.advance(recv);
+ }
+ timeout = SDL_GetTicks() - starttime;
+ renderprogress(min(float(timeout)/RETRIEVELIMIT, 1.0f), text);
+ if(interceptkey(SDLK_ESCAPE)) timeout = RETRIEVELIMIT + 1;
+ if(timeout > RETRIEVELIMIT) break;
+ }
+
+ if(data.length()) data.add('\0');
+ enet_socket_destroy(sock);
}
bool updatedservers = false;
void updatefrommaster()
{
- vector<char> data;
- retrieveservers(data);
- if(data.empty()) conoutf(CON_ERROR, "master server not replying");
- else
- {
- clearservers();
- char *line = data.getbuf();
- while(char *end = (char *)memchr(line, '\n', data.length() - (line - data.getbuf())))
- {
- *end = '\0';
-
- const char *args = line;
- while(args < end && !iscubespace(*args)) args++;
- int cmdlen = args - line;
- while(args < end && iscubespace(*args)) args++;
-
- if(matchstring(line, cmdlen, "addserver"))
- {
- string ip;
- int port;
- if(sscanf(args, "%100s %d", ip, &port) == 2) addserver(ip, port);
- }
- else if(matchstring(line, cmdlen, "echo")) conoutf("\f1%s", args);
-
- line = end + 1;
- }
- }
- refreshservers();
- updatedservers = true;
+ vector<char> data;
+ retrieveservers(data);
+ if(data.empty()) conoutf(CON_ERROR, "master server not replying");
+ else
+ {
+ clearservers();
+ char *line = data.getbuf();
+ while(char *end = (char *)memchr(line, '\n', data.length() - (line - data.getbuf())))
+ {
+ *end = '\0';
+
+ const char *args = line;
+ while(args < end && !iscubespace(*args)) args++;
+ int cmdlen = args - line;
+ while(args < end && iscubespace(*args)) args++;
+
+ if(matchstring(line, cmdlen, "addserver"))
+ {
+ string ip;
+ int port;
+ if(sscanf(args, "%100s %d", ip, &port) == 2) addserver(ip, port);
+ }
+ else if(matchstring(line, cmdlen, "echo")) conoutf("\f1%s", args);
+
+ line = end + 1;
+ }
+ }
+ refreshservers();
+ updatedservers = true;
}
void initservers()
{
- selectedserver = NULL;
- if(autoupdateservers && !updatedservers) updatefrommaster();
+ selectedserver = NULL;
+ if(autoupdateservers && !updatedservers) updatefrommaster();
}
ICOMMAND(addserver, "sis", (const char *name, int *port, const char *password), addserver(name, *port, password[0] ? password : NULL));
@@ -722,32 +722,32 @@ COMMAND(initservers, "");
void writeservercfg()
{
- if(!game::savedservers()) return;
- stream *f = openutf8file(path(game::savedservers(), true), "w");
- if(!f) return;
- int kept = 0;
- loopv(servers)
- {
- serverinfo *s = servers[i];
- if(s->keep)
- {
- if(!kept) f->printf("// servers that should never be cleared from the server list\n\n");
- if(s->password) f->printf("keepserver %s %d %s\n", escapeid(s->name), s->port, escapestring(s->password));
- else f->printf("keepserver %s %d\n", escapeid(s->name), s->port);
- kept++;
- }
- }
- if(kept) f->printf("\n");
- f->printf("// servers connected to are added here automatically\n\n");
- loopv(servers)
- {
- serverinfo *s = servers[i];
- if(!s->keep)
- {
- if(s->password) f->printf("addserver %s %d %s\n", escapeid(s->name), s->port, escapestring(s->password));
- else f->printf("addserver %s %d\n", escapeid(s->name), s->port);
- }
- }
- delete f;
+ if(!game::savedservers()) return;
+ stream *f = openutf8file(path(game::savedservers(), true), "w");
+ if(!f) return;
+ int kept = 0;
+ loopv(servers)
+ {
+ serverinfo *s = servers[i];
+ if(s->keep)
+ {
+ if(!kept) f->printf("// servers that should never be cleared from the server list\n\n");
+ if(s->password) f->printf("keepserver %s %d %s\n", escapeid(s->name), s->port, escapestring(s->password));
+ else f->printf("keepserver %s %d\n", escapeid(s->name), s->port);
+ kept++;
+ }
+ }
+ if(kept) f->printf("\n");
+ f->printf("// servers connected to are added here automatically\n\n");
+ loopv(servers)
+ {
+ serverinfo *s = servers[i];
+ if(!s->keep)
+ {
+ if(s->password) f->printf("addserver %s %d %s\n", escapeid(s->name), s->port, escapestring(s->password));
+ else f->printf("addserver %s %d\n", escapeid(s->name), s->port);
+ }
+ }
+ delete f;
}
diff --git a/src/engine/shader.cpp b/src/engine/shader.cpp
index da17d4f..f7a740e 100644
--- a/src/engine/shader.cpp
+++ b/src/engine/shader.cpp
@@ -17,1206 +17,1201 @@ VAR(reservevpparams, 1, 16, 0);
VAR(maxvsuniforms, 1, 0, 0);
VAR(maxfsuniforms, 1, 0, 0);
VAR(maxvaryings, 1, 0, 0);
-VAR(dbgshader, 0, 0, 2);
void loadshaders()
{
- standardshaders = true;
- execfile("data/glsl.cfg");
- standardshaders = false;
+ standardshaders = true;
+ execfile("data/glsl.cfg");
+ standardshaders = false;
- nullshader = lookupshaderbyname("null");
- hudshader = lookupshaderbyname("hud");
- hudnotextureshader = lookupshaderbyname("hudnotexture");
- stdworldshader = lookupshaderbyname("stdworld");
- if(!nullshader || !hudshader || !hudnotextureshader || !stdworldshader) fatal("cannot find shader definitions");
+ nullshader = lookupshaderbyname("null");
+ hudshader = lookupshaderbyname("hud");
+ hudnotextureshader = lookupshaderbyname("hudnotexture");
+ stdworldshader = lookupshaderbyname("stdworld");
+ if(!nullshader || !hudshader || !hudnotextureshader || !stdworldshader) fatal("cannot find shader definitions");
- dummyslot.shader = stdworldshader;
+ dummyslot.shader = stdworldshader;
- textureshader = lookupshaderbyname("texture");
- notextureshader = lookupshaderbyname("notexture");
- nocolorshader = lookupshaderbyname("nocolor");
- foggedshader = lookupshaderbyname("fogged");
- foggednotextureshader = lookupshaderbyname("foggednotexture");
-
- nullshader->set();
+ textureshader = lookupshaderbyname("texture");
+ notextureshader = lookupshaderbyname("notexture");
+ nocolorshader = lookupshaderbyname("nocolor");
+ foggedshader = lookupshaderbyname("fogged");
+ foggednotextureshader = lookupshaderbyname("foggednotexture");
- loadedshaders = true;
+ nullshader->set();
+
+ loadedshaders = true;
}
-Shader *lookupshaderbyname(const char *name)
-{
- Shader *s = shaders.access(name);
- return s && s->loaded() ? s : NULL;
+Shader *lookupshaderbyname(const char *name)
+{
+ Shader *s = shaders.access(name);
+ return s && s->loaded() ? s : NULL;
}
Shader *generateshader(const char *name, const char *fmt, ...)
{
- if(!loadedshaders) return NULL;
- Shader *s = name ? lookupshaderbyname(name) : NULL;
- if(!s)
- {
- defvformatstring(cmd, fmt, fmt);
- bool wasstandard = standardshaders;
- standardshaders = true;
- execute(cmd);
- standardshaders = wasstandard;
- s = name ? lookupshaderbyname(name) : NULL;
- if(!s) s = nullshader;
- }
- return s;
+ if(!loadedshaders) return NULL;
+ Shader *s = name ? lookupshaderbyname(name) : NULL;
+ if(!s)
+ {
+ defvformatstring(cmd, fmt, fmt);
+ bool wasstandard = standardshaders;
+ standardshaders = true;
+ execute(cmd);
+ standardshaders = wasstandard;
+ s = name ? lookupshaderbyname(name) : NULL;
+ if(!s) s = nullshader;
+ }
+ return s;
}
static void showglslinfo(GLenum type, GLuint obj, const char *name, const char **parts = NULL, int numparts = 0)
{
- GLint length = 0;
- if(type) glGetShaderiv_(obj, GL_INFO_LOG_LENGTH, &length);
- else glGetProgramiv_(obj, GL_INFO_LOG_LENGTH, &length);
- if(length > 1)
- {
- conoutf(CON_ERROR, "GLSL ERROR (%s:%s)", type == GL_VERTEX_SHADER ? "VS" : (type == GL_FRAGMENT_SHADER ? "FS" : "PROG"), name);
- FILE *l = getlogfile();
- if(l)
- {
- GLchar *log = new GLchar[length];
- if(type) glGetShaderInfoLog_(obj, length, &length, log);
- else glGetProgramInfoLog_(obj, length, &length, log);
- fprintf(l, "%s\n", log);
- bool partlines = log[0] != '0';
- int line = 0;
- loopi(numparts)
- {
- const char *part = parts[i];
- int startline = line;
- while(*part)
- {
- const char *next = strchr(part, '\n');
- if(++line > 1000) goto done;
- if(partlines) fprintf(l, "%d(%d): ", i, line - startline); else fprintf(l, "%d: ", line);
- fwrite(part, 1, next ? next - part + 1 : strlen(part), l);
- if(!next) { fputc('\n', l); break; }
- part = next + 1;
- }
- }
- done:
- delete[] log;
- }
- }
+ GLint length = 0;
+ if(type) glGetShaderiv_(obj, GL_INFO_LOG_LENGTH, &length);
+ else glGetProgramiv_(obj, GL_INFO_LOG_LENGTH, &length);
+ if(length > 1)
+ {
+ conoutf(CON_ERROR, "GLSL ERROR (%s:%s)", type == GL_VERTEX_SHADER ? "VS" : (type == GL_FRAGMENT_SHADER ? "FS" : "PROG"), name);
+ FILE *l = getlogfile();
+ if(l)
+ {
+ GLchar *log = new GLchar[length];
+ if(type) glGetShaderInfoLog_(obj, length, &length, log);
+ else glGetProgramInfoLog_(obj, length, &length, log);
+ fprintf(l, "%s\n", log);
+ bool partlines = log[0] != '0';
+ int line = 0;
+ loopi(numparts)
+ {
+ const char *part = parts[i];
+ int startline = line;
+ while(*part)
+ {
+ const char *next = strchr(part, '\n');
+ if(++line > 1000) goto done;
+ if(partlines) fprintf(l, "%d(%d): ", i, line - startline); else fprintf(l, "%d: ", line);
+ fwrite(part, 1, next ? next - part + 1 : strlen(part), l);
+ if(!next) { fputc('\n', l); break; }
+ part = next + 1;
+ }
+ }
+ done:
+ delete[] log;
+ }
+ }
}
-static void compileglslshader(GLenum type, GLuint &obj, const char *def, const char *name, bool msg = true)
-{
- const char *source = def + strspn(def, " \t\r\n");
- const char *parts[16];
- int numparts = 0;
- static const struct { int version; const char * const header; } glslversions[] =
- {
- { 330, "#version 330\n" },
- { 150, "#version 150\n" },
- { 130, "#version 130\n" },
- { 120, "#version 120\n" }
- };
- loopi(sizeof(glslversions)/sizeof(glslversions[0])) if(glslversion >= glslversions[i].version)
- {
- parts[numparts++] = glslversions[i].header;
- break;
- }
- if(glslversion >= 130)
- {
- if(type == GL_VERTEX_SHADER) parts[numparts++] =
- "#define attribute in\n"
- "#define varying out\n";
- else if(type == GL_FRAGMENT_SHADER)
- {
- parts[numparts++] = "#define varying in\n";
- if(glslversion < 150) parts[numparts++] = "precision highp float;\n";
- if(glversion >= 300) parts[numparts++] =
- "out vec4 cube2_FragColor;\n"
- "#define gl_FragColor cube2_FragColor\n";
- }
- parts[numparts++] =
- "#define texture2D(sampler, coords) texture(sampler, coords)\n"
- "#define texture2DProj(sampler, coords) textureProj(sampler, coords)\n"
- "#define textureCube(sampler, coords) texture(sampler, coords)\n";
- }
- parts[numparts++] = source;
-
- obj = glCreateShader_(type);
- glShaderSource_(obj, numparts, (const GLchar **)parts, NULL);
- glCompileShader_(obj);
- GLint success;
- glGetShaderiv_(obj, GL_COMPILE_STATUS, &success);
- if(!success)
- {
- if(msg) showglslinfo(type, obj, name, parts, numparts);
- glDeleteShader_(obj);
- obj = 0;
- }
- else if(dbgshader > 1 && msg) showglslinfo(type, obj, name, parts, numparts);
-}
-
-VAR(dbgubo, 0, 0, 1);
+static void compileglslshader(GLenum type, GLuint &obj, const char *def, const char *name, bool msg = true)
+{
+ const char *source = def + strspn(def, " \t\r\n");
+ const char *parts[16];
+ int numparts = 0;
+ static const struct { int version; const char * const header; } glslversions[] =
+ {
+ { 330, "#version 330\n" },
+ { 150, "#version 150\n" },
+ { 130, "#version 130\n" },
+ { 120, "#version 120\n" }
+ };
+ loopi(sizeof(glslversions)/sizeof(glslversions[0])) if(glslversion >= glslversions[i].version)
+ {
+ parts[numparts++] = glslversions[i].header;
+ break;
+ }
+ if(glslversion >= 130)
+ {
+ if(type == GL_VERTEX_SHADER) parts[numparts++] =
+ "#define attribute in\n"
+ "#define varying out\n";
+ else if(type == GL_FRAGMENT_SHADER)
+ {
+ parts[numparts++] = "#define varying in\n";
+ if(glslversion < 150) parts[numparts++] = "precision highp float;\n";
+ if(glversion >= 300) parts[numparts++] =
+ "out vec4 cube2_FragColor;\n"
+ "#define gl_FragColor cube2_FragColor\n";
+ }
+ parts[numparts++] =
+ "#define texture2D(sampler, coords) texture(sampler, coords)\n"
+ "#define texture2DProj(sampler, coords) textureProj(sampler, coords)\n"
+ "#define textureCube(sampler, coords) texture(sampler, coords)\n";
+ }
+ parts[numparts++] = source;
+
+ obj = glCreateShader_(type);
+ glShaderSource_(obj, numparts, (const GLchar **)parts, NULL);
+ glCompileShader_(obj);
+ GLint success;
+ glGetShaderiv_(obj, GL_COMPILE_STATUS, &success);
+ if(!success)
+ {
+ if(msg) showglslinfo(type, obj, name, parts, numparts);
+ glDeleteShader_(obj);
+ obj = 0;
+ }
+}
static void bindglsluniform(Shader &s, UniformLoc &u)
{
- u.loc = glGetUniformLocation_(s.program, u.name);
- if(!u.blockname || !hasUBO) return;
- GLuint bidx = glGetUniformBlockIndex_(s.program, u.blockname);
- GLuint uidx = GL_INVALID_INDEX;
- glGetUniformIndices_(s.program, 1, &u.name, &uidx);
- if(bidx != GL_INVALID_INDEX && uidx != GL_INVALID_INDEX)
- {
- GLint sizeval = 0, offsetval = 0, strideval = 0;
- glGetActiveUniformBlockiv_(s.program, bidx, GL_UNIFORM_BLOCK_DATA_SIZE, &sizeval);
- if(sizeval <= 0) return;
- glGetActiveUniformsiv_(s.program, 1, &uidx, GL_UNIFORM_OFFSET, &offsetval);
- if(u.stride > 0)
- {
- glGetActiveUniformsiv_(s.program, 1, &uidx, GL_UNIFORM_ARRAY_STRIDE, &strideval);
- if(strideval > u.stride) return;
- }
- u.offset = offsetval;
- u.size = sizeval;
- glUniformBlockBinding_(s.program, bidx, u.binding);
- if(dbgubo) conoutf(CON_DEBUG, "UBO: %s:%s:%d, offset: %d, size: %d, stride: %d", u.name, u.blockname, u.binding, offsetval, sizeval, strideval);
- }
+ u.loc = glGetUniformLocation_(s.program, u.name);
+ if(!u.blockname || !hasUBO) return;
+ GLuint bidx = glGetUniformBlockIndex_(s.program, u.blockname);
+ GLuint uidx = GL_INVALID_INDEX;
+ glGetUniformIndices_(s.program, 1, &u.name, &uidx);
+ if(bidx != GL_INVALID_INDEX && uidx != GL_INVALID_INDEX)
+ {
+ GLint sizeval = 0, offsetval = 0, strideval = 0;
+ glGetActiveUniformBlockiv_(s.program, bidx, GL_UNIFORM_BLOCK_DATA_SIZE, &sizeval);
+ if(sizeval <= 0) return;
+ glGetActiveUniformsiv_(s.program, 1, &uidx, GL_UNIFORM_OFFSET, &offsetval);
+ if(u.stride > 0)
+ {
+ glGetActiveUniformsiv_(s.program, 1, &uidx, GL_UNIFORM_ARRAY_STRIDE, &strideval);
+ if(strideval > u.stride) return;
+ }
+ u.offset = offsetval;
+ u.size = sizeval;
+ glUniformBlockBinding_(s.program, bidx, u.binding);
+ }
}
static void linkglslprogram(Shader &s, bool msg = true)
{
- s.program = s.vsobj && s.psobj ? glCreateProgram_() : 0;
- GLint success = 0;
- if(s.program)
- {
- glAttachShader_(s.program, s.vsobj);
- glAttachShader_(s.program, s.psobj);
- uint attribs = 0;
- loopv(s.attriblocs)
- {
- AttribLoc &a = s.attriblocs[i];
- glBindAttribLocation_(s.program, a.loc, a.name);
- attribs |= 1<<a.loc;
- }
- loopi(gle::MAXATTRIBS) if(!(attribs&(1<<i))) glBindAttribLocation_(s.program, i, gle::attribnames[i]);
- if(glversion >= 300)
- {
- glBindFragDataLocation_(s.program, 0, "cube2_FragColor");
- }
- glLinkProgram_(s.program);
- glGetProgramiv_(s.program, GL_LINK_STATUS, &success);
- }
- if(success)
- {
- glUseProgram_(s.program);
- loopi(8)
- {
- static const char * const texnames[8] = { "tex0", "tex1", "tex2", "tex3", "tex4", "tex5", "tex6", "tex7" };
- GLint loc = glGetUniformLocation_(s.program, texnames[i]);
- if(loc != -1) glUniform1i_(loc, i);
- }
- loopv(s.defaultparams)
- {
- SlotShaderParamState &param = s.defaultparams[i];
- param.loc = glGetUniformLocation_(s.program, param.name);
- }
- loopv(s.uniformlocs) bindglsluniform(s, s.uniformlocs[i]);
- glUseProgram_(0);
- }
- else if(s.program)
- {
- if(msg) showglslinfo(GL_FALSE, s.program, s.name);
- glDeleteProgram_(s.program);
- s.program = 0;
- }
+ s.program = s.vsobj && s.psobj ? glCreateProgram_() : 0;
+ GLint success = 0;
+ if(s.program)
+ {
+ glAttachShader_(s.program, s.vsobj);
+ glAttachShader_(s.program, s.psobj);
+ uint attribs = 0;
+ loopv(s.attriblocs)
+ {
+ AttribLoc &a = s.attriblocs[i];
+ glBindAttribLocation_(s.program, a.loc, a.name);
+ attribs |= 1<<a.loc;
+ }
+ loopi(gle::MAXATTRIBS) if(!(attribs&(1<<i))) glBindAttribLocation_(s.program, i, gle::attribnames[i]);
+ if(glversion >= 300)
+ {
+ glBindFragDataLocation_(s.program, 0, "cube2_FragColor");
+ }
+ glLinkProgram_(s.program);
+ glGetProgramiv_(s.program, GL_LINK_STATUS, &success);
+ }
+ if(success)
+ {
+ glUseProgram_(s.program);
+ loopi(8)
+ {
+ static const char * const texnames[8] = { "tex0", "tex1", "tex2", "tex3", "tex4", "tex5", "tex6", "tex7" };
+ GLint loc = glGetUniformLocation_(s.program, texnames[i]);
+ if(loc != -1) glUniform1i_(loc, i);
+ }
+ loopv(s.defaultparams)
+ {
+ SlotShaderParamState &param = s.defaultparams[i];
+ param.loc = glGetUniformLocation_(s.program, param.name);
+ }
+ loopv(s.uniformlocs) bindglsluniform(s, s.uniformlocs[i]);
+ glUseProgram_(0);
+ }
+ else if(s.program)
+ {
+ if(msg) showglslinfo(GL_FALSE, s.program, s.name);
+ glDeleteProgram_(s.program);
+ s.program = 0;
+ }
}
int getlocalparam(const char *name)
{
- return localparams.access(name, int(localparams.numelems));
+ return localparams.access(name, int(localparams.numelems));
}
static int addlocalparam(Shader &s, const char *name, int loc, int size, GLenum format)
{
- int idx = getlocalparam(name);
- if(idx >= s.localparamremap.length())
- {
- int n = idx + 1 - s.localparamremap.length();
- memset(s.localparamremap.pad(n), 0xFF, n);
- }
- s.localparamremap[idx] = s.localparams.length();
-
- LocalShaderParamState &l = s.localparams.add();
- l.name = name;
- l.loc = loc;
- l.size = size;
- l.format = format;
- return idx;
+ int idx = getlocalparam(name);
+ if(idx >= s.localparamremap.length())
+ {
+ int n = idx + 1 - s.localparamremap.length();
+ memset(s.localparamremap.pad(n), 0xFF, n);
+ }
+ s.localparamremap[idx] = s.localparams.length();
+
+ LocalShaderParamState &l = s.localparams.add();
+ l.name = name;
+ l.loc = loc;
+ l.size = size;
+ l.format = format;
+ return idx;
}
GlobalShaderParamState *getglobalparam(const char *name)
{
- GlobalShaderParamState *param = globalparams.access(name);
- if(!param)
- {
- param = &globalparams[name];
- param->name = name;
- memset(param->buf, -1, sizeof(param->buf));
- param->version = -1;
- }
- return param;
+ GlobalShaderParamState *param = globalparams.access(name);
+ if(!param)
+ {
+ param = &globalparams[name];
+ param->name = name;
+ memset(param->buf, -1, sizeof(param->buf));
+ param->version = -1;
+ }
+ return param;
}
static GlobalShaderParamUse *addglobalparam(Shader &s, GlobalShaderParamState *param, int loc, int size, GLenum format)
{
- GlobalShaderParamUse &g = s.globalparams.add();
- g.param = param;
- g.version = -2;
- g.loc = loc;
- g.size = size;
- g.format = format;
- return &g;
+ GlobalShaderParamUse &g = s.globalparams.add();
+ g.param = param;
+ g.version = -2;
+ g.loc = loc;
+ g.size = size;
+ g.format = format;
+ return &g;
}
static void setglsluniformformat(Shader &s, const char *name, GLenum format, int size)
{
- switch(format)
- {
- case GL_FLOAT:
- case GL_FLOAT_VEC2:
- case GL_FLOAT_VEC3:
- case GL_FLOAT_VEC4:
- case GL_INT:
- case GL_INT_VEC2:
- case GL_INT_VEC3:
- case GL_INT_VEC4:
- case GL_BOOL:
- case GL_BOOL_VEC2:
- case GL_BOOL_VEC3:
- case GL_BOOL_VEC4:
- case GL_FLOAT_MAT2:
- case GL_FLOAT_MAT3:
- case GL_FLOAT_MAT4:
- break;
- default:
- return;
- }
- if(!strncmp(name, "gl_", 3)) return;
-
- int loc = glGetUniformLocation_(s.program, name);
- if(loc < 0) return;
- loopvj(s.defaultparams) if(s.defaultparams[j].loc == loc)
- {
- s.defaultparams[j].format = format;
- return;
- }
- loopvj(s.uniformlocs) if(s.uniformlocs[j].loc == loc) return;
- loopvj(s.globalparams) if(s.globalparams[j].loc == loc) return;
- loopvj(s.localparams) if(s.localparams[j].loc == loc) return;
-
- name = getshaderparamname(name);
- GlobalShaderParamState *param = globalparams.access(name);
- if(param) addglobalparam(s, param, loc, size, format);
- else addlocalparam(s, name, loc, size, format);
+ switch(format)
+ {
+ case GL_FLOAT:
+ case GL_FLOAT_VEC2:
+ case GL_FLOAT_VEC3:
+ case GL_FLOAT_VEC4:
+ case GL_INT:
+ case GL_INT_VEC2:
+ case GL_INT_VEC3:
+ case GL_INT_VEC4:
+ case GL_BOOL:
+ case GL_BOOL_VEC2:
+ case GL_BOOL_VEC3:
+ case GL_BOOL_VEC4:
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT4:
+ break;
+ default:
+ return;
+ }
+ if(!strncmp(name, "gl_", 3)) return;
+
+ int loc = glGetUniformLocation_(s.program, name);
+ if(loc < 0) return;
+ loopvj(s.defaultparams) if(s.defaultparams[j].loc == loc)
+ {
+ s.defaultparams[j].format = format;
+ return;
+ }
+ loopvj(s.uniformlocs) if(s.uniformlocs[j].loc == loc) return;
+ loopvj(s.globalparams) if(s.globalparams[j].loc == loc) return;
+ loopvj(s.localparams) if(s.localparams[j].loc == loc) return;
+
+ name = getshaderparamname(name);
+ GlobalShaderParamState *param = globalparams.access(name);
+ if(param) addglobalparam(s, param, loc, size, format);
+ else addlocalparam(s, name, loc, size, format);
}
static void allocglslactiveuniforms(Shader &s)
{
- GLint numactive = 0;
- glGetProgramiv_(s.program, GL_ACTIVE_UNIFORMS, &numactive);
- string name;
- loopi(numactive)
- {
- GLsizei namelen = 0;
- GLint size = 0;
- GLenum format = GL_FLOAT_VEC4;
- name[0] = '\0';
- glGetActiveUniform_(s.program, i, sizeof(name)-1, &namelen, &size, &format, name);
- if(namelen <= 0 || size <= 0) continue;
- name[clamp(int(namelen), 0, (int)sizeof(name)-2)] = '\0';
- char *brak = strchr(name, '[');
- if(brak) *brak = '\0';
- setglsluniformformat(s, name, format, size);
- }
+ GLint numactive = 0;
+ glGetProgramiv_(s.program, GL_ACTIVE_UNIFORMS, &numactive);
+ string name;
+ loopi(numactive)
+ {
+ GLsizei namelen = 0;
+ GLint size = 0;
+ GLenum format = GL_FLOAT_VEC4;
+ name[0] = '\0';
+ glGetActiveUniform_(s.program, i, sizeof(name)-1, &namelen, &size, &format, name);
+ if(namelen <= 0 || size <= 0) continue;
+ name[clamp(int(namelen), 0, (int)sizeof(name)-2)] = '\0';
+ char *brak = strchr(name, '[');
+ if(brak) *brak = '\0';
+ setglsluniformformat(s, name, format, size);
+ }
}
void Shader::allocparams(Slot *slot)
{
- if(slot)
- {
+ if(slot)
+ {
#define UNIFORMTEX(name, tmu) \
- { \
- loc = glGetUniformLocation_(program, name); \
- int val = tmu; \
- if(loc != -1) glUniform1i_(loc, val); \
- }
- int loc, tmu = 2;
- if(type & SHADER_NORMALSLMS)
- {
- UNIFORMTEX("lmcolor", 1);
- UNIFORMTEX("lmdir", 2);
- tmu++;
- }
- else UNIFORMTEX("lightmap", 1);
- if(type & SHADER_ENVMAP) UNIFORMTEX("envmap", tmu++);
- UNIFORMTEX("shadowmap", 7);
- int stex = 0;
- loopv(slot->sts)
- {
- Slot::Tex &t = slot->sts[i];
- switch(t.type)
- {
- case TEX_DIFFUSE: UNIFORMTEX("diffusemap", 0); break;
- case TEX_NORMAL: UNIFORMTEX("normalmap", tmu++); break;
- case TEX_GLOW: UNIFORMTEX("glowmap", tmu++); break;
- case TEX_DECAL: UNIFORMTEX("decal", tmu++); break;
- case TEX_SPEC: if(t.combined<0) UNIFORMTEX("specmap", tmu++); break;
- case TEX_DEPTH: if(t.combined<0) UNIFORMTEX("depthmap", tmu++); break;
- case TEX_ALPHA: if(t.combined<0) UNIFORMTEX("alphamap", tmu++); break;
- case TEX_UNKNOWN:
- {
- defformatstring(sname, "stex%d", stex++);
- UNIFORMTEX(sname, tmu++);
- break;
- }
- }
- }
- }
- allocglslactiveuniforms(*this);
+ { \
+ loc = glGetUniformLocation_(program, name); \
+ int val = tmu; \
+ if(loc != -1) glUniform1i_(loc, val); \
+ }
+ int loc, tmu = 2;
+ if(type & SHADER_NORMALSLMS)
+ {
+ UNIFORMTEX("lmcolor", 1);
+ UNIFORMTEX("lmdir", 2);
+ tmu++;
+ }
+ else UNIFORMTEX("lightmap", 1);
+ if(type & SHADER_ENVMAP) UNIFORMTEX("envmap", tmu++);
+ UNIFORMTEX("shadowmap", 7);
+ int stex = 0;
+ loopv(slot->sts)
+ {
+ Slot::Tex &t = slot->sts[i];
+ switch(t.type)
+ {
+ case TEX_DIFFUSE: UNIFORMTEX("diffusemap", 0); break;
+ case TEX_NORMAL: UNIFORMTEX("normalmap", tmu++); break;
+ case TEX_GLOW: UNIFORMTEX("glowmap", tmu++); break;
+ case TEX_DECAL: UNIFORMTEX("decal", tmu++); break;
+ case TEX_SPEC: if(t.combined<0) UNIFORMTEX("specmap", tmu++); break;
+ case TEX_DEPTH: if(t.combined<0) UNIFORMTEX("depthmap", tmu++); break;
+ case TEX_ALPHA: if(t.combined<0) UNIFORMTEX("alphamap", tmu++); break;
+ case TEX_UNKNOWN:
+ {
+ defformatstring(sname, "stex%d", stex++);
+ UNIFORMTEX(sname, tmu++);
+ break;
+ }
+ }
+ }
+ }
+ allocglslactiveuniforms(*this);
}
int GlobalShaderParamState::nextversion = 0;
void GlobalShaderParamState::resetversions()
{
- enumerate(shaders, Shader, s,
- {
- loopv(s.globalparams)
- {
- GlobalShaderParamUse &u = s.globalparams[i];
- if(u.version != u.param->version) u.version = -2;
- }
- });
- nextversion = 0;
- enumerate(globalparams, GlobalShaderParamState, g, { g.version = ++nextversion; });
- enumerate(shaders, Shader, s,
- {
- loopv(s.globalparams)
- {
- GlobalShaderParamUse &u = s.globalparams[i];
- if(u.version >= 0) u.version = u.param->version;
- }
- });
+ enumerate(shaders, Shader, s,
+ {
+ loopv(s.globalparams)
+ {
+ GlobalShaderParamUse &u = s.globalparams[i];
+ if(u.version != u.param->version) u.version = -2;
+ }
+ });
+ nextversion = 0;
+ enumerate(globalparams, GlobalShaderParamState, g, { g.version = ++nextversion; });
+ enumerate(shaders, Shader, s,
+ {
+ loopv(s.globalparams)
+ {
+ GlobalShaderParamUse &u = s.globalparams[i];
+ if(u.version >= 0) u.version = u.param->version;
+ }
+ });
}
static float *findslotparam(Slot &s, const char *name, float *noval = NULL)
{
- loopv(s.params)
- {
- SlotShaderParam &param = s.params[i];
- if(name == param.name) return param.val;
- }
- loopv(s.shader->defaultparams)
- {
- SlotShaderParamState &param = s.shader->defaultparams[i];
- if(name == param.name) return param.val;
- }
- return noval;
+ loopv(s.params)
+ {
+ SlotShaderParam &param = s.params[i];
+ if(name == param.name) return param.val;
+ }
+ loopv(s.shader->defaultparams)
+ {
+ SlotShaderParamState &param = s.shader->defaultparams[i];
+ if(name == param.name) return param.val;
+ }
+ return noval;
}
static float *findslotparam(VSlot &s, const char *name, float *noval = NULL)
{
- loopv(s.params)
- {
- SlotShaderParam &param = s.params[i];
- if(name == param.name) return param.val;
- }
- return findslotparam(*s.slot, name, noval);
+ loopv(s.params)
+ {
+ SlotShaderParam &param = s.params[i];
+ if(name == param.name) return param.val;
+ }
+ return findslotparam(*s.slot, name, noval);
}
static inline void setslotparam(SlotShaderParamState &l, const float *val)
{
- switch(l.format)
- {
- case GL_BOOL:
- case GL_FLOAT: glUniform1fv_(l.loc, 1, val); break;
- case GL_BOOL_VEC2:
- case GL_FLOAT_VEC2: glUniform2fv_(l.loc, 1, val); break;
- case GL_BOOL_VEC3:
- case GL_FLOAT_VEC3: glUniform3fv_(l.loc, 1, val); break;
- case GL_BOOL_VEC4:
- case GL_FLOAT_VEC4: glUniform4fv_(l.loc, 1, val); break;
- case GL_INT: glUniform1i_(l.loc, int(val[0])); break;
- case GL_INT_VEC2: glUniform2i_(l.loc, int(val[0]), int(val[1])); break;
- case GL_INT_VEC3: glUniform3i_(l.loc, int(val[0]), int(val[1]), int(val[2])); break;
- case GL_INT_VEC4: glUniform4i_(l.loc, int(val[0]), int(val[1]), int(val[2]), int(val[3])); break;
- }
+ switch(l.format)
+ {
+ case GL_BOOL:
+ case GL_FLOAT: glUniform1fv_(l.loc, 1, val); break;
+ case GL_BOOL_VEC2:
+ case GL_FLOAT_VEC2: glUniform2fv_(l.loc, 1, val); break;
+ case GL_BOOL_VEC3:
+ case GL_FLOAT_VEC3: glUniform3fv_(l.loc, 1, val); break;
+ case GL_BOOL_VEC4:
+ case GL_FLOAT_VEC4: glUniform4fv_(l.loc, 1, val); break;
+ case GL_INT: glUniform1i_(l.loc, int(val[0])); break;
+ case GL_INT_VEC2: glUniform2i_(l.loc, int(val[0]), int(val[1])); break;
+ case GL_INT_VEC3: glUniform3i_(l.loc, int(val[0]), int(val[1]), int(val[2])); break;
+ case GL_INT_VEC4: glUniform4i_(l.loc, int(val[0]), int(val[1]), int(val[2]), int(val[3])); break;
+ }
}
#define SETSLOTPARAM(l, mask, i, val) do { \
- if(!(mask&(1<<i))) { \
- mask |= 1<<i; \
- setslotparam(l, val); \
- } \
+ if(!(mask&(1<<i))) { \
+ mask |= 1<<i; \
+ setslotparam(l, val); \
+ } \
} while(0)
#define SETSLOTPARAMS(slotparams) \
- loopv(slotparams) \
- { \
- SlotShaderParam &p = slotparams[i]; \
- if(!defaultparams.inrange(p.loc)) continue; \
- SlotShaderParamState &l = defaultparams[p.loc]; \
- SETSLOTPARAM(l, unimask, p.loc, p.val); \
- }
+ loopv(slotparams) \
+ { \
+ SlotShaderParam &p = slotparams[i]; \
+ if(!defaultparams.inrange(p.loc)) continue; \
+ SlotShaderParamState &l = defaultparams[p.loc]; \
+ SETSLOTPARAM(l, unimask, p.loc, p.val); \
+ }
#define SETDEFAULTPARAMS \
- loopv(defaultparams) \
- { \
- SlotShaderParamState &l = defaultparams[i]; \
- SETSLOTPARAM(l, unimask, i, l.val); \
- }
+ loopv(defaultparams) \
+ { \
+ SlotShaderParamState &l = defaultparams[i]; \
+ SETSLOTPARAM(l, unimask, i, l.val); \
+ }
void Shader::setslotparams(Slot &slot)
{
- uint unimask = 0;
- SETSLOTPARAMS(slot.params)
- SETDEFAULTPARAMS
+ uint unimask = 0;
+ SETSLOTPARAMS(slot.params)
+ SETDEFAULTPARAMS
}
void Shader::setslotparams(Slot &slot, VSlot &vslot)
{
- uint unimask = 0;
- if(vslot.slot == &slot)
- {
- SETSLOTPARAMS(vslot.params)
- SETSLOTPARAMS(slot.params)
- SETDEFAULTPARAMS
- }
- else
- {
- SETSLOTPARAMS(slot.params)
- SETDEFAULTPARAMS
- }
+ uint unimask = 0;
+ if(vslot.slot == &slot)
+ {
+ SETSLOTPARAMS(vslot.params)
+ SETSLOTPARAMS(slot.params)
+ SETDEFAULTPARAMS
+ }
+ else
+ {
+ SETSLOTPARAMS(slot.params)
+ SETDEFAULTPARAMS
+ }
}
void Shader::bindprograms()
{
- if(this == lastshader || type&(SHADER_DEFERRED|SHADER_INVALID)) return;
- glUseProgram_(program);
- lastshader = this;
+ if(this == lastshader || type&(SHADER_DEFERRED|SHADER_INVALID)) return;
+ glUseProgram_(program);
+ lastshader = this;
}
bool Shader::compile()
{
- if(!vsstr) vsobj = !reusevs || reusevs->invalid() ? 0 : reusevs->vsobj;
- else compileglslshader(GL_VERTEX_SHADER, vsobj, vsstr, name, dbgshader || !variantshader);
- if(!psstr) psobj = !reuseps || reuseps->invalid() ? 0 : reuseps->psobj;
- else compileglslshader(GL_FRAGMENT_SHADER, psobj, psstr, name, dbgshader || !variantshader);
- linkglslprogram(*this, !variantshader);
- return program!=0;
+ if(!vsstr) vsobj = !reusevs || reusevs->invalid() ? 0 : reusevs->vsobj;
+ else compileglslshader(GL_VERTEX_SHADER, vsobj, vsstr, name, !variantshader);
+ if(!psstr) psobj = !reuseps || reuseps->invalid() ? 0 : reuseps->psobj;
+ else compileglslshader(GL_FRAGMENT_SHADER, psobj, psstr, name, !variantshader);
+ linkglslprogram(*this, !variantshader);
+ return program!=0;
}
void Shader::cleanup(bool invalid)
{
- detailshader = NULL;
- used = false;
- if(vsobj) { if(!reusevs) glDeleteShader_(vsobj); vsobj = 0; }
- if(psobj) { if(!reuseps) glDeleteShader_(psobj); psobj = 0; }
- if(program) { glDeleteProgram_(program); program = 0; }
- localparams.setsize(0);
- localparamremap.setsize(0);
- globalparams.setsize(0);
- if(standard || invalid)
- {
- type = SHADER_INVALID;
- DELETEA(vsstr);
- DELETEA(psstr);
- DELETEA(defer);
- variants.setsize(0);
- DELETEA(variantrows);
- defaultparams.setsize(0);
- attriblocs.setsize(0);
- uniformlocs.setsize(0);
- altshader = NULL;
- loopi(MAXSHADERDETAIL) fastshader[i] = this;
- reusevs = reuseps = NULL;
- }
- else loopv(defaultparams) defaultparams[i].loc = -1;
- owner = NULL;
+ detailshader = NULL;
+ used = false;
+ if(vsobj) { if(!reusevs) glDeleteShader_(vsobj); vsobj = 0; }
+ if(psobj) { if(!reuseps) glDeleteShader_(psobj); psobj = 0; }
+ if(program) { glDeleteProgram_(program); program = 0; }
+ localparams.setsize(0);
+ localparamremap.setsize(0);
+ globalparams.setsize(0);
+ if(standard || invalid)
+ {
+ type = SHADER_INVALID;
+ DELETEA(vsstr);
+ DELETEA(psstr);
+ DELETEA(defer);
+ variants.setsize(0);
+ DELETEA(variantrows);
+ defaultparams.setsize(0);
+ attriblocs.setsize(0);
+ uniformlocs.setsize(0);
+ altshader = NULL;
+ loopi(MAXSHADERDETAIL) fastshader[i] = this;
+ reusevs = reuseps = NULL;
+ }
+ else loopv(defaultparams) defaultparams[i].loc = -1;
+ owner = NULL;
}
bool Shader::isnull(const Shader *s) { return !s; }
static void genattriblocs(Shader &s, const char *vs, const char *ps, Shader *reusevs, Shader *reuseps)
{
- static int len = strlen("//:attrib");
- string name;
- int loc;
- if(reusevs) s.attriblocs = reusevs->attriblocs;
- else while((vs = strstr(vs, "//:attrib")))
- {
- if(sscanf(vs, "//:attrib %100s %d", name, &loc) == 2)
- s.attriblocs.add(AttribLoc(getshaderparamname(name), loc));
- vs += len;
- }
+ static int len = strlen("//:attrib");
+ string name;
+ int loc;
+ if(reusevs) s.attriblocs = reusevs->attriblocs;
+ else while((vs = strstr(vs, "//:attrib")))
+ {
+ if(sscanf(vs, "//:attrib %100s %d", name, &loc) == 2)
+ s.attriblocs.add(AttribLoc(getshaderparamname(name), loc));
+ vs += len;
+ }
}
static void genuniformlocs(Shader &s, const char *vs, const char *ps, Shader *reusevs, Shader *reuseps)
{
- static int len = strlen("//:uniform");
- string name, blockname;
- int binding, stride;
- if(reusevs) s.uniformlocs = reusevs->uniformlocs;
- else while((vs = strstr(vs, "//:uniform")))
- {
- int numargs = sscanf(vs, "//:uniform %100s %100s %d %d", name, blockname, &binding, &stride);
- if(numargs >= 3) s.uniformlocs.add(UniformLoc(getshaderparamname(name), getshaderparamname(blockname), binding, numargs >= 4 ? stride : 0));
- else if(numargs >= 1) s.uniformlocs.add(UniformLoc(getshaderparamname(name)));
- vs += len;
- }
+ static int len = strlen("//:uniform");
+ string name, blockname;
+ int binding, stride;
+ if(reusevs) s.uniformlocs = reusevs->uniformlocs;
+ else while((vs = strstr(vs, "//:uniform")))
+ {
+ int numargs = sscanf(vs, "//:uniform %100s %100s %d %d", name, blockname, &binding, &stride);
+ if(numargs >= 3) s.uniformlocs.add(UniformLoc(getshaderparamname(name), getshaderparamname(blockname), binding, numargs >= 4 ? stride : 0));
+ else if(numargs >= 1) s.uniformlocs.add(UniformLoc(getshaderparamname(name)));
+ vs += len;
+ }
}
Shader *newshader(int type, const char *name, const char *vs, const char *ps, Shader *variant = NULL, int row = 0)
{
- if(Shader::lastshader)
- {
- glUseProgram_(0);
- Shader::lastshader = NULL;
- }
-
- Shader *exists = shaders.access(name);
- char *rname = exists ? exists->name : newstring(name);
- Shader &s = shaders[rname];
- s.name = rname;
- s.vsstr = newstring(vs);
- s.psstr = newstring(ps);
- DELETEA(s.defer);
- s.type = type;
- s.variantshader = variant;
- s.standard = standardshaders;
- if(forceshaders) s.forced = true;
- s.reusevs = s.reuseps = NULL;
- if(variant)
- {
- int row = 0, col = 0;
- if(!vs[0] || sscanf(vs, "%d , %d", &row, &col) >= 1)
- {
- DELETEA(s.vsstr);
- s.reusevs = !vs[0] ? variant : variant->getvariant(col, row);
- }
- row = col = 0;
- if(!ps[0] || sscanf(ps, "%d , %d", &row, &col) >= 1)
- {
- DELETEA(s.psstr);
- s.reuseps = !ps[0] ? variant : variant->getvariant(col, row);
- }
- }
- if(variant) loopv(variant->defaultparams) s.defaultparams.add(variant->defaultparams[i]);
- else loopv(slotparams) s.defaultparams.add(slotparams[i]);
- s.attriblocs.setsize(0);
- s.uniformlocs.setsize(0);
- genattriblocs(s, vs, ps, s.reusevs, s.reuseps);
- genuniformlocs(s, vs, ps, s.reusevs, s.reuseps);
- if(!s.compile())
- {
- s.cleanup(true);
- if(variant) shaders.remove(rname);
- return NULL;
- }
- if(variant) variant->addvariant(row, &s);
- s.fixdetailshader();
- return &s;
+ if(Shader::lastshader)
+ {
+ glUseProgram_(0);
+ Shader::lastshader = NULL;
+ }
+
+ Shader *exists = shaders.access(name);
+ char *rname = exists ? exists->name : newstring(name);
+ Shader &s = shaders[rname];
+ s.name = rname;
+ s.vsstr = newstring(vs);
+ s.psstr = newstring(ps);
+ DELETEA(s.defer);
+ s.type = type;
+ s.variantshader = variant;
+ s.standard = standardshaders;
+ if(forceshaders) s.forced = true;
+ s.reusevs = s.reuseps = NULL;
+ if(variant)
+ {
+ int row = 0, col = 0;
+ if(!vs[0] || sscanf(vs, "%d , %d", &row, &col) >= 1)
+ {
+ DELETEA(s.vsstr);
+ s.reusevs = !vs[0] ? variant : variant->getvariant(col, row);
+ }
+ row = col = 0;
+ if(!ps[0] || sscanf(ps, "%d , %d", &row, &col) >= 1)
+ {
+ DELETEA(s.psstr);
+ s.reuseps = !ps[0] ? variant : variant->getvariant(col, row);
+ }
+ }
+ if(variant) loopv(variant->defaultparams) s.defaultparams.add(variant->defaultparams[i]);
+ else loopv(slotparams) s.defaultparams.add(slotparams[i]);
+ s.attriblocs.setsize(0);
+ s.uniformlocs.setsize(0);
+ genattriblocs(s, vs, ps, s.reusevs, s.reuseps);
+ genuniformlocs(s, vs, ps, s.reusevs, s.reuseps);
+ if(!s.compile())
+ {
+ s.cleanup(true);
+ if(variant) shaders.remove(rname);
+ return NULL;
+ }
+ if(variant) variant->addvariant(row, &s);
+ s.fixdetailshader();
+ return &s;
}
void setupshaders()
{
- GLint val;
- glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &val);
- maxvsuniforms = val/4;
- glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &val);
- maxfsuniforms = val/4;
- if(glversion < 300)
- {
- glGetIntegerv(GL_MAX_VARYING_COMPONENTS, &val);
- maxvaryings = val;
- }
-
- standardshaders = true;
- nullshader = newshader(0, "<init>null",
- "attribute vec4 vvertex;\n"
- "void main(void) {\n"
- " gl_Position = vvertex;\n"
- "}\n",
- "void main(void) {\n"
- " gl_FragColor = vec4(1.0, 0.0, 1.0, 0.0);\n"
- "}\n");
- hudshader = newshader(0, "<init>hud",
- "attribute vec4 vvertex, vcolor;\n"
- "attribute vec2 vtexcoord0;\n"
- "uniform mat4 hudmatrix;\n"
- "varying vec2 texcoord0;\n"
- "varying vec4 color;\n"
- "void main(void) {\n"
- " gl_Position = hudmatrix * vvertex;\n"
- " texcoord0 = vtexcoord0;\n"
- " color = vcolor;\n"
- "}\n",
- "varying vec2 texcoord0;\n"
- "varying vec4 color;\n"
- "uniform sampler2D tex0;\n"
- "void main(void) {\n"
- " gl_FragColor = color * texture2D(tex0, texcoord0);\n"
- "}\n");
- hudnotextureshader = newshader(0, "<init>hudnotexture",
- "attribute vec4 vvertex, vcolor;\n"
- "uniform mat4 hudmatrix;\n"
- "varying vec4 color;\n"
- "void main(void) {\n"
- " gl_Position = hudmatrix * vvertex;\n"
- " color = vcolor;\n"
- "}\n",
- "varying vec4 color;\n"
- "void main(void) {\n"
- " gl_FragColor = color;\n"
- "}\n");
- standardshaders = false;
-
- if(!nullshader || !hudshader || !hudnotextureshader) fatal("failed to setup shaders");
-
- dummyslot.shader = nullshader;
+ GLint val;
+ glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &val);
+ maxvsuniforms = val/4;
+ glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &val);
+ maxfsuniforms = val/4;
+ if(glversion < 300)
+ {
+ glGetIntegerv(GL_MAX_VARYING_COMPONENTS, &val);
+ maxvaryings = val;
+ }
+
+ standardshaders = true;
+ nullshader = newshader(0, "<init>null",
+ "attribute vec4 vvertex;\n"
+ "void main(void) {\n"
+ " gl_Position = vvertex;\n"
+ "}\n",
+ "void main(void) {\n"
+ " gl_FragColor = vec4(1.0, 0.0, 1.0, 0.0);\n"
+ "}\n");
+ hudshader = newshader(0, "<init>hud",
+ "attribute vec4 vvertex, vcolor;\n"
+ "attribute vec2 vtexcoord0;\n"
+ "uniform mat4 hudmatrix;\n"
+ "varying vec2 texcoord0;\n"
+ "varying vec4 color;\n"
+ "void main(void) {\n"
+ " gl_Position = hudmatrix * vvertex;\n"
+ " texcoord0 = vtexcoord0;\n"
+ " color = vcolor;\n"
+ "}\n",
+ "varying vec2 texcoord0;\n"
+ "varying vec4 color;\n"
+ "uniform sampler2D tex0;\n"
+ "void main(void) {\n"
+ " gl_FragColor = color * texture2D(tex0, texcoord0);\n"
+ "}\n");
+ hudnotextureshader = newshader(0, "<init>hudnotexture",
+ "attribute vec4 vvertex, vcolor;\n"
+ "uniform mat4 hudmatrix;\n"
+ "varying vec4 color;\n"
+ "void main(void) {\n"
+ " gl_Position = hudmatrix * vvertex;\n"
+ " color = vcolor;\n"
+ "}\n",
+ "varying vec4 color;\n"
+ "void main(void) {\n"
+ " gl_FragColor = color;\n"
+ "}\n");
+ standardshaders = false;
+
+ if(!nullshader || !hudshader || !hudnotextureshader) fatal("failed to setup shaders");
+
+ dummyslot.shader = nullshader;
}
static const char *findglslmain(const char *s)
{
- const char *main = strstr(s, "main");
- if(!main) return NULL;
- for(; main >= s; main--) switch(*main) { case '\r': case '\n': case ';': return main + 1; }
- return s;
+ const char *main = strstr(s, "main");
+ if(!main) return NULL;
+ for(; main >= s; main--) switch(*main) { case '\r': case '\n': case ';': return main + 1; }
+ return s;
}
static void gengenericvariant(Shader &s, const char *sname, const char *vs, const char *ps, int row = 0)
{
- int rowoffset = 0;
- bool vschanged = false, pschanged = false;
- vector<char> vsv, psv;
- vsv.put(vs, strlen(vs)+1);
- psv.put(ps, strlen(ps)+1);
-
- static const int len = strlen("//:variant"), olen = strlen("override");
- for(char *vspragma = vsv.getbuf();; vschanged = true)
- {
- vspragma = strstr(vspragma, "//:variant");
- if(!vspragma) break;
- if(sscanf(vspragma + len, "row %d", &rowoffset) == 1) continue;
- memset(vspragma, ' ', len);
- vspragma += len;
- if(!strncmp(vspragma, "override", olen))
- {
- memset(vspragma, ' ', olen);
- vspragma += olen;
- char *end = vspragma + strcspn(vspragma, "\n\r");
- end += strspn(end, "\n\r");
- int endlen = strcspn(end, "\n\r");
- memset(end, ' ', endlen);
- }
- }
- for(char *pspragma = psv.getbuf();; pschanged = true)
- {
- pspragma = strstr(pspragma, "//:variant");
- if(!pspragma) break;
- if(sscanf(pspragma + len, "row %d", &rowoffset) == 1) continue;
- memset(pspragma, ' ', len);
- pspragma += len;
- if(!strncmp(pspragma, "override", olen))
- {
- memset(pspragma, ' ', olen);
- pspragma += olen;
- char *end = pspragma + strcspn(pspragma, "\n\r");
- end += strspn(end, "\n\r");
- int endlen = strcspn(end, "\n\r");
- memset(end, ' ', endlen);
- }
- }
- row += rowoffset;
- if(row < 0 || row >= MAXVARIANTROWS) return;
- int col = s.numvariants(row);
- defformatstring(varname, "<variant:%d,%d>%s", col, row, sname);
- string reuse;
- if(col) formatstring(reuse, "%d", row);
- else copystring(reuse, "");
- newshader(s.type, varname, vschanged ? vsv.getbuf() : reuse, pschanged ? psv.getbuf() : reuse, &s, row);
+ int rowoffset = 0;
+ bool vschanged = false, pschanged = false;
+ vector<char> vsv, psv;
+ vsv.put(vs, strlen(vs)+1);
+ psv.put(ps, strlen(ps)+1);
+
+ static const int len = strlen("//:variant"), olen = strlen("override");
+ for(char *vspragma = vsv.getbuf();; vschanged = true)
+ {
+ vspragma = strstr(vspragma, "//:variant");
+ if(!vspragma) break;
+ if(sscanf(vspragma + len, "row %d", &rowoffset) == 1) continue;
+ memset(vspragma, ' ', len);
+ vspragma += len;
+ if(!strncmp(vspragma, "override", olen))
+ {
+ memset(vspragma, ' ', olen);
+ vspragma += olen;
+ char *end = vspragma + strcspn(vspragma, "\n\r");
+ end += strspn(end, "\n\r");
+ int endlen = strcspn(end, "\n\r");
+ memset(end, ' ', endlen);
+ }
+ }
+ for(char *pspragma = psv.getbuf();; pschanged = true)
+ {
+ pspragma = strstr(pspragma, "//:variant");
+ if(!pspragma) break;
+ if(sscanf(pspragma + len, "row %d", &rowoffset) == 1) continue;
+ memset(pspragma, ' ', len);
+ pspragma += len;
+ if(!strncmp(pspragma, "override", olen))
+ {
+ memset(pspragma, ' ', olen);
+ pspragma += olen;
+ char *end = pspragma + strcspn(pspragma, "\n\r");
+ end += strspn(end, "\n\r");
+ int endlen = strcspn(end, "\n\r");
+ memset(end, ' ', endlen);
+ }
+ }
+ row += rowoffset;
+ if(row < 0 || row >= MAXVARIANTROWS) return;
+ int col = s.numvariants(row);
+ defformatstring(varname, "<variant:%d,%d>%s", col, row, sname);
+ string reuse;
+ if(col) formatstring(reuse, "%d", row);
+ else copystring(reuse, "");
+ newshader(s.type, varname, vschanged ? vsv.getbuf() : reuse, pschanged ? psv.getbuf() : reuse, &s, row);
}
static bool genwatervariant(Shader &s, const char *sname, const char *vs, const char *ps, int row = 2)
{
- if(!strstr(vs, "//:water") && !strstr(ps, "//:water")) return false;
-
- vector<char> vsw, psw;
-
- const char *vsmain = findglslmain(vs), *vsend = strrchr(vs, '}');
- if(!vsmain || !vsend) return false;
- vsw.put(vs, vsmain - vs);
- const char *fadeparams = "\nuniform vec4 waterfadeparams;\nvarying float fadedepth;\n";
- vsw.put(fadeparams, strlen(fadeparams));
- vsw.put(vsmain, vsend - vsmain);
- const char *fadedef = "\nfadedepth = vvertex.z*waterfadeparams.x + waterfadeparams.y;\n";
- vsw.put(fadedef, strlen(fadedef));
- vsw.put(vsend, strlen(vsend)+1);
-
- const char *psmain = findglslmain(ps), *psend = strrchr(ps, '}');
- if(!psmain || !psend) return false;
- psw.put(ps, psmain - ps);
- const char *fadeinterp = "\nvarying float fadedepth;\n";
- psw.put(fadeinterp, strlen(fadeinterp));
- psw.put(psmain, psend - psmain);
- const char *fade = "\ngl_FragColor.a = fadedepth;\n";
- psw.put(fade, strlen(fade));
- psw.put(psend, strlen(psend)+1);
-
- defformatstring(name, "<water>%s", sname);
- Shader *variant = newshader(s.type, name, vsw.getbuf(), psw.getbuf(), &s, row);
- return variant!=NULL;
+ if(!strstr(vs, "//:water") && !strstr(ps, "//:water")) return false;
+
+ vector<char> vsw, psw;
+
+ const char *vsmain = findglslmain(vs), *vsend = strrchr(vs, '}');
+ if(!vsmain || !vsend) return false;
+ vsw.put(vs, vsmain - vs);
+ const char *fadeparams = "\nuniform vec4 waterfadeparams;\nvarying float fadedepth;\n";
+ vsw.put(fadeparams, strlen(fadeparams));
+ vsw.put(vsmain, vsend - vsmain);
+ const char *fadedef = "\nfadedepth = vvertex.z*waterfadeparams.x + waterfadeparams.y;\n";
+ vsw.put(fadedef, strlen(fadedef));
+ vsw.put(vsend, strlen(vsend)+1);
+
+ const char *psmain = findglslmain(ps), *psend = strrchr(ps, '}');
+ if(!psmain || !psend) return false;
+ psw.put(ps, psmain - ps);
+ const char *fadeinterp = "\nvarying float fadedepth;\n";
+ psw.put(fadeinterp, strlen(fadeinterp));
+ psw.put(psmain, psend - psmain);
+ const char *fade = "\ngl_FragColor.a = fadedepth;\n";
+ psw.put(fade, strlen(fade));
+ psw.put(psend, strlen(psend)+1);
+
+ defformatstring(name, "<water>%s", sname);
+ Shader *variant = newshader(s.type, name, vsw.getbuf(), psw.getbuf(), &s, row);
+ return variant!=NULL;
}
bool minimizedynlighttcusage() { return glversion < 300 && maxvaryings < 48; }
static void gendynlightvariant(Shader &s, const char *sname, const char *vs, const char *ps, int row = 0)
{
- int numlights = minimizedynlighttcusage() ? 1 : MAXDYNLIGHTS;
-
- const char *vspragma = strstr(vs, "//:dynlight"), *pspragma = strstr(ps, "//:dynlight");
- if(!vspragma || !pspragma) return;
-
- string pslight;
- vspragma += strcspn(vspragma, "\n");
- if(*vspragma) vspragma++;
-
- if(sscanf(pspragma, "//:dynlight %100s", pslight)!=1) return;
-
- pspragma += strcspn(pspragma, "\n");
- if(*pspragma) pspragma++;
-
- const char *vsmain = findglslmain(vs), *psmain = findglslmain(ps);
- if(vsmain > vspragma) vsmain = vs;
- if(psmain > pspragma) psmain = ps;
-
- vector<char> vsdl, psdl;
- loopi(MAXDYNLIGHTS)
- {
- vsdl.setsize(0);
- psdl.setsize(0);
- if(vsmain >= vs) vsdl.put(vs, vsmain - vs);
- if(psmain >= ps) psdl.put(ps, psmain - ps);
-
- defformatstring(pos, "uniform vec4 dynlightpos[%d];\n", i+1);
- vsdl.put(pos, strlen(pos));
- psdl.put(pos, strlen(pos));
- defformatstring(color, "uniform vec3 dynlightcolor[%d];\n", i+1);
- psdl.put(color, strlen(color));
-
- loopk(min(i+1, numlights))
- {
- defformatstring(dir, "%sdynlight%ddir%s", !k ? "varying vec3 " : " ", k, k==i || k+1==numlights ? ";\n" : ",");
- vsdl.put(dir, strlen(dir));
- psdl.put(dir, strlen(dir));
- }
-
- vsdl.put(vsmain, vspragma-vsmain);
- psdl.put(psmain, pspragma-psmain);
-
- loopk(i+1)
- {
- defformatstring(tc,
- k<numlights ?
- "dynlight%ddir = vvertex.xyz*dynlightpos[%d].w + dynlightpos[%d].xyz;\n" :
- "vec3 dynlight%ddir = dynlight0dir*dynlightpos[%d].w + dynlightpos[%d].xyz;\n",
- k, k, k);
- if(k < numlights) vsdl.put(tc, strlen(tc));
- else psdl.put(tc, strlen(tc));
-
- defformatstring(dl,
- "%s.rgb += dynlightcolor[%d] * (1.0 - clamp(dot(dynlight%ddir, dynlight%ddir), 0.0, 1.0));\n",
- pslight, k, k, k);
- psdl.put(dl, strlen(dl));
- }
-
- vsdl.put(vspragma, strlen(vspragma)+1);
- psdl.put(pspragma, strlen(pspragma)+1);
-
- defformatstring(name, "<dynlight %d>%s", i+1, sname);
- Shader *variant = newshader(s.type, name, vsdl.getbuf(), psdl.getbuf(), &s, row);
- if(!variant) return;
- if(row < 4) genwatervariant(s, name, vsdl.getbuf(), psdl.getbuf(), row+2);
- }
+ int numlights = minimizedynlighttcusage() ? 1 : MAXDYNLIGHTS;
+
+ const char *vspragma = strstr(vs, "//:dynlight"), *pspragma = strstr(ps, "//:dynlight");
+ if(!vspragma || !pspragma) return;
+
+ string pslight;
+ vspragma += strcspn(vspragma, "\n");
+ if(*vspragma) vspragma++;
+
+ if(sscanf(pspragma, "//:dynlight %100s", pslight)!=1) return;
+
+ pspragma += strcspn(pspragma, "\n");
+ if(*pspragma) pspragma++;
+
+ const char *vsmain = findglslmain(vs), *psmain = findglslmain(ps);
+ if(vsmain > vspragma) vsmain = vs;
+ if(psmain > pspragma) psmain = ps;
+
+ vector<char> vsdl, psdl;
+ loopi(MAXDYNLIGHTS)
+ {
+ vsdl.setsize(0);
+ psdl.setsize(0);
+ if(vsmain >= vs) vsdl.put(vs, vsmain - vs);
+ if(psmain >= ps) psdl.put(ps, psmain - ps);
+
+ defformatstring(pos, "uniform vec4 dynlightpos[%d];\n", i+1);
+ vsdl.put(pos, strlen(pos));
+ psdl.put(pos, strlen(pos));
+ defformatstring(color, "uniform vec3 dynlightcolor[%d];\n", i+1);
+ psdl.put(color, strlen(color));
+
+ loopk(min(i+1, numlights))
+ {
+ defformatstring(dir, "%sdynlight%ddir%s", !k ? "varying vec3 " : " ", k, k==i || k+1==numlights ? ";\n" : ",");
+ vsdl.put(dir, strlen(dir));
+ psdl.put(dir, strlen(dir));
+ }
+
+ vsdl.put(vsmain, vspragma-vsmain);
+ psdl.put(psmain, pspragma-psmain);
+
+ loopk(i+1)
+ {
+ defformatstring(tc,
+ k<numlights ?
+ "dynlight%ddir = vvertex.xyz*dynlightpos[%d].w + dynlightpos[%d].xyz;\n" :
+ "vec3 dynlight%ddir = dynlight0dir*dynlightpos[%d].w + dynlightpos[%d].xyz;\n",
+ k, k, k);
+ if(k < numlights) vsdl.put(tc, strlen(tc));
+ else psdl.put(tc, strlen(tc));
+
+ defformatstring(dl,
+ "%s.rgb += dynlightcolor[%d] * (1.0 - clamp(dot(dynlight%ddir, dynlight%ddir), 0.0, 1.0));\n",
+ pslight, k, k, k);
+ psdl.put(dl, strlen(dl));
+ }
+
+ vsdl.put(vspragma, strlen(vspragma)+1);
+ psdl.put(pspragma, strlen(pspragma)+1);
+
+ defformatstring(name, "<dynlight %d>%s", i+1, sname);
+ Shader *variant = newshader(s.type, name, vsdl.getbuf(), psdl.getbuf(), &s, row);
+ if(!variant) return;
+ if(row < 4) genwatervariant(s, name, vsdl.getbuf(), psdl.getbuf(), row+2);
+ }
}
static void genshadowmapvariant(Shader &s, const char *sname, const char *vs, const char *ps, int row = 1)
{
- const char *vspragma = strstr(vs, "//:shadowmap"), *pspragma = strstr(ps, "//:shadowmap");
- if(!vspragma || !pspragma) return;
-
- string pslight;
- vspragma += strcspn(vspragma, "\n");
- if(*vspragma) vspragma++;
-
- if(sscanf(pspragma, "//:shadowmap %100s", pslight)!=1) return;
-
- pspragma += strcspn(pspragma, "\n");
- if(*pspragma) pspragma++;
-
- const char *vsmain = findglslmain(vs), *psmain = findglslmain(ps);
- if(vsmain > vspragma) vsmain = vs;
- if(psmain > pspragma) psmain = ps;
-
- vector<char> vssm, pssm;
- if(vsmain >= vs) vssm.put(vs, vsmain - vs);
- if(psmain >= ps) pssm.put(ps, psmain - ps);
-
- const char *vsdecl =
- "uniform mat4 shadowmapproject;\n"
- "varying vec3 shadowmaptc;\n";
- vssm.put(vsdecl, strlen(vsdecl));
-
- const char *psdecl =
- "uniform sampler2D shadowmap;\n"
- "uniform vec4 shadowmapambient;\n"
- "varying vec3 shadowmaptc;\n";
- pssm.put(psdecl, strlen(psdecl));
-
- vssm.put(vsmain, vspragma-vsmain);
- pssm.put(psmain, pspragma-psmain);
-
- extern int smoothshadowmappeel;
- const char *tcgen =
- "shadowmaptc = vec3(shadowmapproject * vvertex);\n";
- vssm.put(tcgen, strlen(tcgen));
- const char *sm =
- smoothshadowmappeel ?
- "vec4 smvals = texture2D(shadowmap, shadowmaptc.xy);\n"
- "vec2 smdiff = clamp(smvals.xz - shadowmaptc.zz*smvals.y, 0.0, 1.0);\n"
- "float shadowed = clamp((smdiff.x > 0.0 ? smvals.w : 0.0) - 8.0*smdiff.y, 0.0, 1.0);\n" :
-
- "vec4 smvals = texture2D(shadowmap, shadowmaptc.xy);\n"
- "float smtest = shadowmaptc.z*smvals.y;\n"
- "float shadowed = smtest < smvals.x && smtest > smvals.z ? smvals.w : 0.0;\n";
- pssm.put(sm, strlen(sm));
- defformatstring(smlight,
- "%s.rgb -= shadowed*clamp(%s.rgb - shadowmapambient.rgb, 0.0, 1.0);\n",
- pslight, pslight);
- pssm.put(smlight, strlen(smlight));
-
- vssm.put(vspragma, strlen(vspragma)+1);
- pssm.put(pspragma, strlen(pspragma)+1);
-
- defformatstring(name, "<shadowmap>%s", sname);
- Shader *variant = newshader(s.type, name, vssm.getbuf(), pssm.getbuf(), &s, row);
- if(!variant) return;
- genwatervariant(s, name, vssm.getbuf(), pssm.getbuf(), row+2);
-
- if(strstr(vs, "//:dynlight")) gendynlightvariant(s, name, vssm.getbuf(), pssm.getbuf(), row);
+ const char *vspragma = strstr(vs, "//:shadowmap"), *pspragma = strstr(ps, "//:shadowmap");
+ if(!vspragma || !pspragma) return;
+
+ string pslight;
+ vspragma += strcspn(vspragma, "\n");
+ if(*vspragma) vspragma++;
+
+ if(sscanf(pspragma, "//:shadowmap %100s", pslight)!=1) return;
+
+ pspragma += strcspn(pspragma, "\n");
+ if(*pspragma) pspragma++;
+
+ const char *vsmain = findglslmain(vs), *psmain = findglslmain(ps);
+ if(vsmain > vspragma) vsmain = vs;
+ if(psmain > pspragma) psmain = ps;
+
+ vector<char> vssm, pssm;
+ if(vsmain >= vs) vssm.put(vs, vsmain - vs);
+ if(psmain >= ps) pssm.put(ps, psmain - ps);
+
+ const char *vsdecl =
+ "uniform mat4 shadowmapproject;\n"
+ "varying vec3 shadowmaptc;\n";
+ vssm.put(vsdecl, strlen(vsdecl));
+
+ const char *psdecl =
+ "uniform sampler2D shadowmap;\n"
+ "uniform vec4 shadowmapambient;\n"
+ "varying vec3 shadowmaptc;\n";
+ pssm.put(psdecl, strlen(psdecl));
+
+ vssm.put(vsmain, vspragma-vsmain);
+ pssm.put(psmain, pspragma-psmain);
+
+ extern int smoothshadowmappeel;
+ const char *tcgen =
+ "shadowmaptc = vec3(shadowmapproject * vvertex);\n";
+ vssm.put(tcgen, strlen(tcgen));
+ const char *sm =
+ smoothshadowmappeel ?
+ "vec4 smvals = texture2D(shadowmap, shadowmaptc.xy);\n"
+ "vec2 smdiff = clamp(smvals.xz - shadowmaptc.zz*smvals.y, 0.0, 1.0);\n"
+ "float shadowed = clamp((smdiff.x > 0.0 ? smvals.w : 0.0) - 8.0*smdiff.y, 0.0, 1.0);\n" :
+
+ "vec4 smvals = texture2D(shadowmap, shadowmaptc.xy);\n"
+ "float smtest = shadowmaptc.z*smvals.y;\n"
+ "float shadowed = smtest < smvals.x && smtest > smvals.z ? smvals.w : 0.0;\n";
+ pssm.put(sm, strlen(sm));
+ defformatstring(smlight,
+ "%s.rgb -= shadowed*clamp(%s.rgb - shadowmapambient.rgb, 0.0, 1.0);\n",
+ pslight, pslight);
+ pssm.put(smlight, strlen(smlight));
+
+ vssm.put(vspragma, strlen(vspragma)+1);
+ pssm.put(pspragma, strlen(pspragma)+1);
+
+ defformatstring(name, "<shadowmap>%s", sname);
+ Shader *variant = newshader(s.type, name, vssm.getbuf(), pssm.getbuf(), &s, row);
+ if(!variant) return;
+ genwatervariant(s, name, vssm.getbuf(), pssm.getbuf(), row+2);
+
+ if(strstr(vs, "//:dynlight")) gendynlightvariant(s, name, vssm.getbuf(), pssm.getbuf(), row);
}
static void genfogshader(vector<char> &vsbuf, vector<char> &psbuf, const char *vs, const char *ps)
{
- const char *vspragma = strstr(vs, "//:fog"), *pspragma = strstr(ps, "//:fog");
- if(!vspragma && !pspragma) return;
- static const int pragmalen = strlen("//:fog");
- const char *vsmain = findglslmain(vs), *vsend = strrchr(vs, '}');
- if(vsmain && vsend)
- {
- vsbuf.put(vs, vsmain - vs);
- const char *fogparams = "\nuniform vec4 fogplane;\nvarying float fogcoord;\n";
- vsbuf.put(fogparams, strlen(fogparams));
- vsbuf.put(vsmain, vsend - vsmain);
- const char *vsfog = "\nfogcoord = dot(fogplane, gl_Position);\n";
- vsbuf.put(vsfog, strlen(vsfog));
- vsbuf.put(vsend, strlen(vsend)+1);
- }
- const char *psmain = findglslmain(ps), *psend = strrchr(ps, '}');
- if(psmain && psend)
- {
- psbuf.put(ps, psmain - ps);
- const char *fogparams =
- "\nuniform vec3 fogcolor;\n"
- "uniform vec2 fogparams;\n"
- "varying float fogcoord;\n";
- psbuf.put(fogparams, strlen(fogparams));
- psbuf.put(psmain, psend - psmain);
- const char *psdef = "\n#define FOG_COLOR ";
- const char *psfog =
- pspragma && !strncmp(pspragma+pragmalen, "rgba", 4) ?
- "\ngl_FragColor = mix((FOG_COLOR), gl_FragColor, clamp(fogcoord*fogparams.x + fogparams.y, 0.0, 1.0));\n" :
- "\ngl_FragColor.rgb = mix((FOG_COLOR).rgb, gl_FragColor.rgb, clamp(fogcoord*fogparams.x + fogparams.y, 0.0, 1.0));\n";
- int clen = 0;
- if(pspragma)
- {
- pspragma += pragmalen;
- while(iscubealpha(*pspragma)) pspragma++;
- while(*pspragma && !iscubespace(*pspragma)) pspragma++;
- pspragma += strspn(pspragma, " \t\v\f");
- clen = strcspn(pspragma, "\r\n");
- }
- if(clen <= 0) { pspragma = "fogcolor"; clen = strlen(pspragma); }
- psbuf.put(psdef, strlen(psdef));
- psbuf.put(pspragma, clen);
- psbuf.put(psfog, strlen(psfog));
- psbuf.put(psend, strlen(psend)+1);
- }
+ const char *vspragma = strstr(vs, "//:fog"), *pspragma = strstr(ps, "//:fog");
+ if(!vspragma && !pspragma) return;
+ static const int pragmalen = strlen("//:fog");
+ const char *vsmain = findglslmain(vs), *vsend = strrchr(vs, '}');
+ if(vsmain && vsend)
+ {
+ vsbuf.put(vs, vsmain - vs);
+ const char *fogparams = "\nuniform vec4 fogplane;\nvarying float fogcoord;\n";
+ vsbuf.put(fogparams, strlen(fogparams));
+ vsbuf.put(vsmain, vsend - vsmain);
+ const char *vsfog = "\nfogcoord = dot(fogplane, gl_Position);\n";
+ vsbuf.put(vsfog, strlen(vsfog));
+ vsbuf.put(vsend, strlen(vsend)+1);
+ }
+ const char *psmain = findglslmain(ps), *psend = strrchr(ps, '}');
+ if(psmain && psend)
+ {
+ psbuf.put(ps, psmain - ps);
+ const char *fogparams =
+ "\nuniform vec3 fogcolor;\n"
+ "uniform vec2 fogparams;\n"
+ "varying float fogcoord;\n";
+ psbuf.put(fogparams, strlen(fogparams));
+ psbuf.put(psmain, psend - psmain);
+ const char *psdef = "\n#define FOG_COLOR ";
+ const char *psfog =
+ pspragma && !strncmp(pspragma+pragmalen, "rgba", 4) ?
+ "\ngl_FragColor = mix((FOG_COLOR), gl_FragColor, clamp(fogcoord*fogparams.x + fogparams.y, 0.0, 1.0));\n" :
+ "\ngl_FragColor.rgb = mix((FOG_COLOR).rgb, gl_FragColor.rgb, clamp(fogcoord*fogparams.x + fogparams.y, 0.0, 1.0));\n";
+ int clen = 0;
+ if(pspragma)
+ {
+ pspragma += pragmalen;
+ while(iscubealpha(*pspragma)) pspragma++;
+ while(*pspragma && !iscubespace(*pspragma)) pspragma++;
+ pspragma += strspn(pspragma, " \t\v\f");
+ clen = strcspn(pspragma, "\r\n");
+ }
+ if(clen <= 0) { pspragma = "fogcolor"; clen = strlen(pspragma); }
+ psbuf.put(psdef, strlen(psdef));
+ psbuf.put(pspragma, clen);
+ psbuf.put(psfog, strlen(psfog));
+ psbuf.put(psend, strlen(psend)+1);
+ }
}
static void genuniformdefs(vector<char> &vsbuf, vector<char> &psbuf, const char *vs, const char *ps, Shader *variant = NULL)
{
- if(variant ? variant->defaultparams.empty() : slotparams.empty()) return;
- const char *vsmain = findglslmain(vs), *psmain = findglslmain(ps);
- if(!vsmain || !psmain) return;
- vsbuf.put(vs, vsmain - vs);
- psbuf.put(ps, psmain - ps);
- if(variant) loopv(variant->defaultparams)
- {
- defformatstring(uni, "\nuniform vec4 %s;\n", variant->defaultparams[i].name);
- vsbuf.put(uni, strlen(uni));
- psbuf.put(uni, strlen(uni));
- }
- else loopv(slotparams)
- {
- defformatstring(uni, "\nuniform vec4 %s;\n", slotparams[i].name);
- vsbuf.put(uni, strlen(uni));
- psbuf.put(uni, strlen(uni));
- }
- vsbuf.put(vsmain, strlen(vsmain)+1);
- psbuf.put(psmain, strlen(psmain)+1);
+ if(variant ? variant->defaultparams.empty() : slotparams.empty()) return;
+ const char *vsmain = findglslmain(vs), *psmain = findglslmain(ps);
+ if(!vsmain || !psmain) return;
+ vsbuf.put(vs, vsmain - vs);
+ psbuf.put(ps, psmain - ps);
+ if(variant) loopv(variant->defaultparams)
+ {
+ defformatstring(uni, "\nuniform vec4 %s;\n", variant->defaultparams[i].name);
+ vsbuf.put(uni, strlen(uni));
+ psbuf.put(uni, strlen(uni));
+ }
+ else loopv(slotparams)
+ {
+ defformatstring(uni, "\nuniform vec4 %s;\n", slotparams[i].name);
+ vsbuf.put(uni, strlen(uni));
+ psbuf.put(uni, strlen(uni));
+ }
+ vsbuf.put(vsmain, strlen(vsmain)+1);
+ psbuf.put(psmain, strlen(psmain)+1);
}
VAR(defershaders, 0, 1, 1);
void defershader(int *type, const char *name, const char *contents)
{
- Shader *exists = shaders.access(name);
- if(exists && !exists->invalid()) return;
- if(!defershaders) { execute(contents); return; }
- char *rname = exists ? exists->name : newstring(name);
- Shader &s = shaders[rname];
- s.name = rname;
- DELETEA(s.defer);
- s.defer = newstring(contents);
- s.type = SHADER_DEFERRED | *type;
- s.standard = standardshaders;
+ Shader *exists = shaders.access(name);
+ if(exists && !exists->invalid()) return;
+ if(!defershaders) { execute(contents); return; }
+ char *rname = exists ? exists->name : newstring(name);
+ Shader &s = shaders[rname];
+ s.name = rname;
+ DELETEA(s.defer);
+ s.defer = newstring(contents);
+ s.type = SHADER_DEFERRED | *type;
+ s.standard = standardshaders;
}
void Shader::force()
{
- if(!deferred() || !defer) return;
-
- char *cmd = defer;
- defer = NULL;
- bool wasstandard = standardshaders, wasforcing = forceshaders;
- int oldflags = identflags;
- standardshaders = standard;
- forceshaders = false;
- identflags &= ~IDF_PERSIST;
- slotparams.shrink(0);
- execute(cmd);
- identflags = oldflags;
- forceshaders = wasforcing;
- standardshaders = wasstandard;
- delete[] cmd;
-
- if(deferred())
- {
- DELETEA(defer);
- type = SHADER_INVALID;
- }
+ if(!deferred() || !defer) return;
+
+ char *cmd = defer;
+ defer = NULL;
+ bool wasstandard = standardshaders, wasforcing = forceshaders;
+ int oldflags = identflags;
+ standardshaders = standard;
+ forceshaders = false;
+ identflags &= ~IDF_PERSIST;
+ slotparams.shrink(0);
+ execute(cmd);
+ identflags = oldflags;
+ forceshaders = wasforcing;
+ standardshaders = wasstandard;
+ delete[] cmd;
+
+ if(deferred())
+ {
+ DELETEA(defer);
+ type = SHADER_INVALID;
+ }
}
void fixshaderdetail()
{
- // must null out separately because fixdetailshader can recursively set it
- enumerate(shaders, Shader, s, { if(!s.forced) s.detailshader = NULL; });
- enumerate(shaders, Shader, s, { if(s.forced) s.fixdetailshader(); });
- linkslotshaders();
+ // must null out separately because fixdetailshader can recursively set it
+ enumerate(shaders, Shader, s, { if(!s.forced) s.detailshader = NULL; });
+ enumerate(shaders, Shader, s, { if(s.forced) s.fixdetailshader(); });
+ linkslotshaders();
}
int Shader::uniformlocversion()
{
- static int version = 0;
- if(++version >= 0) return version;
- version = 0;
- enumerate(shaders, Shader, s, { loopvj(s.uniformlocs) s.uniformlocs[j].version = -1; });
- return version;
+ static int version = 0;
+ if(++version >= 0) return version;
+ version = 0;
+ enumerate(shaders, Shader, s, { loopvj(s.uniformlocs) s.uniformlocs[j].version = -1; });
+ return version;
}
VARFP(shaderdetail, 0, MAXSHADERDETAIL, MAXSHADERDETAIL, fixshaderdetail());
void Shader::fixdetailshader(bool shouldforce, bool recurse)
{
- Shader *alt = this;
- detailshader = NULL;
- do
- {
- Shader *cur = shaderdetail < MAXSHADERDETAIL ? alt->fastshader[shaderdetail] : alt;
- if(cur->deferred() && shouldforce) cur->force();
- if(!cur->invalid())
- {
- if(cur->deferred()) break;
- detailshader = cur;
- break;
- }
- alt = alt->altshader;
- } while(alt && alt!=this);
-
- if(recurse && detailshader) loopv(detailshader->variants) detailshader->variants[i]->fixdetailshader(shouldforce, false);
+ Shader *alt = this;
+ detailshader = NULL;
+ do
+ {
+ Shader *cur = shaderdetail < MAXSHADERDETAIL ? alt->fastshader[shaderdetail] : alt;
+ if(cur->deferred() && shouldforce) cur->force();
+ if(!cur->invalid())
+ {
+ if(cur->deferred()) break;
+ detailshader = cur;
+ break;
+ }
+ alt = alt->altshader;
+ } while(alt && alt!=this);
+
+ if(recurse && detailshader) loopv(detailshader->variants) detailshader->variants[i]->fixdetailshader(shouldforce, false);
}
Shader *useshaderbyname(const char *name)
{
- Shader *s = shaders.access(name);
- if(!s) return NULL;
- if(!s->detailshader) s->fixdetailshader();
- s->forced = true;
- return s;
+ Shader *s = shaders.access(name);
+ if(!s) return NULL;
+ if(!s->detailshader) s->fixdetailshader();
+ s->forced = true;
+ return s;
}
void shader(int *type, char *name, char *vs, char *ps)
{
- if(lookupshaderbyname(name)) return;
-
- defformatstring(info, "shader %s", name);
- renderprogress(loadprogress, info);
+ if(lookupshaderbyname(name)) return;
+
+ defformatstring(info, "shader %s", name);
+ renderprogress(loadprogress, info);
- vector<char> vsbuf, psbuf, vsbak, psbak;
+ vector<char> vsbuf, psbuf, vsbak, psbak;
#define GENSHADER(cond, body) \
- if(cond) \
- { \
- if(vsbuf.length()) { vsbak.setsize(0); vsbak.put(vs, strlen(vs)+1); vs = vsbak.getbuf(); vsbuf.setsize(0); } \
- if(psbuf.length()) { psbak.setsize(0); psbak.put(ps, strlen(ps)+1); ps = psbak.getbuf(); psbuf.setsize(0); } \
- body; \
- if(vsbuf.length()) vs = vsbuf.getbuf(); \
- if(psbuf.length()) ps = psbuf.getbuf(); \
- }
- GENSHADER(slotparams.length(), genuniformdefs(vsbuf, psbuf, vs, ps));
- GENSHADER(strstr(vs, "//:fog") || strstr(ps, "//:fog"), genfogshader(vsbuf, psbuf, vs, ps));
- Shader *s = newshader(*type, name, vs, ps);
- if(s)
- {
- if(strstr(vs, "//:water")) genwatervariant(*s, s->name, vs, ps);
- if(strstr(vs, "//:shadowmap")) genshadowmapvariant(*s, s->name, vs, ps);
- if(strstr(vs, "//:dynlight")) gendynlightvariant(*s, s->name, vs, ps);
- }
- slotparams.shrink(0);
+ if(cond) \
+ { \
+ if(vsbuf.length()) { vsbak.setsize(0); vsbak.put(vs, strlen(vs)+1); vs = vsbak.getbuf(); vsbuf.setsize(0); } \
+ if(psbuf.length()) { psbak.setsize(0); psbak.put(ps, strlen(ps)+1); ps = psbak.getbuf(); psbuf.setsize(0); } \
+ body; \
+ if(vsbuf.length()) vs = vsbuf.getbuf(); \
+ if(psbuf.length()) ps = psbuf.getbuf(); \
+ }
+ GENSHADER(slotparams.length(), genuniformdefs(vsbuf, psbuf, vs, ps));
+ GENSHADER(strstr(vs, "//:fog") || strstr(ps, "//:fog"), genfogshader(vsbuf, psbuf, vs, ps));
+ Shader *s = newshader(*type, name, vs, ps);
+ if(s)
+ {
+ if(strstr(vs, "//:water")) genwatervariant(*s, s->name, vs, ps);
+ if(strstr(vs, "//:shadowmap")) genshadowmapvariant(*s, s->name, vs, ps);
+ if(strstr(vs, "//:dynlight")) gendynlightvariant(*s, s->name, vs, ps);
+ }
+ slotparams.shrink(0);
}
void variantshader(int *type, char *name, int *row, char *vs, char *ps)
{
- if(*row < 0)
- {
- shader(type, name, vs, ps);
- return;
- }
- else if(*row >= MAXVARIANTROWS) return;
-
- Shader *s = lookupshaderbyname(name);
- if(!s) return;
-
- defformatstring(varname, "<variant:%d,%d>%s", s->numvariants(*row), *row, name);
- //defformatstring(info, "shader %s", varname);
- //renderprogress(loadprogress, info);
- vector<char> vsbuf, psbuf, vsbak, psbak;
- GENSHADER(s->defaultparams.length(), genuniformdefs(vsbuf, psbuf, vs, ps, s));
- GENSHADER(strstr(vs, "//:fog") || strstr(ps, "//:fog"), genfogshader(vsbuf, psbuf, vs, ps));
- Shader *v = newshader(*type, varname, vs, ps, s, *row);
- if(v)
- {
- if(strstr(vs, "//:dynlight")) gendynlightvariant(*s, varname, vs, ps, *row);
- if(strstr(ps, "//:variant") || strstr(vs, "//:variant")) gengenericvariant(*s, varname, vs, ps, *row);
- }
+ if(*row < 0)
+ {
+ shader(type, name, vs, ps);
+ return;
+ }
+ else if(*row >= MAXVARIANTROWS) return;
+
+ Shader *s = lookupshaderbyname(name);
+ if(!s) return;
+
+ defformatstring(varname, "<variant:%d,%d>%s", s->numvariants(*row), *row, name);
+ //defformatstring(info, "shader %s", varname);
+ //renderprogress(loadprogress, info);
+ vector<char> vsbuf, psbuf, vsbak, psbak;
+ GENSHADER(s->defaultparams.length(), genuniformdefs(vsbuf, psbuf, vs, ps, s));
+ GENSHADER(strstr(vs, "//:fog") || strstr(ps, "//:fog"), genfogshader(vsbuf, psbuf, vs, ps));
+ Shader *v = newshader(*type, varname, vs, ps, s, *row);
+ if(v)
+ {
+ if(strstr(vs, "//:dynlight")) gendynlightvariant(*s, varname, vs, ps, *row);
+ if(strstr(ps, "//:variant") || strstr(vs, "//:variant")) gengenericvariant(*s, varname, vs, ps, *row);
+ }
}
void setshader(char *name)
{
- slotparams.shrink(0);
- Shader *s = shaders.access(name);
- if(!s)
- {
- conoutf(CON_ERROR, "no such shader: %s", name);
- }
- else slotshader = s;
+ slotparams.shrink(0);
+ Shader *s = shaders.access(name);
+ if(!s)
+ {
+ conoutf(CON_ERROR, "no such shader: %s", name);
+ }
+ else slotshader = s;
}
void resetslotshader()
{
- slotshader = NULL;
- slotparams.shrink(0);
+ slotshader = NULL;
+ slotparams.shrink(0);
}
void setslotshader(Slot &s)
{
- s.shader = slotshader;
- if(!s.shader)
- {
- s.shader = stdworldshader;
- return;
- }
- loopv(slotparams) s.params.add(slotparams[i]);
+ s.shader = slotshader;
+ if(!s.shader)
+ {
+ s.shader = stdworldshader;
+ return;
+ }
+ loopv(slotparams) s.params.add(slotparams[i]);
}
static void linkslotshaderparams(vector<SlotShaderParam> &params, Shader *sh, bool load)
{
- if(sh) loopv(params)
- {
- int loc = -1;
- SlotShaderParam &param = params[i];
- loopv(sh->defaultparams)
- {
- SlotShaderParamState &dparam = sh->defaultparams[i];
- if(dparam.name==param.name)
- {
- if(memcmp(param.val, dparam.val, sizeof(param.val))) loc = i;
- break;
- }
- }
- param.loc = loc;
- }
- else if(load) loopv(params) params[i].loc = -1;
+ if(sh) loopv(params)
+ {
+ int loc = -1;
+ SlotShaderParam &param = params[i];
+ loopv(sh->defaultparams)
+ {
+ SlotShaderParamState &dparam = sh->defaultparams[i];
+ if(dparam.name==param.name)
+ {
+ if(memcmp(param.val, dparam.val, sizeof(param.val))) loc = i;
+ break;
+ }
+ }
+ param.loc = loc;
+ }
+ else if(load) loopv(params) params[i].loc = -1;
}
void linkslotshader(Slot &s, bool load)
{
- if(!s.shader) return;
+ if(!s.shader) return;
- if(load && !s.shader->detailshader) s.shader->fixdetailshader();
+ if(load && !s.shader->detailshader) s.shader->fixdetailshader();
- linkslotshaderparams(s.params, s.shader->detailshader, load);
+ linkslotshaderparams(s.params, s.shader->detailshader, load);
}
void linkvslotshader(VSlot &s, bool load)
{
- if(!s.slot->shader) return;
+ if(!s.slot->shader) return;
- Shader *sh = s.slot->shader->detailshader;
- linkslotshaderparams(s.params, sh, load);
+ Shader *sh = s.slot->shader->detailshader;
+ linkslotshaderparams(s.params, sh, load);
- if(!sh) return;
+ if(!sh) return;
- if(s.slot->texmask&(1<<TEX_GLOW))
- {
- static const char *paramname = getshaderparamname("glowcolor");
- const float *param = findslotparam(s, paramname);
- if(param) s.glowcolor = vec(param).clamp(0, 1);
- }
+ if(s.slot->texmask&(1<<TEX_GLOW))
+ {
+ static const char *paramname = getshaderparamname("glowcolor");
+ const float *param = findslotparam(s, paramname);
+ if(param) s.glowcolor = vec(param).clamp(0, 1);
+ }
}
void altshader(char *origname, char *altname)
{
- Shader *orig = shaders.access(origname), *alt = shaders.access(altname);
- if(!orig || !alt) return;
- orig->altshader = alt;
- orig->fixdetailshader(false);
+ Shader *orig = shaders.access(origname), *alt = shaders.access(altname);
+ if(!orig || !alt) return;
+ orig->altshader = alt;
+ orig->fixdetailshader(false);
}
void fastshader(char *nice, char *fast, int *detail)
{
- Shader *ns = shaders.access(nice), *fs = shaders.access(fast);
- if(!ns || !fs) return;
- loopi(min(*detail+1, MAXSHADERDETAIL)) ns->fastshader[i] = fs;
- ns->fixdetailshader(false);
+ Shader *ns = shaders.access(nice), *fs = shaders.access(fast);
+ if(!ns || !fs) return;
+ loopi(min(*detail+1, MAXSHADERDETAIL)) ns->fastshader[i] = fs;
+ ns->fixdetailshader(false);
}
COMMAND(shader, "isss");
@@ -1233,28 +1228,28 @@ static hashset<const char *> shaderparamnames(256);
const char *getshaderparamname(const char *name, bool insert)
{
- const char *exists = shaderparamnames.find(name, NULL);
- if(exists || !insert) return exists;
- return shaderparamnames.add(newstring(name));
+ const char *exists = shaderparamnames.find(name, NULL);
+ if(exists || !insert) return exists;
+ return shaderparamnames.add(newstring(name));
}
void addslotparam(const char *name, float x, float y, float z, float w)
{
- if(name) name = getshaderparamname(name);
- loopv(slotparams)
- {
- SlotShaderParam &param = slotparams[i];
- if(param.name==name)
- {
- param.val[0] = x;
- param.val[1] = y;
- param.val[2] = z;
- param.val[3] = w;
- return;
- }
- }
- SlotShaderParam param = {name, -1, {x, y, z, w}};
- slotparams.add(param);
+ if(name) name = getshaderparamname(name);
+ loopv(slotparams)
+ {
+ SlotShaderParam &param = slotparams[i];
+ if(param.name==name)
+ {
+ param.val[0] = x;
+ param.val[1] = y;
+ param.val[2] = z;
+ param.val[3] = w;
+ return;
+ }
+ }
+ SlotShaderParam param = {name, -1, {x, y, z, w}};
+ slotparams.add(param);
}
ICOMMAND(setuniformparam, "sffff", (char *name, float *x, float *y, float *z, float *w), addslotparam(name, *x, *y, *z, *w));
@@ -1265,10 +1260,10 @@ ICOMMAND(defuniformparam, "sffff", (char *name, float *x, float *y, float *z, fl
struct postfxtex
{
- GLuint id;
- int scale, used;
+ GLuint id;
+ int scale, used;
- postfxtex() : id(0), scale(0), used(-1) {}
+ postfxtex() : id(0), scale(0), used(-1) {}
};
vector<postfxtex> postfxtexs;
int postfxbinds[NUMPOSTFXBINDS];
@@ -1277,246 +1272,246 @@ int postfxw = 0, postfxh = 0;
struct postfxpass
{
- Shader *shader;
- vec4 params;
- uint inputs, freeinputs;
- int outputbind, outputscale;
+ Shader *shader;
+ vec4 params;
+ uint inputs, freeinputs;
+ int outputbind, outputscale;
- postfxpass() : shader(NULL), inputs(1), freeinputs(1), outputbind(0), outputscale(0) {}
+ postfxpass() : shader(NULL), inputs(1), freeinputs(1), outputbind(0), outputscale(0) {}
};
vector<postfxpass> postfxpasses;
static int allocatepostfxtex(int scale)
{
- loopv(postfxtexs)
- {
- postfxtex &t = postfxtexs[i];
- if(t.scale==scale && t.used < 0) return i;
- }
- postfxtex &t = postfxtexs.add();
- t.scale = scale;
- glGenTextures(1, &t.id);
- createtexture(t.id, max(screenw>>scale, 1), max(screenh>>scale, 1), NULL, 3, 1, GL_RGB);
- return postfxtexs.length()-1;
+ loopv(postfxtexs)
+ {
+ postfxtex &t = postfxtexs[i];
+ if(t.scale==scale && t.used < 0) return i;
+ }
+ postfxtex &t = postfxtexs.add();
+ t.scale = scale;
+ glGenTextures(1, &t.id);
+ createtexture(t.id, max(screenw>>scale, 1), max(screenh>>scale, 1), NULL, 3, 1, GL_RGB);
+ return postfxtexs.length()-1;
}
void cleanuppostfx(bool fullclean)
{
- if(fullclean && postfxfb)
- {
- glDeleteFramebuffers_(1, &postfxfb);
- postfxfb = 0;
- }
+ if(fullclean && postfxfb)
+ {
+ glDeleteFramebuffers_(1, &postfxfb);
+ postfxfb = 0;
+ }
- loopv(postfxtexs) glDeleteTextures(1, &postfxtexs[i].id);
- postfxtexs.shrink(0);
+ loopv(postfxtexs) glDeleteTextures(1, &postfxtexs[i].id);
+ postfxtexs.shrink(0);
- postfxw = 0;
- postfxh = 0;
+ postfxw = 0;
+ postfxh = 0;
}
void renderpostfx()
{
- if(postfxpasses.empty()) return;
-
- if(postfxw != screenw || postfxh != screenh)
- {
- cleanuppostfx(false);
- postfxw = screenw;
- postfxh = screenh;
- }
-
- int binds[NUMPOSTFXBINDS];
- loopi(NUMPOSTFXBINDS) binds[i] = -1;
- loopv(postfxtexs) postfxtexs[i].used = -1;
-
- binds[0] = allocatepostfxtex(0);
- postfxtexs[binds[0]].used = 0;
- glBindTexture(GL_TEXTURE_2D, postfxtexs[binds[0]].id);
- glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, screenw, screenh);
-
- if(postfxpasses.length() > 1)
- {
- if(!postfxfb) glGenFramebuffers_(1, &postfxfb);
- glBindFramebuffer_(GL_FRAMEBUFFER, postfxfb);
- }
-
- GLOBALPARAMF(millis, lastmillis/1000.0f);
-
- loopv(postfxpasses)
- {
- postfxpass &p = postfxpasses[i];
-
- int tex = -1;
- if(!postfxpasses.inrange(i+1))
- {
- if(postfxpasses.length() > 1) glBindFramebuffer_(GL_FRAMEBUFFER, 0);
- }
- else
- {
- tex = allocatepostfxtex(p.outputscale);
- glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, postfxtexs[tex].id, 0);
- }
-
- int w = tex >= 0 ? max(screenw>>postfxtexs[tex].scale, 1) : screenw,
- h = tex >= 0 ? max(screenh>>postfxtexs[tex].scale, 1) : screenh;
- glViewport(0, 0, w, h);
- p.shader->set();
- LOCALPARAM(params, p.params);
- int tw = w, th = h, tmu = 0;
- loopj(NUMPOSTFXBINDS) if(p.inputs&(1<<j) && binds[j] >= 0)
- {
- if(!tmu)
- {
- tw = max(screenw>>postfxtexs[binds[j]].scale, 1);
- th = max(screenh>>postfxtexs[binds[j]].scale, 1);
- }
- else glActiveTexture_(GL_TEXTURE0 + tmu);
- glBindTexture(GL_TEXTURE_2D, postfxtexs[binds[j]].id);
- ++tmu;
- }
- if(tmu) glActiveTexture_(GL_TEXTURE0);
- LOCALPARAMF(postfxscale, 1.0f/tw, 1.0f/th);
- screenquad(1, 1);
-
- loopj(NUMPOSTFXBINDS) if(p.freeinputs&(1<<j) && binds[j] >= 0)
- {
- postfxtexs[binds[j]].used = -1;
- binds[j] = -1;
- }
- if(tex >= 0)
- {
- if(binds[p.outputbind] >= 0) postfxtexs[binds[p.outputbind]].used = -1;
- binds[p.outputbind] = tex;
- postfxtexs[tex].used = p.outputbind;
- }
- }
+ if(postfxpasses.empty()) return;
+
+ if(postfxw != screenw || postfxh != screenh)
+ {
+ cleanuppostfx(false);
+ postfxw = screenw;
+ postfxh = screenh;
+ }
+
+ int binds[NUMPOSTFXBINDS];
+ loopi(NUMPOSTFXBINDS) binds[i] = -1;
+ loopv(postfxtexs) postfxtexs[i].used = -1;
+
+ binds[0] = allocatepostfxtex(0);
+ postfxtexs[binds[0]].used = 0;
+ glBindTexture(GL_TEXTURE_2D, postfxtexs[binds[0]].id);
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, screenw, screenh);
+
+ if(postfxpasses.length() > 1)
+ {
+ if(!postfxfb) glGenFramebuffers_(1, &postfxfb);
+ glBindFramebuffer_(GL_FRAMEBUFFER, postfxfb);
+ }
+
+ GLOBALPARAMF(millis, lastmillis/1000.0f);
+
+ loopv(postfxpasses)
+ {
+ postfxpass &p = postfxpasses[i];
+
+ int tex = -1;
+ if(!postfxpasses.inrange(i+1))
+ {
+ if(postfxpasses.length() > 1) glBindFramebuffer_(GL_FRAMEBUFFER, 0);
+ }
+ else
+ {
+ tex = allocatepostfxtex(p.outputscale);
+ glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, postfxtexs[tex].id, 0);
+ }
+
+ int w = tex >= 0 ? max(screenw>>postfxtexs[tex].scale, 1) : screenw,
+ h = tex >= 0 ? max(screenh>>postfxtexs[tex].scale, 1) : screenh;
+ glViewport(0, 0, w, h);
+ p.shader->set();
+ LOCALPARAM(params, p.params);
+ int tw = w, th = h, tmu = 0;
+ loopj(NUMPOSTFXBINDS) if(p.inputs&(1<<j) && binds[j] >= 0)
+ {
+ if(!tmu)
+ {
+ tw = max(screenw>>postfxtexs[binds[j]].scale, 1);
+ th = max(screenh>>postfxtexs[binds[j]].scale, 1);
+ }
+ else glActiveTexture_(GL_TEXTURE0 + tmu);
+ glBindTexture(GL_TEXTURE_2D, postfxtexs[binds[j]].id);
+ ++tmu;
+ }
+ if(tmu) glActiveTexture_(GL_TEXTURE0);
+ LOCALPARAMF(postfxscale, 1.0f/tw, 1.0f/th);
+ screenquad(1, 1);
+
+ loopj(NUMPOSTFXBINDS) if(p.freeinputs&(1<<j) && binds[j] >= 0)
+ {
+ postfxtexs[binds[j]].used = -1;
+ binds[j] = -1;
+ }
+ if(tex >= 0)
+ {
+ if(binds[p.outputbind] >= 0) postfxtexs[binds[p.outputbind]].used = -1;
+ binds[p.outputbind] = tex;
+ postfxtexs[tex].used = p.outputbind;
+ }
+ }
}
static bool addpostfx(const char *name, int outputbind, int outputscale, uint inputs, uint freeinputs, const vec4 &params)
{
- if(!*name) return false;
- Shader *s = useshaderbyname(name);
- if(!s)
- {
- conoutf(CON_ERROR, "no such postfx shader: %s", name);
- return false;
- }
- postfxpass &p = postfxpasses.add();
- p.shader = s;
- p.outputbind = outputbind;
- p.outputscale = outputscale;
- p.inputs = inputs;
- p.freeinputs = freeinputs;
- p.params = params;
- return true;
+ if(!*name) return false;
+ Shader *s = useshaderbyname(name);
+ if(!s)
+ {
+ conoutf(CON_ERROR, "no such postfx shader: %s", name);
+ return false;
+ }
+ postfxpass &p = postfxpasses.add();
+ p.shader = s;
+ p.outputbind = outputbind;
+ p.outputscale = outputscale;
+ p.inputs = inputs;
+ p.freeinputs = freeinputs;
+ p.params = params;
+ return true;
}
void clearpostfx()
{
- postfxpasses.shrink(0);
- cleanuppostfx(false);
+ postfxpasses.shrink(0);
+ cleanuppostfx(false);
}
COMMAND(clearpostfx, "");
ICOMMAND(addpostfx, "siisffff", (char *name, int *bind, int *scale, char *inputs, float *x, float *y, float *z, float *w),
{
- int inputmask = inputs[0] ? 0 : 1;
- int freemask = inputs[0] ? 0 : 1;
- bool freeinputs = true;
- for(; *inputs; inputs++) if(isdigit(*inputs))
- {
- inputmask |= 1<<(*inputs-'0');
- if(freeinputs) freemask |= 1<<(*inputs-'0');
- }
- else if(*inputs=='+') freeinputs = false;
- else if(*inputs=='-') freeinputs = true;
- inputmask &= (1<<NUMPOSTFXBINDS)-1;
- freemask &= (1<<NUMPOSTFXBINDS)-1;
- addpostfx(name, clamp(*bind, 0, NUMPOSTFXBINDS-1), max(*scale, 0), inputmask, freemask, vec4(*x, *y, *z, *w));
+ int inputmask = inputs[0] ? 0 : 1;
+ int freemask = inputs[0] ? 0 : 1;
+ bool freeinputs = true;
+ for(; *inputs; inputs++) if(isdigit(*inputs))
+ {
+ inputmask |= 1<<(*inputs-'0');
+ if(freeinputs) freemask |= 1<<(*inputs-'0');
+ }
+ else if(*inputs=='+') freeinputs = false;
+ else if(*inputs=='-') freeinputs = true;
+ inputmask &= (1<<NUMPOSTFXBINDS)-1;
+ freemask &= (1<<NUMPOSTFXBINDS)-1;
+ addpostfx(name, clamp(*bind, 0, NUMPOSTFXBINDS-1), max(*scale, 0), inputmask, freemask, vec4(*x, *y, *z, *w));
});
ICOMMAND(setpostfx, "sffff", (char *name, float *x, float *y, float *z, float *w),
{
- clearpostfx();
- if(name[0]) addpostfx(name, 0, 0, 1, 1, vec4(*x, *y, *z, *w));
+ clearpostfx();
+ if(name[0]) addpostfx(name, 0, 0, 1, 1, vec4(*x, *y, *z, *w));
});
void cleanupshaders()
{
- cleanuppostfx(true);
+ cleanuppostfx(true);
- loadedshaders = false;
- nullshader = hudshader = hudnotextureshader = textureshader = notextureshader = nocolorshader = foggedshader = foggednotextureshader = stdworldshader = NULL;
- enumerate(shaders, Shader, s, s.cleanup());
- Shader::lastshader = NULL;
- glUseProgram_(0);
+ loadedshaders = false;
+ nullshader = hudshader = hudnotextureshader = textureshader = notextureshader = nocolorshader = foggedshader = foggednotextureshader = stdworldshader = NULL;
+ enumerate(shaders, Shader, s, s.cleanup());
+ Shader::lastshader = NULL;
+ glUseProgram_(0);
}
void reloadshaders()
{
- identflags &= ~IDF_PERSIST;
- loadshaders();
- identflags |= IDF_PERSIST;
- linkslotshaders();
- enumerate(shaders, Shader, s,
- {
- if(!s.standard && !(s.type&(SHADER_DEFERRED|SHADER_INVALID)) && !s.variantshader)
- {
- defformatstring(info, "shader %s", s.name);
- renderprogress(0.0, info);
- if(!s.compile()) s.cleanup(true);
- loopv(s.variants)
- {
- Shader *v = s.variants[i];
- if((v->reusevs && v->reusevs->invalid()) ||
- (v->reuseps && v->reuseps->invalid()) ||
- !v->compile())
- v->cleanup(true);
- }
- }
- if(s.forced && !s.detailshader) s.fixdetailshader();
- });
+ identflags &= ~IDF_PERSIST;
+ loadshaders();
+ identflags |= IDF_PERSIST;
+ linkslotshaders();
+ enumerate(shaders, Shader, s,
+ {
+ if(!s.standard && !(s.type&(SHADER_DEFERRED|SHADER_INVALID)) && !s.variantshader)
+ {
+ defformatstring(info, "shader %s", s.name);
+ renderprogress(0.0, info);
+ if(!s.compile()) s.cleanup(true);
+ loopv(s.variants)
+ {
+ Shader *v = s.variants[i];
+ if((v->reusevs && v->reusevs->invalid()) ||
+ (v->reuseps && v->reuseps->invalid()) ||
+ !v->compile())
+ v->cleanup(true);
+ }
+ }
+ if(s.forced && !s.detailshader) s.fixdetailshader();
+ });
}
void setupblurkernel(int radius, float sigma, float *weights, float *offsets)
{
- if(radius<1 || radius>MAXBLURRADIUS) return;
- sigma *= 2*radius;
- float total = 1.0f/sigma;
- weights[0] = total;
- offsets[0] = 0;
- // rely on bilinear filtering to sample 2 pixels at once
- // transforms a*X + b*Y into (u+v)*[X*u/(u+v) + Y*(1 - u/(u+v))]
- loopi(radius)
- {
- float weight1 = exp(-((2*i)*(2*i)) / (2*sigma*sigma)) / sigma,
- weight2 = exp(-((2*i+1)*(2*i+1)) / (2*sigma*sigma)) / sigma,
- scale = weight1 + weight2,
- offset = 2*i+1 + weight2 / scale;
- weights[i+1] = scale;
- offsets[i+1] = offset;
- total += 2*scale;
- }
- loopi(radius+1) weights[i] /= total;
- for(int i = radius+1; i <= MAXBLURRADIUS; i++) weights[i] = offsets[i] = 0;
+ if(radius<1 || radius>MAXBLURRADIUS) return;
+ sigma *= 2*radius;
+ float total = 1.0f/sigma;
+ weights[0] = total;
+ offsets[0] = 0;
+ // rely on bilinear filtering to sample 2 pixels at once
+ // transforms a*X + b*Y into (u+v)*[X*u/(u+v) + Y*(1 - u/(u+v))]
+ loopi(radius)
+ {
+ float weight1 = exp(-((2*i)*(2*i)) / (2*sigma*sigma)) / sigma,
+ weight2 = exp(-((2*i+1)*(2*i+1)) / (2*sigma*sigma)) / sigma,
+ scale = weight1 + weight2,
+ offset = 2*i+1 + weight2 / scale;
+ weights[i+1] = scale;
+ offsets[i+1] = offset;
+ total += 2*scale;
+ }
+ loopi(radius+1) weights[i] /= total;
+ for(int i = radius+1; i <= MAXBLURRADIUS; i++) weights[i] = offsets[i] = 0;
}
void setblurshader(int pass, int size, int radius, float *weights, float *offsets)
{
- if(radius<1 || radius>MAXBLURRADIUS) return;
- static Shader *blurshader[7][2] = { { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL } };
- Shader *&s = blurshader[radius-1][pass];
- if(!s)
- {
- defformatstring(name, "blur%c%d", 'x'+pass, radius);
- s = lookupshaderbyname(name);
- }
- s->set();
- LOCALPARAMV(weights, weights, 8);
- float scaledoffsets[8];
- loopk(8) scaledoffsets[k] = offsets[k]/size;
- LOCALPARAMV(offsets, scaledoffsets, 8);
+ if(radius<1 || radius>MAXBLURRADIUS) return;
+ static Shader *blurshader[7][2] = { { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL } };
+ Shader *&s = blurshader[radius-1][pass];
+ if(!s)
+ {
+ defformatstring(name, "blur%c%d", 'x'+pass, radius);
+ s = lookupshaderbyname(name);
+ }
+ s->set();
+ LOCALPARAMV(weights, weights, 8);
+ float scaledoffsets[8];
+ loopk(8) scaledoffsets[k] = offsets[k]/size;
+ LOCALPARAMV(offsets, scaledoffsets, 8);
}
diff --git a/src/engine/shadowmap.cpp b/src/engine/shadowmap.cpp
index 7b7f894..2216452 100644
--- a/src/engine/shadowmap.cpp
+++ b/src/engine/shadowmap.cpp
@@ -13,8 +13,8 @@ VARFP(shadowmapprecision, 0, 0, 1, cleanshadowmap());
bvec shadowmapambientcolor(0, 0, 0);
HVARFR(shadowmapambient, 0, 0, 0xFFFFFF,
{
- if(shadowmapambient <= 255) shadowmapambient |= (shadowmapambient<<8) | (shadowmapambient<<16);
- shadowmapambientcolor = bvec((shadowmapambient>>16)&0xFF, (shadowmapambient>>8)&0xFF, shadowmapambient&0xFF);
+ if(shadowmapambient <= 255) shadowmapambient |= (shadowmapambient<<8) | (shadowmapambient<<16);
+ shadowmapambientcolor = bvec((shadowmapambient>>16)&0xFF, (shadowmapambient>>8)&0xFF, shadowmapambient&0xFF);
});
VARP(shadowmapintensity, 0, 40, 100);
@@ -29,54 +29,54 @@ float shadowmapmaxz = 0;
void setshadowdir(int angle)
{
- shadowdir = vec(0, SHADOWSKEW, 1);
- shadowdir.rotate_around_z(angle*RAD);
+ shadowdir = vec(0, SHADOWSKEW, 1);
+ shadowdir.rotate_around_z(angle*RAD);
}
VARFR(shadowmapangle, 0, 0, 360, setshadowdir(shadowmapangle));
void guessshadowdir()
{
- if(shadowmapangle) return;
- vec dir;
- if(!sunlightcolor.iszero()) dir = sunlightdir;
- else
- {
- vec lightpos(0, 0, 0), casterpos(0, 0, 0);
- int numlights = 0, numcasters = 0;
- const vector<extentity *> &ents = entities::getents();
- loopv(ents)
- {
- extentity &e = *ents[i];
- switch(e.type)
- {
- case ET_LIGHT:
- if(!e.attr1) { lightpos.add(e.o); numlights++; }
- break;
-
- case ET_MAPMODEL:
- casterpos.add(e.o);
- numcasters++;
- break;
-
- default:
- if(e.type<ET_GAMESPECIFIC) break;
- casterpos.add(e.o);
- numcasters++;
- break;
- }
- }
- if(!numlights || !numcasters) return;
- lightpos.div(numlights);
- casterpos.div(numcasters);
- dir = vec(lightpos).sub(casterpos);
- }
- dir.z = 0;
- if(dir.iszero()) return;
- dir.normalize();
- dir.mul(SHADOWSKEW);
- dir.z = 1;
- shadowdir = dir;
+ if(shadowmapangle) return;
+ vec dir;
+ if(!sunlightcolor.iszero()) dir = sunlightdir;
+ else
+ {
+ vec lightpos(0, 0, 0), casterpos(0, 0, 0);
+ int numlights = 0, numcasters = 0;
+ const vector<extentity *> &ents = entities::getents();
+ loopv(ents)
+ {
+ extentity &e = *ents[i];
+ switch(e.type)
+ {
+ case ET_LIGHT:
+ if(!e.attr1) { lightpos.add(e.o); numlights++; }
+ break;
+
+ case ET_MAPMODEL:
+ casterpos.add(e.o);
+ numcasters++;
+ break;
+
+ default:
+ if(e.type<ET_GAMESPECIFIC) break;
+ casterpos.add(e.o);
+ numcasters++;
+ break;
+ }
+ }
+ if(!numlights || !numcasters) return;
+ lightpos.div(numlights);
+ casterpos.div(numcasters);
+ dir = vec(lightpos).sub(casterpos);
+ }
+ dir.z = 0;
+ if(dir.iszero()) return;
+ dir.normalize();
+ dir.mul(SHADOWSKEW);
+ dir.z = 1;
+ shadowdir = dir;
}
bool shadowmapping = false;
@@ -90,190 +90,190 @@ VAR(smoothshadowmappeel, 1, 0, 0);
static struct shadowmaptexture : rendertarget
{
- const GLenum *colorformats() const
- {
- static const GLenum rgbafmts[] = { GL_RGBA16F, GL_RGBA16, GL_RGBA, GL_RGBA8, GL_FALSE };
- return &rgbafmts[fpshadowmap && hasTF ? 0 : (shadowmapprecision ? 1 : 2)];
- }
-
- bool swaptexs() const { return true; }
-
- bool scissorblur(int &x, int &y, int &w, int &h)
- {
- x = max(int(floor((scissorx1+1)/2*vieww)) - 2*blursize, 2);
- y = max(int(floor((scissory1+1)/2*viewh)) - 2*blursize, 2);
- w = min(int(ceil((scissorx2+1)/2*vieww)) + 2*blursize, vieww-2) - x;
- h = min(int(ceil((scissory2+1)/2*viewh)) + 2*blursize, viewh-2) - y;
- return true;
- }
-
- bool scissorrender(int &x, int &y, int &w, int &h)
- {
- x = y = 2;
- w = vieww - 2*2;
- h = viewh - 2*2;
- return true;
- }
-
- void doclear()
- {
- glClearColor(0, 0, 0, 0);
- glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
- }
-
- bool dorender()
- {
- vec skewdir(shadowdir);
- skewdir.rotate_around_z(-camera1->yaw*RAD);
-
- vec dir;
- vecfromyawpitch(camera1->yaw, camera1->pitch, 1, 0, dir);
- dir.z = 0;
- dir.mul(shadowmapradius);
-
- vec dirx, diry;
- vecfromyawpitch(camera1->yaw, 0, 0, 1, dirx);
- vecfromyawpitch(camera1->yaw, 0, 1, 0, diry);
- shadowoffset.x = -fmod(dirx.dot(camera1->o) - skewdir.x*camera1->o.z, 2.0f*shadowmapradius/vieww);
- shadowoffset.y = -fmod(diry.dot(camera1->o) - skewdir.y*camera1->o.z, 2.0f*shadowmapradius/viewh);
-
- shadowmatrix.ortho(-shadowmapradius, shadowmapradius, -shadowmapradius, shadowmapradius, -shadowmapdist, shadowmapdist);
- shadowmatrix.mul(matrix3(vec(1, 0, 0), vec(0, 1, 0), vec(skewdir.x, skewdir.y, 1)));
- shadowmatrix.translate(skewdir.x*shadowmapheight + shadowoffset.x, skewdir.y*shadowmapheight + shadowoffset.y + dir.magnitude(), -shadowmapheight);
- shadowmatrix.rotate_around_z((camera1->yaw+180)*-RAD);
- shadowmatrix.translate(vec(camera1->o).neg());
- GLOBALPARAM(shadowmatrix, shadowmatrix);
-
- shadowfocus = camera1->o;
- shadowfocus.add(dir);
- shadowfocus.add(vec(shadowdir).mul(shadowmapheight));
- shadowfocus.add(dirx.mul(shadowoffset.x));
- shadowfocus.add(diry.mul(shadowoffset.y));
-
- gle::colorf(0, 0, 0);
-
- GLOBALPARAMF(shadowmapbias, -shadowmapbias/float(shadowmapdist), 1 - (shadowmapbias + (smoothshadowmappeel ? 0 : shadowmappeelbias))/float(shadowmapdist));
-
- shadowmapcasters = 0;
- shadowmapmaxz = shadowfocus.z - shadowmapdist;
- shadowmapping = true;
- rendergame();
- shadowmapping = false;
- shadowmapmaxz = min(shadowmapmaxz, shadowfocus.z);
-
- if(shadowmapcasters && smdepthpeel)
- {
- int sx, sy, sw, sh;
- bool scissoring = rtscissor && scissorblur(sx, sy, sw, sh) && sw > 0 && sh > 0;
- if(scissoring) glScissor(sx, sy, sw, sh);
- if(!rtscissor || scissoring) rendershadowmapreceivers();
- }
-
- return shadowmapcasters>0;
- }
+ const GLenum *colorformats() const
+ {
+ static const GLenum rgbafmts[] = { GL_RGBA16F, GL_RGBA16, GL_RGBA, GL_RGBA8, GL_FALSE };
+ return &rgbafmts[fpshadowmap && hasTF ? 0 : (shadowmapprecision ? 1 : 2)];
+ }
+
+ bool swaptexs() const { return true; }
+
+ bool scissorblur(int &x, int &y, int &w, int &h)
+ {
+ x = max(int(floor((scissorx1+1)/2*vieww)) - 2*blursize, 2);
+ y = max(int(floor((scissory1+1)/2*viewh)) - 2*blursize, 2);
+ w = min(int(ceil((scissorx2+1)/2*vieww)) + 2*blursize, vieww-2) - x;
+ h = min(int(ceil((scissory2+1)/2*viewh)) + 2*blursize, viewh-2) - y;
+ return true;
+ }
+
+ bool scissorrender(int &x, int &y, int &w, int &h)
+ {
+ x = y = 2;
+ w = vieww - 2*2;
+ h = viewh - 2*2;
+ return true;
+ }
+
+ void doclear()
+ {
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ }
+
+ bool dorender()
+ {
+ vec skewdir(shadowdir);
+ skewdir.rotate_around_z(-camera1->yaw*RAD);
+
+ vec dir;
+ vecfromyawpitch(camera1->yaw, camera1->pitch, 1, 0, dir);
+ dir.z = 0;
+ dir.mul(shadowmapradius);
+
+ vec dirx, diry;
+ vecfromyawpitch(camera1->yaw, 0, 0, 1, dirx);
+ vecfromyawpitch(camera1->yaw, 0, 1, 0, diry);
+ shadowoffset.x = -fmod(dirx.dot(camera1->o) - skewdir.x*camera1->o.z, 2.0f*shadowmapradius/vieww);
+ shadowoffset.y = -fmod(diry.dot(camera1->o) - skewdir.y*camera1->o.z, 2.0f*shadowmapradius/viewh);
+
+ shadowmatrix.ortho(-shadowmapradius, shadowmapradius, -shadowmapradius, shadowmapradius, -shadowmapdist, shadowmapdist);
+ shadowmatrix.mul(matrix3(vec(1, 0, 0), vec(0, 1, 0), vec(skewdir.x, skewdir.y, 1)));
+ shadowmatrix.translate(skewdir.x*shadowmapheight + shadowoffset.x, skewdir.y*shadowmapheight + shadowoffset.y + dir.magnitude(), -shadowmapheight);
+ shadowmatrix.rotate_around_z((camera1->yaw+180)*-RAD);
+ shadowmatrix.translate(vec(camera1->o).neg());
+ GLOBALPARAM(shadowmatrix, shadowmatrix);
+
+ shadowfocus = camera1->o;
+ shadowfocus.add(dir);
+ shadowfocus.add(vec(shadowdir).mul(shadowmapheight));
+ shadowfocus.add(dirx.mul(shadowoffset.x));
+ shadowfocus.add(diry.mul(shadowoffset.y));
+
+ gle::colorf(0, 0, 0);
+
+ GLOBALPARAMF(shadowmapbias, -shadowmapbias/float(shadowmapdist), 1 - (shadowmapbias + (smoothshadowmappeel ? 0 : shadowmappeelbias))/float(shadowmapdist));
+
+ shadowmapcasters = 0;
+ shadowmapmaxz = shadowfocus.z - shadowmapdist;
+ shadowmapping = true;
+ rendergame();
+ shadowmapping = false;
+ shadowmapmaxz = min(shadowmapmaxz, shadowfocus.z);
+
+ if(shadowmapcasters && smdepthpeel)
+ {
+ int sx, sy, sw, sh;
+ bool scissoring = rtscissor && scissorblur(sx, sy, sw, sh) && sw > 0 && sh > 0;
+ if(scissoring) glScissor(sx, sy, sw, sh);
+ if(!rtscissor || scissoring) rendershadowmapreceivers();
+ }
+
+ return shadowmapcasters>0;
+ }
} shadowmaptex;
void cleanshadowmap()
{
- shadowmaptex.cleanup(true);
+ shadowmaptex.cleanup(true);
}
void calcshadowmapbb(const vec &o, float xyrad, float zrad, float &x1, float &y1, float &x2, float &y2)
{
- vec skewdir(shadowdir);
- skewdir.rotate_around_z(-camera1->yaw*RAD);
-
- vec ro(o);
- ro.sub(camera1->o);
- ro.rotate_around_z(-(camera1->yaw+180)*RAD);
- ro.x += ro.z * skewdir.x + shadowoffset.x;
- ro.y += ro.z * skewdir.y + shadowmapradius * cosf(camera1->pitch*RAD) + shadowoffset.y;
-
- vec high(ro), low(ro);
- high.x += zrad * skewdir.x;
- high.y += zrad * skewdir.y;
- low.x -= zrad * skewdir.x;
- low.y -= zrad * skewdir.y;
-
- x1 = (min(high.x, low.x) - xyrad) / shadowmapradius;
- y1 = (min(high.y, low.y) - xyrad) / shadowmapradius;
- x2 = (max(high.x, low.x) + xyrad) / shadowmapradius;
- y2 = (max(high.y, low.y) + xyrad) / shadowmapradius;
+ vec skewdir(shadowdir);
+ skewdir.rotate_around_z(-camera1->yaw*RAD);
+
+ vec ro(o);
+ ro.sub(camera1->o);
+ ro.rotate_around_z(-(camera1->yaw+180)*RAD);
+ ro.x += ro.z * skewdir.x + shadowoffset.x;
+ ro.y += ro.z * skewdir.y + shadowmapradius * cosf(camera1->pitch*RAD) + shadowoffset.y;
+
+ vec high(ro), low(ro);
+ high.x += zrad * skewdir.x;
+ high.y += zrad * skewdir.y;
+ low.x -= zrad * skewdir.x;
+ low.y -= zrad * skewdir.y;
+
+ x1 = (min(high.x, low.x) - xyrad) / shadowmapradius;
+ y1 = (min(high.y, low.y) - xyrad) / shadowmapradius;
+ x2 = (max(high.x, low.x) + xyrad) / shadowmapradius;
+ y2 = (max(high.y, low.y) + xyrad) / shadowmapradius;
}
bool addshadowmapcaster(const vec &o, float xyrad, float zrad)
{
- if(o.z + zrad <= shadowfocus.z - shadowmapdist || o.z - zrad >= shadowfocus.z) return false;
+ if(o.z + zrad <= shadowfocus.z - shadowmapdist || o.z - zrad >= shadowfocus.z) return false;
- shadowmapmaxz = max(shadowmapmaxz, o.z + zrad);
+ shadowmapmaxz = max(shadowmapmaxz, o.z + zrad);
- float x1, y1, x2, y2;
- calcshadowmapbb(o, xyrad, zrad, x1, y1, x2, y2);
+ float x1, y1, x2, y2;
+ calcshadowmapbb(o, xyrad, zrad, x1, y1, x2, y2);
- if(!shadowmaptex.addblurtiles(x1, y1, x2, y2, 2)) return false;
+ if(!shadowmaptex.addblurtiles(x1, y1, x2, y2, 2)) return false;
- shadowmapcasters++;
- return true;
+ shadowmapcasters++;
+ return true;
}
bool isshadowmapreceiver(vtxarray *va)
{
- if(!shadowmap || !shadowmapcasters) return false;
+ if(!shadowmap || !shadowmapcasters) return false;
- if(va->shadowmapmax.z <= shadowfocus.z - shadowmapdist || va->shadowmapmin.z >= shadowmapmaxz) return false;
+ if(va->shadowmapmax.z <= shadowfocus.z - shadowmapdist || va->shadowmapmin.z >= shadowmapmaxz) return false;
- float xyrad = SQRT2*0.5f*max(va->shadowmapmax.x-va->shadowmapmin.x, va->shadowmapmax.y-va->shadowmapmin.y),
- zrad = 0.5f*(va->shadowmapmax.z-va->shadowmapmin.z),
- x1, y1, x2, y2;
- if(xyrad<0 || zrad<0) return false;
+ float xyrad = SQRT2*0.5f*max(va->shadowmapmax.x-va->shadowmapmin.x, va->shadowmapmax.y-va->shadowmapmin.y),
+ zrad = 0.5f*(va->shadowmapmax.z-va->shadowmapmin.z),
+ x1, y1, x2, y2;
+ if(xyrad<0 || zrad<0) return false;
- vec center = vec(va->shadowmapmin).add(vec(va->shadowmapmax)).mul(0.5f);
- calcshadowmapbb(center, xyrad, zrad, x1, y1, x2, y2);
+ vec center = vec(va->shadowmapmin).add(vec(va->shadowmapmax)).mul(0.5f);
+ calcshadowmapbb(center, xyrad, zrad, x1, y1, x2, y2);
- return shadowmaptex.checkblurtiles(x1, y1, x2, y2, 2);
+ return shadowmaptex.checkblurtiles(x1, y1, x2, y2, 2);
#if 0
- // cheaper inexact test
- float dz = va->o.z + va->size/2 - shadowfocus.z;
- float cx = shadowfocus.x + dz*shadowdir.x, cy = shadowfocus.y + dz*shadowdir.y;
- float skew = va->size/2*SHADOWSKEW;
- if(!shadowmap || !shadowmaptex ||
- va->o.z + va->size <= shadowfocus.z - shadowmapdist || va->o.z >= shadowmapmaxz ||
- va->o.x + va->size <= cx - shadowmapradius-skew || va->o.x >= cx + shadowmapradius+skew ||
- va->o.y + va->size <= cy - shadowmapradius-skew || va->o.y >= cy + shadowmapradius+skew)
- return false;
- return true;
+ // cheaper inexact test
+ float dz = va->o.z + va->size/2 - shadowfocus.z;
+ float cx = shadowfocus.x + dz*shadowdir.x, cy = shadowfocus.y + dz*shadowdir.y;
+ float skew = va->size/2*SHADOWSKEW;
+ if(!shadowmap || !shadowmaptex ||
+ va->o.z + va->size <= shadowfocus.z - shadowmapdist || va->o.z >= shadowmapmaxz ||
+ va->o.x + va->size <= cx - shadowmapradius-skew || va->o.x >= cx + shadowmapradius+skew ||
+ va->o.y + va->size <= cy - shadowmapradius-skew || va->o.y >= cy + shadowmapradius+skew)
+ return false;
+ return true;
#endif
}
bool isshadowmapcaster(const vec &o, float rad)
{
- // cheaper inexact test
- float dz = o.z - shadowfocus.z;
- float cx = shadowfocus.x + dz*shadowdir.x, cy = shadowfocus.y + dz*shadowdir.y;
- float skew = rad*SHADOWSKEW;
- if(!shadowmapping ||
- o.z + rad <= shadowfocus.z - shadowmapdist || o.z - rad >= shadowfocus.z ||
- o.x + rad <= cx - shadowmapradius-skew || o.x - rad >= cx + shadowmapradius+skew ||
- o.y + rad <= cy - shadowmapradius-skew || o.y - rad >= cy + shadowmapradius+skew)
- return false;
- return true;
+ // cheaper inexact test
+ float dz = o.z - shadowfocus.z;
+ float cx = shadowfocus.x + dz*shadowdir.x, cy = shadowfocus.y + dz*shadowdir.y;
+ float skew = rad*SHADOWSKEW;
+ if(!shadowmapping ||
+ o.z + rad <= shadowfocus.z - shadowmapdist || o.z - rad >= shadowfocus.z ||
+ o.x + rad <= cx - shadowmapradius-skew || o.x - rad >= cx + shadowmapradius+skew ||
+ o.y + rad <= cy - shadowmapradius-skew || o.y - rad >= cy + shadowmapradius+skew)
+ return false;
+ return true;
}
void pushshadowmap()
{
- if(!shadowmap || !shadowmaptex.rendertex) return;
+ if(!shadowmap || !shadowmaptex.rendertex) return;
- glActiveTexture_(GL_TEXTURE7);
- glBindTexture(GL_TEXTURE_2D, shadowmaptex.rendertex);
+ glActiveTexture_(GL_TEXTURE7);
+ glBindTexture(GL_TEXTURE_2D, shadowmaptex.rendertex);
- matrix4 m = shadowmatrix;
- m.projective(-1, 1-shadowmapbias/float(shadowmapdist));
- GLOBALPARAM(shadowmapproject, m);
+ matrix4 m = shadowmatrix;
+ m.projective(-1, 1-shadowmapbias/float(shadowmapdist));
+ GLOBALPARAM(shadowmapproject, m);
- glActiveTexture_(GL_TEXTURE0);
+ glActiveTexture_(GL_TEXTURE0);
- float r, g, b;
+ float r, g, b;
if(!shadowmapambient)
{
if(skylightcolor[0] || skylightcolor[1] || skylightcolor[2])
@@ -283,24 +283,24 @@ void pushshadowmap()
b = max(25.0f, 0.4f*ambientcolor[2] + 0.6f*max(ambientcolor[2], skylightcolor[2]));
}
else
- {
- r = max(25.0f, 2.0f*ambientcolor[0]);
- g = max(25.0f, 2.0f*ambientcolor[1]);
- b = max(25.0f, 2.0f*ambientcolor[2]);
- }
- }
- else { r = shadowmapambientcolor[0]; g = shadowmapambientcolor[1]; b = shadowmapambientcolor[2]; }
- GLOBALPARAMF(shadowmapambient, r/255.0f, g/255.0f, b/255.0f);
+ {
+ r = max(25.0f, 2.0f*ambientcolor[0]);
+ g = max(25.0f, 2.0f*ambientcolor[1]);
+ b = max(25.0f, 2.0f*ambientcolor[2]);
+ }
+ }
+ else { r = shadowmapambientcolor[0]; g = shadowmapambientcolor[1]; b = shadowmapambientcolor[2]; }
+ GLOBALPARAMF(shadowmapambient, r/255.0f, g/255.0f, b/255.0f);
}
void popshadowmap()
{
- if(!shadowmap || !shadowmaptex.rendertex) return;
+ if(!shadowmap || !shadowmaptex.rendertex) return;
}
void rendershadowmap()
{
- if(!shadowmap) return;
+ if(!shadowmap) return;
- shadowmaptex.render(1<<shadowmapsize, 1<<shadowmapsize, blurshadowmap, blursmsigma/100.0f);
+ shadowmaptex.render(1<<shadowmapsize, 1<<shadowmapsize, blurshadowmap, blursmsigma/100.0f);
}
diff --git a/src/engine/skelmodel.h b/src/engine/skelmodel.h
index 6d36b35..53d586d 100644
--- a/src/engine/skelmodel.h
+++ b/src/engine/skelmodel.h
@@ -9,1853 +9,1853 @@ VAR(testtags, 0, 0, 1);
struct skelmodel : animmodel
{
- struct vert { vec pos, norm; vec2 tc; int blend, interpindex; };
- struct vvert { vec pos; vec2 tc; };
- struct vvertn : vvert { vec norm; };
- struct vvertbump : vvert { squat tangent; };
- struct vvertw { uchar weights[4]; uchar bones[4]; };
- struct vvertnw : vvertn, vvertw {};
- struct vvertbumpw : vvertbump, vvertw {};
- struct bumpvert { quat tangent; };
- struct tri { ushort vert[3]; };
-
- struct blendcombo
- {
- int uses, interpindex;
- float weights[4];
- uchar bones[4], interpbones[4];
-
- blendcombo() : uses(1)
- {
- }
-
- bool operator==(const blendcombo &c) const
- {
- loopk(4) if(bones[k] != c.bones[k]) return false;
- loopk(4) if(weights[k] != c.weights[k]) return false;
- return true;
- }
-
- int size() const
- {
- int i = 1;
- while(i < 4 && weights[i]) i++;
- return i;
- }
-
- static bool sortcmp(const blendcombo &x, const blendcombo &y)
- {
- loopi(4)
- {
- if(x.weights[i])
- {
- if(!y.weights[i]) return true;
- }
- else if(y.weights[i]) return false;
- else break;
- }
- return false;
- }
-
- int addweight(int sorted, float weight, int bone)
- {
- if(weight <= 1e-3f) return sorted;
- loopk(sorted) if(weight > weights[k])
- {
- for(int l = min(sorted-1, 2); l >= k; l--)
- {
- weights[l+1] = weights[l];
- bones[l+1] = bones[l];
- }
- weights[k] = weight;
- bones[k] = bone;
- return sorted<4 ? sorted+1 : sorted;
- }
- if(sorted>=4) return sorted;
- weights[sorted] = weight;
- bones[sorted] = bone;
- return sorted+1;
- }
-
- void finalize(int sorted)
- {
- loopj(4-sorted) { weights[sorted+j] = 0; bones[sorted+j] = 0; }
- if(sorted <= 0) return;
- float total = 0;
- loopj(sorted) total += weights[j];
- total = 1.0f/total;
- loopj(sorted) weights[j] *= total;
- }
-
- void serialize(vvertw &v)
- {
- if(interpindex >= 0)
- {
- v.weights[0] = 255;
- loopk(3) v.weights[k+1] = 0;
- v.bones[0] = 2*interpindex;
- loopk(3) v.bones[k+1] = v.bones[0];
- }
- else
- {
- int total = 0;
- loopk(4) total += (v.weights[k] = uchar(0.5f + weights[k]*255));
- while(total > 255)
- {
- loopk(4) if(v.weights[k] > 0 && total > 255) { v.weights[k]--; total--; }
- }
- while(total < 255)
- {
- loopk(4) if(v.weights[k] < 255 && total < 255) { v.weights[k]++; total++; }
- }
- loopk(4) v.bones[k] = 2*interpbones[k];
- }
- }
- };
-
-
- struct animcacheentry
- {
- animstate as[MAXANIMPARTS];
- float pitch;
- int millis;
- uchar *partmask;
- ragdolldata *ragdoll;
-
- animcacheentry() : ragdoll(NULL)
- {
- loopk(MAXANIMPARTS) as[k].cur.fr1 = as[k].prev.fr1 = -1;
- }
-
- bool operator==(const animcacheentry &c) const
- {
- loopi(MAXANIMPARTS) if(as[i]!=c.as[i]) return false;
- return pitch==c.pitch && partmask==c.partmask && ragdoll==c.ragdoll && (!ragdoll || min(millis, c.millis) >= ragdoll->lastmove);
- }
- };
-
- struct vbocacheentry : animcacheentry
- {
- GLuint vbuf;
- int owner;
-
- vbocacheentry() : vbuf(0), owner(-1) {}
- };
-
- struct skelcacheentry : animcacheentry
- {
- dualquat *bdata;
- int version;
- bool dirty;
-
- skelcacheentry() : bdata(NULL), version(-1), dirty(false) {}
-
- void nextversion()
- {
- version = Shader::uniformlocversion();
- dirty = true;
- }
- };
-
- struct blendcacheentry : skelcacheentry
- {
- int owner;
-
- blendcacheentry() : owner(-1) {}
- };
-
- struct skelmeshgroup;
-
- struct skelmesh : mesh
- {
- vert *verts;
- bumpvert *bumpverts;
- tri *tris;
- int numverts, numtris, maxweights;
-
- int voffset, eoffset, elen;
- ushort minvert, maxvert;
-
- skelmesh() : verts(NULL), bumpverts(NULL), tris(NULL), numverts(0), numtris(0), maxweights(0)
- {
- }
-
- virtual ~skelmesh()
- {
- DELETEA(verts);
- DELETEA(bumpverts);
- DELETEA(tris);
- }
-
- int addblendcombo(const blendcombo &c)
- {
- maxweights = max(maxweights, c.size());
- return ((skelmeshgroup *)group)->addblendcombo(c);
- }
-
- void smoothnorms(float limit = 0, bool areaweight = true)
- {
- mesh::smoothnorms(verts, numverts, tris, numtris, limit, areaweight);
- }
-
- void buildnorms(bool areaweight = true)
- {
- mesh::buildnorms(verts, numverts, tris, numtris, areaweight);
- }
-
- void calctangents(bool areaweight = true)
- {
- if(bumpverts) return;
- bumpverts = new bumpvert[numverts];
- mesh::calctangents(bumpverts, verts, verts, numverts, tris, numtris, areaweight);
- }
-
- void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m)
- {
- loopj(numverts)
- {
- vec v = m.transform(verts[j].pos);
- loopi(3)
- {
- bbmin[i] = min(bbmin[i], v[i]);
- bbmax[i] = max(bbmax[i], v[i]);
- }
- }
- }
-
- void genBIH(BIH::mesh &m)
- {
- m.tris = (const BIH::tri *)tris;
- m.numtris = numtris;
- m.pos = (const uchar *)&verts->pos;
- m.posstride = sizeof(vert);
- m.tc = (const uchar *)&verts->tc;
- m.tcstride = sizeof(vert);
- }
-
- static inline void assignvert(vvertn &vv, int j, vert &v, blendcombo &c)
- {
- vv.pos = v.pos;
- vv.norm = v.norm;
- vv.tc = v.tc;
- }
-
- inline void assignvert(vvertbump &vv, int j, vert &v, blendcombo &c)
- {
- vv.pos = v.pos;
- vv.tc = v.tc;
- vv.tangent = bumpverts[j].tangent;
- }
-
- static inline void assignvert(vvertnw &vv, int j, vert &v, blendcombo &c)
- {
- vv.pos = v.pos;
- vv.norm = v.norm;
- vv.tc = v.tc;
- c.serialize(vv);
- }
-
- inline void assignvert(vvertbumpw &vv, int j, vert &v, blendcombo &c)
- {
- vv.pos = v.pos;
- vv.tc = v.tc;
- vv.tangent = bumpverts[j].tangent;
- c.serialize(vv);
- }
-
- template<class T>
- int genvbo(vector<ushort> &idxs, int offset, vector<T> &vverts)
- {
- voffset = offset;
- eoffset = idxs.length();
- loopi(numverts)
- {
- vert &v = verts[i];
- assignvert(vverts.add(), i, v, ((skelmeshgroup *)group)->blendcombos[v.blend]);
- }
- loopi(numtris) loopj(3) idxs.add(voffset + tris[i].vert[j]);
- elen = idxs.length()-eoffset;
- minvert = voffset;
- maxvert = voffset + numverts-1;
- return numverts;
- }
-
- template<class T>
- int genvbo(vector<ushort> &idxs, int offset, vector<T> &vverts, int *htdata, int htlen)
- {
- voffset = offset;
- eoffset = idxs.length();
- minvert = 0xFFFF;
- loopi(numtris)
- {
- tri &t = tris[i];
- loopj(3)
- {
- int index = t.vert[j];
- vert &v = verts[index];
- T vv;
- assignvert(vv, index, v, ((skelmeshgroup *)group)->blendcombos[v.blend]);
- int htidx = hthash(v.pos)&(htlen-1);
- loopk(htlen)
- {
- int &vidx = htdata[(htidx+k)&(htlen-1)];
- if(vidx < 0) { vidx = idxs.add(ushort(vverts.length())); vverts.add(vv); break; }
- else if(!memcmp(&vverts[vidx], &vv, sizeof(vv))) { minvert = min(minvert, idxs.add(ushort(vidx))); break; }
- }
- }
- }
- elen = idxs.length()-eoffset;
- minvert = min(minvert, ushort(voffset));
- maxvert = max(minvert, ushort(vverts.length()-1));
- return vverts.length()-voffset;
- }
-
- int genvbo(vector<ushort> &idxs, int offset)
- {
- loopi(numverts) verts[i].interpindex = ((skelmeshgroup *)group)->remapblend(verts[i].blend);
-
- voffset = offset;
- eoffset = idxs.length();
- loopi(numtris)
- {
- tri &t = tris[i];
- loopj(3) idxs.add(voffset+t.vert[j]);
- }
- minvert = voffset;
- maxvert = voffset + numverts-1;
- elen = idxs.length()-eoffset;
- return numverts;
- }
-
- template<class T>
- static inline void fillvert(T &vv, int j, vert &v)
- {
- vv.tc = v.tc;
- }
-
- template<class T>
- void fillverts(T *vdata)
- {
- vdata += voffset;
- loopi(numverts) fillvert(vdata[i], i, verts[i]);
- }
-
- void interpverts(const dualquat * RESTRICT bdata1, const dualquat * RESTRICT bdata2, bool tangents, void * RESTRICT vdata, skin &s)
- {
- const int blendoffset = ((skelmeshgroup *)group)->skel->numgpubones;
- bdata2 -= blendoffset;
-
- #define IPLOOP(type, dosetup, dotransform) \
- loopi(numverts) \
- { \
- const vert &src = verts[i]; \
- type &dst = ((type * RESTRICT)vdata)[i]; \
- dosetup; \
- const dualquat &b = (src.interpindex < blendoffset ? bdata1 : bdata2)[src.interpindex]; \
- dst.pos = b.transform(src.pos); \
- dotransform; \
- }
-
- if(tangents)
- {
- IPLOOP(vvertbump, bumpvert &bsrc = bumpverts[i],
- {
- quat q = b.transform(bsrc.tangent);
- fixqtangent(q, bsrc.tangent.w);
- dst.tangent = q;
- });
- }
- else
- {
- IPLOOP(vvertn, ,
- {
- dst.norm = b.transformnormal(src.norm);
- });
- }
-
- #undef IPLOOP
- }
-
- void setshader(Shader *s)
- {
- skelmeshgroup *g = (skelmeshgroup *)group;
- if(glaring)
- {
- if(!g->skel->usegpuskel) s->setvariant(0, 1);
- else s->setvariant(min(maxweights, g->vweights), 1);
- }
- else if(!g->skel->usegpuskel) s->set();
- else s->setvariant(min(maxweights, g->vweights)-1, 0);
- }
-
- void render(const animstate *as, skin &s, vbocacheentry &vc)
- {
- if(!Shader::lastshader) return;
- glDrawRangeElements_(GL_TRIANGLES, minvert, maxvert, elen, GL_UNSIGNED_SHORT, &((skelmeshgroup *)group)->edata[eoffset]);
- glde++;
- xtravertsva += numverts;
- }
- };
-
-
- struct tag
- {
- char *name;
- int bone;
- matrix4x3 matrix;
-
- tag() : name(NULL) {}
- ~tag() { DELETEA(name); }
- };
-
- struct skelanimspec
- {
- char *name;
- int frame, range;
-
- skelanimspec() : name(NULL), frame(0), range(0) {}
- ~skelanimspec()
- {
- DELETEA(name);
- }
- };
-
- struct boneinfo
- {
- const char *name;
- int parent, children, next, group, scheduled, interpindex, interpparent, ragdollindex, correctindex;
- float pitchscale, pitchoffset, pitchmin, pitchmax;
- dualquat base, invbase;
-
- boneinfo() : name(NULL), parent(-1), children(-1), next(-1), group(INT_MAX), scheduled(-1), interpindex(-1), interpparent(-1), ragdollindex(-1), correctindex(-1), pitchscale(0), pitchoffset(0), pitchmin(0), pitchmax(0) {}
- ~boneinfo()
- {
- DELETEA(name);
- }
- };
-
- struct antipode
- {
- int parent, child;
-
- antipode(int parent, int child) : parent(parent), child(child) {}
- };
-
- struct pitchdep
- {
- int bone, parent;
- dualquat pose;
- };
-
- struct pitchtarget
- {
- int bone, frame, corrects, deps;
- float pitchmin, pitchmax, deviated;
- dualquat pose;
- };
-
- struct pitchcorrect
- {
- int bone, target, parent;
- float pitchmin, pitchmax, pitchscale, pitchangle, pitchtotal;
-
- pitchcorrect() : parent(-1), pitchangle(0), pitchtotal(0) {}
- };
-
- struct skeleton
- {
- char *name;
- int shared;
- vector<skelmeshgroup *> users;
- boneinfo *bones;
- int numbones, numinterpbones, numgpubones, numframes;
- dualquat *framebones;
- vector<skelanimspec> skelanims;
- vector<tag> tags;
- vector<antipode> antipodes;
- ragdollskel *ragdoll;
- vector<pitchdep> pitchdeps;
- vector<pitchtarget> pitchtargets;
- vector<pitchcorrect> pitchcorrects;
-
- bool usegpuskel;
- vector<skelcacheentry> skelcache;
- hashtable<GLuint, int> blendoffsets;
-
- skeleton() : name(NULL), shared(0), bones(NULL), numbones(0), numinterpbones(0), numgpubones(0), numframes(0), framebones(NULL), ragdoll(NULL), usegpuskel(false), blendoffsets(32)
- {
- }
-
- ~skeleton()
- {
- DELETEA(name);
- DELETEA(bones);
- DELETEA(framebones);
- DELETEP(ragdoll);
- loopv(skelcache)
- {
- DELETEA(skelcache[i].bdata);
- }
- }
-
- skelanimspec *findskelanim(const char *name, char sep = '\0')
- {
- int len = sep ? strlen(name) : 0;
- loopv(skelanims)
- {
- if(skelanims[i].name)
- {
- if(sep)
- {
- const char *end = strchr(skelanims[i].name, ':');
- if(end && end - skelanims[i].name == len && !memcmp(name, skelanims[i].name, len)) return &skelanims[i];
- }
- if(!strcmp(name, skelanims[i].name)) return &skelanims[i];
- }
- }
- return NULL;
- }
-
- skelanimspec &addskelanim(const char *name)
- {
- skelanimspec &sa = skelanims.add();
- sa.name = name ? newstring(name) : NULL;
- return sa;
- }
-
- int findbone(const char *name)
- {
- loopi(numbones) if(bones[i].name && !strcmp(bones[i].name, name)) return i;
- return -1;
- }
-
- int findtag(const char *name)
- {
- loopv(tags) if(!strcmp(tags[i].name, name)) return i;
- return -1;
- }
-
- bool addtag(const char *name, int bone, const matrix4x3 &matrix)
- {
- int idx = findtag(name);
- if(idx >= 0)
- {
- if(!testtags) return false;
- tag &t = tags[idx];
- t.bone = bone;
- t.matrix = matrix;
- }
- else
- {
- tag &t = tags.add();
- t.name = newstring(name);
- t.bone = bone;
- t.matrix = matrix;
- }
- return true;
- }
-
- void calcantipodes()
- {
- antipodes.shrink(0);
- vector<int> schedule;
- loopi(numbones)
- {
- if(bones[i].group >= numbones)
- {
- bones[i].scheduled = schedule.length();
- schedule.add(i);
- }
- else bones[i].scheduled = -1;
- }
- loopv(schedule)
- {
- int bone = schedule[i];
- const boneinfo &info = bones[bone];
- loopj(numbones) if(abs(bones[j].group) == bone && bones[j].scheduled < 0)
- {
- antipodes.add(antipode(info.interpindex, bones[j].interpindex));
- bones[j].scheduled = schedule.length();
- schedule.add(j);
- }
- if(i + 1 == schedule.length())
- {
- int conflict = INT_MAX;
- loopj(numbones) if(bones[j].group < numbones && bones[j].scheduled < 0) conflict = min(conflict, abs(bones[j].group));
- if(conflict < numbones)
- {
- bones[conflict].scheduled = schedule.length();
- schedule.add(conflict);
- }
- }
- }
- }
-
- void remapbones()
- {
- loopi(numbones)
- {
- boneinfo &info = bones[i];
- info.interpindex = -1;
- info.ragdollindex = -1;
- }
- numgpubones = 0;
- loopv(users)
- {
- skelmeshgroup *group = users[i];
- loopvj(group->blendcombos)
- {
- blendcombo &c = group->blendcombos[j];
- loopk(4)
- {
- if(!c.weights[k]) { c.interpbones[k] = k > 0 ? c.interpbones[k-1] : 0; continue; }
- boneinfo &info = bones[c.bones[k]];
- if(info.interpindex < 0) info.interpindex = numgpubones++;
- c.interpbones[k] = info.interpindex;
- if(info.group < 0) continue;
- loopl(4)
- {
- if(!c.weights[l]) break;
- if(l == k) continue;
- int parent = c.bones[l];
- if(info.parent == parent || (info.parent >= 0 && info.parent == bones[parent].parent)) { info.group = -info.parent; break; }
- if(info.group <= parent) continue;
- int child = c.bones[k];
- while(parent > child) parent = bones[parent].parent;
- if(parent != child) info.group = c.bones[l];
- }
- }
- }
- }
- numinterpbones = numgpubones;
- loopv(tags)
- {
- boneinfo &info = bones[tags[i].bone];
- if(info.interpindex < 0) info.interpindex = numinterpbones++;
- }
- if(ragdoll)
- {
- loopv(ragdoll->joints)
- {
- boneinfo &info = bones[ragdoll->joints[i].bone];
- if(info.interpindex < 0) info.interpindex = numinterpbones++;
- info.ragdollindex = i;
- }
- }
- loopi(numbones)
- {
- boneinfo &info = bones[i];
- if(info.interpindex < 0) continue;
- for(int parent = info.parent; parent >= 0 && bones[parent].interpindex < 0; parent = bones[parent].parent)
- bones[parent].interpindex = numinterpbones++;
- }
- loopi(numbones)
- {
- boneinfo &info = bones[i];
- if(info.interpindex < 0) continue;
- info.interpparent = info.parent >= 0 ? bones[info.parent].interpindex : -1;
- }
- if(ragdoll)
- {
- loopi(numbones)
- {
- boneinfo &info = bones[i];
- if(info.interpindex < 0 || info.ragdollindex >= 0) continue;
- for(int parent = info.parent; parent >= 0; parent = bones[parent].parent)
- {
- if(bones[parent].ragdollindex >= 0) { ragdoll->addreljoint(i, bones[parent].ragdollindex); break; }
- }
- }
- }
- calcantipodes();
- }
-
-
- void addpitchdep(int bone, int frame)
- {
- for(; bone >= 0; bone = bones[bone].parent)
- {
- int pos = pitchdeps.length();
- loopvj(pitchdeps) if(bone <= pitchdeps[j].bone)
- {
- if(bone == pitchdeps[j].bone) goto nextbone;
- pos = j;
- break;
- }
- {
- pitchdep d;
- d.bone = bone;
- d.parent = -1;
- d.pose = framebones[frame*numbones + bone];
- pitchdeps.insert(pos, d);
- }
- nextbone:;
- }
- }
-
- int findpitchdep(int bone)
- {
- loopv(pitchdeps) if(bone <= pitchdeps[i].bone) return bone == pitchdeps[i].bone ? i : -1;
- return -1;
- }
-
- int findpitchcorrect(int bone)
- {
- loopv(pitchcorrects) if(bone <= pitchcorrects[i].bone) return bone == pitchcorrects[i].bone ? i : -1;
- return -1;
- }
-
- void initpitchdeps()
- {
- pitchdeps.setsize(0);
- if(pitchtargets.empty()) return;
- loopv(pitchtargets)
- {
- pitchtarget &t = pitchtargets[i];
- t.deps = -1;
- addpitchdep(t.bone, t.frame);
- }
- loopv(pitchdeps)
- {
- pitchdep &d = pitchdeps[i];
- int parent = bones[d.bone].parent;
- if(parent >= 0)
- {
- int j = findpitchdep(parent);
- if(j >= 0)
- {
- d.parent = j;
- d.pose.mul(pitchdeps[j].pose, dualquat(d.pose));
- }
- }
- }
- loopv(pitchtargets)
- {
- pitchtarget &t = pitchtargets[i];
- int j = findpitchdep(t.bone);
- if(j >= 0)
- {
- t.deps = j;
- t.pose = pitchdeps[j].pose;
- }
- t.corrects = -1;
- for(int parent = t.bone; parent >= 0; parent = bones[parent].parent)
- {
- t.corrects = findpitchcorrect(parent);
- if(t.corrects >= 0) break;
- }
- }
- loopv(pitchcorrects)
- {
- pitchcorrect &c = pitchcorrects[i];
- bones[c.bone].correctindex = i;
- c.parent = -1;
- for(int parent = c.bone;;)
- {
- parent = bones[parent].parent;
- if(parent < 0) break;
- c.parent = findpitchcorrect(parent);
- if(c.parent >= 0) break;
- }
- }
- }
-
- void optimize()
- {
- cleanup();
- if(ragdoll) ragdoll->setup();
- remapbones();
- initpitchdeps();
- }
-
- void expandbonemask(uchar *expansion, int bone, int val)
- {
- expansion[bone] = val;
- bone = bones[bone].children;
- while(bone>=0) { expandbonemask(expansion, bone, val); bone = bones[bone].next; }
- }
-
- void applybonemask(ushort *mask, uchar *partmask, int partindex)
- {
- if(!mask || *mask==BONEMASK_END) return;
- uchar *expansion = new uchar[numbones];
- memset(expansion, *mask&BONEMASK_NOT ? 1 : 0, numbones);
- while(*mask!=BONEMASK_END)
- {
- expandbonemask(expansion, *mask&BONEMASK_BONE, *mask&BONEMASK_NOT ? 0 : 1);
- mask++;
- }
- loopi(numbones) if(expansion[i]) partmask[i] = partindex;
- delete[] expansion;
- }
-
- void linkchildren()
- {
- loopi(numbones)
- {
- boneinfo &b = bones[i];
- b.children = -1;
- if(b.parent<0) b.next = -1;
- else
- {
- b.next = bones[b.parent].children;
- bones[b.parent].children = i;
- }
- }
- }
-
- int availgpubones() const { return min(maxvsuniforms - reservevpparams - 10, maxskelanimdata) / 2; }
- bool gpuaccelerate() const { return numframes && gpuskel && numgpubones<=availgpubones(); }
-
- float calcdeviation(const vec &axis, const vec &forward, const dualquat &pose1, const dualquat &pose2)
- {
- vec forward1 = pose1.transformnormal(forward).project(axis).normalize(),
- forward2 = pose2.transformnormal(forward).project(axis).normalize(),
- daxis = vec().cross(forward1, forward2);
- float dx = clamp(forward1.dot(forward2), -1.0f, 1.0f), dy = clamp(daxis.magnitude(), -1.0f, 1.0f);
- if(daxis.dot(axis) < 0) dy = -dy;
- return atan2f(dy, dx)/RAD;
- }
-
- void calcpitchcorrects(float pitch, const vec &axis, const vec &forward)
- {
- loopv(pitchtargets)
- {
- pitchtarget &t = pitchtargets[i];
- t.deviated = calcdeviation(axis, forward, t.pose, pitchdeps[t.deps].pose);
- }
- loopv(pitchcorrects)
- {
- pitchcorrect &c = pitchcorrects[i];
- c.pitchangle = c.pitchtotal = 0;
- }
- loopvj(pitchtargets)
- {
- pitchtarget &t = pitchtargets[j];
- float tpitch = pitch - t.deviated;
- for(int parent = t.corrects; parent >= 0; parent = pitchcorrects[parent].parent)
- tpitch -= pitchcorrects[parent].pitchangle;
- if(t.pitchmin || t.pitchmax) tpitch = clamp(tpitch, t.pitchmin, t.pitchmax);
- loopv(pitchcorrects)
- {
- pitchcorrect &c = pitchcorrects[i];
- if(c.target != j) continue;
- float total = c.parent >= 0 ? pitchcorrects[c.parent].pitchtotal : 0,
- avail = tpitch - total,
- used = tpitch*c.pitchscale;
- if(c.pitchmin || c.pitchmax)
- {
- if(used < 0) used = clamp(c.pitchmin, used, 0.0f);
- else used = clamp(c.pitchmax, 0.0f, used);
- }
- if(used < 0) used = clamp(avail, used, 0.0f);
- else used = clamp(avail, 0.0f, used);
- c.pitchangle = used;
- c.pitchtotal = used + total;
- }
- }
- }
-
- #define INTERPBONE(bone) \
- const animstate &s = as[partmask[bone]]; \
- const framedata &f = partframes[partmask[bone]]; \
- dualquat d; \
- (d = f.fr1[bone]).mul((1-s.cur.t)*s.interp); \
- d.accumulate(f.fr2[bone], s.cur.t*s.interp); \
- if(s.interp<1) \
- { \
- d.accumulate(f.pfr1[bone], (1-s.prev.t)*(1-s.interp)); \
- d.accumulate(f.pfr2[bone], s.prev.t*(1-s.interp)); \
- }
-
- void interpbones(const animstate *as, float pitch, const vec &axis, const vec &forward, int numanimparts, const uchar *partmask, skelcacheentry &sc)
- {
- if(!sc.bdata) sc.bdata = new dualquat[numinterpbones];
- sc.nextversion();
- struct framedata
- {
- const dualquat *fr1, *fr2, *pfr1, *pfr2;
- } partframes[MAXANIMPARTS];
- loopi(numanimparts)
- {
- partframes[i].fr1 = &framebones[as[i].cur.fr1*numbones];
- partframes[i].fr2 = &framebones[as[i].cur.fr2*numbones];
- if(as[i].interp<1)
- {
- partframes[i].pfr1 = &framebones[as[i].prev.fr1*numbones];
- partframes[i].pfr2 = &framebones[as[i].prev.fr2*numbones];
- }
- }
- loopv(pitchdeps)
- {
- pitchdep &p = pitchdeps[i];
- INTERPBONE(p.bone);
- d.normalize();
- if(p.parent >= 0) p.pose.mul(pitchdeps[p.parent].pose, d);
- else p.pose = d;
- }
- calcpitchcorrects(pitch, axis, forward);
- loopi(numbones) if(bones[i].interpindex>=0)
- {
- INTERPBONE(i);
- const boneinfo &b = bones[i];
- d.normalize();
- if(b.interpparent<0) sc.bdata[b.interpindex] = d;
- else sc.bdata[b.interpindex].mul(sc.bdata[b.interpparent], d);
- float angle;
- if(b.pitchscale) { angle = b.pitchscale*pitch + b.pitchoffset; if(b.pitchmin || b.pitchmax) angle = clamp(angle, b.pitchmin, b.pitchmax); }
- else if(b.correctindex >= 0) angle = pitchcorrects[b.correctindex].pitchangle;
- else continue;
- if(as->cur.anim&ANIM_NOPITCH || (as->interp < 1 && as->prev.anim&ANIM_NOPITCH))
- angle *= (as->cur.anim&ANIM_NOPITCH ? 0 : as->interp) + (as->interp < 1 && as->prev.anim&ANIM_NOPITCH ? 0 : 1-as->interp);
- sc.bdata[b.interpindex].mulorient(quat(axis, angle*RAD), b.base);
- }
- loopv(antipodes) sc.bdata[antipodes[i].child].fixantipodal(sc.bdata[antipodes[i].parent]);
- }
-
- void initragdoll(ragdolldata &d, skelcacheentry &sc, part *p)
- {
- const dualquat *bdata = sc.bdata;
- loopv(ragdoll->joints)
- {
- const ragdollskel::joint &j = ragdoll->joints[i];
- const boneinfo &b = bones[j.bone];
- const dualquat &q = bdata[b.interpindex];
- loopk(3) if(j.vert[k] >= 0)
- {
- ragdollskel::vert &v = ragdoll->verts[j.vert[k]];
- ragdolldata::vert &dv = d.verts[j.vert[k]];
- dv.pos.add(q.transform(v.pos).mul(v.weight));
- }
- }
- if(ragdoll->animjoints) loopv(ragdoll->joints)
- {
- const ragdollskel::joint &j = ragdoll->joints[i];
- const boneinfo &b = bones[j.bone];
- const dualquat &q = bdata[b.interpindex];
- d.calcanimjoint(i, matrix4x3(q));
- }
- loopv(ragdoll->verts)
- {
- ragdolldata::vert &dv = d.verts[i];
- matrixstack[matrixpos].transform(vec(dv.pos).add(p->translate).mul(p->model->scale), dv.pos);
- }
- loopv(ragdoll->reljoints)
- {
- const ragdollskel::reljoint &r = ragdoll->reljoints[i];
- const ragdollskel::joint &j = ragdoll->joints[r.parent];
- const boneinfo &br = bones[r.bone], &bj = bones[j.bone];
- d.reljoints[i].mul(dualquat(bdata[bj.interpindex]).invert(), bdata[br.interpindex]);
- }
- }
-
- void genragdollbones(ragdolldata &d, skelcacheentry &sc, part *p)
- {
- if(!sc.bdata) sc.bdata = new dualquat[numinterpbones];
- sc.nextversion();
- loopv(ragdoll->joints)
- {
- const ragdollskel::joint &j = ragdoll->joints[i];
- const boneinfo &b = bones[j.bone];
- vec pos(0, 0, 0);
- loopk(3) if(j.vert[k]>=0) pos.add(d.verts[j.vert[k]].pos);
- pos.mul(j.weight/p->model->scale).sub(p->translate);
- matrix4x3 m;
- m.mul(d.tris[j.tri], pos, d.animjoints ? d.animjoints[i] : j.orient);
- sc.bdata[b.interpindex] = dualquat(m);
- }
- loopv(ragdoll->reljoints)
- {
- const ragdollskel::reljoint &r = ragdoll->reljoints[i];
- const ragdollskel::joint &j = ragdoll->joints[r.parent];
- const boneinfo &br = bones[r.bone], &bj = bones[j.bone];
- sc.bdata[br.interpindex].mul(sc.bdata[bj.interpindex], d.reljoints[i]);
- }
- loopv(antipodes) sc.bdata[antipodes[i].child].fixantipodal(sc.bdata[antipodes[i].parent]);
- }
-
- void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n)
- {
- matrix4x3 t;
- t.mul(bones[tags[i].bone].base, tags[i].matrix);
- t.posttranslate(p->translate, p->model->scale);
- n.mul(m, t);
- }
-
- void calctags(part *p, skelcacheentry *sc = NULL)
- {
- loopv(p->links)
- {
- linkedpart &l = p->links[i];
- tag &t = tags[l.tag];
- dualquat q;
- if(sc) q.mul(sc->bdata[bones[t.bone].interpindex], bones[t.bone].base);
- else q = bones[t.bone].base;
- matrix4x3 m;
- m.mul(q, t.matrix);
- m.d.add(p->translate).mul(p->model->scale);
- l.matrix = m;
- }
- }
-
- void cleanup(bool full = true)
- {
- loopv(skelcache)
- {
- skelcacheentry &sc = skelcache[i];
- loopj(MAXANIMPARTS) sc.as[j].cur.fr1 = -1;
- DELETEA(sc.bdata);
- }
- skelcache.setsize(0);
- blendoffsets.clear();
- if(full) loopv(users) users[i]->cleanup();
- }
-
- bool canpreload() { return !numframes || gpuaccelerate(); }
-
- void preload()
- {
- if(!numframes) return;
- if(skelcache.empty())
- {
- usegpuskel = gpuaccelerate();
- }
- }
-
- skelcacheentry &checkskelcache(part *p, const animstate *as, float pitch, const vec &axis, const vec &forward, ragdolldata *rdata)
- {
- if(skelcache.empty())
- {
- usegpuskel = gpuaccelerate();
- }
-
- int numanimparts = ((skelpart *)as->owner)->numanimparts;
- uchar *partmask = ((skelpart *)as->owner)->partmask;
- skelcacheentry *sc = NULL;
- bool match = false;
- loopv(skelcache)
- {
- skelcacheentry &c = skelcache[i];
- loopj(numanimparts) if(c.as[j]!=as[j]) goto mismatch;
- if(c.pitch != pitch || c.partmask != partmask || c.ragdoll != rdata || (rdata && c.millis < rdata->lastmove)) goto mismatch;
- match = true;
- sc = &c;
- break;
- mismatch:
- if(c.millis < lastmillis) { sc = &c; break; }
- }
- if(!sc) sc = &skelcache.add();
- if(!match)
- {
- loopi(numanimparts) sc->as[i] = as[i];
- sc->pitch = pitch;
- sc->partmask = partmask;
- sc->ragdoll = rdata;
- if(rdata) genragdollbones(*rdata, *sc, p);
- else interpbones(as, pitch, axis, forward, numanimparts, partmask, *sc);
- }
- sc->millis = lastmillis;
- return *sc;
- }
-
- int getblendoffset(UniformLoc &u)
- {
- int &offset = blendoffsets.access(Shader::lastshader->program, -1);
- if(offset < 0)
- {
- defformatstring(offsetname, "%s[%d]", u.name, 2*numgpubones);
- offset = glGetUniformLocation_(Shader::lastshader->program, offsetname);
- }
- return offset;
- }
-
- void setglslbones(UniformLoc &u, skelcacheentry &sc, skelcacheentry &bc, int count)
- {
- if(u.version == bc.version && u.data == bc.bdata) return;
- glUniform4fv_(u.loc, 2*numgpubones, sc.bdata[0].real.v);
- if(count > 0)
- {
- int offset = getblendoffset(u);
- if(offset >= 0) glUniform4fv_(offset, 2*count, bc.bdata[0].real.v);
- }
- u.version = bc.version;
- u.data = bc.bdata;
- }
-
- void setgpubones(skelcacheentry &sc, blendcacheentry *bc, int count)
- {
- if(!Shader::lastshader) return;
- if(Shader::lastshader->uniformlocs.length() < 1) return;
- UniformLoc &u = Shader::lastshader->uniformlocs[0];
- setglslbones(u, sc, bc ? *bc : sc, count);
- }
-
- bool shouldcleanup() const
- {
- return numframes && (skelcache.empty() || gpuaccelerate()!=usegpuskel);
- }
- };
-
- struct skelmeshgroup : meshgroup
- {
- skeleton *skel;
-
- vector<blendcombo> blendcombos;
- int numblends[4];
-
- static const int MAXBLENDCACHE = 16;
- blendcacheentry blendcache[MAXBLENDCACHE];
-
- static const int MAXVBOCACHE = 16;
- vbocacheentry vbocache[MAXVBOCACHE];
-
- ushort *edata;
- GLuint ebuf;
- bool vtangents;
- int vlen, vertsize, vblends, vweights;
- uchar *vdata;
-
- skelmeshgroup() : skel(NULL), edata(NULL), ebuf(0), vtangents(false), vlen(0), vertsize(0), vblends(0), vweights(0), vdata(NULL)
- {
- memset(numblends, 0, sizeof(numblends));
- }
-
- virtual ~skelmeshgroup()
- {
- if(skel)
- {
- if(skel->shared) skel->users.removeobj(this);
- else DELETEP(skel);
- }
- if(ebuf) glDeleteBuffers_(1, &ebuf);
- loopi(MAXBLENDCACHE)
- {
- DELETEA(blendcache[i].bdata);
- }
- loopi(MAXVBOCACHE)
- {
- if(vbocache[i].vbuf) glDeleteBuffers_(1, &vbocache[i].vbuf);
- }
- DELETEA(vdata);
- }
-
- void shareskeleton(char *name)
- {
- if(!name)
- {
- skel = new skeleton;
- skel->users.add(this);
- return;
- }
-
- static hashnameset<skeleton *> skeletons;
- if(skeletons.access(name)) skel = skeletons[name];
- else
- {
- skel = new skeleton;
- skel->name = newstring(name);
- skeletons.add(skel);
- }
- skel->users.add(this);
- skel->shared++;
- }
-
- int findtag(const char *name)
- {
- return skel->findtag(name);
- }
-
- void *animkey() { return skel; }
- int totalframes() const { return max(skel->numframes, 1); }
-
- virtual skelanimspec *loadanim(const char *filename) { return NULL; }
-
- void genvbo(bool tangents, vbocacheentry &vc)
- {
- if(!vc.vbuf) glGenBuffers_(1, &vc.vbuf);
- if(ebuf) return;
-
- vector<ushort> idxs;
-
- if(tangents) loopv(meshes) ((skelmesh *)meshes[i])->calctangents();
-
- vtangents = tangents;
- vlen = 0;
- vblends = 0;
- if(skel->numframes && !skel->usegpuskel)
- {
- vweights = 1;
- loopv(blendcombos)
- {
- blendcombo &c = blendcombos[i];
- c.interpindex = c.weights[1] ? skel->numgpubones + vblends++ : -1;
- }
-
- vertsize = tangents ? sizeof(vvertbump) : sizeof(vvertn);
- loopv(meshes) vlen += ((skelmesh *)meshes[i])->genvbo(idxs, vlen);
- DELETEA(vdata);
- vdata = new uchar[vlen*vertsize];
- #define FILLVDATA(type) do { \
- loopv(meshes) ((skelmesh *)meshes[i])->fillverts((type *)vdata); \
- } while(0)
- if(tangents) FILLVDATA(vvertbump);
- else FILLVDATA(vvertn);
- #undef FILLVDATA
- }
- else
- {
- if(skel->numframes)
- {
- vweights = 4;
- int availbones = skel->availgpubones() - skel->numgpubones;
- while(vweights > 1 && availbones >= numblends[vweights-1]) availbones -= numblends[--vweights];
- loopv(blendcombos)
- {
- blendcombo &c = blendcombos[i];
- c.interpindex = c.size() > vweights ? skel->numgpubones + vblends++ : -1;
- }
- }
- else
- {
- vweights = 0;
- loopv(blendcombos) blendcombos[i].interpindex = -1;
- }
-
- gle::bindvbo(vc.vbuf);
- #define GENVBO(type, args) do { \
- vertsize = sizeof(type); \
- vector<type> vverts; \
- loopv(meshes) vlen += ((skelmesh *)meshes[i])->genvbo args; \
- glBufferData_(GL_ARRAY_BUFFER, vverts.length()*sizeof(type), vverts.getbuf(), GL_STATIC_DRAW); \
- } while(0)
- #define GENVBOANIM(type) GENVBO(type, (idxs, vlen, vverts))
- #define GENVBOSTAT(type) GENVBO(type, (idxs, vlen, vverts, htdata, htlen))
- if(skel->numframes)
- {
- if(tangents) GENVBOANIM(vvertbumpw);
- else GENVBOANIM(vvertnw);
- }
- else
- {
- int numverts = 0, htlen = 128;
- loopv(meshes) numverts += ((skelmesh *)meshes[i])->numverts;
- while(htlen < numverts) htlen *= 2;
- if(numverts*4 > htlen*3) htlen *= 2;
- int *htdata = new int[htlen];
- memset(htdata, -1, htlen*sizeof(int));
- if(tangents) GENVBOSTAT(vvertbump);
- else GENVBOSTAT(vvertn);
- delete[] htdata;
- }
- #undef GENVBO
- #undef GENVBOANIM
- #undef GENVBOSTAT
- gle::clearvbo();
- }
-
- glGenBuffers_(1, &ebuf);
- gle::bindebo(ebuf);
- glBufferData_(GL_ELEMENT_ARRAY_BUFFER, idxs.length()*sizeof(ushort), idxs.getbuf(), GL_STATIC_DRAW);
- gle::clearebo();
- }
-
- void bindvbo(const animstate *as, vbocacheentry &vc, skelcacheentry *sc = NULL, blendcacheentry *bc = NULL)
- {
- vvert *vverts = 0;
- bindpos(ebuf, vc.vbuf, &vverts->pos, vertsize);
- if(as->cur.anim&ANIM_NOSKIN)
- {
- if(enabletc) disabletc();
- if(enablenormals) disablenormals();
- if(enabletangents) disabletangents();
- }
- else
- {
- if(vtangents)
- {
- if(enablenormals) disablenormals();
- vvertbump *vvertbumps = 0;
- bindtangents(&vvertbumps->tangent, vertsize);
- }
- else
- {
- if(enabletangents) disabletangents();
- vvertn *vvertns = 0;
- bindnormals(&vvertns->norm, vertsize);
- }
-
- bindtc(&vverts->tc, vertsize);
- }
- if(!sc || !skel->usegpuskel)
- {
- if(enablebones) disablebones();
- }
- else
- {
- if(vtangents)
- {
- vvertbumpw *vvertbumpws = 0;
- bindbones(vvertbumpws->weights, vvertbumpws->bones, vertsize);
- }
- else
- {
- vvertnw *vvertnws = 0;
- bindbones(vvertnws->weights, vvertnws->bones, vertsize);
- }
- }
- }
-
- void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n)
- {
- skel->concattagtransform(p, i, m, n);
- }
-
- int addblendcombo(const blendcombo &c)
- {
- loopv(blendcombos) if(blendcombos[i]==c)
- {
- blendcombos[i].uses += c.uses;
- return i;
- }
- numblends[c.size()-1]++;
- blendcombo &a = blendcombos.add(c);
- return a.interpindex = blendcombos.length()-1;
- }
-
- void sortblendcombos()
- {
- blendcombos.sort(blendcombo::sortcmp);
- int *remap = new int[blendcombos.length()];
- loopv(blendcombos) remap[blendcombos[i].interpindex] = i;
- loopv(meshes)
- {
- skelmesh *m = (skelmesh *)meshes[i];
- loopj(m->numverts)
- {
- vert &v = m->verts[j];
- v.blend = remap[v.blend];
- }
- }
- delete[] remap;
- }
-
- int remapblend(int blend)
- {
- const blendcombo &c = blendcombos[blend];
- return c.weights[1] ? c.interpindex : c.interpbones[0];
- }
-
- static inline void blendbones(dualquat &d, const dualquat *bdata, const blendcombo &c)
- {
- d = bdata[c.interpbones[0]];
- d.mul(c.weights[0]);
- d.accumulate(bdata[c.interpbones[1]], c.weights[1]);
- if(c.weights[2])
- {
- d.accumulate(bdata[c.interpbones[2]], c.weights[2]);
- if(c.weights[3]) d.accumulate(bdata[c.interpbones[3]], c.weights[3]);
- }
- }
-
- void blendbones(const skelcacheentry &sc, blendcacheentry &bc)
- {
- bc.nextversion();
- if(!bc.bdata) bc.bdata = new dualquat[vblends];
- dualquat *dst = bc.bdata - skel->numgpubones;
- bool normalize = !skel->usegpuskel || vweights<=1;
- loopv(blendcombos)
- {
- const blendcombo &c = blendcombos[i];
- if(c.interpindex<0) break;
- dualquat &d = dst[c.interpindex];
- blendbones(d, sc.bdata, c);
- if(normalize) d.normalize();
- }
- }
-
- void cleanup()
- {
- loopi(MAXBLENDCACHE)
- {
- blendcacheentry &c = blendcache[i];
- DELETEA(c.bdata);
- c.owner = -1;
- }
- loopi(MAXVBOCACHE)
- {
- vbocacheentry &c = vbocache[i];
- if(c.vbuf) { glDeleteBuffers_(1, &c.vbuf); c.vbuf = 0; }
- c.owner = -1;
- }
- if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; }
- if(skel) skel->cleanup(false);
- }
-
- #define SEARCHCACHE(cachesize, cacheentry, cache, reusecheck) \
- loopi(cachesize) \
- { \
- cacheentry &c = cache[i]; \
- if(c.owner==owner) \
- { \
- if(c==sc) return c; \
- else c.owner = -1; \
- break; \
- } \
- } \
- loopi(cachesize-1) \
- { \
- cacheentry &c = cache[i]; \
- if(reusecheck c.owner < 0 || c.millis < lastmillis) \
- return c; \
- } \
- return cache[cachesize-1];
-
- vbocacheentry &checkvbocache(skelcacheentry &sc, int owner)
- {
- SEARCHCACHE(MAXVBOCACHE, vbocacheentry, vbocache, !c.vbuf || );
- }
-
- blendcacheentry &checkblendcache(skelcacheentry &sc, int owner)
- {
- SEARCHCACHE(MAXBLENDCACHE, blendcacheentry, blendcache, )
- }
-
- void preload(part *p)
- {
- if(!skel->canpreload()) return;
- bool tangents = false;
- loopv(p->skins) if(p->skins[i].tangents()) tangents = true;
- if(skel->shouldcleanup()) skel->cleanup();
- else if(tangents!=vtangents) cleanup();
- skel->preload();
- if(!vbocache->vbuf) genvbo(tangents, *vbocache);
- }
-
- void render(const animstate *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p)
- {
- bool tangents = false;
- loopv(p->skins) if(p->skins[i].tangents()) tangents = true;
- if(skel->shouldcleanup()) { skel->cleanup(); disablevbo(); }
- else if(tangents!=vtangents) { cleanup(); disablevbo(); }
-
- if(!skel->numframes)
- {
- if(!(as->cur.anim&ANIM_NORENDER))
- {
- if(!vbocache->vbuf) genvbo(tangents, *vbocache);
- bindvbo(as, *vbocache);
- loopv(meshes)
- {
- skelmesh *m = (skelmesh *)meshes[i];
- p->skins[i].bind(m, as);
- m->render(as, p->skins[i], *vbocache);
- }
- }
- skel->calctags(p);
- return;
- }
-
- skelcacheentry &sc = skel->checkskelcache(p, as, pitch, axis, forward, as->cur.anim&ANIM_RAGDOLL || !d || !d->ragdoll || d->ragdoll->skel != skel->ragdoll ? NULL : d->ragdoll);
- if(!(as->cur.anim&ANIM_NORENDER))
- {
- int owner = &sc-&skel->skelcache[0];
- vbocacheentry &vc = skel->usegpuskel ? *vbocache : checkvbocache(sc, owner);
- vc.millis = lastmillis;
- if(!vc.vbuf) genvbo(tangents, vc);
- blendcacheentry *bc = NULL;
- if(vblends)
- {
- bc = &checkblendcache(sc, owner);
- bc->millis = lastmillis;
- if(bc->owner!=owner)
- {
- bc->owner = owner;
- *(animcacheentry *)bc = sc;
- blendbones(sc, *bc);
- }
- }
- if(!skel->usegpuskel && vc.owner!=owner)
- {
- vc.owner = owner;
- (animcacheentry &)vc = sc;
- loopv(meshes)
- {
- skelmesh &m = *(skelmesh *)meshes[i];
- m.interpverts(sc.bdata, bc ? bc->bdata : NULL, tangents, vdata + m.voffset*vertsize, p->skins[i]);
- }
- gle::bindvbo(vc.vbuf);
- glBufferData_(GL_ARRAY_BUFFER, vlen*vertsize, vdata, GL_STREAM_DRAW);
- }
-
- bindvbo(as, vc, &sc, bc);
- loopv(meshes)
- {
- skelmesh *m = (skelmesh *)meshes[i];
- p->skins[i].bind(m, as);
- if(skel->usegpuskel) skel->setgpubones(sc, bc, vblends);
- m->render(as, p->skins[i], vc);
- }
- }
-
- skel->calctags(p, &sc);
-
- if(as->cur.anim&ANIM_RAGDOLL && skel->ragdoll && !d->ragdoll)
- {
- d->ragdoll = new ragdolldata(skel->ragdoll, p->model->scale);
- skel->initragdoll(*d->ragdoll, sc, p);
- d->ragdoll->init(d);
- }
- }
- };
-
- struct animpartmask
- {
- animpartmask *next;
- int numbones;
- uchar bones[1];
- };
-
- struct skelpart : part
- {
- animpartmask *buildingpartmask;
-
- uchar *partmask;
-
- skelpart(animmodel *model, int index = 0) : part(model, index), buildingpartmask(NULL), partmask(NULL)
- {
- }
-
- virtual ~skelpart()
- {
- DELETEA(buildingpartmask);
- }
-
- uchar *sharepartmask(animpartmask *o)
- {
- static animpartmask *partmasks = NULL;
- animpartmask *p = partmasks;
- for(; p; p = p->next) if(p->numbones==o->numbones && !memcmp(p->bones, o->bones, p->numbones))
- {
- delete[] (uchar *)o;
- return p->bones;
- }
-
- o->next = p;
- partmasks = o;
- return o->bones;
- }
-
- animpartmask *newpartmask()
- {
- animpartmask *p = (animpartmask *)new uchar[sizeof(animpartmask) + ((skelmeshgroup *)meshes)->skel->numbones-1];
- p->numbones = ((skelmeshgroup *)meshes)->skel->numbones;
- memset(p->bones, 0, p->numbones);
- return p;
- }
-
- void initanimparts()
- {
- DELETEA(buildingpartmask);
- buildingpartmask = newpartmask();
- }
-
- bool addanimpart(ushort *bonemask)
- {
- if(!buildingpartmask || numanimparts>=MAXANIMPARTS) return false;
- ((skelmeshgroup *)meshes)->skel->applybonemask(bonemask, buildingpartmask->bones, numanimparts);
- numanimparts++;
- return true;
- }
-
- void endanimparts()
- {
- if(buildingpartmask)
- {
- partmask = sharepartmask(buildingpartmask);
- buildingpartmask = NULL;
- }
-
- ((skelmeshgroup *)meshes)->skel->optimize();
- }
-
- void loaded()
- {
- endanimparts();
- part::loaded();
- }
- };
-
- skelmodel(const char *name) : animmodel(name)
- {
- }
-
- int linktype(animmodel *m) const
- {
- return type()==m->type() &&
- ((skelmeshgroup *)parts[0]->meshes)->skel == ((skelmeshgroup *)m->parts[0]->meshes)->skel ?
- LINK_REUSE :
- LINK_TAG;
- }
-
- bool skeletal() const { return true; }
-
- skelpart &addpart()
- {
- flushpart();
- skelpart *p = new skelpart(this, parts.length());
- parts.add(p);
- return *p;
- }
+ struct vert { vec pos, norm; vec2 tc; int blend, interpindex; };
+ struct vvert { vec pos; vec2 tc; };
+ struct vvertn : vvert { vec norm; };
+ struct vvertbump : vvert { squat tangent; };
+ struct vvertw { uchar weights[4]; uchar bones[4]; };
+ struct vvertnw : vvertn, vvertw {};
+ struct vvertbumpw : vvertbump, vvertw {};
+ struct bumpvert { quat tangent; };
+ struct tri { ushort vert[3]; };
+
+ struct blendcombo
+ {
+ int uses, interpindex;
+ float weights[4];
+ uchar bones[4], interpbones[4];
+
+ blendcombo() : uses(1)
+ {
+ }
+
+ bool operator==(const blendcombo &c) const
+ {
+ loopk(4) if(bones[k] != c.bones[k]) return false;
+ loopk(4) if(weights[k] != c.weights[k]) return false;
+ return true;
+ }
+
+ int size() const
+ {
+ int i = 1;
+ while(i < 4 && weights[i]) i++;
+ return i;
+ }
+
+ static bool sortcmp(const blendcombo &x, const blendcombo &y)
+ {
+ loopi(4)
+ {
+ if(x.weights[i])
+ {
+ if(!y.weights[i]) return true;
+ }
+ else if(y.weights[i]) return false;
+ else break;
+ }
+ return false;
+ }
+
+ int addweight(int sorted, float weight, int bone)
+ {
+ if(weight <= 1e-3f) return sorted;
+ loopk(sorted) if(weight > weights[k])
+ {
+ for(int l = min(sorted-1, 2); l >= k; l--)
+ {
+ weights[l+1] = weights[l];
+ bones[l+1] = bones[l];
+ }
+ weights[k] = weight;
+ bones[k] = bone;
+ return sorted<4 ? sorted+1 : sorted;
+ }
+ if(sorted>=4) return sorted;
+ weights[sorted] = weight;
+ bones[sorted] = bone;
+ return sorted+1;
+ }
+
+ void finalize(int sorted)
+ {
+ loopj(4-sorted) { weights[sorted+j] = 0; bones[sorted+j] = 0; }
+ if(sorted <= 0) return;
+ float total = 0;
+ loopj(sorted) total += weights[j];
+ total = 1.0f/total;
+ loopj(sorted) weights[j] *= total;
+ }
+
+ void serialize(vvertw &v)
+ {
+ if(interpindex >= 0)
+ {
+ v.weights[0] = 255;
+ loopk(3) v.weights[k+1] = 0;
+ v.bones[0] = 2*interpindex;
+ loopk(3) v.bones[k+1] = v.bones[0];
+ }
+ else
+ {
+ int total = 0;
+ loopk(4) total += (v.weights[k] = uchar(0.5f + weights[k]*255));
+ while(total > 255)
+ {
+ loopk(4) if(v.weights[k] > 0 && total > 255) { v.weights[k]--; total--; }
+ }
+ while(total < 255)
+ {
+ loopk(4) if(v.weights[k] < 255 && total < 255) { v.weights[k]++; total++; }
+ }
+ loopk(4) v.bones[k] = 2*interpbones[k];
+ }
+ }
+ };
+
+
+ struct animcacheentry
+ {
+ animstate as[MAXANIMPARTS];
+ float pitch;
+ int millis;
+ uchar *partmask;
+ ragdolldata *ragdoll;
+
+ animcacheentry() : ragdoll(NULL)
+ {
+ loopk(MAXANIMPARTS) as[k].cur.fr1 = as[k].prev.fr1 = -1;
+ }
+
+ bool operator==(const animcacheentry &c) const
+ {
+ loopi(MAXANIMPARTS) if(as[i]!=c.as[i]) return false;
+ return pitch==c.pitch && partmask==c.partmask && ragdoll==c.ragdoll && (!ragdoll || min(millis, c.millis) >= ragdoll->lastmove);
+ }
+ };
+
+ struct vbocacheentry : animcacheentry
+ {
+ GLuint vbuf;
+ int owner;
+
+ vbocacheentry() : vbuf(0), owner(-1) {}
+ };
+
+ struct skelcacheentry : animcacheentry
+ {
+ dualquat *bdata;
+ int version;
+ bool dirty;
+
+ skelcacheentry() : bdata(NULL), version(-1), dirty(false) {}
+
+ void nextversion()
+ {
+ version = Shader::uniformlocversion();
+ dirty = true;
+ }
+ };
+
+ struct blendcacheentry : skelcacheentry
+ {
+ int owner;
+
+ blendcacheentry() : owner(-1) {}
+ };
+
+ struct skelmeshgroup;
+
+ struct skelmesh : mesh
+ {
+ vert *verts;
+ bumpvert *bumpverts;
+ tri *tris;
+ int numverts, numtris, maxweights;
+
+ int voffset, eoffset, elen;
+ ushort minvert, maxvert;
+
+ skelmesh() : verts(NULL), bumpverts(NULL), tris(NULL), numverts(0), numtris(0), maxweights(0)
+ {
+ }
+
+ virtual ~skelmesh()
+ {
+ DELETEA(verts);
+ DELETEA(bumpverts);
+ DELETEA(tris);
+ }
+
+ int addblendcombo(const blendcombo &c)
+ {
+ maxweights = max(maxweights, c.size());
+ return ((skelmeshgroup *)group)->addblendcombo(c);
+ }
+
+ void smoothnorms(float limit = 0, bool areaweight = true)
+ {
+ mesh::smoothnorms(verts, numverts, tris, numtris, limit, areaweight);
+ }
+
+ void buildnorms(bool areaweight = true)
+ {
+ mesh::buildnorms(verts, numverts, tris, numtris, areaweight);
+ }
+
+ void calctangents(bool areaweight = true)
+ {
+ if(bumpverts) return;
+ bumpverts = new bumpvert[numverts];
+ mesh::calctangents(bumpverts, verts, verts, numverts, tris, numtris, areaweight);
+ }
+
+ void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m)
+ {
+ loopj(numverts)
+ {
+ vec v = m.transform(verts[j].pos);
+ loopi(3)
+ {
+ bbmin[i] = min(bbmin[i], v[i]);
+ bbmax[i] = max(bbmax[i], v[i]);
+ }
+ }
+ }
+
+ void genBIH(BIH::mesh &m)
+ {
+ m.tris = (const BIH::tri *)tris;
+ m.numtris = numtris;
+ m.pos = (const uchar *)&verts->pos;
+ m.posstride = sizeof(vert);
+ m.tc = (const uchar *)&verts->tc;
+ m.tcstride = sizeof(vert);
+ }
+
+ static inline void assignvert(vvertn &vv, int j, vert &v, blendcombo &c)
+ {
+ vv.pos = v.pos;
+ vv.norm = v.norm;
+ vv.tc = v.tc;
+ }
+
+ inline void assignvert(vvertbump &vv, int j, vert &v, blendcombo &c)
+ {
+ vv.pos = v.pos;
+ vv.tc = v.tc;
+ vv.tangent = bumpverts[j].tangent;
+ }
+
+ static inline void assignvert(vvertnw &vv, int j, vert &v, blendcombo &c)
+ {
+ vv.pos = v.pos;
+ vv.norm = v.norm;
+ vv.tc = v.tc;
+ c.serialize(vv);
+ }
+
+ inline void assignvert(vvertbumpw &vv, int j, vert &v, blendcombo &c)
+ {
+ vv.pos = v.pos;
+ vv.tc = v.tc;
+ vv.tangent = bumpverts[j].tangent;
+ c.serialize(vv);
+ }
+
+ template<class T>
+ int genvbo(vector<ushort> &idxs, int offset, vector<T> &vverts)
+ {
+ voffset = offset;
+ eoffset = idxs.length();
+ loopi(numverts)
+ {
+ vert &v = verts[i];
+ assignvert(vverts.add(), i, v, ((skelmeshgroup *)group)->blendcombos[v.blend]);
+ }
+ loopi(numtris) loopj(3) idxs.add(voffset + tris[i].vert[j]);
+ elen = idxs.length()-eoffset;
+ minvert = voffset;
+ maxvert = voffset + numverts-1;
+ return numverts;
+ }
+
+ template<class T>
+ int genvbo(vector<ushort> &idxs, int offset, vector<T> &vverts, int *htdata, int htlen)
+ {
+ voffset = offset;
+ eoffset = idxs.length();
+ minvert = 0xFFFF;
+ loopi(numtris)
+ {
+ tri &t = tris[i];
+ loopj(3)
+ {
+ int index = t.vert[j];
+ vert &v = verts[index];
+ T vv;
+ assignvert(vv, index, v, ((skelmeshgroup *)group)->blendcombos[v.blend]);
+ int htidx = hthash(v.pos)&(htlen-1);
+ loopk(htlen)
+ {
+ int &vidx = htdata[(htidx+k)&(htlen-1)];
+ if(vidx < 0) { vidx = idxs.add(ushort(vverts.length())); vverts.add(vv); break; }
+ else if(!memcmp(&vverts[vidx], &vv, sizeof(vv))) { minvert = min(minvert, idxs.add(ushort(vidx))); break; }
+ }
+ }
+ }
+ elen = idxs.length()-eoffset;
+ minvert = min(minvert, ushort(voffset));
+ maxvert = max(minvert, ushort(vverts.length()-1));
+ return vverts.length()-voffset;
+ }
+
+ int genvbo(vector<ushort> &idxs, int offset)
+ {
+ loopi(numverts) verts[i].interpindex = ((skelmeshgroup *)group)->remapblend(verts[i].blend);
+
+ voffset = offset;
+ eoffset = idxs.length();
+ loopi(numtris)
+ {
+ tri &t = tris[i];
+ loopj(3) idxs.add(voffset+t.vert[j]);
+ }
+ minvert = voffset;
+ maxvert = voffset + numverts-1;
+ elen = idxs.length()-eoffset;
+ return numverts;
+ }
+
+ template<class T>
+ static inline void fillvert(T &vv, int j, vert &v)
+ {
+ vv.tc = v.tc;
+ }
+
+ template<class T>
+ void fillverts(T *vdata)
+ {
+ vdata += voffset;
+ loopi(numverts) fillvert(vdata[i], i, verts[i]);
+ }
+
+ void interpverts(const dualquat * RESTRICT bdata1, const dualquat * RESTRICT bdata2, bool tangents, void * RESTRICT vdata, skin &s)
+ {
+ const int blendoffset = ((skelmeshgroup *)group)->skel->numgpubones;
+ bdata2 -= blendoffset;
+
+ #define IPLOOP(type, dosetup, dotransform) \
+ loopi(numverts) \
+ { \
+ const vert &src = verts[i]; \
+ type &dst = ((type * RESTRICT)vdata)[i]; \
+ dosetup; \
+ const dualquat &b = (src.interpindex < blendoffset ? bdata1 : bdata2)[src.interpindex]; \
+ dst.pos = b.transform(src.pos); \
+ dotransform; \
+ }
+
+ if(tangents)
+ {
+ IPLOOP(vvertbump, bumpvert &bsrc = bumpverts[i],
+ {
+ quat q = b.transform(bsrc.tangent);
+ fixqtangent(q, bsrc.tangent.w);
+ dst.tangent = q;
+ });
+ }
+ else
+ {
+ IPLOOP(vvertn, ,
+ {
+ dst.norm = b.transformnormal(src.norm);
+ });
+ }
+
+ #undef IPLOOP
+ }
+
+ void setshader(Shader *s)
+ {
+ skelmeshgroup *g = (skelmeshgroup *)group;
+ if(glaring)
+ {
+ if(!g->skel->usegpuskel) s->setvariant(0, 1);
+ else s->setvariant(min(maxweights, g->vweights), 1);
+ }
+ else if(!g->skel->usegpuskel) s->set();
+ else s->setvariant(min(maxweights, g->vweights)-1, 0);
+ }
+
+ void render(const animstate *as, skin &s, vbocacheentry &vc)
+ {
+ if(!Shader::lastshader) return;
+ glDrawRangeElements_(GL_TRIANGLES, minvert, maxvert, elen, GL_UNSIGNED_SHORT, &((skelmeshgroup *)group)->edata[eoffset]);
+ glde++;
+ xtravertsva += numverts;
+ }
+ };
+
+
+ struct tag
+ {
+ char *name;
+ int bone;
+ matrix4x3 matrix;
+
+ tag() : name(NULL) {}
+ ~tag() { DELETEA(name); }
+ };
+
+ struct skelanimspec
+ {
+ char *name;
+ int frame, range;
+
+ skelanimspec() : name(NULL), frame(0), range(0) {}
+ ~skelanimspec()
+ {
+ DELETEA(name);
+ }
+ };
+
+ struct boneinfo
+ {
+ const char *name;
+ int parent, children, next, group, scheduled, interpindex, interpparent, ragdollindex, correctindex;
+ float pitchscale, pitchoffset, pitchmin, pitchmax;
+ dualquat base, invbase;
+
+ boneinfo() : name(NULL), parent(-1), children(-1), next(-1), group(INT_MAX), scheduled(-1), interpindex(-1), interpparent(-1), ragdollindex(-1), correctindex(-1), pitchscale(0), pitchoffset(0), pitchmin(0), pitchmax(0) {}
+ ~boneinfo()
+ {
+ DELETEA(name);
+ }
+ };
+
+ struct antipode
+ {
+ int parent, child;
+
+ antipode(int parent, int child) : parent(parent), child(child) {}
+ };
+
+ struct pitchdep
+ {
+ int bone, parent;
+ dualquat pose;
+ };
+
+ struct pitchtarget
+ {
+ int bone, frame, corrects, deps;
+ float pitchmin, pitchmax, deviated;
+ dualquat pose;
+ };
+
+ struct pitchcorrect
+ {
+ int bone, target, parent;
+ float pitchmin, pitchmax, pitchscale, pitchangle, pitchtotal;
+
+ pitchcorrect() : parent(-1), pitchangle(0), pitchtotal(0) {}
+ };
+
+ struct skeleton
+ {
+ char *name;
+ int shared;
+ vector<skelmeshgroup *> users;
+ boneinfo *bones;
+ int numbones, numinterpbones, numgpubones, numframes;
+ dualquat *framebones;
+ vector<skelanimspec> skelanims;
+ vector<tag> tags;
+ vector<antipode> antipodes;
+ ragdollskel *ragdoll;
+ vector<pitchdep> pitchdeps;
+ vector<pitchtarget> pitchtargets;
+ vector<pitchcorrect> pitchcorrects;
+
+ bool usegpuskel;
+ vector<skelcacheentry> skelcache;
+ hashtable<GLuint, int> blendoffsets;
+
+ skeleton() : name(NULL), shared(0), bones(NULL), numbones(0), numinterpbones(0), numgpubones(0), numframes(0), framebones(NULL), ragdoll(NULL), usegpuskel(false), blendoffsets(32)
+ {
+ }
+
+ ~skeleton()
+ {
+ DELETEA(name);
+ DELETEA(bones);
+ DELETEA(framebones);
+ DELETEP(ragdoll);
+ loopv(skelcache)
+ {
+ DELETEA(skelcache[i].bdata);
+ }
+ }
+
+ skelanimspec *findskelanim(const char *name, char sep = '\0')
+ {
+ int len = sep ? strlen(name) : 0;
+ loopv(skelanims)
+ {
+ if(skelanims[i].name)
+ {
+ if(sep)
+ {
+ const char *end = strchr(skelanims[i].name, ':');
+ if(end && end - skelanims[i].name == len && !memcmp(name, skelanims[i].name, len)) return &skelanims[i];
+ }
+ if(!strcmp(name, skelanims[i].name)) return &skelanims[i];
+ }
+ }
+ return NULL;
+ }
+
+ skelanimspec &addskelanim(const char *name)
+ {
+ skelanimspec &sa = skelanims.add();
+ sa.name = name ? newstring(name) : NULL;
+ return sa;
+ }
+
+ int findbone(const char *name)
+ {
+ loopi(numbones) if(bones[i].name && !strcmp(bones[i].name, name)) return i;
+ return -1;
+ }
+
+ int findtag(const char *name)
+ {
+ loopv(tags) if(!strcmp(tags[i].name, name)) return i;
+ return -1;
+ }
+
+ bool addtag(const char *name, int bone, const matrix4x3 &matrix)
+ {
+ int idx = findtag(name);
+ if(idx >= 0)
+ {
+ if(!testtags) return false;
+ tag &t = tags[idx];
+ t.bone = bone;
+ t.matrix = matrix;
+ }
+ else
+ {
+ tag &t = tags.add();
+ t.name = newstring(name);
+ t.bone = bone;
+ t.matrix = matrix;
+ }
+ return true;
+ }
+
+ void calcantipodes()
+ {
+ antipodes.shrink(0);
+ vector<int> schedule;
+ loopi(numbones)
+ {
+ if(bones[i].group >= numbones)
+ {
+ bones[i].scheduled = schedule.length();
+ schedule.add(i);
+ }
+ else bones[i].scheduled = -1;
+ }
+ loopv(schedule)
+ {
+ int bone = schedule[i];
+ const boneinfo &info = bones[bone];
+ loopj(numbones) if(abs(bones[j].group) == bone && bones[j].scheduled < 0)
+ {
+ antipodes.add(antipode(info.interpindex, bones[j].interpindex));
+ bones[j].scheduled = schedule.length();
+ schedule.add(j);
+ }
+ if(i + 1 == schedule.length())
+ {
+ int conflict = INT_MAX;
+ loopj(numbones) if(bones[j].group < numbones && bones[j].scheduled < 0) conflict = min(conflict, abs(bones[j].group));
+ if(conflict < numbones)
+ {
+ bones[conflict].scheduled = schedule.length();
+ schedule.add(conflict);
+ }
+ }
+ }
+ }
+
+ void remapbones()
+ {
+ loopi(numbones)
+ {
+ boneinfo &info = bones[i];
+ info.interpindex = -1;
+ info.ragdollindex = -1;
+ }
+ numgpubones = 0;
+ loopv(users)
+ {
+ skelmeshgroup *group = users[i];
+ loopvj(group->blendcombos)
+ {
+ blendcombo &c = group->blendcombos[j];
+ loopk(4)
+ {
+ if(!c.weights[k]) { c.interpbones[k] = k > 0 ? c.interpbones[k-1] : 0; continue; }
+ boneinfo &info = bones[c.bones[k]];
+ if(info.interpindex < 0) info.interpindex = numgpubones++;
+ c.interpbones[k] = info.interpindex;
+ if(info.group < 0) continue;
+ loopl(4)
+ {
+ if(!c.weights[l]) break;
+ if(l == k) continue;
+ int parent = c.bones[l];
+ if(info.parent == parent || (info.parent >= 0 && info.parent == bones[parent].parent)) { info.group = -info.parent; break; }
+ if(info.group <= parent) continue;
+ int child = c.bones[k];
+ while(parent > child) parent = bones[parent].parent;
+ if(parent != child) info.group = c.bones[l];
+ }
+ }
+ }
+ }
+ numinterpbones = numgpubones;
+ loopv(tags)
+ {
+ boneinfo &info = bones[tags[i].bone];
+ if(info.interpindex < 0) info.interpindex = numinterpbones++;
+ }
+ if(ragdoll)
+ {
+ loopv(ragdoll->joints)
+ {
+ boneinfo &info = bones[ragdoll->joints[i].bone];
+ if(info.interpindex < 0) info.interpindex = numinterpbones++;
+ info.ragdollindex = i;
+ }
+ }
+ loopi(numbones)
+ {
+ boneinfo &info = bones[i];
+ if(info.interpindex < 0) continue;
+ for(int parent = info.parent; parent >= 0 && bones[parent].interpindex < 0; parent = bones[parent].parent)
+ bones[parent].interpindex = numinterpbones++;
+ }
+ loopi(numbones)
+ {
+ boneinfo &info = bones[i];
+ if(info.interpindex < 0) continue;
+ info.interpparent = info.parent >= 0 ? bones[info.parent].interpindex : -1;
+ }
+ if(ragdoll)
+ {
+ loopi(numbones)
+ {
+ boneinfo &info = bones[i];
+ if(info.interpindex < 0 || info.ragdollindex >= 0) continue;
+ for(int parent = info.parent; parent >= 0; parent = bones[parent].parent)
+ {
+ if(bones[parent].ragdollindex >= 0) { ragdoll->addreljoint(i, bones[parent].ragdollindex); break; }
+ }
+ }
+ }
+ calcantipodes();
+ }
+
+
+ void addpitchdep(int bone, int frame)
+ {
+ for(; bone >= 0; bone = bones[bone].parent)
+ {
+ int pos = pitchdeps.length();
+ loopvj(pitchdeps) if(bone <= pitchdeps[j].bone)
+ {
+ if(bone == pitchdeps[j].bone) goto nextbone;
+ pos = j;
+ break;
+ }
+ {
+ pitchdep d;
+ d.bone = bone;
+ d.parent = -1;
+ d.pose = framebones[frame*numbones + bone];
+ pitchdeps.insert(pos, d);
+ }
+ nextbone:;
+ }
+ }
+
+ int findpitchdep(int bone)
+ {
+ loopv(pitchdeps) if(bone <= pitchdeps[i].bone) return bone == pitchdeps[i].bone ? i : -1;
+ return -1;
+ }
+
+ int findpitchcorrect(int bone)
+ {
+ loopv(pitchcorrects) if(bone <= pitchcorrects[i].bone) return bone == pitchcorrects[i].bone ? i : -1;
+ return -1;
+ }
+
+ void initpitchdeps()
+ {
+ pitchdeps.setsize(0);
+ if(pitchtargets.empty()) return;
+ loopv(pitchtargets)
+ {
+ pitchtarget &t = pitchtargets[i];
+ t.deps = -1;
+ addpitchdep(t.bone, t.frame);
+ }
+ loopv(pitchdeps)
+ {
+ pitchdep &d = pitchdeps[i];
+ int parent = bones[d.bone].parent;
+ if(parent >= 0)
+ {
+ int j = findpitchdep(parent);
+ if(j >= 0)
+ {
+ d.parent = j;
+ d.pose.mul(pitchdeps[j].pose, dualquat(d.pose));
+ }
+ }
+ }
+ loopv(pitchtargets)
+ {
+ pitchtarget &t = pitchtargets[i];
+ int j = findpitchdep(t.bone);
+ if(j >= 0)
+ {
+ t.deps = j;
+ t.pose = pitchdeps[j].pose;
+ }
+ t.corrects = -1;
+ for(int parent = t.bone; parent >= 0; parent = bones[parent].parent)
+ {
+ t.corrects = findpitchcorrect(parent);
+ if(t.corrects >= 0) break;
+ }
+ }
+ loopv(pitchcorrects)
+ {
+ pitchcorrect &c = pitchcorrects[i];
+ bones[c.bone].correctindex = i;
+ c.parent = -1;
+ for(int parent = c.bone;;)
+ {
+ parent = bones[parent].parent;
+ if(parent < 0) break;
+ c.parent = findpitchcorrect(parent);
+ if(c.parent >= 0) break;
+ }
+ }
+ }
+
+ void optimize()
+ {
+ cleanup();
+ if(ragdoll) ragdoll->setup();
+ remapbones();
+ initpitchdeps();
+ }
+
+ void expandbonemask(uchar *expansion, int bone, int val)
+ {
+ expansion[bone] = val;
+ bone = bones[bone].children;
+ while(bone>=0) { expandbonemask(expansion, bone, val); bone = bones[bone].next; }
+ }
+
+ void applybonemask(ushort *mask, uchar *partmask, int partindex)
+ {
+ if(!mask || *mask==BONEMASK_END) return;
+ uchar *expansion = new uchar[numbones];
+ memset(expansion, *mask&BONEMASK_NOT ? 1 : 0, numbones);
+ while(*mask!=BONEMASK_END)
+ {
+ expandbonemask(expansion, *mask&BONEMASK_BONE, *mask&BONEMASK_NOT ? 0 : 1);
+ mask++;
+ }
+ loopi(numbones) if(expansion[i]) partmask[i] = partindex;
+ delete[] expansion;
+ }
+
+ void linkchildren()
+ {
+ loopi(numbones)
+ {
+ boneinfo &b = bones[i];
+ b.children = -1;
+ if(b.parent<0) b.next = -1;
+ else
+ {
+ b.next = bones[b.parent].children;
+ bones[b.parent].children = i;
+ }
+ }
+ }
+
+ int availgpubones() const { return min(maxvsuniforms - reservevpparams - 10, maxskelanimdata) / 2; }
+ bool gpuaccelerate() const { return numframes && gpuskel && numgpubones<=availgpubones(); }
+
+ float calcdeviation(const vec &axis, const vec &forward, const dualquat &pose1, const dualquat &pose2)
+ {
+ vec forward1 = pose1.transformnormal(forward).project(axis).normalize(),
+ forward2 = pose2.transformnormal(forward).project(axis).normalize(),
+ daxis = vec().cross(forward1, forward2);
+ float dx = clamp(forward1.dot(forward2), -1.0f, 1.0f), dy = clamp(daxis.magnitude(), -1.0f, 1.0f);
+ if(daxis.dot(axis) < 0) dy = -dy;
+ return atan2f(dy, dx)/RAD;
+ }
+
+ void calcpitchcorrects(float pitch, const vec &axis, const vec &forward)
+ {
+ loopv(pitchtargets)
+ {
+ pitchtarget &t = pitchtargets[i];
+ t.deviated = calcdeviation(axis, forward, t.pose, pitchdeps[t.deps].pose);
+ }
+ loopv(pitchcorrects)
+ {
+ pitchcorrect &c = pitchcorrects[i];
+ c.pitchangle = c.pitchtotal = 0;
+ }
+ loopvj(pitchtargets)
+ {
+ pitchtarget &t = pitchtargets[j];
+ float tpitch = pitch - t.deviated;
+ for(int parent = t.corrects; parent >= 0; parent = pitchcorrects[parent].parent)
+ tpitch -= pitchcorrects[parent].pitchangle;
+ if(t.pitchmin || t.pitchmax) tpitch = clamp(tpitch, t.pitchmin, t.pitchmax);
+ loopv(pitchcorrects)
+ {
+ pitchcorrect &c = pitchcorrects[i];
+ if(c.target != j) continue;
+ float total = c.parent >= 0 ? pitchcorrects[c.parent].pitchtotal : 0,
+ avail = tpitch - total,
+ used = tpitch*c.pitchscale;
+ if(c.pitchmin || c.pitchmax)
+ {
+ if(used < 0) used = clamp(c.pitchmin, used, 0.0f);
+ else used = clamp(c.pitchmax, 0.0f, used);
+ }
+ if(used < 0) used = clamp(avail, used, 0.0f);
+ else used = clamp(avail, 0.0f, used);
+ c.pitchangle = used;
+ c.pitchtotal = used + total;
+ }
+ }
+ }
+
+ #define INTERPBONE(bone) \
+ const animstate &s = as[partmask[bone]]; \
+ const framedata &f = partframes[partmask[bone]]; \
+ dualquat d; \
+ (d = f.fr1[bone]).mul((1-s.cur.t)*s.interp); \
+ d.accumulate(f.fr2[bone], s.cur.t*s.interp); \
+ if(s.interp<1) \
+ { \
+ d.accumulate(f.pfr1[bone], (1-s.prev.t)*(1-s.interp)); \
+ d.accumulate(f.pfr2[bone], s.prev.t*(1-s.interp)); \
+ }
+
+ void interpbones(const animstate *as, float pitch, const vec &axis, const vec &forward, int numanimparts, const uchar *partmask, skelcacheentry &sc)
+ {
+ if(!sc.bdata) sc.bdata = new dualquat[numinterpbones];
+ sc.nextversion();
+ struct framedata
+ {
+ const dualquat *fr1, *fr2, *pfr1, *pfr2;
+ } partframes[MAXANIMPARTS];
+ loopi(numanimparts)
+ {
+ partframes[i].fr1 = &framebones[as[i].cur.fr1*numbones];
+ partframes[i].fr2 = &framebones[as[i].cur.fr2*numbones];
+ if(as[i].interp<1)
+ {
+ partframes[i].pfr1 = &framebones[as[i].prev.fr1*numbones];
+ partframes[i].pfr2 = &framebones[as[i].prev.fr2*numbones];
+ }
+ }
+ loopv(pitchdeps)
+ {
+ pitchdep &p = pitchdeps[i];
+ INTERPBONE(p.bone);
+ d.normalize();
+ if(p.parent >= 0) p.pose.mul(pitchdeps[p.parent].pose, d);
+ else p.pose = d;
+ }
+ calcpitchcorrects(pitch, axis, forward);
+ loopi(numbones) if(bones[i].interpindex>=0)
+ {
+ INTERPBONE(i);
+ const boneinfo &b = bones[i];
+ d.normalize();
+ if(b.interpparent<0) sc.bdata[b.interpindex] = d;
+ else sc.bdata[b.interpindex].mul(sc.bdata[b.interpparent], d);
+ float angle;
+ if(b.pitchscale) { angle = b.pitchscale*pitch + b.pitchoffset; if(b.pitchmin || b.pitchmax) angle = clamp(angle, b.pitchmin, b.pitchmax); }
+ else if(b.correctindex >= 0) angle = pitchcorrects[b.correctindex].pitchangle;
+ else continue;
+ if(as->cur.anim&ANIM_NOPITCH || (as->interp < 1 && as->prev.anim&ANIM_NOPITCH))
+ angle *= (as->cur.anim&ANIM_NOPITCH ? 0 : as->interp) + (as->interp < 1 && as->prev.anim&ANIM_NOPITCH ? 0 : 1-as->interp);
+ sc.bdata[b.interpindex].mulorient(quat(axis, angle*RAD), b.base);
+ }
+ loopv(antipodes) sc.bdata[antipodes[i].child].fixantipodal(sc.bdata[antipodes[i].parent]);
+ }
+
+ void initragdoll(ragdolldata &d, skelcacheentry &sc, part *p)
+ {
+ const dualquat *bdata = sc.bdata;
+ loopv(ragdoll->joints)
+ {
+ const ragdollskel::joint &j = ragdoll->joints[i];
+ const boneinfo &b = bones[j.bone];
+ const dualquat &q = bdata[b.interpindex];
+ loopk(3) if(j.vert[k] >= 0)
+ {
+ ragdollskel::vert &v = ragdoll->verts[j.vert[k]];
+ ragdolldata::vert &dv = d.verts[j.vert[k]];
+ dv.pos.add(q.transform(v.pos).mul(v.weight));
+ }
+ }
+ if(ragdoll->animjoints) loopv(ragdoll->joints)
+ {
+ const ragdollskel::joint &j = ragdoll->joints[i];
+ const boneinfo &b = bones[j.bone];
+ const dualquat &q = bdata[b.interpindex];
+ d.calcanimjoint(i, matrix4x3(q));
+ }
+ loopv(ragdoll->verts)
+ {
+ ragdolldata::vert &dv = d.verts[i];
+ matrixstack[matrixpos].transform(vec(dv.pos).add(p->translate).mul(p->model->scale), dv.pos);
+ }
+ loopv(ragdoll->reljoints)
+ {
+ const ragdollskel::reljoint &r = ragdoll->reljoints[i];
+ const ragdollskel::joint &j = ragdoll->joints[r.parent];
+ const boneinfo &br = bones[r.bone], &bj = bones[j.bone];
+ d.reljoints[i].mul(dualquat(bdata[bj.interpindex]).invert(), bdata[br.interpindex]);
+ }
+ }
+
+ void genragdollbones(ragdolldata &d, skelcacheentry &sc, part *p)
+ {
+ if(!sc.bdata) sc.bdata = new dualquat[numinterpbones];
+ sc.nextversion();
+ loopv(ragdoll->joints)
+ {
+ const ragdollskel::joint &j = ragdoll->joints[i];
+ const boneinfo &b = bones[j.bone];
+ vec pos(0, 0, 0);
+ loopk(3) if(j.vert[k]>=0) pos.add(d.verts[j.vert[k]].pos);
+ pos.mul(j.weight/p->model->scale).sub(p->translate);
+ matrix4x3 m;
+ m.mul(d.tris[j.tri], pos, d.animjoints ? d.animjoints[i] : j.orient);
+ sc.bdata[b.interpindex] = dualquat(m);
+ }
+ loopv(ragdoll->reljoints)
+ {
+ const ragdollskel::reljoint &r = ragdoll->reljoints[i];
+ const ragdollskel::joint &j = ragdoll->joints[r.parent];
+ const boneinfo &br = bones[r.bone], &bj = bones[j.bone];
+ sc.bdata[br.interpindex].mul(sc.bdata[bj.interpindex], d.reljoints[i]);
+ }
+ loopv(antipodes) sc.bdata[antipodes[i].child].fixantipodal(sc.bdata[antipodes[i].parent]);
+ }
+
+ void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n)
+ {
+ matrix4x3 t;
+ t.mul(bones[tags[i].bone].base, tags[i].matrix);
+ t.posttranslate(p->translate, p->model->scale);
+ n.mul(m, t);
+ }
+
+ void calctags(part *p, skelcacheentry *sc = NULL)
+ {
+ loopv(p->links)
+ {
+ linkedpart &l = p->links[i];
+ tag &t = tags[l.tag];
+ dualquat q;
+ if(sc) q.mul(sc->bdata[bones[t.bone].interpindex], bones[t.bone].base);
+ else q = bones[t.bone].base;
+ matrix4x3 m;
+ m.mul(q, t.matrix);
+ m.d.add(p->translate).mul(p->model->scale);
+ l.matrix = m;
+ }
+ }
+
+ void cleanup(bool full = true)
+ {
+ loopv(skelcache)
+ {
+ skelcacheentry &sc = skelcache[i];
+ loopj(MAXANIMPARTS) sc.as[j].cur.fr1 = -1;
+ DELETEA(sc.bdata);
+ }
+ skelcache.setsize(0);
+ blendoffsets.clear();
+ if(full) loopv(users) users[i]->cleanup();
+ }
+
+ bool canpreload() { return !numframes || gpuaccelerate(); }
+
+ void preload()
+ {
+ if(!numframes) return;
+ if(skelcache.empty())
+ {
+ usegpuskel = gpuaccelerate();
+ }
+ }
+
+ skelcacheentry &checkskelcache(part *p, const animstate *as, float pitch, const vec &axis, const vec &forward, ragdolldata *rdata)
+ {
+ if(skelcache.empty())
+ {
+ usegpuskel = gpuaccelerate();
+ }
+
+ int numanimparts = ((skelpart *)as->owner)->numanimparts;
+ uchar *partmask = ((skelpart *)as->owner)->partmask;
+ skelcacheentry *sc = NULL;
+ bool match = false;
+ loopv(skelcache)
+ {
+ skelcacheentry &c = skelcache[i];
+ loopj(numanimparts) if(c.as[j]!=as[j]) goto mismatch;
+ if(c.pitch != pitch || c.partmask != partmask || c.ragdoll != rdata || (rdata && c.millis < rdata->lastmove)) goto mismatch;
+ match = true;
+ sc = &c;
+ break;
+ mismatch:
+ if(c.millis < lastmillis) { sc = &c; break; }
+ }
+ if(!sc) sc = &skelcache.add();
+ if(!match)
+ {
+ loopi(numanimparts) sc->as[i] = as[i];
+ sc->pitch = pitch;
+ sc->partmask = partmask;
+ sc->ragdoll = rdata;
+ if(rdata) genragdollbones(*rdata, *sc, p);
+ else interpbones(as, pitch, axis, forward, numanimparts, partmask, *sc);
+ }
+ sc->millis = lastmillis;
+ return *sc;
+ }
+
+ int getblendoffset(UniformLoc &u)
+ {
+ int &offset = blendoffsets.access(Shader::lastshader->program, -1);
+ if(offset < 0)
+ {
+ defformatstring(offsetname, "%s[%d]", u.name, 2*numgpubones);
+ offset = glGetUniformLocation_(Shader::lastshader->program, offsetname);
+ }
+ return offset;
+ }
+
+ void setglslbones(UniformLoc &u, skelcacheentry &sc, skelcacheentry &bc, int count)
+ {
+ if(u.version == bc.version && u.data == bc.bdata) return;
+ glUniform4fv_(u.loc, 2*numgpubones, sc.bdata[0].real.v);
+ if(count > 0)
+ {
+ int offset = getblendoffset(u);
+ if(offset >= 0) glUniform4fv_(offset, 2*count, bc.bdata[0].real.v);
+ }
+ u.version = bc.version;
+ u.data = bc.bdata;
+ }
+
+ void setgpubones(skelcacheentry &sc, blendcacheentry *bc, int count)
+ {
+ if(!Shader::lastshader) return;
+ if(Shader::lastshader->uniformlocs.length() < 1) return;
+ UniformLoc &u = Shader::lastshader->uniformlocs[0];
+ setglslbones(u, sc, bc ? *bc : sc, count);
+ }
+
+ bool shouldcleanup() const
+ {
+ return numframes && (skelcache.empty() || gpuaccelerate()!=usegpuskel);
+ }
+ };
+
+ struct skelmeshgroup : meshgroup
+ {
+ skeleton *skel;
+
+ vector<blendcombo> blendcombos;
+ int numblends[4];
+
+ static const int MAXBLENDCACHE = 16;
+ blendcacheentry blendcache[MAXBLENDCACHE];
+
+ static const int MAXVBOCACHE = 16;
+ vbocacheentry vbocache[MAXVBOCACHE];
+
+ ushort *edata;
+ GLuint ebuf;
+ bool vtangents;
+ int vlen, vertsize, vblends, vweights;
+ uchar *vdata;
+
+ skelmeshgroup() : skel(NULL), edata(NULL), ebuf(0), vtangents(false), vlen(0), vertsize(0), vblends(0), vweights(0), vdata(NULL)
+ {
+ memset(numblends, 0, sizeof(numblends));
+ }
+
+ virtual ~skelmeshgroup()
+ {
+ if(skel)
+ {
+ if(skel->shared) skel->users.removeobj(this);
+ else DELETEP(skel);
+ }
+ if(ebuf) glDeleteBuffers_(1, &ebuf);
+ loopi(MAXBLENDCACHE)
+ {
+ DELETEA(blendcache[i].bdata);
+ }
+ loopi(MAXVBOCACHE)
+ {
+ if(vbocache[i].vbuf) glDeleteBuffers_(1, &vbocache[i].vbuf);
+ }
+ DELETEA(vdata);
+ }
+
+ void shareskeleton(char *name)
+ {
+ if(!name)
+ {
+ skel = new skeleton;
+ skel->users.add(this);
+ return;
+ }
+
+ static hashnameset<skeleton *> skeletons;
+ if(skeletons.access(name)) skel = skeletons[name];
+ else
+ {
+ skel = new skeleton;
+ skel->name = newstring(name);
+ skeletons.add(skel);
+ }
+ skel->users.add(this);
+ skel->shared++;
+ }
+
+ int findtag(const char *name)
+ {
+ return skel->findtag(name);
+ }
+
+ void *animkey() { return skel; }
+ int totalframes() const { return max(skel->numframes, 1); }
+
+ virtual skelanimspec *loadanim(const char *filename) { (void) filename; return NULL; }
+
+ void genvbo(bool tangents, vbocacheentry &vc)
+ {
+ if(!vc.vbuf) glGenBuffers_(1, &vc.vbuf);
+ if(ebuf) return;
+
+ vector<ushort> idxs;
+
+ if(tangents) loopv(meshes) ((skelmesh *)meshes[i])->calctangents();
+
+ vtangents = tangents;
+ vlen = 0;
+ vblends = 0;
+ if(skel->numframes && !skel->usegpuskel)
+ {
+ vweights = 1;
+ loopv(blendcombos)
+ {
+ blendcombo &c = blendcombos[i];
+ c.interpindex = c.weights[1] ? skel->numgpubones + vblends++ : -1;
+ }
+
+ vertsize = tangents ? sizeof(vvertbump) : sizeof(vvertn);
+ loopv(meshes) vlen += ((skelmesh *)meshes[i])->genvbo(idxs, vlen);
+ DELETEA(vdata);
+ vdata = new uchar[vlen*vertsize];
+ #define FILLVDATA(type) do { \
+ loopv(meshes) ((skelmesh *)meshes[i])->fillverts((type *)vdata); \
+ } while(0)
+ if(tangents) FILLVDATA(vvertbump);
+ else FILLVDATA(vvertn);
+ #undef FILLVDATA
+ }
+ else
+ {
+ if(skel->numframes)
+ {
+ vweights = 4;
+ int availbones = skel->availgpubones() - skel->numgpubones;
+ while(vweights > 1 && availbones >= numblends[vweights-1]) availbones -= numblends[--vweights];
+ loopv(blendcombos)
+ {
+ blendcombo &c = blendcombos[i];
+ c.interpindex = c.size() > vweights ? skel->numgpubones + vblends++ : -1;
+ }
+ }
+ else
+ {
+ vweights = 0;
+ loopv(blendcombos) blendcombos[i].interpindex = -1;
+ }
+
+ gle::bindvbo(vc.vbuf);
+ #define GENVBO(type, args) do { \
+ vertsize = sizeof(type); \
+ vector<type> vverts; \
+ loopv(meshes) vlen += ((skelmesh *)meshes[i])->genvbo args; \
+ glBufferData_(GL_ARRAY_BUFFER, vverts.length()*sizeof(type), vverts.getbuf(), GL_STATIC_DRAW); \
+ } while(0)
+ #define GENVBOANIM(type) GENVBO(type, (idxs, vlen, vverts))
+ #define GENVBOSTAT(type) GENVBO(type, (idxs, vlen, vverts, htdata, htlen))
+ if(skel->numframes)
+ {
+ if(tangents) GENVBOANIM(vvertbumpw);
+ else GENVBOANIM(vvertnw);
+ }
+ else
+ {
+ int numverts = 0, htlen = 128;
+ loopv(meshes) numverts += ((skelmesh *)meshes[i])->numverts;
+ while(htlen < numverts) htlen *= 2;
+ if(numverts*4 > htlen*3) htlen *= 2;
+ int *htdata = new int[htlen];
+ memset(htdata, -1, htlen*sizeof(int));
+ if(tangents) GENVBOSTAT(vvertbump);
+ else GENVBOSTAT(vvertn);
+ delete[] htdata;
+ }
+ #undef GENVBO
+ #undef GENVBOANIM
+ #undef GENVBOSTAT
+ gle::clearvbo();
+ }
+
+ glGenBuffers_(1, &ebuf);
+ gle::bindebo(ebuf);
+ glBufferData_(GL_ELEMENT_ARRAY_BUFFER, idxs.length()*sizeof(ushort), idxs.getbuf(), GL_STATIC_DRAW);
+ gle::clearebo();
+ }
+
+ void bindvbo(const animstate *as, vbocacheentry &vc, skelcacheentry *sc = NULL, blendcacheentry *bc = NULL)
+ {
+ vvert *vverts = 0;
+ bindpos(ebuf, vc.vbuf, &vverts->pos, vertsize);
+ if(as->cur.anim&ANIM_NOSKIN)
+ {
+ if(enabletc) disabletc();
+ if(enablenormals) disablenormals();
+ if(enabletangents) disabletangents();
+ }
+ else
+ {
+ if(vtangents)
+ {
+ if(enablenormals) disablenormals();
+ vvertbump *vvertbumps = 0;
+ bindtangents(&vvertbumps->tangent, vertsize);
+ }
+ else
+ {
+ if(enabletangents) disabletangents();
+ vvertn *vvertns = 0;
+ bindnormals(&vvertns->norm, vertsize);
+ }
+
+ bindtc(&vverts->tc, vertsize);
+ }
+ if(!sc || !skel->usegpuskel)
+ {
+ if(enablebones) disablebones();
+ }
+ else
+ {
+ if(vtangents)
+ {
+ vvertbumpw *vvertbumpws = 0;
+ bindbones(vvertbumpws->weights, vvertbumpws->bones, vertsize);
+ }
+ else
+ {
+ vvertnw *vvertnws = 0;
+ bindbones(vvertnws->weights, vvertnws->bones, vertsize);
+ }
+ }
+ }
+
+ void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n)
+ {
+ skel->concattagtransform(p, i, m, n);
+ }
+
+ int addblendcombo(const blendcombo &c)
+ {
+ loopv(blendcombos) if(blendcombos[i]==c)
+ {
+ blendcombos[i].uses += c.uses;
+ return i;
+ }
+ numblends[c.size()-1]++;
+ blendcombo &a = blendcombos.add(c);
+ return a.interpindex = blendcombos.length()-1;
+ }
+
+ void sortblendcombos()
+ {
+ blendcombos.sort(blendcombo::sortcmp);
+ int *remap = new int[blendcombos.length()];
+ loopv(blendcombos) remap[blendcombos[i].interpindex] = i;
+ loopv(meshes)
+ {
+ skelmesh *m = (skelmesh *)meshes[i];
+ loopj(m->numverts)
+ {
+ vert &v = m->verts[j];
+ v.blend = remap[v.blend];
+ }
+ }
+ delete[] remap;
+ }
+
+ int remapblend(int blend)
+ {
+ const blendcombo &c = blendcombos[blend];
+ return c.weights[1] ? c.interpindex : c.interpbones[0];
+ }
+
+ static inline void blendbones(dualquat &d, const dualquat *bdata, const blendcombo &c)
+ {
+ d = bdata[c.interpbones[0]];
+ d.mul(c.weights[0]);
+ d.accumulate(bdata[c.interpbones[1]], c.weights[1]);
+ if(c.weights[2])
+ {
+ d.accumulate(bdata[c.interpbones[2]], c.weights[2]);
+ if(c.weights[3]) d.accumulate(bdata[c.interpbones[3]], c.weights[3]);
+ }
+ }
+
+ void blendbones(const skelcacheentry &sc, blendcacheentry &bc)
+ {
+ bc.nextversion();
+ if(!bc.bdata) bc.bdata = new dualquat[vblends];
+ dualquat *dst = bc.bdata - skel->numgpubones;
+ bool normalize = !skel->usegpuskel || vweights<=1;
+ loopv(blendcombos)
+ {
+ const blendcombo &c = blendcombos[i];
+ if(c.interpindex<0) break;
+ dualquat &d = dst[c.interpindex];
+ blendbones(d, sc.bdata, c);
+ if(normalize) d.normalize();
+ }
+ }
+
+ void cleanup()
+ {
+ loopi(MAXBLENDCACHE)
+ {
+ blendcacheentry &c = blendcache[i];
+ DELETEA(c.bdata);
+ c.owner = -1;
+ }
+ loopi(MAXVBOCACHE)
+ {
+ vbocacheentry &c = vbocache[i];
+ if(c.vbuf) { glDeleteBuffers_(1, &c.vbuf); c.vbuf = 0; }
+ c.owner = -1;
+ }
+ if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; }
+ if(skel) skel->cleanup(false);
+ }
+
+ #define SEARCHCACHE(cachesize, cacheentry, cache, reusecheck) \
+ loopi(cachesize) \
+ { \
+ cacheentry &c = cache[i]; \
+ if(c.owner==owner) \
+ { \
+ if(c==sc) return c; \
+ else c.owner = -1; \
+ break; \
+ } \
+ } \
+ loopi(cachesize-1) \
+ { \
+ cacheentry &c = cache[i]; \
+ if(reusecheck c.owner < 0 || c.millis < lastmillis) \
+ return c; \
+ } \
+ return cache[cachesize-1];
+
+ vbocacheentry &checkvbocache(skelcacheentry &sc, int owner)
+ {
+ SEARCHCACHE(MAXVBOCACHE, vbocacheentry, vbocache, !c.vbuf || );
+ }
+
+ blendcacheentry &checkblendcache(skelcacheentry &sc, int owner)
+ {
+ SEARCHCACHE(MAXBLENDCACHE, blendcacheentry, blendcache, )
+ }
+
+ void preload(part *p)
+ {
+ if(!skel->canpreload()) return;
+ bool tangents = false;
+ loopv(p->skins) if(p->skins[i].tangents()) tangents = true;
+ if(skel->shouldcleanup()) skel->cleanup();
+ else if(tangents!=vtangents) cleanup();
+ skel->preload();
+ if(!vbocache->vbuf) genvbo(tangents, *vbocache);
+ }
+
+ void render(const animstate *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p)
+ {
+ bool tangents = false;
+ loopv(p->skins) if(p->skins[i].tangents()) tangents = true;
+ if(skel->shouldcleanup()) { skel->cleanup(); disablevbo(); }
+ else if(tangents!=vtangents) { cleanup(); disablevbo(); }
+
+ if(!skel->numframes)
+ {
+ if(!(as->cur.anim&ANIM_NORENDER))
+ {
+ if(!vbocache->vbuf) genvbo(tangents, *vbocache);
+ bindvbo(as, *vbocache);
+ loopv(meshes)
+ {
+ skelmesh *m = (skelmesh *)meshes[i];
+ p->skins[i].bind(m, as);
+ m->render(as, p->skins[i], *vbocache);
+ }
+ }
+ skel->calctags(p);
+ return;
+ }
+
+ skelcacheentry &sc = skel->checkskelcache(p, as, pitch, axis, forward, as->cur.anim&ANIM_RAGDOLL || !d || !d->ragdoll || d->ragdoll->skel != skel->ragdoll ? NULL : d->ragdoll);
+ if(!(as->cur.anim&ANIM_NORENDER))
+ {
+ int owner = &sc-&skel->skelcache[0];
+ vbocacheentry &vc = skel->usegpuskel ? *vbocache : checkvbocache(sc, owner);
+ vc.millis = lastmillis;
+ if(!vc.vbuf) genvbo(tangents, vc);
+ blendcacheentry *bc = NULL;
+ if(vblends)
+ {
+ bc = &checkblendcache(sc, owner);
+ bc->millis = lastmillis;
+ if(bc->owner!=owner)
+ {
+ bc->owner = owner;
+ *(animcacheentry *)bc = sc;
+ blendbones(sc, *bc);
+ }
+ }
+ if(!skel->usegpuskel && vc.owner!=owner)
+ {
+ vc.owner = owner;
+ (animcacheentry &)vc = sc;
+ loopv(meshes)
+ {
+ skelmesh &m = *(skelmesh *)meshes[i];
+ m.interpverts(sc.bdata, bc ? bc->bdata : NULL, tangents, vdata + m.voffset*vertsize, p->skins[i]);
+ }
+ gle::bindvbo(vc.vbuf);
+ glBufferData_(GL_ARRAY_BUFFER, vlen*vertsize, vdata, GL_STREAM_DRAW);
+ }
+
+ bindvbo(as, vc, &sc, bc);
+ loopv(meshes)
+ {
+ skelmesh *m = (skelmesh *)meshes[i];
+ p->skins[i].bind(m, as);
+ if(skel->usegpuskel) skel->setgpubones(sc, bc, vblends);
+ m->render(as, p->skins[i], vc);
+ }
+ }
+
+ skel->calctags(p, &sc);
+
+ if(as->cur.anim&ANIM_RAGDOLL && skel->ragdoll && !d->ragdoll)
+ {
+ d->ragdoll = new ragdolldata(skel->ragdoll, p->model->scale);
+ skel->initragdoll(*d->ragdoll, sc, p);
+ d->ragdoll->init(d);
+ }
+ }
+ };
+
+ struct animpartmask
+ {
+ animpartmask *next;
+ int numbones;
+ uchar bones[1];
+ };
+
+ struct skelpart : part
+ {
+ animpartmask *buildingpartmask;
+
+ uchar *partmask;
+
+ skelpart(animmodel *model, int index = 0) : part(model, index), buildingpartmask(NULL), partmask(NULL)
+ {
+ }
+
+ virtual ~skelpart()
+ {
+ DELETEA(buildingpartmask);
+ }
+
+ uchar *sharepartmask(animpartmask *o)
+ {
+ static animpartmask *partmasks = NULL;
+ animpartmask *p = partmasks;
+ for(; p; p = p->next) if(p->numbones==o->numbones && !memcmp(p->bones, o->bones, p->numbones))
+ {
+ delete[] (uchar *)o;
+ return p->bones;
+ }
+
+ o->next = p;
+ partmasks = o;
+ return o->bones;
+ }
+
+ animpartmask *newpartmask()
+ {
+ animpartmask *p = (animpartmask *)new uchar[sizeof(animpartmask) + ((skelmeshgroup *)meshes)->skel->numbones-1];
+ p->numbones = ((skelmeshgroup *)meshes)->skel->numbones;
+ memset(p->bones, 0, p->numbones);
+ return p;
+ }
+
+ void initanimparts()
+ {
+ DELETEA(buildingpartmask);
+ buildingpartmask = newpartmask();
+ }
+
+ bool addanimpart(ushort *bonemask)
+ {
+ if(!buildingpartmask || numanimparts>=MAXANIMPARTS) return false;
+ ((skelmeshgroup *)meshes)->skel->applybonemask(bonemask, buildingpartmask->bones, numanimparts);
+ numanimparts++;
+ return true;
+ }
+
+ void endanimparts()
+ {
+ if(buildingpartmask)
+ {
+ partmask = sharepartmask(buildingpartmask);
+ buildingpartmask = NULL;
+ }
+
+ ((skelmeshgroup *)meshes)->skel->optimize();
+ }
+
+ void loaded()
+ {
+ endanimparts();
+ part::loaded();
+ }
+ };
+
+ skelmodel(const char *name) : animmodel(name)
+ {
+ }
+
+ int linktype(animmodel *m) const
+ {
+ return type()==m->type() &&
+ ((skelmeshgroup *)parts[0]->meshes)->skel == ((skelmeshgroup *)m->parts[0]->meshes)->skel ?
+ LINK_REUSE :
+ LINK_TAG;
+ }
+
+ bool skeletal() const { return true; }
+
+ skelpart &addpart()
+ {
+ flushpart();
+ skelpart *p = new skelpart(this, parts.length());
+ parts.add(p);
+ return *p;
+ }
};
struct skeladjustment
{
- float yaw, pitch, roll;
- vec translate;
-
- skeladjustment(float yaw, float pitch, float roll, const vec &translate) : yaw(yaw), pitch(pitch), roll(roll), translate(translate) {}
-
- void adjust(dualquat &dq)
- {
- if(yaw) dq.mulorient(quat(vec(0, 0, 1), yaw*RAD));
- if(pitch) dq.mulorient(quat(vec(0, -1, 0), pitch*RAD));
- if(roll) dq.mulorient(quat(vec(-1, 0, 0), roll*RAD));
- if(!translate.iszero()) dq.translate(translate);
- }
+ float yaw, pitch, roll;
+ vec translate;
+
+ skeladjustment(float yaw, float pitch, float roll, const vec &translate) : yaw(yaw), pitch(pitch), roll(roll), translate(translate) {}
+
+ void adjust(dualquat &dq)
+ {
+ if(yaw) dq.mulorient(quat(vec(0, 0, 1), yaw*RAD));
+ if(pitch) dq.mulorient(quat(vec(0, -1, 0), pitch*RAD));
+ if(roll) dq.mulorient(quat(vec(-1, 0, 0), roll*RAD));
+ if(!translate.iszero()) dq.translate(translate);
+ }
};
template<class MDL> struct skelloader : modelloader<MDL, skelmodel>
{
- static vector<skeladjustment> adjustments;
+ static vector<skeladjustment> adjustments;
- skelloader(const char *name) : modelloader<MDL, skelmodel>(name) {}
+ skelloader(const char *name) : modelloader<MDL, skelmodel>(name) {}
- void flushpart()
- {
- adjustments.setsize(0);
- }
+ void flushpart()
+ {
+ adjustments.setsize(0);
+ }
};
template<class MDL> vector<skeladjustment> skelloader<MDL>::adjustments;
template<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmesh>
{
- typedef modelcommands<MDL, struct MDL::skelmesh> commands;
- typedef struct MDL::skeleton skeleton;
- typedef struct MDL::skelmeshgroup meshgroup;
- typedef struct MDL::skelpart part;
- typedef struct MDL::skin skin;
- typedef struct MDL::boneinfo boneinfo;
- typedef struct MDL::skelanimspec animspec;
- typedef struct MDL::pitchdep pitchdep;
- typedef struct MDL::pitchtarget pitchtarget;
- typedef struct MDL::pitchcorrect pitchcorrect;
-
- static void loadpart(char *meshfile, char *skelname, float *smooth)
- {
- if(!MDL::loading) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
- defformatstring(filename, "%s/%s", MDL::dir, meshfile);
- part &mdl = MDL::loading->addpart();
- mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0;
- mdl.meshes = MDL::loading->sharemeshes(path(filename), skelname[0] ? skelname : NULL, double(*smooth > 0 ? cos(clamp(*smooth, 0.0f, 180.0f)*RAD) : 2));
- if(!mdl.meshes) conoutf(CON_ERROR, "could not load %s", filename);
- else
- {
- mdl.initanimparts();
- mdl.initskins();
- }
- }
-
- static void settag(char *name, char *tagname, float *tx, float *ty, float *tz, float *rx, float *ry, float *rz)
- {
- if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
- part &mdl = *(part *)MDL::loading->parts.last();
- int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1;
- if(i >= 0)
- {
- float cx = *rx ? cosf(*rx/2*RAD) : 1, sx = *rx ? sinf(*rx/2*RAD) : 0,
- cy = *ry ? cosf(*ry/2*RAD) : 1, sy = *ry ? sinf(*ry/2*RAD) : 0,
- cz = *rz ? cosf(*rz/2*RAD) : 1, sz = *rz ? sinf(*rz/2*RAD) : 0;
- matrix4x3 m(matrix3(quat(sx*cy*cz - cx*sy*sz, cx*sy*cz + sx*cy*sz, cx*cy*sz - sx*sy*cz, cx*cy*cz + sx*sy*sz)),
- vec(*tx, *ty, *tz));
- ((meshgroup *)mdl.meshes)->skel->addtag(tagname, i, m);
- return;
- }
- conoutf(CON_ERROR, "could not find bone %s for tag %s", name, tagname);
- }
-
- static void setpitch(char *name, float *pitchscale, float *pitchoffset, float *pitchmin, float *pitchmax)
- {
- if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
- part &mdl = *(part *)MDL::loading->parts.last();
-
- if(name[0])
- {
- int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1;
- if(i>=0)
- {
- boneinfo &b = ((meshgroup *)mdl.meshes)->skel->bones[i];
- b.pitchscale = *pitchscale;
- b.pitchoffset = *pitchoffset;
- if(*pitchmin || *pitchmax)
- {
- b.pitchmin = *pitchmin;
- b.pitchmax = *pitchmax;
- }
- else
- {
- b.pitchmin = -360*fabs(b.pitchscale) + b.pitchoffset;
- b.pitchmax = 360*fabs(b.pitchscale) + b.pitchoffset;
- }
- return;
- }
- conoutf(CON_ERROR, "could not find bone %s to pitch", name);
- return;
- }
-
- mdl.pitchscale = *pitchscale;
- mdl.pitchoffset = *pitchoffset;
- if(*pitchmin || *pitchmax)
- {
- mdl.pitchmin = *pitchmin;
- mdl.pitchmax = *pitchmax;
- }
- else
- {
- mdl.pitchmin = -360*fabs(mdl.pitchscale) + mdl.pitchoffset;
- mdl.pitchmax = 360*fabs(mdl.pitchscale) + mdl.pitchoffset;
- }
- }
-
- static void setpitchtarget(char *name, char *animfile, int *frameoffset, float *pitchmin, float *pitchmax)
- {
- if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
- part &mdl = *(part *)MDL::loading->parts.last();
- if(!mdl.meshes) return;
- defformatstring(filename, "%s/%s", MDL::dir, animfile);
- animspec *sa = ((meshgroup *)mdl.meshes)->loadanim(path(filename));
- if(!sa) { conoutf(CON_ERROR, "could not load %s anim file %s", MDL::formatname(), filename); return; }
- skeleton *skel = ((meshgroup *)mdl.meshes)->skel;
- int bone = skel ? skel->findbone(name) : -1;
- if(bone < 0)
- {
- conoutf(CON_ERROR, "could not find bone %s to pitch target", name);
- return;
- }
- loopv(skel->pitchtargets) if(skel->pitchtargets[i].bone == bone) return;
- pitchtarget &t = skel->pitchtargets.add();
- t.bone = bone;
- t.frame = sa->frame + clamp(*frameoffset, 0, sa->range-1);
- t.pitchmin = *pitchmin;
- t.pitchmax = *pitchmax;
- }
-
- static void setpitchcorrect(char *name, char *targetname, float *scale, float *pitchmin, float *pitchmax)
- {
- if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
- part &mdl = *(part *)MDL::loading->parts.last();
- if(!mdl.meshes) return;
- skeleton *skel = ((meshgroup *)mdl.meshes)->skel;
- int bone = skel ? skel->findbone(name) : -1;
- if(bone < 0)
- {
- conoutf(CON_ERROR, "could not find bone %s to pitch correct", name);
- return;
- }
- if(skel->findpitchcorrect(bone) >= 0) return;
- int targetbone = skel->findbone(targetname), target = -1;
- if(targetbone >= 0) loopv(skel->pitchtargets) if(skel->pitchtargets[i].bone == targetbone) { target = i; break; }
- if(target < 0)
- {
- conoutf(CON_ERROR, "could not find pitch target %s to pitch correct %s", targetname, name);
- return;
- }
- pitchcorrect c;
- c.bone = bone;
- c.target = target;
- c.pitchmin = *pitchmin;
- c.pitchmax = *pitchmax;
- c.pitchscale = *scale;
- int pos = skel->pitchcorrects.length();
- loopv(skel->pitchcorrects) if(bone <= skel->pitchcorrects[i].bone) { pos = i; break; }
- skel->pitchcorrects.insert(pos, c);
- }
-
- static void setanim(char *anim, char *animfile, float *speed, int *priority, int *startoffset, int *endoffset)
- {
- if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
-
- vector<int> anims;
- findanims(anim, anims);
- if(anims.empty()) conoutf(CON_ERROR, "could not find animation %s", anim);
- else
- {
- part *p = (part *)MDL::loading->parts.last();
- if(!p->meshes) return;
- defformatstring(filename, "%s/%s", MDL::dir, animfile);
- animspec *sa = ((meshgroup *)p->meshes)->loadanim(path(filename));
- if(!sa) conoutf(CON_ERROR, "could not load %s anim file %s", MDL::formatname(), filename);
- else loopv(anims)
- {
- int start = sa->frame, end = sa->range;
- if(*startoffset > 0) start += min(*startoffset, end-1);
- else if(*startoffset < 0) start += max(end + *startoffset, 0);
- end -= start - sa->frame;
- if(*endoffset > 0) end = min(end, *endoffset);
- else if(*endoffset < 0) end = max(end + *endoffset, 1);
- MDL::loading->parts.last()->setanim(p->numanimparts-1, anims[i], start, end, *speed, *priority);
- }
- }
- }
-
- static void setanimpart(char *maskstr)
- {
- if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
-
- part *p = (part *)MDL::loading->parts.last();
-
- vector<char *> bonestrs;
- explodelist(maskstr, bonestrs);
- vector<ushort> bonemask;
- loopv(bonestrs)
- {
- char *bonestr = bonestrs[i];
- int bone = p->meshes ? ((meshgroup *)p->meshes)->skel->findbone(bonestr[0]=='!' ? bonestr+1 : bonestr) : -1;
- if(bone<0) { conoutf(CON_ERROR, "could not find bone %s for anim part mask [%s]", bonestr, maskstr); bonestrs.deletearrays(); return; }
- bonemask.add(bone | (bonestr[0]=='!' ? BONEMASK_NOT : 0));
- }
- bonestrs.deletearrays();
- bonemask.sort();
- if(bonemask.length()) bonemask.add(BONEMASK_END);
-
- if(!p->addanimpart(bonemask.getbuf())) conoutf(CON_ERROR, "too many animation parts");
- }
-
- static void setadjust(char *name, float *yaw, float *pitch, float *roll, float *tx, float *ty, float *tz)
- {
- if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
- part &mdl = *(part *)MDL::loading->parts.last();
-
- if(!name[0]) return;
- int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1;
- if(i < 0) { conoutf(CON_ERROR, "could not find bone %s to adjust", name); return; }
- while(!MDL::adjustments.inrange(i)) MDL::adjustments.add(skeladjustment(0, 0, 0, vec(0, 0, 0)));
- MDL::adjustments[i] = skeladjustment(*yaw, *pitch, *roll, vec(*tx/4, *ty/4, *tz/4));
- }
-
- skelcommands()
- {
- if(MDL::multiparted()) this->modelcommand(loadpart, "load", "ssf");
- this->modelcommand(settag, "tag", "ssffffff");
- this->modelcommand(setpitch, "pitch", "sffff");
- this->modelcommand(setpitchtarget, "pitchtarget", "ssiff");
- this->modelcommand(setpitchcorrect, "pitchcorrect", "ssfff");
- if(MDL::animated())
- {
- this->modelcommand(setanim, "anim", "ssfiii");
- this->modelcommand(setanimpart, "animpart", "s");
- this->modelcommand(setadjust, "adjust", "sffffff");
- }
- }
+ typedef modelcommands<MDL, struct MDL::skelmesh> commands;
+ typedef struct MDL::skeleton skeleton;
+ typedef struct MDL::skelmeshgroup meshgroup;
+ typedef struct MDL::skelpart part;
+ typedef struct MDL::skin skin;
+ typedef struct MDL::boneinfo boneinfo;
+ typedef struct MDL::skelanimspec animspec;
+ typedef struct MDL::pitchdep pitchdep;
+ typedef struct MDL::pitchtarget pitchtarget;
+ typedef struct MDL::pitchcorrect pitchcorrect;
+
+ static void loadpart(char *meshfile, char *skelname, float *smooth)
+ {
+ if(!MDL::loading) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+ defformatstring(filename, "%s/%s", MDL::dir, meshfile);
+ part &mdl = MDL::loading->addpart();
+ mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0;
+ mdl.meshes = MDL::loading->sharemeshes(path(filename), skelname[0] ? skelname : NULL, double(*smooth > 0 ? cos(clamp(*smooth, 0.0f, 180.0f)*RAD) : 2));
+ if(!mdl.meshes) conoutf(CON_ERROR, "could not load %s", filename);
+ else
+ {
+ mdl.initanimparts();
+ mdl.initskins();
+ }
+ }
+
+ static void settag(char *name, char *tagname, float *tx, float *ty, float *tz, float *rx, float *ry, float *rz)
+ {
+ if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+ part &mdl = *(part *)MDL::loading->parts.last();
+ int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1;
+ if(i >= 0)
+ {
+ float cx = *rx ? cosf(*rx/2*RAD) : 1, sx = *rx ? sinf(*rx/2*RAD) : 0,
+ cy = *ry ? cosf(*ry/2*RAD) : 1, sy = *ry ? sinf(*ry/2*RAD) : 0,
+ cz = *rz ? cosf(*rz/2*RAD) : 1, sz = *rz ? sinf(*rz/2*RAD) : 0;
+ matrix4x3 m(matrix3(quat(sx*cy*cz - cx*sy*sz, cx*sy*cz + sx*cy*sz, cx*cy*sz - sx*sy*cz, cx*cy*cz + sx*sy*sz)),
+ vec(*tx, *ty, *tz));
+ ((meshgroup *)mdl.meshes)->skel->addtag(tagname, i, m);
+ return;
+ }
+ conoutf(CON_ERROR, "could not find bone %s for tag %s", name, tagname);
+ }
+
+ static void setpitch(char *name, float *pitchscale, float *pitchoffset, float *pitchmin, float *pitchmax)
+ {
+ if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+ part &mdl = *(part *)MDL::loading->parts.last();
+
+ if(name[0])
+ {
+ int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1;
+ if(i>=0)
+ {
+ boneinfo &b = ((meshgroup *)mdl.meshes)->skel->bones[i];
+ b.pitchscale = *pitchscale;
+ b.pitchoffset = *pitchoffset;
+ if(*pitchmin || *pitchmax)
+ {
+ b.pitchmin = *pitchmin;
+ b.pitchmax = *pitchmax;
+ }
+ else
+ {
+ b.pitchmin = -360*fabs(b.pitchscale) + b.pitchoffset;
+ b.pitchmax = 360*fabs(b.pitchscale) + b.pitchoffset;
+ }
+ return;
+ }
+ conoutf(CON_ERROR, "could not find bone %s to pitch", name);
+ return;
+ }
+
+ mdl.pitchscale = *pitchscale;
+ mdl.pitchoffset = *pitchoffset;
+ if(*pitchmin || *pitchmax)
+ {
+ mdl.pitchmin = *pitchmin;
+ mdl.pitchmax = *pitchmax;
+ }
+ else
+ {
+ mdl.pitchmin = -360*fabs(mdl.pitchscale) + mdl.pitchoffset;
+ mdl.pitchmax = 360*fabs(mdl.pitchscale) + mdl.pitchoffset;
+ }
+ }
+
+ static void setpitchtarget(char *name, char *animfile, int *frameoffset, float *pitchmin, float *pitchmax)
+ {
+ if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+ part &mdl = *(part *)MDL::loading->parts.last();
+ if(!mdl.meshes) return;
+ defformatstring(filename, "%s/%s", MDL::dir, animfile);
+ animspec *sa = ((meshgroup *)mdl.meshes)->loadanim(path(filename));
+ if(!sa) { conoutf(CON_ERROR, "could not load %s anim file %s", MDL::formatname(), filename); return; }
+ skeleton *skel = ((meshgroup *)mdl.meshes)->skel;
+ int bone = skel ? skel->findbone(name) : -1;
+ if(bone < 0)
+ {
+ conoutf(CON_ERROR, "could not find bone %s to pitch target", name);
+ return;
+ }
+ loopv(skel->pitchtargets) if(skel->pitchtargets[i].bone == bone) return;
+ pitchtarget &t = skel->pitchtargets.add();
+ t.bone = bone;
+ t.frame = sa->frame + clamp(*frameoffset, 0, sa->range-1);
+ t.pitchmin = *pitchmin;
+ t.pitchmax = *pitchmax;
+ }
+
+ static void setpitchcorrect(char *name, char *targetname, float *scale, float *pitchmin, float *pitchmax)
+ {
+ if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+ part &mdl = *(part *)MDL::loading->parts.last();
+ if(!mdl.meshes) return;
+ skeleton *skel = ((meshgroup *)mdl.meshes)->skel;
+ int bone = skel ? skel->findbone(name) : -1;
+ if(bone < 0)
+ {
+ conoutf(CON_ERROR, "could not find bone %s to pitch correct", name);
+ return;
+ }
+ if(skel->findpitchcorrect(bone) >= 0) return;
+ int targetbone = skel->findbone(targetname), target = -1;
+ if(targetbone >= 0) loopv(skel->pitchtargets) if(skel->pitchtargets[i].bone == targetbone) { target = i; break; }
+ if(target < 0)
+ {
+ conoutf(CON_ERROR, "could not find pitch target %s to pitch correct %s", targetname, name);
+ return;
+ }
+ pitchcorrect c;
+ c.bone = bone;
+ c.target = target;
+ c.pitchmin = *pitchmin;
+ c.pitchmax = *pitchmax;
+ c.pitchscale = *scale;
+ int pos = skel->pitchcorrects.length();
+ loopv(skel->pitchcorrects) if(bone <= skel->pitchcorrects[i].bone) { pos = i; break; }
+ skel->pitchcorrects.insert(pos, c);
+ }
+
+ static void setanim(char *anim, char *animfile, float *speed, int *priority, int *startoffset, int *endoffset)
+ {
+ if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+
+ vector<int> anims;
+ findanims(anim, anims);
+ if(anims.empty()) conoutf(CON_ERROR, "could not find animation %s", anim);
+ else
+ {
+ part *p = (part *)MDL::loading->parts.last();
+ if(!p->meshes) return;
+ defformatstring(filename, "%s/%s", MDL::dir, animfile);
+ animspec *sa = ((meshgroup *)p->meshes)->loadanim(path(filename));
+ if(!sa) conoutf(CON_ERROR, "could not load %s anim file %s", MDL::formatname(), filename);
+ else loopv(anims)
+ {
+ int start = sa->frame, end = sa->range;
+ if(*startoffset > 0) start += min(*startoffset, end-1);
+ else if(*startoffset < 0) start += max(end + *startoffset, 0);
+ end -= start - sa->frame;
+ if(*endoffset > 0) end = min(end, *endoffset);
+ else if(*endoffset < 0) end = max(end + *endoffset, 1);
+ MDL::loading->parts.last()->setanim(p->numanimparts-1, anims[i], start, end, *speed, *priority);
+ }
+ }
+ }
+
+ static void setanimpart(char *maskstr)
+ {
+ if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+
+ part *p = (part *)MDL::loading->parts.last();
+
+ vector<char *> bonestrs;
+ explodelist(maskstr, bonestrs);
+ vector<ushort> bonemask;
+ loopv(bonestrs)
+ {
+ char *bonestr = bonestrs[i];
+ int bone = p->meshes ? ((meshgroup *)p->meshes)->skel->findbone(bonestr[0]=='!' ? bonestr+1 : bonestr) : -1;
+ if(bone<0) { conoutf(CON_ERROR, "could not find bone %s for anim part mask [%s]", bonestr, maskstr); bonestrs.deletearrays(); return; }
+ bonemask.add(bone | (bonestr[0]=='!' ? BONEMASK_NOT : 0));
+ }
+ bonestrs.deletearrays();
+ bonemask.sort();
+ if(bonemask.length()) bonemask.add(BONEMASK_END);
+
+ if(!p->addanimpart(bonemask.getbuf())) conoutf(CON_ERROR, "too many animation parts");
+ }
+
+ static void setadjust(char *name, float *yaw, float *pitch, float *roll, float *tx, float *ty, float *tz)
+ {
+ if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+ part &mdl = *(part *)MDL::loading->parts.last();
+
+ if(!name[0]) return;
+ int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1;
+ if(i < 0) { conoutf(CON_ERROR, "could not find bone %s to adjust", name); return; }
+ while(!MDL::adjustments.inrange(i)) MDL::adjustments.add(skeladjustment(0, 0, 0, vec(0, 0, 0)));
+ MDL::adjustments[i] = skeladjustment(*yaw, *pitch, *roll, vec(*tx/4, *ty/4, *tz/4));
+ }
+
+ skelcommands()
+ {
+ if(MDL::multiparted()) this->modelcommand(loadpart, "load", "ssf");
+ this->modelcommand(settag, "tag", "ssffffff");
+ this->modelcommand(setpitch, "pitch", "sffff");
+ this->modelcommand(setpitchtarget, "pitchtarget", "ssiff");
+ this->modelcommand(setpitchcorrect, "pitchcorrect", "ssfff");
+ if(MDL::animated())
+ {
+ this->modelcommand(setanim, "anim", "ssfiii");
+ this->modelcommand(setanimpart, "animpart", "s");
+ this->modelcommand(setadjust, "adjust", "sffffff");
+ }
+ }
};
diff --git a/src/engine/sound.cpp b/src/engine/sound.cpp
index 18cc0ab..d10386d 100644
--- a/src/engine/sound.cpp
+++ b/src/engine/sound.cpp
@@ -7,115 +7,115 @@ bool nosound = true;
struct soundsample
{
- char *name;
- Mix_Chunk *chunk;
+ char *name;
+ Mix_Chunk *chunk;
- soundsample() : name(NULL), chunk(NULL) {}
- ~soundsample() { DELETEA(name); }
+ soundsample() : name(NULL), chunk(NULL) {}
+ ~soundsample() { DELETEA(name); }
- void cleanup() { if(chunk) { Mix_FreeChunk(chunk); chunk = NULL; } }
- bool load(bool msg = false);
+ void cleanup() { if(chunk) { Mix_FreeChunk(chunk); chunk = NULL; } }
+ bool load(bool msg = false);
};
struct soundslot
{
- soundsample *sample;
- int volume;
+ soundsample *sample;
+ int volume;
};
struct soundconfig
{
- int slots, numslots;
- int maxuses;
+ int slots, numslots;
+ int maxuses;
- bool hasslot(const soundslot *p, const vector<soundslot> &v) const
- {
- return p >= v.getbuf() + slots && p < v.getbuf() + slots+numslots && slots+numslots < v.length();
- }
+ bool hasslot(const soundslot *p, const vector<soundslot> &v) const
+ {
+ return p >= v.getbuf() + slots && p < v.getbuf() + slots+numslots && slots+numslots < v.length();
+ }
- int chooseslot(int flags) const
- {
- if(flags&SND_NO_ALT || numslots <= 1) return slots;
- if(flags&SND_USE_ALT) return slots + 1 + rnd(numslots - 1);
- return slots + rnd(numslots);
- }
+ int chooseslot(int flags) const
+ {
+ if(flags&SND_NO_ALT || numslots <= 1) return slots;
+ if(flags&SND_USE_ALT) return slots + 1 + rnd(numslots - 1);
+ return slots + rnd(numslots);
+ }
};
struct soundchannel
{
- int id;
- bool inuse;
- vec loc;
- soundslot *slot;
- extentity *ent;
- int radius, volume, pan, flags;
- bool dirty;
-
- soundchannel(int id) : id(id) { reset(); }
-
- bool hasloc() const { return loc.x >= -1e15f; }
- void clearloc() { loc = vec(-1e16f, -1e16f, -1e16f); }
-
- void reset()
- {
- inuse = false;
- clearloc();
- slot = NULL;
- ent = NULL;
- radius = 0;
- volume = -1;
- pan = -1;
- flags = 0;
- dirty = false;
- }
+ int id;
+ bool inuse;
+ vec loc;
+ soundslot *slot;
+ extentity *ent;
+ int radius, volume, pan, flags;
+ bool dirty;
+
+ soundchannel(int id) : id(id) { reset(); }
+
+ bool hasloc() const { return loc.x >= -1e15f; }
+ void clearloc() { loc = vec(-1e16f, -1e16f, -1e16f); }
+
+ void reset()
+ {
+ inuse = false;
+ clearloc();
+ slot = NULL;
+ ent = NULL;
+ radius = 0;
+ volume = -1;
+ pan = -1;
+ flags = 0;
+ dirty = false;
+ }
};
vector<soundchannel> channels;
int maxchannels = 0;
soundchannel &newchannel(int n, soundslot *slot, const vec *loc = NULL, extentity *ent = NULL, int flags = 0, int radius = 0)
{
- if(ent)
- {
- loc = &ent->o;
- ent->flags |= EF_SOUND;
- }
- while(!channels.inrange(n)) channels.add(channels.length());
- soundchannel &chan = channels[n];
- chan.reset();
- chan.inuse = true;
- if(loc) chan.loc = *loc;
- chan.slot = slot;
- chan.ent = ent;
- chan.flags = 0;
- chan.radius = radius;
- return chan;
+ if(ent)
+ {
+ loc = &ent->o;
+ ent->flags |= EF_SOUND;
+ }
+ while(!channels.inrange(n)) channels.add(channels.length());
+ soundchannel &chan = channels[n];
+ chan.reset();
+ chan.inuse = true;
+ if(loc) chan.loc = *loc;
+ chan.slot = slot;
+ chan.ent = ent;
+ chan.flags = 0;
+ chan.radius = radius;
+ return chan;
}
void freechannel(int n)
{
- if(!channels.inrange(n) || !channels[n].inuse) return;
- soundchannel &chan = channels[n];
- chan.inuse = false;
- if(chan.ent) chan.ent->flags &= ~EF_SOUND;
+ if(!channels.inrange(n) || !channels[n].inuse) return;
+ soundchannel &chan = channels[n];
+ chan.inuse = false;
+ if(chan.ent) chan.ent->flags &= ~EF_SOUND;
}
void syncchannel(soundchannel &chan)
{
- if(!chan.dirty) return;
- if(!Mix_FadingChannel(chan.id)) Mix_Volume(chan.id, chan.volume);
- Mix_SetPanning(chan.id, 255-chan.pan, chan.pan);
- chan.dirty = false;
+ if(!chan.dirty) return;
+ if(!Mix_FadingChannel(chan.id)) Mix_Volume(chan.id, chan.volume);
+ Mix_SetPanning(chan.id, 255-chan.pan, chan.pan);
+ chan.dirty = false;
}
void stopchannels()
{
- loopv(channels)
- {
- soundchannel &chan = channels[i];
- if(!chan.inuse) continue;
- Mix_HaltChannel(i);
- freechannel(i);
- }
+ loopv(channels)
+ {
+ soundchannel &chan = channels[i];
+ if(!chan.inuse) continue;
+ Mix_HaltChannel(i);
+ freechannel(i);
+ }
}
void setmusicvol(int musicvol);
@@ -123,9 +123,9 @@ extern int musicvol;
static int curvol = 0;
VARFP(soundvol, 0, 255, 255,
{
- if(!soundvol) { stopchannels(); setmusicvol(0); }
- else if(!curvol) setmusicvol(musicvol);
- curvol = soundvol;
+ if(!soundvol) { stopchannels(); setmusicvol(0); }
+ else if(!curvol) setmusicvol(musicvol);
+ curvol = soundvol;
});
VARFP(musicvol, 0, 128, 255, setmusicvol(soundvol ? musicvol : 0));
@@ -137,28 +137,27 @@ stream *musicstream = NULL;
void setmusicvol(int musicvol)
{
- if(nosound) return;
- if(music) Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
+ if(nosound) return;
+ if(music) Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
}
void stopmusic()
{
- if(nosound) return;
- DELETEA(musicfile);
- DELETEA(musicdonecmd);
- if(music)
- {
- Mix_HaltMusic();
- Mix_FreeMusic(music);
- music = NULL;
- }
- if(musicrw) { SDL_FreeRW(musicrw); musicrw = NULL; }
- DELETEP(musicstream);
+ if(nosound) return;
+ DELETEA(musicfile);
+ DELETEA(musicdonecmd);
+ if(music)
+ {
+ Mix_HaltMusic();
+ Mix_FreeMusic(music);
+ music = NULL;
+ }
+ if(musicrw) { SDL_FreeRW(musicrw); musicrw = NULL; }
+ DELETEP(musicstream);
}
-#define AUDIODRIVER ""
bool shouldinitaudio = true;
-SVARF(audiodriver, AUDIODRIVER, { shouldinitaudio = true; initwarning("sound configuration", INIT_RESET, CHANGE_SOUND); });
+SVARF(audiodriver, "", { shouldinitaudio = true; initwarning("sound configuration", INIT_RESET, CHANGE_SOUND); });
VARF(usesound, 0, 1, 1, { shouldinitaudio = true; initwarning("sound configuration", INIT_RESET, CHANGE_SOUND); });
VARF(soundchans, 1, 32, 128, initwarning("sound configuration", INIT_RESET, CHANGE_SOUND));
VARF(soundfreq, 0, MIX_DEFAULT_FREQUENCY, 48000, initwarning("sound configuration", INIT_RESET, CHANGE_SOUND));
@@ -166,377 +165,377 @@ VARF(soundbufferlen, 128, 1024, 4096, initwarning("sound configuration", INIT_RE
bool initaudio()
{
- static string fallback = "";
- static bool initfallback = true;
- static bool restorefallback = false;
- if(initfallback)
- {
- initfallback = false;
- if(char *env = SDL_getenv("SDL_AUDIODRIVER")) copystring(fallback, env);
- }
- if(!fallback[0] && audiodriver[0])
- {
- vector<char*> drivers;
- explodelist(audiodriver, drivers);
- loopv(drivers)
- {
- restorefallback = true;
- SDL_setenv("SDL_AUDIODRIVER", drivers[i], 1);
- if(SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0)
- {
- drivers.deletearrays();
- return true;
- }
- }
- drivers.deletearrays();
- }
- if(restorefallback)
- {
- restorefallback = false;
- unsetenv("SDL_AUDIODRIVER");
- }
- if(SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0) return true;
- conoutf(CON_ERROR, "sound init failed: %s", SDL_GetError());
- return false;
+ static string fallback = "";
+ static bool initfallback = true;
+ static bool restorefallback = false;
+ if(initfallback)
+ {
+ initfallback = false;
+ if(char *env = SDL_getenv("SDL_AUDIODRIVER")) copystring(fallback, env);
+ }
+ if(!fallback[0] && audiodriver[0])
+ {
+ vector<char*> drivers;
+ explodelist(audiodriver, drivers);
+ loopv(drivers)
+ {
+ restorefallback = true;
+ SDL_setenv("SDL_AUDIODRIVER", drivers[i], 1);
+ if(SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0)
+ {
+ drivers.deletearrays();
+ return true;
+ }
+ }
+ drivers.deletearrays();
+ }
+ if(restorefallback)
+ {
+ restorefallback = false;
+ unsetenv("SDL_AUDIODRIVER");
+ }
+ if(SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0) return true;
+ conoutf(CON_ERROR, "sound init failed: %s", SDL_GetError());
+ return false;
}
void initsound()
{
- SDL_version version;
- SDL_GetVersion(&version);
- if(version.major == 2 && version.minor == 0 && version.patch == 6)
- {
- nosound = true;
- if(usesound) conoutf(CON_ERROR, "audio is broken in SDL 2.0.6");
- return;
- }
-
- if(shouldinitaudio)
- {
- shouldinitaudio = false;
- if(SDL_WasInit(SDL_INIT_AUDIO)) SDL_QuitSubSystem(SDL_INIT_AUDIO);
- if(!usesound || !initaudio())
- {
- nosound = true;
- return;
- }
- }
-
- if(Mix_OpenAudio(soundfreq, MIX_DEFAULT_FORMAT, 2, soundbufferlen)<0)
- {
- nosound = true;
- conoutf(CON_ERROR, "sound init failed (SDL_mixer): %s", Mix_GetError());
- return;
- }
+ SDL_version version;
+ SDL_GetVersion(&version);
+ if(version.major == 2 && version.minor == 0 && version.patch == 6)
+ {
+ nosound = true;
+ if(usesound) conoutf(CON_ERROR, "audio is broken in SDL 2.0.6");
+ return;
+ }
+
+ if(shouldinitaudio)
+ {
+ shouldinitaudio = false;
+ if(SDL_WasInit(SDL_INIT_AUDIO)) SDL_QuitSubSystem(SDL_INIT_AUDIO);
+ if(!usesound || !initaudio())
+ {
+ nosound = true;
+ return;
+ }
+ }
+
+ if(Mix_OpenAudio(soundfreq, MIX_DEFAULT_FORMAT, 2, soundbufferlen)<0)
+ {
+ nosound = true;
+ conoutf(CON_ERROR, "sound init failed (SDL_mixer): %s", Mix_GetError());
+ return;
+ }
Mix_AllocateChannels(soundchans);
- maxchannels = soundchans;
- nosound = false;
+ maxchannels = soundchans;
+ nosound = false;
}
void musicdone()
{
- if(music) { Mix_HaltMusic(); Mix_FreeMusic(music); music = NULL; }
- if(musicrw) { SDL_FreeRW(musicrw); musicrw = NULL; }
- DELETEP(musicstream);
- DELETEA(musicfile);
- if(!musicdonecmd) return;
- char *cmd = musicdonecmd;
- musicdonecmd = NULL;
- execute(cmd);
- delete[] cmd;
+ if(music) { Mix_HaltMusic(); Mix_FreeMusic(music); music = NULL; }
+ if(musicrw) { SDL_FreeRW(musicrw); musicrw = NULL; }
+ DELETEP(musicstream);
+ DELETEA(musicfile);
+ if(!musicdonecmd) return;
+ char *cmd = musicdonecmd;
+ musicdonecmd = NULL;
+ execute(cmd);
+ delete[] cmd;
}
Mix_Music *loadmusic(const char *name)
{
- if(!musicstream) musicstream = openzipfile(name, "rb");
- if(musicstream)
- {
- if(!musicrw) musicrw = musicstream->rwops();
- if(!musicrw) DELETEP(musicstream);
- }
- if(musicrw) music = Mix_LoadMUSType_RW(musicrw, MUS_NONE, 0);
- else music = Mix_LoadMUS(findfile(name, "rb"));
- if(!music)
- {
- if(musicrw) { SDL_FreeRW(musicrw); musicrw = NULL; }
- DELETEP(musicstream);
- }
- return music;
+ if(!musicstream) musicstream = openzipfile(name, "rb");
+ if(musicstream)
+ {
+ if(!musicrw) musicrw = musicstream->rwops();
+ if(!musicrw) DELETEP(musicstream);
+ }
+ if(musicrw) music = Mix_LoadMUSType_RW(musicrw, MUS_NONE, 0);
+ else music = Mix_LoadMUS(findfile(name, "rb"));
+ if(!music)
+ {
+ if(musicrw) { SDL_FreeRW(musicrw); musicrw = NULL; }
+ DELETEP(musicstream);
+ }
+ return music;
}
void startmusic(char *name, char *cmd)
{
- if(nosound) return;
- stopmusic();
- if(soundvol && musicvol && *name)
- {
- defformatstring(file, "packages/%s", name);
- path(file);
- if(loadmusic(file))
- {
- DELETEA(musicfile);
- DELETEA(musicdonecmd);
- musicfile = newstring(file);
- if(cmd[0]) musicdonecmd = newstring(cmd);
- Mix_PlayMusic(music, cmd[0] ? 0 : -1);
- Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
- intret(1);
- }
- else
- {
- conoutf(CON_ERROR, "could not play music: %s", file);
- intret(0);
- }
- }
+ if(nosound) return;
+ stopmusic();
+ if(soundvol && musicvol && *name)
+ {
+ defformatstring(file, "packages/%s", name);
+ path(file);
+ if(loadmusic(file))
+ {
+ DELETEA(musicfile);
+ DELETEA(musicdonecmd);
+ musicfile = newstring(file);
+ if(cmd[0]) musicdonecmd = newstring(cmd);
+ Mix_PlayMusic(music, cmd[0] ? 0 : -1);
+ Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
+ intret(1);
+ }
+ else
+ {
+ conoutf(CON_ERROR, "could not play music: %s", file);
+ intret(0);
+ }
+ }
}
COMMANDN(music, startmusic, "ss");
static Mix_Chunk *loadwav(const char *name)
{
- Mix_Chunk *c = NULL;
- stream *z = openzipfile(name, "rb");
- if(z)
- {
- SDL_RWops *rw = z->rwops();
- if(rw)
- {
- c = Mix_LoadWAV_RW(rw, 0);
- SDL_FreeRW(rw);
- }
- delete z;
- }
- if(!c) c = Mix_LoadWAV(findfile(name, "rb"));
- return c;
+ Mix_Chunk *c = NULL;
+ stream *z = openzipfile(name, "rb");
+ if(z)
+ {
+ SDL_RWops *rw = z->rwops();
+ if(rw)
+ {
+ c = Mix_LoadWAV_RW(rw, 0);
+ SDL_FreeRW(rw);
+ }
+ delete z;
+ }
+ if(!c) c = Mix_LoadWAV(findfile(name, "rb"));
+ return c;
}
template<class T> static void scalewav(T* dst, T* src, size_t len, int scale)
{
- len /= sizeof(T);
- const T* end = src + len;
- if(scale==2) for(; src < end; src++, dst += scale)
- {
- T s = src[0];
- dst[0] = s;
- dst[1] = s;
- }
- else if(scale==4) for(; src < end; src++, dst += scale)
- {
- T s = src[0];
- dst[0] = s;
- dst[1] = s;
- dst[2] = s;
- dst[3] = s;
- }
- else for(; src < end; src++)
- {
- T s = src[0];
- loopi(scale) *dst++ = s;
- }
+ len /= sizeof(T);
+ const T* end = src + len;
+ if(scale==2) for(; src < end; src++, dst += scale)
+ {
+ T s = src[0];
+ dst[0] = s;
+ dst[1] = s;
+ }
+ else if(scale==4) for(; src < end; src++, dst += scale)
+ {
+ T s = src[0];
+ dst[0] = s;
+ dst[1] = s;
+ dst[2] = s;
+ dst[3] = s;
+ }
+ else for(; src < end; src++)
+ {
+ T s = src[0];
+ loopi(scale) *dst++ = s;
+ }
}
static Mix_Chunk *loadwavscaled(const char *name)
{
- int mixerfreq = 0;
- Uint16 mixerformat = 0;
- int mixerchannels = 0;
- if(!Mix_QuerySpec(&mixerfreq, &mixerformat, &mixerchannels)) return NULL;
-
- SDL_AudioSpec spec;
- Uint8 *audiobuf = NULL;
- Uint32 audiolen = 0;
- stream *z = openzipfile(name, "rb");
- if(z)
- {
- SDL_RWops *rw = z->rwops();
- if(rw)
- {
- SDL_LoadWAV_RW(rw, 0, &spec, &audiobuf, &audiolen);
- SDL_FreeRW(rw);
- }
- delete z;
- }
- if(!audiobuf) SDL_LoadWAV(findfile(name, "rb"), &spec, &audiobuf, &audiolen);
- if(!audiobuf) return NULL;
- int samplesize = ((spec.format&0xFF)/8) * spec.channels;
- int scale = mixerfreq / spec.freq;
- if(scale >= 2)
- {
- Uint8 *scalebuf = (Uint8*)SDL_malloc(audiolen * scale);
- if(scalebuf)
- {
- switch(samplesize)
- {
- case 1: scalewav((uchar*)scalebuf, (uchar*)audiobuf, audiolen, scale); break;
- case 2: scalewav((ushort*)scalebuf, (ushort*)audiobuf, audiolen, scale); break;
- case 4: scalewav((uint*)scalebuf, (uint*)audiobuf, audiolen, scale); break;
- case 8: scalewav((ullong*)scalebuf, (ullong*)audiobuf, audiolen, scale); break;
- default: SDL_free(scalebuf); scalebuf = NULL; break;
- }
- if(scalebuf)
- {
- SDL_free(audiobuf);
- audiobuf = scalebuf;
- audiolen *= scale;
- spec.freq *= scale;
- }
- }
- }
- if(spec.freq != mixerfreq || spec.format != mixerformat || spec.channels != mixerchannels)
- {
- SDL_AudioCVT cvt;
- if(SDL_BuildAudioCVT(&cvt, spec.format, spec.channels, spec.freq, mixerformat, mixerchannels, mixerfreq) < 0)
- {
- SDL_free(audiobuf);
- return NULL;
- }
- if(cvt.filters[0])
- {
- cvt.len = audiolen & ~(samplesize-1);
- cvt.buf = (Uint8*)SDL_malloc(cvt.len * cvt.len_mult);
- if(!cvt.buf) { SDL_free(audiobuf); return NULL; }
- SDL_memcpy(cvt.buf, audiobuf, cvt.len);
- SDL_free(audiobuf);
- if(SDL_ConvertAudio(&cvt) < 0) { SDL_free(cvt.buf); return NULL; }
- audiobuf = cvt.buf;
- audiolen = cvt.len_cvt;
- }
- }
- Mix_Chunk *c = Mix_QuickLoad_RAW(audiobuf, audiolen);
- if(!c) { SDL_free(audiobuf); return NULL; }
- c->allocated = 1;
- return c;
+ int mixerfreq = 0;
+ Uint16 mixerformat = 0;
+ int mixerchannels = 0;
+ if(!Mix_QuerySpec(&mixerfreq, &mixerformat, &mixerchannels)) return NULL;
+
+ SDL_AudioSpec spec;
+ Uint8 *audiobuf = NULL;
+ Uint32 audiolen = 0;
+ stream *z = openzipfile(name, "rb");
+ if(z)
+ {
+ SDL_RWops *rw = z->rwops();
+ if(rw)
+ {
+ SDL_LoadWAV_RW(rw, 0, &spec, &audiobuf, &audiolen);
+ SDL_FreeRW(rw);
+ }
+ delete z;
+ }
+ if(!audiobuf) SDL_LoadWAV(findfile(name, "rb"), &spec, &audiobuf, &audiolen);
+ if(!audiobuf) return NULL;
+ int samplesize = ((spec.format&0xFF)/8) * spec.channels;
+ int scale = mixerfreq / spec.freq;
+ if(scale >= 2)
+ {
+ Uint8 *scalebuf = (Uint8*)SDL_malloc(audiolen * scale);
+ if(scalebuf)
+ {
+ switch(samplesize)
+ {
+ case 1: scalewav((uchar*)scalebuf, (uchar*)audiobuf, audiolen, scale); break;
+ case 2: scalewav((ushort*)scalebuf, (ushort*)audiobuf, audiolen, scale); break;
+ case 4: scalewav((uint*)scalebuf, (uint*)audiobuf, audiolen, scale); break;
+ case 8: scalewav((ullong*)scalebuf, (ullong*)audiobuf, audiolen, scale); break;
+ default: SDL_free(scalebuf); scalebuf = NULL; break;
+ }
+ if(scalebuf)
+ {
+ SDL_free(audiobuf);
+ audiobuf = scalebuf;
+ audiolen *= scale;
+ spec.freq *= scale;
+ }
+ }
+ }
+ if(spec.freq != mixerfreq || spec.format != mixerformat || spec.channels != mixerchannels)
+ {
+ SDL_AudioCVT cvt;
+ if(SDL_BuildAudioCVT(&cvt, spec.format, spec.channels, spec.freq, mixerformat, mixerchannels, mixerfreq) < 0)
+ {
+ SDL_free(audiobuf);
+ return NULL;
+ }
+ if(cvt.filters[0])
+ {
+ cvt.len = audiolen & ~(samplesize-1);
+ cvt.buf = (Uint8*)SDL_malloc(cvt.len * cvt.len_mult);
+ if(!cvt.buf) { SDL_free(audiobuf); return NULL; }
+ SDL_memcpy(cvt.buf, audiobuf, cvt.len);
+ SDL_free(audiobuf);
+ if(SDL_ConvertAudio(&cvt) < 0) { SDL_free(cvt.buf); return NULL; }
+ audiobuf = cvt.buf;
+ audiolen = cvt.len_cvt;
+ }
+ }
+ Mix_Chunk *c = Mix_QuickLoad_RAW(audiobuf, audiolen);
+ if(!c) { SDL_free(audiobuf); return NULL; }
+ c->allocated = 1;
+ return c;
}
VARFP(fixwav, 0, 1, 1, initwarning("sound configuration", INIT_LOAD, CHANGE_SOUND));
bool soundsample::load(bool msg)
{
- if(chunk) return true;
- if(!name[0]) return false;
-
- static const char * const exts[] = { "", ".wav", ".ogg" };
- string filename;
- loopi(sizeof(exts)/sizeof(exts[0]))
- {
- formatstring(filename, "packages/sounds/%s%s", name, exts[i]);
- if(msg && !i) renderprogress(0, filename);
- path(filename);
- if(fixwav)
- {
- size_t len = strlen(filename);
- if(len >= 4 && !strcasecmp(filename + len - 4, ".wav"))
- {
- chunk = loadwavscaled(filename);
- if(chunk) return true;
- }
- }
- chunk = loadwav(filename);
- if(chunk) return true;
- }
-
- conoutf(CON_ERROR, "failed to load sample: packages/sounds/%s", name);
- return false;
+ if(chunk) return true;
+ if(!name[0]) return false;
+
+ static const char * const exts[] = { "", ".wav", ".ogg" };
+ string filename;
+ loopi(sizeof(exts)/sizeof(exts[0]))
+ {
+ formatstring(filename, "packages/sounds/%s%s", name, exts[i]);
+ if(msg && !i) renderprogress(0, filename);
+ path(filename);
+ if(fixwav)
+ {
+ size_t len = strlen(filename);
+ if(len >= 4 && !strcasecmp(filename + len - 4, ".wav"))
+ {
+ chunk = loadwavscaled(filename);
+ if(chunk) return true;
+ }
+ }
+ chunk = loadwav(filename);
+ if(chunk) return true;
+ }
+
+ conoutf(CON_ERROR, "failed to load sample: packages/sounds/%s", name);
+ return false;
}
static hashnameset<soundsample> samples;
static void cleanupsamples()
{
- enumerate(samples, soundsample, s, s.cleanup());
+ enumerate(samples, soundsample, s, s.cleanup());
}
static struct soundtype
{
- vector<soundslot> slots;
- vector<soundconfig> configs;
-
- int findsound(const char *name, int vol)
- {
- loopv(configs)
- {
- soundconfig &s = configs[i];
- loopj(s.numslots)
- {
- soundslot &c = slots[s.slots+j];
- if(!strcmp(c.sample->name, name) && (!vol || c.volume==vol)) return i;
- }
- }
- return -1;
- }
-
- int addslot(const char *name, int vol)
- {
- soundsample *s = samples.access(name);
- if(!s)
- {
- char *n = newstring(name);
- s = &samples[n];
- s->name = n;
- s->chunk = NULL;
- }
- soundslot *oldslots = slots.getbuf();
- int oldlen = slots.length();
- soundslot &slot = slots.add();
- // soundslots.add() may relocate slot pointers
- if(slots.getbuf() != oldslots) loopv(channels)
- {
- soundchannel &chan = channels[i];
- if(chan.inuse && chan.slot >= oldslots && chan.slot < &oldslots[oldlen])
- chan.slot = &slots[chan.slot - oldslots];
- }
- slot.sample = s;
- slot.volume = vol ? vol : 100;
- return oldlen;
- }
-
- int addsound(const char *name, int vol, int maxuses = 0)
- {
- soundconfig &s = configs.add();
- s.slots = addslot(name, vol);
- s.numslots = 1;
- s.maxuses = maxuses;
- return configs.length()-1;
- }
-
- void addalt(const char *name, int vol)
- {
- if(configs.empty()) return;
- addslot(name, vol);
- configs.last().numslots++;
- }
-
- void clear()
- {
- slots.setsize(0);
- configs.setsize(0);
- }
-
- void reset()
- {
- loopv(channels)
- {
- soundchannel &chan = channels[i];
- if(chan.inuse && slots.inbuf(chan.slot))
- {
- Mix_HaltChannel(i);
- freechannel(i);
- }
- }
- clear();
- }
-
- void preloadsound(int n)
- {
- if(nosound || !configs.inrange(n)) return;
- soundconfig &config = configs[n];
- loopk(config.numslots) slots[config.slots+k].sample->load(true);
- }
-
- bool playing(const soundchannel &chan, const soundconfig &config) const
- {
- return chan.inuse && config.hasslot(chan.slot, slots);
- }
+ vector<soundslot> slots;
+ vector<soundconfig> configs;
+
+ int findsound(const char *name, int vol)
+ {
+ loopv(configs)
+ {
+ soundconfig &s = configs[i];
+ loopj(s.numslots)
+ {
+ soundslot &c = slots[s.slots+j];
+ if(!strcmp(c.sample->name, name) && (!vol || c.volume==vol)) return i;
+ }
+ }
+ return -1;
+ }
+
+ int addslot(const char *name, int vol)
+ {
+ soundsample *s = samples.access(name);
+ if(!s)
+ {
+ char *n = newstring(name);
+ s = &samples[n];
+ s->name = n;
+ s->chunk = NULL;
+ }
+ soundslot *oldslots = slots.getbuf();
+ int oldlen = slots.length();
+ soundslot &slot = slots.add();
+ // soundslots.add() may relocate slot pointers
+ if(slots.getbuf() != oldslots) loopv(channels)
+ {
+ soundchannel &chan = channels[i];
+ if(chan.inuse && chan.slot >= oldslots && chan.slot < &oldslots[oldlen])
+ chan.slot = &slots[chan.slot - oldslots];
+ }
+ slot.sample = s;
+ slot.volume = vol ? vol : 100;
+ return oldlen;
+ }
+
+ int addsound(const char *name, int vol, int maxuses = 0)
+ {
+ soundconfig &s = configs.add();
+ s.slots = addslot(name, vol);
+ s.numslots = 1;
+ s.maxuses = maxuses;
+ return configs.length()-1;
+ }
+
+ void addalt(const char *name, int vol)
+ {
+ if(configs.empty()) return;
+ addslot(name, vol);
+ configs.last().numslots++;
+ }
+
+ void clear()
+ {
+ slots.setsize(0);
+ configs.setsize(0);
+ }
+
+ void reset()
+ {
+ loopv(channels)
+ {
+ soundchannel &chan = channels[i];
+ if(chan.inuse && slots.inbuf(chan.slot))
+ {
+ Mix_HaltChannel(i);
+ freechannel(i);
+ }
+ }
+ clear();
+ }
+
+ void preloadsound(int n)
+ {
+ if(nosound || !configs.inrange(n)) return;
+ soundconfig &config = configs[n];
+ loopk(config.numslots) slots[config.slots+k].sample->load(true);
+ }
+
+ bool playing(const soundchannel &chan, const soundconfig &config) const
+ {
+ return chan.inuse && config.hasslot(chan.slot, slots);
+ }
} gamesounds, mapsounds;
void registersound(char *name, int *vol) { intret(gamesounds.addsound(name, *vol, 0)); }
@@ -556,318 +555,313 @@ ICOMMAND(nummapsounds, "", (), intret(mapsounds.configs.length()));
void soundreset()
{
- gamesounds.reset();
+ gamesounds.reset();
}
COMMAND(soundreset, "");
void mapsoundreset()
{
- mapsounds.reset();
+ mapsounds.reset();
}
COMMAND(mapsoundreset, "");
void resetchannels()
{
- loopv(channels) if(channels[i].inuse) freechannel(i);
- channels.shrink(0);
+ loopv(channels) if(channels[i].inuse) freechannel(i);
+ channels.shrink(0);
}
void clear_sound()
{
- if(nosound) return;
- stopmusic();
+ if(nosound) return;
+ stopmusic();
- cleanupsamples();
- gamesounds.clear();
- mapsounds.clear();
- samples.clear();
- Mix_CloseAudio();
- resetchannels();
+ cleanupsamples();
+ gamesounds.clear();
+ mapsounds.clear();
+ samples.clear();
+ Mix_CloseAudio();
+ resetchannels();
}
void stopmapsounds()
{
- loopv(channels) if(channels[i].inuse && channels[i].ent)
- {
- Mix_HaltChannel(i);
- freechannel(i);
- }
+ loopv(channels) if(channels[i].inuse && channels[i].ent)
+ {
+ Mix_HaltChannel(i);
+ freechannel(i);
+ }
}
void clearmapsounds()
{
- stopmapsounds();
- mapsounds.clear();
+ stopmapsounds();
+ mapsounds.clear();
}
void stopmapsound(extentity *e)
{
- loopv(channels)
- {
- soundchannel &chan = channels[i];
- if(chan.inuse && chan.ent == e)
- {
- Mix_HaltChannel(i);
- freechannel(i);
- }
- }
+ loopv(channels)
+ {
+ soundchannel &chan = channels[i];
+ if(chan.inuse && chan.ent == e)
+ {
+ Mix_HaltChannel(i);
+ freechannel(i);
+ }
+ }
}
void checkmapsounds()
{
- const vector<extentity *> &ents = entities::getents();
- loopv(ents)
- {
- extentity &e = *ents[i];
- if(e.type!=ET_SOUND) continue;
- if(camera1->o.dist(e.o) < e.attr2)
- {
- if(!(e.flags&EF_SOUND)) playsound(e.attr1, NULL, &e, SND_MAP, -1);
- }
- else if(e.flags&EF_SOUND) stopmapsound(&e);
- }
+ const vector<extentity *> &ents = entities::getents();
+ loopv(ents)
+ {
+ extentity &e = *ents[i];
+ if(e.type!=ET_SOUND) continue;
+ if(camera1->o.dist(e.o) < e.attr2)
+ {
+ if(!(e.flags&EF_SOUND)) playsound(e.attr1, NULL, &e, SND_MAP, -1);
+ }
+ else if(e.flags&EF_SOUND) stopmapsound(&e);
+ }
}
VAR(stereo, 0, 1, 1);
bool updatechannel(soundchannel &chan)
{
- if(!chan.slot) return false;
- int vol = soundvol, pan = 255/2;
- if(chan.hasloc())
- {
- vec v;
- float dist = chan.loc.dist(camera1->o, v);
- int rad = 0;
- if(chan.ent)
- {
- rad = chan.ent->attr2;
- if(chan.ent->attr3)
- {
- rad -= chan.ent->attr3;
- dist -= chan.ent->attr3;
- }
- }
- else if(chan.radius > 0) rad = chan.radius;
- if(rad > 0) vol -= int(clamp(dist/rad, 0.0f, 1.0f)*soundvol); // simple mono distance attenuation
- if(stereo && (v.x != 0 || v.y != 0) && dist>0)
- {
- v.rotate_around_z(-camera1->yaw*RAD);
- pan = int(255.9f*(0.5f - 0.5f*v.x/v.magnitude2())); // range is from 0 (left) to 255 (right)
- }
- }
- vol = (vol*MIX_MAX_VOLUME*chan.slot->volume)/255/255;
- vol = min(vol, MIX_MAX_VOLUME);
- if(vol == chan.volume && pan == chan.pan) return false;
- chan.volume = vol;
- chan.pan = pan;
- chan.dirty = true;
- return true;
+ if(!chan.slot) return false;
+ int vol = soundvol, pan = 255/2;
+ if(chan.hasloc())
+ {
+ vec v;
+ float dist = chan.loc.dist(camera1->o, v);
+ int rad = 0;
+ if(chan.ent)
+ {
+ rad = chan.ent->attr2;
+ if(chan.ent->attr3)
+ {
+ rad -= chan.ent->attr3;
+ dist -= chan.ent->attr3;
+ }
+ }
+ else if(chan.radius > 0) rad = chan.radius;
+ if(rad > 0) vol -= int(clamp(dist/rad, 0.0f, 1.0f)*soundvol); // simple mono distance attenuation
+ if(stereo && (v.x != 0 || v.y != 0) && dist>0)
+ {
+ v.rotate_around_z(-camera1->yaw*RAD);
+ pan = int(255.9f*(0.5f - 0.5f*v.x/v.magnitude2())); // range is from 0 (left) to 255 (right)
+ }
+ }
+ vol = (vol*MIX_MAX_VOLUME*chan.slot->volume)/255/255;
+ vol = min(vol, MIX_MAX_VOLUME);
+ if(vol == chan.volume && pan == chan.pan) return false;
+ chan.volume = vol;
+ chan.pan = pan;
+ chan.dirty = true;
+ return true;
}
void reclaimchannels()
{
- loopv(channels)
- {
- soundchannel &chan = channels[i];
- if(chan.inuse && !Mix_Playing(i)) freechannel(i);
- }
+ loopv(channels)
+ {
+ soundchannel &chan = channels[i];
+ if(chan.inuse && !Mix_Playing(i)) freechannel(i);
+ }
}
void syncchannels()
{
- loopv(channels)
- {
- soundchannel &chan = channels[i];
- if(chan.inuse && chan.hasloc() && updatechannel(chan)) syncchannel(chan);
- }
+ loopv(channels)
+ {
+ soundchannel &chan = channels[i];
+ if(chan.inuse && chan.hasloc() && updatechannel(chan)) syncchannel(chan);
+ }
}
VARP(minimizedsounds, 0, 0, 1);
void updatesounds()
{
- if(nosound) return;
- if(minimized && !minimizedsounds) stopsounds();
- else
- {
- reclaimchannels();
- if(mainmenu) stopmapsounds();
- else checkmapsounds();
- syncchannels();
- }
- if(music)
- {
- if(!Mix_PlayingMusic()) musicdone();
- else if(Mix_PausedMusic()) Mix_ResumeMusic();
- }
+ if(nosound) return;
+ if(minimized && !minimizedsounds) stopsounds();
+ else
+ {
+ reclaimchannels();
+ if(mainmenu) stopmapsounds();
+ else checkmapsounds();
+ syncchannels();
+ }
+ if(music)
+ {
+ if(!Mix_PlayingMusic()) musicdone();
+ else if(Mix_PausedMusic()) Mix_ResumeMusic();
+ }
}
VARP(maxsoundsatonce, 0, 7, 100);
-VAR(dbgsound, 0, 0, 1);
-
void preloadsound(int n)
{
- gamesounds.preloadsound(n);
+ gamesounds.preloadsound(n);
}
void preloadmapsound(int n)
{
- mapsounds.preloadsound(n);
+ mapsounds.preloadsound(n);
}
void preloadmapsounds()
{
- const vector<extentity *> &ents = entities::getents();
- loopv(ents)
- {
- extentity &e = *ents[i];
- if(e.type==ET_SOUND) mapsounds.preloadsound(e.attr1);
- }
+ const vector<extentity *> &ents = entities::getents();
+ loopv(ents)
+ {
+ extentity &e = *ents[i];
+ if(e.type==ET_SOUND) mapsounds.preloadsound(e.attr1);
+ }
}
int playsound(int n, const vec *loc, extentity *ent, int flags, int loops, int fade, int chanid, int radius, int expire)
{
- if(nosound || !soundvol || (minimized && !minimizedsounds)) return -1;
-
- soundtype &sounds = ent || flags&SND_MAP ? mapsounds : gamesounds;
- if(!sounds.configs.inrange(n)) { conoutf(CON_WARN, "unregistered sound: %d", n); return -1; }
- soundconfig &config = sounds.configs[n];
-
- if(loc)
- {
- // cull sounds that are unlikely to be heard
- int maxrad = game::maxsoundradius(n);
- if(radius <= 0 || maxrad < radius) radius = maxrad;
- if(camera1->o.dist(*loc) > 1.5f*radius)
- {
- if(channels.inrange(chanid) && sounds.playing(channels[chanid], config))
- {
- Mix_HaltChannel(chanid);
- freechannel(chanid);
- }
- return -1;
- }
- }
-
- if(chanid < 0)
- {
- if(config.maxuses)
- {
- int uses = 0;
- loopv(channels) if(sounds.playing(channels[i], config) && ++uses >= config.maxuses) return -1;
- }
-
- // avoid bursts of sounds with heavy packetloss and in sp
- static int soundsatonce = 0, lastsoundmillis = 0;
- if(totalmillis == lastsoundmillis) soundsatonce++; else soundsatonce = 1;
- lastsoundmillis = totalmillis;
- if(maxsoundsatonce && soundsatonce > maxsoundsatonce) return -1;
- }
-
- if(channels.inrange(chanid))
- {
- soundchannel &chan = channels[chanid];
- if(sounds.playing(chan, config))
- {
- if(loc) chan.loc = *loc;
- else if(chan.hasloc()) chan.clearloc();
- return chanid;
- }
- }
- if(fade < 0) return -1;
-
- soundslot &slot = sounds.slots[config.chooseslot(flags)];
- if(!slot.sample->chunk && !slot.sample->load()) return -1;
-
- if(dbgsound) conoutf(CON_DEBUG, "sound: %s", slot.sample->name);
-
- chanid = -1;
- loopv(channels) if(!channels[i].inuse) { chanid = i; break; }
- if(chanid < 0 && channels.length() < maxchannels) chanid = channels.length();
- if(chanid < 0) loopv(channels) if(!channels[i].volume) { chanid = i; break; }
- if(chanid < 0) return -1;
-
- soundchannel &chan = newchannel(chanid, &slot, loc, ent, flags, radius);
- updatechannel(chan);
- int playing = -1;
- if(fade)
- {
- Mix_Volume(chanid, chan.volume);
- playing = expire >= 0 ? Mix_FadeInChannelTimed(chanid, slot.sample->chunk, loops, fade, expire) : Mix_FadeInChannel(chanid, slot.sample->chunk, loops, fade);
- }
- else playing = expire >= 0 ? Mix_PlayChannelTimed(chanid, slot.sample->chunk, loops, expire) : Mix_PlayChannel(chanid, slot.sample->chunk, loops);
- if(playing >= 0) syncchannel(chan);
- else freechannel(chanid);
- return playing;
+ if(nosound || !soundvol || (minimized && !minimizedsounds)) return -1;
+
+ soundtype &sounds = ent || flags&SND_MAP ? mapsounds : gamesounds;
+ if(!sounds.configs.inrange(n)) { conoutf(CON_WARN, "unregistered sound: %d", n); return -1; }
+ soundconfig &config = sounds.configs[n];
+
+ if(loc)
+ {
+ // cull sounds that are unlikely to be heard
+ int maxrad = game::maxsoundradius(n);
+ if(radius <= 0 || maxrad < radius) radius = maxrad;
+ if(camera1->o.dist(*loc) > 1.5f*radius)
+ {
+ if(channels.inrange(chanid) && sounds.playing(channels[chanid], config))
+ {
+ Mix_HaltChannel(chanid);
+ freechannel(chanid);
+ }
+ return -1;
+ }
+ }
+
+ if(chanid < 0)
+ {
+ if(config.maxuses)
+ {
+ int uses = 0;
+ loopv(channels) if(sounds.playing(channels[i], config) && ++uses >= config.maxuses) return -1;
+ }
+
+ // avoid bursts of sounds with heavy packetloss and in sp
+ static int soundsatonce = 0, lastsoundmillis = 0;
+ if(totalmillis == lastsoundmillis) soundsatonce++; else soundsatonce = 1;
+ lastsoundmillis = totalmillis;
+ if(maxsoundsatonce && soundsatonce > maxsoundsatonce) return -1;
+ }
+
+ if(channels.inrange(chanid))
+ {
+ soundchannel &chan = channels[chanid];
+ if(sounds.playing(chan, config))
+ {
+ if(loc) chan.loc = *loc;
+ else if(chan.hasloc()) chan.clearloc();
+ return chanid;
+ }
+ }
+ if(fade < 0) return -1;
+
+ soundslot &slot = sounds.slots[config.chooseslot(flags)];
+ if(!slot.sample->chunk && !slot.sample->load()) return -1;
+
+ chanid = -1;
+ loopv(channels) if(!channels[i].inuse) { chanid = i; break; }
+ if(chanid < 0 && channels.length() < maxchannels) chanid = channels.length();
+ if(chanid < 0) loopv(channels) if(!channels[i].volume) { chanid = i; break; }
+ if(chanid < 0) return -1;
+
+ soundchannel &chan = newchannel(chanid, &slot, loc, ent, flags, radius);
+ updatechannel(chan);
+ int playing = -1;
+ if(fade)
+ {
+ Mix_Volume(chanid, chan.volume);
+ playing = expire >= 0 ? Mix_FadeInChannelTimed(chanid, slot.sample->chunk, loops, fade, expire) : Mix_FadeInChannel(chanid, slot.sample->chunk, loops, fade);
+ }
+ else playing = expire >= 0 ? Mix_PlayChannelTimed(chanid, slot.sample->chunk, loops, expire) : Mix_PlayChannel(chanid, slot.sample->chunk, loops);
+ if(playing >= 0) syncchannel(chan);
+ else freechannel(chanid);
+ return playing;
}
void stopsounds()
{
- loopv(channels) if(channels[i].inuse)
- {
- Mix_HaltChannel(i);
- freechannel(i);
- }
+ loopv(channels) if(channels[i].inuse)
+ {
+ Mix_HaltChannel(i);
+ freechannel(i);
+ }
}
bool stopsound(int n, int chanid, int fade)
{
- if(!gamesounds.configs.inrange(n) || !channels.inrange(chanid) || !channels[chanid].inuse || !gamesounds.playing(channels[chanid], gamesounds.configs[n])) return false;
- if(dbgsound) conoutf(CON_DEBUG, "stopsound: %s", channels[chanid].slot->sample->name);
- if(!fade || !Mix_FadeOutChannel(chanid, fade))
- {
- Mix_HaltChannel(chanid);
- freechannel(chanid);
- }
- return true;
+ if(!gamesounds.configs.inrange(n) || !channels.inrange(chanid) || !channels[chanid].inuse || !gamesounds.playing(channels[chanid], gamesounds.configs[n])) return false;
+ if(!fade || !Mix_FadeOutChannel(chanid, fade))
+ {
+ Mix_HaltChannel(chanid);
+ freechannel(chanid);
+ }
+ return true;
}
int playsoundname(const char *s, const vec *loc, int vol, int flags, int loops, int fade, int chanid, int radius, int expire)
{
- if(!vol) vol = 100;
- int id = gamesounds.findsound(s, vol);
- if(id < 0) id = gamesounds.addsound(s, vol);
- return playsound(id, loc, NULL, flags, loops, fade, chanid, radius, expire);
+ if(!vol) vol = 100;
+ int id = gamesounds.findsound(s, vol);
+ if(id < 0) id = gamesounds.addsound(s, vol);
+ return playsound(id, loc, NULL, flags, loops, fade, chanid, radius, expire);
}
ICOMMAND(sound, "i", (int *n), playsound(*n));
void resetsound()
{
- clearchanges(CHANGE_SOUND);
- if(!nosound)
- {
- cleanupsamples();
- if(music)
- {
- Mix_HaltMusic();
- Mix_FreeMusic(music);
- }
- if(musicstream) musicstream->seek(0, SEEK_SET);
- Mix_CloseAudio();
- }
- initsound();
- resetchannels();
- if(nosound)
- {
- DELETEA(musicfile);
- DELETEA(musicdonecmd);
- music = NULL;
- cleanupsamples();
- return;
- }
- if(music && loadmusic(musicfile))
- {
- Mix_PlayMusic(music, musicdonecmd ? 0 : -1);
- Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
- }
- else
- {
- DELETEA(musicfile);
- DELETEA(musicdonecmd);
- }
+ clearchanges(CHANGE_SOUND);
+ if(!nosound)
+ {
+ cleanupsamples();
+ if(music)
+ {
+ Mix_HaltMusic();
+ Mix_FreeMusic(music);
+ }
+ if(musicstream) musicstream->seek(0, SEEK_SET);
+ Mix_CloseAudio();
+ }
+ initsound();
+ resetchannels();
+ if(nosound)
+ {
+ DELETEA(musicfile);
+ DELETEA(musicdonecmd);
+ music = NULL;
+ cleanupsamples();
+ return;
+ }
+ if(music && loadmusic(musicfile))
+ {
+ Mix_PlayMusic(music, musicdonecmd ? 0 : -1);
+ Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
+ }
+ else
+ {
+ DELETEA(musicfile);
+ DELETEA(musicdonecmd);
+ }
}
COMMAND(resetsound, "");
diff --git a/src/engine/textedit.h b/src/engine/textedit.h
index 31f23ed..0dcda1d 100644
--- a/src/engine/textedit.h
+++ b/src/engine/textedit.h
@@ -1,665 +1,664 @@
-
struct editline
{
- enum { CHUNKSIZE = 256 };
-
- char *text;
- int len, maxlen;
-
- editline() : text(NULL), len(0), maxlen(0) {}
- editline(const char *init) : text(NULL), len(0), maxlen(0)
- {
- set(init);
- }
-
- bool empty() { return len <= 0; }
-
- void clear()
- {
- DELETEA(text);
- len = maxlen = 0;
- }
-
- bool grow(int total, const char *fmt = "", ...)
- {
- if(total + 1 <= maxlen) return false;
- maxlen = (total + CHUNKSIZE) - total%CHUNKSIZE;
- char *newtext = new char[maxlen];
- if(fmt)
- {
- va_list args;
- va_start(args, fmt);
- vformatstring(newtext, fmt, args, maxlen);
- va_end(args);
- }
- else newtext[0] = '\0';
- DELETEA(text);
- text = newtext;
- return true;
- }
-
- void set(const char *str, int slen = -1)
- {
- if(slen < 0)
- {
- slen = strlen(str);
- if(!grow(slen, "%s", str)) memcpy(text, str, slen + 1);
- }
- else
- {
- grow(slen);
- memcpy(text, str, slen);
- text[slen] = '\0';
- }
- len = slen;
- }
-
- void prepend(const char *str)
- {
- int slen = strlen(str);
- if(!grow(slen + len, "%s%s", str, text ? text : ""))
- {
- memmove(&text[slen], text, len + 1);
- memcpy(text, str, slen + 1);
- }
- len += slen;
- }
-
- void append(const char *str)
- {
- int slen = strlen(str);
- if(!grow(len + slen, "%s%s", text ? text : "", str)) memcpy(&text[len], str, slen + 1);
- len += slen;
- }
-
- bool read(stream *f, int chop = -1)
- {
- if(chop < 0) chop = INT_MAX; else chop++;
- set("");
- while(len + 1 < chop && f->getline(&text[len], min(maxlen, chop) - len))
- {
- len += strlen(&text[len]);
- if(len > 0 && text[len-1] == '\n')
- {
- text[--len] = '\0';
- return true;
- }
- if(len + 1 >= maxlen && len + 1 < chop) grow(len + CHUNKSIZE, "%s", text);
- }
- if(len + 1 >= chop)
- {
- char buf[CHUNKSIZE];
- while(f->getline(buf, sizeof(buf)))
- {
- int blen = strlen(buf);
- if(blen > 0 && buf[blen-1] == '\n') return true;
- }
- }
- return len > 0;
- }
-
- void del(int start, int count)
- {
- if(!text) return;
- if(start < 0) { count += start; start = 0; }
- if(count <= 0 || start >= len) return;
- if(start + count > len) count = len - start - 1;
- memmove(&text[start], &text[start+count], len + 1 - (start + count));
- len -= count;
- }
-
- void chop(int newlen)
- {
- if(!text) return;
- len = clamp(newlen, 0, len);
- text[len] = '\0';
- }
-
- void insert(char *str, int start, int count = 0)
- {
- if(count <= 0) count = strlen(str);
- start = clamp(start, 0, len);
- grow(len + count, "%s", text ? text : "");
- memmove(&text[start + count], &text[start], len - start + 1);
- memcpy(&text[start], str, count);
- len += count;
- }
-
- void combinelines(vector<editline> &src)
- {
- if(src.empty()) set("");
- else loopv(src)
- {
- if(i) append("\n");
- if(!i) set(src[i].text, src[i].len);
- else insert(src[i].text, len, src[i].len);
- }
- }
+ enum { CHUNKSIZE = 256 };
+
+ char *text;
+ int len, maxlen;
+
+ editline() : text(NULL), len(0), maxlen(0) {}
+ editline(const char *init) : text(NULL), len(0), maxlen(0)
+ {
+ set(init);
+ }
+
+ bool empty() { return len <= 0; }
+
+ void clear()
+ {
+ DELETEA(text);
+ len = maxlen = 0;
+ }
+
+ bool grow(int total, const char *fmt = "", ...)
+ {
+ if(total + 1 <= maxlen) return false;
+ maxlen = (total + CHUNKSIZE) - total%CHUNKSIZE;
+ char *newtext = new char[maxlen];
+ if(fmt)
+ {
+ va_list args;
+ va_start(args, fmt);
+ vformatstring(newtext, fmt, args, maxlen);
+ va_end(args);
+ }
+ else newtext[0] = '\0';
+ DELETEA(text);
+ text = newtext;
+ return true;
+ }
+
+ void set(const char *str, int slen = -1)
+ {
+ if(slen < 0)
+ {
+ slen = strlen(str);
+ if(!grow(slen, "%s", str)) memcpy(text, str, slen + 1);
+ }
+ else
+ {
+ grow(slen);
+ memcpy(text, str, slen);
+ text[slen] = '\0';
+ }
+ len = slen;
+ }
+
+ void prepend(const char *str)
+ {
+ int slen = strlen(str);
+ if(!grow(slen + len, "%s%s", str, text ? text : ""))
+ {
+ memmove(&text[slen], text, len + 1);
+ memcpy(text, str, slen + 1);
+ }
+ len += slen;
+ }
+
+ void append(const char *str)
+ {
+ int slen = strlen(str);
+ if(!grow(len + slen, "%s%s", text ? text : "", str)) memcpy(&text[len], str, slen + 1);
+ len += slen;
+ }
+
+ bool read(stream *f, int chop = -1)
+ {
+ if(chop < 0) chop = INT_MAX; else chop++;
+ set("");
+ while(len + 1 < chop && f->getline(&text[len], min(maxlen, chop) - len))
+ {
+ len += strlen(&text[len]);
+ if(len > 0 && text[len-1] == '\n')
+ {
+ text[--len] = '\0';
+ return true;
+ }
+ if(len + 1 >= maxlen && len + 1 < chop) grow(len + CHUNKSIZE, "%s", text);
+ }
+ if(len + 1 >= chop)
+ {
+ char buf[CHUNKSIZE];
+ while(f->getline(buf, sizeof(buf)))
+ {
+ int blen = strlen(buf);
+ if(blen > 0 && buf[blen-1] == '\n') return true;
+ }
+ }
+ return len > 0;
+ }
+
+ void del(int start, int count)
+ {
+ if(!text) return;
+ if(start < 0) { count += start; start = 0; }
+ if(count <= 0 || start >= len) return;
+ if(start + count > len) count = len - start - 1;
+ memmove(&text[start], &text[start+count], len + 1 - (start + count));
+ len -= count;
+ }
+
+ void chop(int newlen)
+ {
+ if(!text) return;
+ len = clamp(newlen, 0, len);
+ text[len] = '\0';
+ }
+
+ void insert(char *str, int start, int count = 0)
+ {
+ if(count <= 0) count = strlen(str);
+ start = clamp(start, 0, len);
+ grow(len + count, "%s", text ? text : "");
+ memmove(&text[start + count], &text[start], len - start + 1);
+ memcpy(&text[start], str, count);
+ len += count;
+ }
+
+ void combinelines(vector<editline> &src)
+ {
+ if(src.empty()) set("");
+ else loopv(src)
+ {
+ if(i) append("\n");
+ if(!i) set(src[i].text, src[i].len);
+ else insert(src[i].text, len, src[i].len);
+ }
+ }
};
struct editor
{
- int mode; //editor mode - 1= keep while focused, 2= keep while used in gui, 3= keep forever (i.e. until mode changes)
- bool active, rendered;
- const char *name;
- const char *filename;
-
- int cx, cy; // cursor position - ensured to be valid after a region() or currentline()
- int mx, my; // selection mark, mx=-1 if following cursor - avoid direct access, instead use region()
- int maxx, maxy; // maxy=-1 if unlimited lines, 1 if single line editor
-
- int scrolly; // vertical scroll offset
-
- bool linewrap;
- int pixelwidth; // required for up/down/hit/draw/bounds
- int pixelheight; // -1 for variable sized, i.e. from bounds()
-
- vector<editline> lines; // MUST always contain at least one line!
-
- editor(const char *name, int mode, const char *initval) :
- mode(mode), active(true), rendered(false), name(newstring(name)), filename(NULL),
- cx(0), cy(0), mx(-1), maxx(-1), maxy(-1), scrolly(0), linewrap(false), pixelwidth(-1), pixelheight(-1)
- {
- //printf("editor %08x '%s'\n", this, name);
- lines.add().set(initval ? initval : "");
- }
-
- ~editor()
- {
- //printf("~editor %08x '%s'\n", this, name);
- DELETEA(name);
- DELETEA(filename);
- clear(NULL);
- }
-
- void clear(const char *init = "")
- {
- cx = cy = 0;
- mark(false);
- loopv(lines) lines[i].clear();
- lines.shrink(0);
- if(init) lines.add().set(init);
- }
-
- void setfile(const char *fname)
- {
- DELETEA(filename);
- if(fname) filename = newstring(fname);
- }
-
- void load()
- {
- if(!filename) return;
- clear(NULL);
- stream *file = openutf8file(filename, "r");
- if(file)
- {
- while(lines.add().read(file, maxx) && (maxy < 0 || lines.length() <= maxy));
- lines.pop().clear();
- delete file;
- }
- if(lines.empty()) lines.add().set("");
- }
-
- void save()
- {
- if(!filename) return;
- stream *file = openutf8file(filename, "w");
- if(!file) return;
- loopv(lines) file->putline(lines[i].text);
- delete file;
- }
-
- void mark(bool enable)
- {
- mx = (enable) ? cx : -1;
- my = cy;
- }
-
- void selectall()
- {
- mx = my = INT_MAX;
- cx = cy = 0;
- }
-
- // constrain results to within buffer - s=start, e=end, return true if a selection range
- // also ensures that cy is always within lines[] and cx is valid
- bool region(int &sx, int &sy, int &ex, int &ey)
- {
- int n = lines.length();
- ASSERT(n != 0);
- if(cy < 0) cy = 0; else if(cy >= n) cy = n-1;
- int len = lines[cy].len;
- if(cx < 0) cx = 0; else if(cx > len) cx = len;
- if(mx >= 0)
- {
- if(my < 0) my = 0; else if(my >= n) my = n-1;
- len = lines[my].len;
- if(mx > len) mx = len;
- }
- sx = (mx >= 0) ? mx : cx;
- sy = (mx >= 0) ? my : cy;
- ex = cx;
- ey = cy;
- if(sy > ey) { swap(sy, ey); swap(sx, ex); }
- else if(sy==ey && sx > ex) swap(sx, ex);
- return (sx != ex) || (sy != ey);
- }
-
- bool region() { int sx, sy, ex, ey; return region(sx, sy, ex, ey); }
-
- // also ensures that cy is always within lines[] and cx is valid
- editline &currentline()
- {
- int n = lines.length();
- ASSERT(n != 0);
- if(cy < 0) cy = 0; else if(cy >= n) cy = n-1;
- if(cx < 0) cx = 0; else if(cx > lines[cy].len) cx = lines[cy].len;
- return lines[cy];
- }
-
- void copyselectionto(editor *b)
- {
- if(b==this) return;
-
- b->clear(NULL);
- int sx, sy, ex, ey;
- region(sx, sy, ex, ey);
- loopi(1+ey-sy)
- {
- if(b->maxy != -1 && b->lines.length() >= b->maxy) break;
- int y = sy+i;
- char *line = lines[y].text;
- int len = lines[y].len;
- if(y == sy && y == ey)
- {
- line += sx;
- len = ex - sx;
- }
- else if(y == sy) line += sx;
- else if(y == ey) len = ex;
- b->lines.add().set(line, len);
- }
- if(b->lines.empty()) b->lines.add().set("");
- }
-
- char *tostring()
- {
- int len = 0;
- loopv(lines) len += lines[i].len + 1;
- char *str = newstring(len);
- int offset = 0;
- loopv(lines)
- {
- editline &l = lines[i];
- memcpy(&str[offset], l.text, l.len);
- offset += l.len;
- str[offset++] = '\n';
- }
- str[offset] = '\0';
- return str;
- }
-
- char *selectiontostring()
- {
- vector<char> buf;
- int sx, sy, ex, ey;
- region(sx, sy, ex, ey);
- loopi(1+ey-sy)
- {
- int y = sy+i;
- char *line = lines[y].text;
- int len = lines[y].len;
- if(y == sy && y == ey)
- {
- line += sx;
- len = ex - sx;
- }
- else if(y == sy) line += sx;
- else if(y == ey) len = ex;
- buf.put(line, len);
- buf.add('\n');
- }
- buf.add('\0');
- return newstring(buf.getbuf(), buf.length()-1);
- }
-
- void removelines(int start, int count)
- {
- loopi(count) lines[start+i].clear();
- lines.remove(start, count);
- }
-
- bool del() // removes the current selection (if any)
- {
- int sx, sy, ex, ey;
- if(!region(sx, sy, ex, ey))
- {
- mark(false);
- return false;
- }
- if(sy == ey)
- {
- if(sx == 0 && ex == lines[ey].len) removelines(sy, 1);
- else lines[sy].del(sx, ex - sx);
- }
- else
- {
- if(ey > sy+1) { removelines(sy+1, ey-(sy+1)); ey = sy+1; }
- if(ex == lines[ey].len) removelines(ey, 1); else lines[ey].del(0, ex);
- if(sx == 0) removelines(sy, 1); else lines[sy].del(sx, lines[sy].len - sx);
- }
- if(lines.empty()) lines.add().set("");
- mark(false);
- cx = sx;
- cy = sy;
- editline &current = currentline();
- if(cx >= current.len && cy < lines.length() - 1)
- {
- current.append(lines[cy+1].text);
- removelines(cy + 1, 1);
- }
- return true;
- }
-
- void insert(char ch)
- {
- del();
- editline &current = currentline();
- if(ch == '\n')
- {
- if(maxy == -1 || cy < maxy-1)
- {
- editline newline(&current.text[cx]);
- current.chop(cx);
- cy = min(lines.length(), cy+1);
- lines.insert(cy, newline);
- }
- else current.chop(cx);
- cx = 0;
- }
- else
- {
- int len = current.len;
- if(maxx >= 0 && len > maxx-1) len = maxx-1;
- if(cx <= len) current.insert(&ch, cx++, 1);
- }
- }
-
- void insert(const char *s)
- {
- while(*s) insert(*s++);
- }
-
- void insertallfrom(editor *b)
- {
- if(b==this) return;
-
- del();
-
- if(b->lines.length() == 1 || maxy == 1)
- {
- editline &current = currentline();
- char *str = b->lines[0].text;
- int slen = b->lines[0].len;
- if(maxx >= 0 && b->lines[0].len + cx > maxx) slen = maxx-cx;
- if(slen > 0)
- {
- int len = current.len;
- if(maxx >= 0 && slen + cx + len > maxx) len = max(0, maxx-(cx+slen));
- current.insert(str, cx, slen);
- cx += slen;
- }
- }
- else
- {
- loopv(b->lines)
- {
- if(!i)
- {
- lines[cy++].append(b->lines[i].text);
- }
- else if(i >= b->lines.length())
- {
- cx = b->lines[i].len;
- lines[cy].prepend(b->lines[i].text);
- }
- else if(maxy < 0 || lines.length() < maxy) lines.insert(cy++, editline(b->lines[i].text));
- }
- }
- }
-
- void key(int code)
- {
- switch(code)
- {
- case SDLK_UP:
- if(linewrap)
- {
- int x, y;
- char *str = currentline().text;
- text_pos(str, cx+1, x, y, pixelwidth);
- if(y > 0) { cx = text_visible(str, x, y-FONTH, pixelwidth); break; }
- }
- cy--;
- break;
- case SDLK_DOWN:
- if(linewrap)
- {
- int x, y, width, height;
- char *str = currentline().text;
- text_pos(str, cx, x, y, pixelwidth);
- text_bounds(str, width, height, pixelwidth);
- y += FONTH;
- if(y < height) { cx = text_visible(str, x, y, pixelwidth); break; }
- }
- cy++;
- break;
- case -4:
- cy--;
- break;
- case -5:
- cy++;
- break;
- case SDLK_PAGEUP:
- cy-=pixelheight/FONTH;
- break;
- case SDLK_PAGEDOWN:
- cy+=pixelheight/FONTH;
- break;
- case SDLK_HOME:
- cx = cy = 0;
- break;
- case SDLK_END:
- cx = cy = INT_MAX;
- break;
- case SDLK_LEFT:
- cx--;
- break;
- case SDLK_RIGHT:
- cx++;
- break;
- case SDLK_DELETE:
- if(!del())
- {
- editline &current = currentline();
- if(cx < current.len) current.del(cx, 1);
- else if(cy < lines.length()-1)
- { //combine with next line
- current.append(lines[cy+1].text);
- removelines(cy+1, 1);
- }
- }
- break;
- case SDLK_BACKSPACE:
- if(!del())
- {
- editline &current = currentline();
- if(cx > 0) current.del(--cx, 1);
- else if(cy > 0)
- { //combine with previous line
- cx = lines[cy-1].len;
- lines[cy-1].append(current.text);
- removelines(cy--, 1);
- }
- }
- break;
- case SDLK_LSHIFT:
- case SDLK_RSHIFT:
- break;
- case SDLK_RETURN:
- insert('\n');
- break;
- case SDLK_TAB:
- insert('\t');
- break;
- }
- }
-
- void input(const char *str, int len)
- {
- loopi(len) insert(str[i]);
- }
-
- void hit(int hitx, int hity, bool dragged)
- {
- int maxwidth = linewrap?pixelwidth:-1;
- int h = 0;
- for(int i = scrolly; i < lines.length(); i++)
- {
- int width, height;
- text_bounds(lines[i].text, width, height, maxwidth);
- if(h + height > pixelheight) break;
-
- if(hity >= h && hity <= h+height)
- {
- int x = text_visible(lines[i].text, hitx, hity-h, maxwidth);
- if(dragged) { mx = x; my = i; } else { cx = x; cy = i; };
- break;
- }
- h+=height;
- }
- }
-
- int limitscrolly()
- {
- int maxwidth = linewrap?pixelwidth:-1;
- int slines = lines.length();
- for(int ph = pixelheight; slines > 0 && ph > 0;)
- {
- int width, height;
- text_bounds(lines[slines-1].text, width, height, maxwidth);
- if(height > ph) break;
- ph -= height;
- slines--;
- }
- return slines;
- }
-
- void draw(int x, int y, int color, bool hit)
- {
- int maxwidth = linewrap?pixelwidth:-1;
-
- int sx, sy, ex, ey;
- bool selection = region(sx, sy, ex, ey);
-
- // fix scrolly so that <cx, cy> is always on screen
- if(cy < scrolly) scrolly = cy;
- else
- {
- if(scrolly < 0) scrolly = 0;
- int h = 0;
- for(int i = cy; i >= scrolly; i--)
- {
- int width, height;
- text_bounds(lines[i].text, width, height, maxwidth);
- if(h + height > pixelheight) { scrolly = i+1; break; }
- h += height;
- }
- }
-
- if(selection)
- {
- // convert from cursor coords into pixel coords
- int psx, psy, pex, pey;
- text_pos(lines[sy].text, sx, psx, psy, maxwidth);
- text_pos(lines[ey].text, ex, pex, pey, maxwidth);
- int maxy = lines.length();
- int h = 0;
- for(int i = scrolly; i < maxy; i++)
- {
- int width, height;
- text_bounds(lines[i].text, width, height, maxwidth);
- if(h + height > pixelheight) { maxy = i; break; }
- if(i == sy) psy += h;
- if(i == ey) { pey += h; break; }
- h += height;
- }
- maxy--;
-
- if(ey >= scrolly && sy <= maxy)
- {
- // crop top/bottom within window
- if(sy < scrolly) { sy = scrolly; psy = 0; psx = 0; }
- if(ey > maxy) { ey = maxy; pey = pixelheight - FONTH; pex = pixelwidth; }
-
- hudnotextureshader->set();
- gle::colorub(0xA0, 0x80, 0x80);
- gle::defvertex(2);
- gle::begin(GL_QUADS);
- if(psy == pey)
- {
- gle::attribf(x+psx, y+psy);
- gle::attribf(x+pex, y+psy);
- gle::attribf(x+pex, y+pey+FONTH);
- gle::attribf(x+psx, y+pey+FONTH);
- }
- else
- { gle::attribf(x+psx, y+psy);
- gle::attribf(x+psx, y+psy+FONTH);
- gle::attribf(x+pixelwidth, y+psy+FONTH);
- gle::attribf(x+pixelwidth, y+psy);
- if(pey-psy > FONTH)
- {
- gle::attribf(x, y+psy+FONTH);
- gle::attribf(x+pixelwidth, y+psy+FONTH);
- gle::attribf(x+pixelwidth, y+pey);
- gle::attribf(x, y+pey);
- }
- gle::attribf(x, y+pey);
- gle::attribf(x, y+pey+FONTH);
- gle::attribf(x+pex, y+pey+FONTH);
- gle::attribf(x+pex, y+pey);
- }
- gle::end();
- hudshader->set();
- }
- }
-
- int h = 0;
- for(int i = scrolly; i < lines.length(); i++)
- {
- int width, height;
- text_bounds(lines[i].text, width, height, maxwidth);
- if(h + height > pixelheight) break;
-
- draw_text(lines[i].text, x, y+h, color>>16, (color>>8)&0xFF, color&0xFF, 0xFF, hit&&(cy==i)?cx:-1, maxwidth);
- if(linewrap && height > FONTH) // line wrap indicator
- {
- hudnotextureshader->set();
- gle::colorub(0x80, 0xA0, 0x80);
- gle::defvertex(2);
- gle::begin(GL_TRIANGLE_STRIP);
- gle::attribf(x, y+h+FONTH);
- gle::attribf(x, y+h+height);
- gle::attribf(x-FONTW/2, y+h+FONTH);
- gle::attribf(x-FONTW/2, y+h+height);
- gle::end();
- hudshader->set();
- }
- h+=height;
- }
- }
+ int mode; //editor mode - 1= keep while focused, 2= keep while used in gui, 3= keep forever (i.e. until mode changes)
+ bool active, rendered;
+ const char *name;
+ const char *filename;
+
+ int cx, cy; // cursor position - ensured to be valid after a region() or currentline()
+ int mx, my; // selection mark, mx=-1 if following cursor - avoid direct access, instead use region()
+ int maxx, maxy; // maxy=-1 if unlimited lines, 1 if single line editor
+
+ int scrolly; // vertical scroll offset
+
+ bool linewrap;
+ int pixelwidth; // required for up/down/hit/draw/bounds
+ int pixelheight; // -1 for variable sized, i.e. from bounds()
+
+ vector<editline> lines; // MUST always contain at least one line!
+
+ editor(const char *name, int mode, const char *initval) :
+ mode(mode), active(true), rendered(false), name(newstring(name)), filename(NULL),
+ cx(0), cy(0), mx(-1), maxx(-1), maxy(-1), scrolly(0), linewrap(false), pixelwidth(-1), pixelheight(-1)
+ {
+ //printf("editor %08x '%s'\n", this, name);
+ lines.add().set(initval ? initval : "");
+ }
+
+ ~editor()
+ {
+ //printf("~editor %08x '%s'\n", this, name);
+ DELETEA(name);
+ DELETEA(filename);
+ clear(NULL);
+ }
+
+ void clear(const char *init = "")
+ {
+ cx = cy = 0;
+ mark(false);
+ loopv(lines) lines[i].clear();
+ lines.shrink(0);
+ if(init) lines.add().set(init);
+ }
+
+ void setfile(const char *fname)
+ {
+ DELETEA(filename);
+ if(fname) filename = newstring(fname);
+ }
+
+ void load()
+ {
+ if(!filename) return;
+ clear(NULL);
+ stream *file = openutf8file(filename, "r");
+ if(file)
+ {
+ while(lines.add().read(file, maxx) && (maxy < 0 || lines.length() <= maxy));
+ lines.pop().clear();
+ delete file;
+ }
+ if(lines.empty()) lines.add().set("");
+ }
+
+ void save()
+ {
+ if(!filename) return;
+ stream *file = openutf8file(filename, "w");
+ if(!file) return;
+ loopv(lines) file->putline(lines[i].text);
+ delete file;
+ }
+
+ void mark(bool enable)
+ {
+ mx = (enable) ? cx : -1;
+ my = cy;
+ }
+
+ void selectall()
+ {
+ mx = my = INT_MAX;
+ cx = cy = 0;
+ }
+
+ // constrain results to within buffer - s=start, e=end, return true if a selection range
+ // also ensures that cy is always within lines[] and cx is valid
+ bool region(int &sx, int &sy, int &ex, int &ey)
+ {
+ int n = lines.length();
+ ASSERT(n != 0);
+ if(cy < 0) cy = 0; else if(cy >= n) cy = n-1;
+ int len = lines[cy].len;
+ if(cx < 0) cx = 0; else if(cx > len) cx = len;
+ if(mx >= 0)
+ {
+ if(my < 0) my = 0; else if(my >= n) my = n-1;
+ len = lines[my].len;
+ if(mx > len) mx = len;
+ }
+ sx = (mx >= 0) ? mx : cx;
+ sy = (mx >= 0) ? my : cy;
+ ex = cx;
+ ey = cy;
+ if(sy > ey) { swap(sy, ey); swap(sx, ex); }
+ else if(sy==ey && sx > ex) swap(sx, ex);
+ return (sx != ex) || (sy != ey);
+ }
+
+ bool region() { int sx, sy, ex, ey; return region(sx, sy, ex, ey); }
+
+ // also ensures that cy is always within lines[] and cx is valid
+ editline &currentline()
+ {
+ int n = lines.length();
+ ASSERT(n != 0);
+ if(cy < 0) cy = 0; else if(cy >= n) cy = n-1;
+ if(cx < 0) cx = 0; else if(cx > lines[cy].len) cx = lines[cy].len;
+ return lines[cy];
+ }
+
+ void copyselectionto(editor *b)
+ {
+ if(b==this) return;
+
+ b->clear(NULL);
+ int sx, sy, ex, ey;
+ region(sx, sy, ex, ey);
+ loopi(1+ey-sy)
+ {
+ if(b->maxy != -1 && b->lines.length() >= b->maxy) break;
+ int y = sy+i;
+ char *line = lines[y].text;
+ int len = lines[y].len;
+ if(y == sy && y == ey)
+ {
+ line += sx;
+ len = ex - sx;
+ }
+ else if(y == sy) line += sx;
+ else if(y == ey) len = ex;
+ b->lines.add().set(line, len);
+ }
+ if(b->lines.empty()) b->lines.add().set("");
+ }
+
+ char *tostring()
+ {
+ int len = 0;
+ loopv(lines) len += lines[i].len + 1;
+ char *str = newstring(len);
+ int offset = 0;
+ loopv(lines)
+ {
+ editline &l = lines[i];
+ memcpy(&str[offset], l.text, l.len);
+ offset += l.len;
+ str[offset++] = '\n';
+ }
+ str[offset] = '\0';
+ return str;
+ }
+
+ char *selectiontostring()
+ {
+ vector<char> buf;
+ int sx, sy, ex, ey;
+ region(sx, sy, ex, ey);
+ loopi(1+ey-sy)
+ {
+ int y = sy+i;
+ char *line = lines[y].text;
+ int len = lines[y].len;
+ if(y == sy && y == ey)
+ {
+ line += sx;
+ len = ex - sx;
+ }
+ else if(y == sy) line += sx;
+ else if(y == ey) len = ex;
+ buf.put(line, len);
+ buf.add('\n');
+ }
+ buf.add('\0');
+ return newstring(buf.getbuf(), buf.length()-1);
+ }
+
+ void removelines(int start, int count)
+ {
+ loopi(count) lines[start+i].clear();
+ lines.remove(start, count);
+ }
+
+ bool del() // removes the current selection (if any)
+ {
+ int sx, sy, ex, ey;
+ if(!region(sx, sy, ex, ey))
+ {
+ mark(false);
+ return false;
+ }
+ if(sy == ey)
+ {
+ if(sx == 0 && ex == lines[ey].len) removelines(sy, 1);
+ else lines[sy].del(sx, ex - sx);
+ }
+ else
+ {
+ if(ey > sy+1) { removelines(sy+1, ey-(sy+1)); ey = sy+1; }
+ if(ex == lines[ey].len) removelines(ey, 1); else lines[ey].del(0, ex);
+ if(sx == 0) removelines(sy, 1); else lines[sy].del(sx, lines[sy].len - sx);
+ }
+ if(lines.empty()) lines.add().set("");
+ mark(false);
+ cx = sx;
+ cy = sy;
+ editline &current = currentline();
+ if(cx >= current.len && cy < lines.length() - 1)
+ {
+ current.append(lines[cy+1].text);
+ removelines(cy + 1, 1);
+ }
+ return true;
+ }
+
+ void insert(char ch)
+ {
+ del();
+ editline &current = currentline();
+ if(ch == '\n')
+ {
+ if(maxy == -1 || cy < maxy-1)
+ {
+ editline newline(&current.text[cx]);
+ current.chop(cx);
+ cy = min(lines.length(), cy+1);
+ lines.insert(cy, newline);
+ }
+ else current.chop(cx);
+ cx = 0;
+ }
+ else
+ {
+ int len = current.len;
+ if(maxx >= 0 && len > maxx-1) len = maxx-1;
+ if(cx <= len) current.insert(&ch, cx++, 1);
+ }
+ }
+
+ void insert(const char *s)
+ {
+ while(*s) insert(*s++);
+ }
+
+ void insertallfrom(editor *b)
+ {
+ if(b==this) return;
+
+ del();
+
+ if(b->lines.length() == 1 || maxy == 1)
+ {
+ editline &current = currentline();
+ char *str = b->lines[0].text;
+ int slen = b->lines[0].len;
+ if(maxx >= 0 && b->lines[0].len + cx > maxx) slen = maxx-cx;
+ if(slen > 0)
+ {
+ int len = current.len;
+ if(maxx >= 0 && slen + cx + len > maxx) len = max(0, maxx-(cx+slen));
+ current.insert(str, cx, slen);
+ cx += slen;
+ }
+ }
+ else
+ {
+ loopv(b->lines)
+ {
+ if(!i)
+ {
+ lines[cy++].append(b->lines[i].text);
+ }
+ else if(i >= b->lines.length())
+ {
+ cx = b->lines[i].len;
+ lines[cy].prepend(b->lines[i].text);
+ }
+ else if(maxy < 0 || lines.length() < maxy) lines.insert(cy++, editline(b->lines[i].text));
+ }
+ }
+ }
+
+ void key(int code)
+ {
+ switch(code)
+ {
+ case SDLK_UP:
+ if(linewrap)
+ {
+ int x, y;
+ char *str = currentline().text;
+ text_pos(str, cx+1, x, y, pixelwidth);
+ if(y > 0) { cx = text_visible(str, x, y-FONTH, pixelwidth); break; }
+ }
+ cy--;
+ break;
+ case SDLK_DOWN:
+ if(linewrap)
+ {
+ int x, y, width, height;
+ char *str = currentline().text;
+ text_pos(str, cx, x, y, pixelwidth);
+ text_bounds(str, width, height, pixelwidth);
+ y += FONTH;
+ if(y < height) { cx = text_visible(str, x, y, pixelwidth); break; }
+ }
+ cy++;
+ break;
+ case -4:
+ cy--;
+ break;
+ case -5:
+ cy++;
+ break;
+ case SDLK_PAGEUP:
+ cy-=pixelheight/FONTH;
+ break;
+ case SDLK_PAGEDOWN:
+ cy+=pixelheight/FONTH;
+ break;
+ case SDLK_HOME:
+ cx = cy = 0;
+ break;
+ case SDLK_END:
+ cx = cy = INT_MAX;
+ break;
+ case SDLK_LEFT:
+ cx--;
+ break;
+ case SDLK_RIGHT:
+ cx++;
+ break;
+ case SDLK_DELETE:
+ if(!del())
+ {
+ editline &current = currentline();
+ if(cx < current.len) current.del(cx, 1);
+ else if(cy < lines.length()-1)
+ { //combine with next line
+ current.append(lines[cy+1].text);
+ removelines(cy+1, 1);
+ }
+ }
+ break;
+ case SDLK_BACKSPACE:
+ if(!del())
+ {
+ editline &current = currentline();
+ if(cx > 0) current.del(--cx, 1);
+ else if(cy > 0)
+ { //combine with previous line
+ cx = lines[cy-1].len;
+ lines[cy-1].append(current.text);
+ removelines(cy--, 1);
+ }
+ }
+ break;
+ case SDLK_LSHIFT:
+ case SDLK_RSHIFT:
+ break;
+ case SDLK_RETURN:
+ insert('\n');
+ break;
+ case SDLK_TAB:
+ insert('\t');
+ break;
+ }
+ }
+
+ void input(const char *str, int len)
+ {
+ loopi(len) insert(str[i]);
+ }
+
+ void hit(int hitx, int hity, bool dragged)
+ {
+ int maxwidth = linewrap?pixelwidth:-1;
+ int h = 0;
+ for(int i = scrolly; i < lines.length(); i++)
+ {
+ int width, height;
+ text_bounds(lines[i].text, width, height, maxwidth);
+ if(h + height > pixelheight) break;
+
+ if(hity >= h && hity <= h+height)
+ {
+ int x = text_visible(lines[i].text, hitx, hity-h, maxwidth);
+ if(dragged) { mx = x; my = i; } else { cx = x; cy = i; };
+ break;
+ }
+ h+=height;
+ }
+ }
+
+ int limitscrolly()
+ {
+ int maxwidth = linewrap?pixelwidth:-1;
+ int slines = lines.length();
+ for(int ph = pixelheight; slines > 0 && ph > 0;)
+ {
+ int width, height;
+ text_bounds(lines[slines-1].text, width, height, maxwidth);
+ if(height > ph) break;
+ ph -= height;
+ slines--;
+ }
+ return slines;
+ }
+
+ void draw(int x, int y, int color, bool hit)
+ {
+ int maxwidth = linewrap?pixelwidth:-1;
+
+ int sx, sy, ex, ey;
+ bool selection = region(sx, sy, ex, ey);
+
+ // fix scrolly so that <cx, cy> is always on screen
+ if(cy < scrolly) scrolly = cy;
+ else
+ {
+ if(scrolly < 0) scrolly = 0;
+ int h = 0;
+ for(int i = cy; i >= scrolly; i--)
+ {
+ int width, height;
+ text_bounds(lines[i].text, width, height, maxwidth);
+ if(h + height > pixelheight) { scrolly = i+1; break; }
+ h += height;
+ }
+ }
+
+ if(selection)
+ {
+ // convert from cursor coords into pixel coords
+ int psx, psy, pex, pey;
+ text_pos(lines[sy].text, sx, psx, psy, maxwidth);
+ text_pos(lines[ey].text, ex, pex, pey, maxwidth);
+ int maxy = lines.length();
+ int h = 0;
+ for(int i = scrolly; i < maxy; i++)
+ {
+ int width, height;
+ text_bounds(lines[i].text, width, height, maxwidth);
+ if(h + height > pixelheight) { maxy = i; break; }
+ if(i == sy) psy += h;
+ if(i == ey) { pey += h; break; }
+ h += height;
+ }
+ maxy--;
+
+ if(ey >= scrolly && sy <= maxy)
+ {
+ // crop top/bottom within window
+ if(sy < scrolly) { sy = scrolly; psy = 0; psx = 0; }
+ if(ey > maxy) { ey = maxy; pey = pixelheight - FONTH; pex = pixelwidth; }
+
+ hudnotextureshader->set();
+ gle::colorub(0xA0, 0x80, 0x80);
+ gle::defvertex(2);
+ gle::begin(GL_QUADS);
+ if(psy == pey)
+ {
+ gle::attribf(x+psx, y+psy);
+ gle::attribf(x+pex, y+psy);
+ gle::attribf(x+pex, y+pey+FONTH);
+ gle::attribf(x+psx, y+pey+FONTH);
+ }
+ else
+ { gle::attribf(x+psx, y+psy);
+ gle::attribf(x+psx, y+psy+FONTH);
+ gle::attribf(x+pixelwidth, y+psy+FONTH);
+ gle::attribf(x+pixelwidth, y+psy);
+ if(pey-psy > FONTH)
+ {
+ gle::attribf(x, y+psy+FONTH);
+ gle::attribf(x+pixelwidth, y+psy+FONTH);
+ gle::attribf(x+pixelwidth, y+pey);
+ gle::attribf(x, y+pey);
+ }
+ gle::attribf(x, y+pey);
+ gle::attribf(x, y+pey+FONTH);
+ gle::attribf(x+pex, y+pey+FONTH);
+ gle::attribf(x+pex, y+pey);
+ }
+ gle::end();
+ hudshader->set();
+ }
+ }
+
+ int h = 0;
+ for(int i = scrolly; i < lines.length(); i++)
+ {
+ int width, height;
+ text_bounds(lines[i].text, width, height, maxwidth);
+ if(h + height > pixelheight) break;
+
+ draw_text(lines[i].text, x, y+h, color>>16, (color>>8)&0xFF, color&0xFF, 0xFF, hit&&(cy==i)?cx:-1, maxwidth);
+ if(linewrap && height > FONTH) // line wrap indicator
+ {
+ hudnotextureshader->set();
+ gle::colorub(0x80, 0xA0, 0x80);
+ gle::defvertex(2);
+ gle::begin(GL_TRIANGLE_STRIP);
+ gle::attribf(x, y+h+FONTH);
+ gle::attribf(x, y+h+height);
+ gle::attribf(x-FONTW/2, y+h+FONTH);
+ gle::attribf(x-FONTW/2, y+h+height);
+ gle::end();
+ hudshader->set();
+ }
+ h+=height;
+ }
+ }
};
// a 'stack' where the last is the current focused editor
@@ -669,85 +668,85 @@ static editor *currentfocus() { return editors.length() ? editors.last() : NULL;
static void readyeditors()
{
- loopv(editors) editors[i]->active = (editors[i]->mode==EDITORFOREVER);
+ loopv(editors) editors[i]->active = (editors[i]->mode==EDITORFOREVER);
}
static void flusheditors()
{
- loopvrev(editors) if(!editors[i]->active)
- {
- editor *e = editors.remove(i);
- DELETEP(e);
- }
+ loopvrev(editors) if(!editors[i]->active)
+ {
+ editor *e = editors.remove(i);
+ DELETEP(e);
+ }
}
static editor *useeditor(const char *name, int mode, bool focus, const char *initval = NULL)
{
- loopv(editors) if(strcmp(editors[i]->name, name) == 0)
- {
- editor *e = editors[i];
- if(focus) { editors.add(e); editors.remove(i); } // re-position as last
- e->active = true;
- return e;
- }
- editor *e = new editor(name, mode, initval);
- if(focus) editors.add(e); else editors.insert(0, e);
- return e;
+ loopv(editors) if(strcmp(editors[i]->name, name) == 0)
+ {
+ editor *e = editors[i];
+ if(focus) { editors.add(e); editors.remove(i); } // re-position as last
+ e->active = true;
+ return e;
+ }
+ editor *e = new editor(name, mode, initval);
+ if(focus) editors.add(e); else editors.insert(0, e);
+ return e;
}
#define TEXTCOMMAND(f, s, d, body) ICOMMAND(f, s, d,\
- editor *top = currentfocus();\
- if(!top || identflags&IDF_OVERRIDDEN) return;\
- body\
+ editor *top = currentfocus();\
+ if(!top || identflags&IDF_OVERRIDDEN) return;\
+ body\
)
ICOMMAND(textlist, "", (), // @DEBUG return list of all the editors
- vector<char> s;
- loopv(editors)
- {
- if(i > 0) s.put(", ", 2);
- s.put(editors[i]->name, strlen(editors[i]->name));
- }
- s.add('\0');
- result(s.getbuf());
+ vector<char> s;
+ loopv(editors)
+ {
+ if(i > 0) s.put(", ", 2);
+ s.put(editors[i]->name, strlen(editors[i]->name));
+ }
+ s.add('\0');
+ result(s.getbuf());
);
TEXTCOMMAND(textshow, "", (), // @DEBUG return the start of the buffer
- editline line;
- line.combinelines(top->lines);
- result(line.text);
- line.clear();
+ editline line;
+ line.combinelines(top->lines);
+ result(line.text);
+ line.clear();
);
ICOMMAND(textfocus, "si", (char *name, int *mode), // focus on a (or create a persistent) specific editor, else returns current name
- if(*name) useeditor(name, *mode<=0 ? EDITORFOREVER : *mode, true);
- else if(editors.length() > 0) result(editors.last()->name);
+ if(*name) useeditor(name, *mode<=0 ? EDITORFOREVER : *mode, true);
+ else if(editors.length() > 0) result(editors.last()->name);
);
TEXTCOMMAND(textprev, "", (), editors.insert(0, top); editors.pop();); // return to the previous editor
TEXTCOMMAND(textmode, "i", (int *m), // (1= keep while focused, 2= keep while used in gui, 3= keep forever (i.e. until mode changes)) topmost editor, return current setting if no args
- if(*m) top->mode = *m;
- else intret(top->mode);
+ if(*m) top->mode = *m;
+ else intret(top->mode);
);
TEXTCOMMAND(textsave, "s", (char *file), // saves the topmost (filename is optional)
- if(*file) top->setfile(path(file, true));
- top->save();
+ if(*file) top->setfile(path(file, true));
+ top->save();
);
TEXTCOMMAND(textload, "s", (char *file), // loads into the topmost editor, returns filename if no args
- if(*file)
- {
- top->setfile(path(file, true));
- top->load();
- }
- else if(top->filename) result(top->filename);
+ if(*file)
+ {
+ top->setfile(path(file, true));
+ top->load();
+ }
+ else if(top->filename) result(top->filename);
);
TEXTCOMMAND(textinit, "sss", (char *name, char *file, char *initval), // loads into named editor if no file assigned and editor has been rendered
{
- editor *e = NULL;
- loopv(editors) if(!strcmp(editors[i]->name, name)) { e = editors[i]; break; }
- if(e && e->rendered && !e->filename && *file && (e->lines.empty() || (e->lines.length() == 1 && !strcmp(e->lines[0].text, initval))))
- {
- e->setfile(path(file, true));
- e->load();
- }
+ editor *e = NULL;
+ loopv(editors) if(!strcmp(editors[i]->name, name)) { e = editors[i]; break; }
+ if(e && e->rendered && !e->filename && *file && (e->lines.empty() || (e->lines.length() == 1 && !strcmp(e->lines[0].text, initval))))
+ {
+ e->setfile(path(file, true));
+ e->load();
+ }
});
#define PASTEBUFFER "#pastebuffer"
@@ -755,16 +754,16 @@ TEXTCOMMAND(textinit, "sss", (char *name, char *file, char *initval), // loads i
TEXTCOMMAND(textcopy, "", (), editor *b = useeditor(PASTEBUFFER, EDITORFOREVER, false); top->copyselectionto(b););
TEXTCOMMAND(textpaste, "", (), editor *b = useeditor(PASTEBUFFER, EDITORFOREVER, false); top->insertallfrom(b););
TEXTCOMMAND(textmark, "i", (int *m), // (1=mark, 2=unmark), return current mark setting if no args
- if(*m) top->mark(*m==1);
- else intret(top->region() ? 1 : 2);
+ if(*m) top->mark(*m==1);
+ else intret(top->region() ? 1 : 2);
);
TEXTCOMMAND(textselectall, "", (), top->selectall(););
TEXTCOMMAND(textclear, "", (), top->clear(););
TEXTCOMMAND(textcurrentline, "", (), result(top->currentline().text););
TEXTCOMMAND(textexec, "i", (int *selected), // execute script commands from the buffer (0=all, 1=selected region only)
- char *script = *selected ? top->selectiontostring() : top->tostring();
- execute(script);
- delete[] script;
+ char *script = *selected ? top->selectiontostring() : top->tostring();
+ execute(script);
+ delete[] script;
);
diff --git a/src/engine/texture.cpp b/src/engine/texture.cpp
index e76b59e..6a0084d 100644
--- a/src/engine/texture.cpp
+++ b/src/engine/texture.cpp
@@ -5,611 +5,611 @@
#ifndef SDL_IMAGE_VERSION_ATLEAST
#define SDL_IMAGE_VERSION_ATLEAST(X, Y, Z) \
- (SDL_VERSIONNUM(SDL_IMAGE_MAJOR_VERSION, SDL_IMAGE_MINOR_VERSION, SDL_IMAGE_PATCHLEVEL) >= SDL_VERSIONNUM(X, Y, Z))
+ (SDL_VERSIONNUM(SDL_IMAGE_MAJOR_VERSION, SDL_IMAGE_MINOR_VERSION, SDL_IMAGE_PATCHLEVEL) >= SDL_VERSIONNUM(X, Y, Z))
#endif
template<int BPP> static void halvetexture(uchar * RESTRICT src, uint sw, uint sh, uint stride, uchar * RESTRICT dst)
{
- for(uchar *yend = &src[sh*stride]; src < yend;)
- {
- for(uchar *xend = &src[sw*BPP], *xsrc = src; xsrc < xend; xsrc += 2*BPP, dst += BPP)
- {
- loopi(BPP) dst[i] = (uint(xsrc[i]) + uint(xsrc[i+BPP]) + uint(xsrc[stride+i]) + uint(xsrc[stride+i+BPP]))>>2;
- }
- src += 2*stride;
- }
+ for(uchar *yend = &src[sh*stride]; src < yend;)
+ {
+ for(uchar *xend = &src[sw*BPP], *xsrc = src; xsrc < xend; xsrc += 2*BPP, dst += BPP)
+ {
+ loopi(BPP) dst[i] = (uint(xsrc[i]) + uint(xsrc[i+BPP]) + uint(xsrc[stride+i]) + uint(xsrc[stride+i+BPP]))>>2;
+ }
+ src += 2*stride;
+ }
}
template<int BPP> static void shifttexture(uchar * RESTRICT src, uint sw, uint sh, uint stride, uchar * RESTRICT dst, uint dw, uint dh)
{
- uint wfrac = sw/dw, hfrac = sh/dh, wshift = 0, hshift = 0;
- while(dw<<wshift < sw) wshift++;
- while(dh<<hshift < sh) hshift++;
- uint tshift = wshift + hshift;
- for(uchar *yend = &src[sh*stride]; src < yend;)
- {
- for(uchar *xend = &src[sw*BPP], *xsrc = src; xsrc < xend; xsrc += wfrac*BPP, dst += BPP)
- {
- uint t[BPP] = {0};
- for(uchar *ycur = xsrc, *xend = &ycur[wfrac*BPP], *yend = &src[hfrac*stride];
- ycur < yend;
- ycur += stride, xend += stride)
- {
- for(uchar *xcur = ycur; xcur < xend; xcur += BPP)
- loopi(BPP) t[i] += xcur[i];
- }
- loopi(BPP) dst[i] = t[i] >> tshift;
- }
- src += hfrac*stride;
- }
+ uint wfrac = sw/dw, hfrac = sh/dh, wshift = 0, hshift = 0;
+ while(dw<<wshift < sw) wshift++;
+ while(dh<<hshift < sh) hshift++;
+ uint tshift = wshift + hshift;
+ for(uchar *yend = &src[sh*stride]; src < yend;)
+ {
+ for(uchar *xend = &src[sw*BPP], *xsrc = src; xsrc < xend; xsrc += wfrac*BPP, dst += BPP)
+ {
+ uint t[BPP] = {0};
+ for(uchar *ycur = xsrc, *xend = &ycur[wfrac*BPP], *yend = &src[hfrac*stride];
+ ycur < yend;
+ ycur += stride, xend += stride)
+ {
+ for(uchar *xcur = ycur; xcur < xend; xcur += BPP)
+ loopi(BPP) t[i] += xcur[i];
+ }
+ loopi(BPP) dst[i] = t[i] >> tshift;
+ }
+ src += hfrac*stride;
+ }
}
template<int BPP> static void scaletexture(uchar * RESTRICT src, uint sw, uint sh, uint stride, uchar * RESTRICT dst, uint dw, uint dh)
{
- uint wfrac = (sw<<12)/dw, hfrac = (sh<<12)/dh, darea = dw*dh, sarea = sw*sh;
- int over, under;
- for(over = 0; (darea>>over) > sarea; over++);
- for(under = 0; (darea<<under) < sarea; under++);
- uint cscale = clamp(under, over - 12, 12),
- ascale = clamp(12 + under - over, 0, 24),
- dscale = ascale + 12 - cscale,
- area = ((ullong)darea<<ascale)/sarea;
- dw *= wfrac;
- dh *= hfrac;
- for(uint y = 0; y < dh; y += hfrac)
- {
- const uint yn = y + hfrac - 1, yi = y>>12, h = (yn>>12) - yi, ylow = ((yn|(-int(h)>>24))&0xFFFU) + 1 - (y&0xFFFU), yhigh = (yn&0xFFFU) + 1;
- const uchar *ysrc = &src[yi*stride];
- for(uint x = 0; x < dw; x += wfrac, dst += BPP)
- {
- const uint xn = x + wfrac - 1, xi = x>>12, w = (xn>>12) - xi, xlow = ((w+0xFFFU)&0x1000U) - (x&0xFFFU), xhigh = (xn&0xFFFU) + 1;
- const uchar *xsrc = &ysrc[xi*BPP], *xend = &xsrc[w*BPP];
- uint t[BPP] = {0};
- for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP)
- loopi(BPP) t[i] += xcur[i];
- loopi(BPP) t[i] = (ylow*(t[i] + ((xsrc[i]*xlow + xend[i]*xhigh)>>12)))>>cscale;
- if(h)
- {
- xsrc += stride;
- xend += stride;
- for(uint hcur = h; --hcur; xsrc += stride, xend += stride)
- {
- uint c[BPP] = {0};
- for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP)
- loopi(BPP) c[i] += xcur[i];
- loopi(BPP) t[i] += ((c[i]<<12) + xsrc[i]*xlow + xend[i]*xhigh)>>cscale;
- }
- uint c[BPP] = {0};
- for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP)
- loopi(BPP) c[i] += xcur[i];
- loopi(BPP) t[i] += (yhigh*(c[i] + ((xsrc[i]*xlow + xend[i]*xhigh)>>12)))>>cscale;
- }
- loopi(BPP) dst[i] = (t[i] * area)>>dscale;
- }
- }
+ uint wfrac = (sw<<12)/dw, hfrac = (sh<<12)/dh, darea = dw*dh, sarea = sw*sh;
+ int over, under;
+ for(over = 0; (darea>>over) > sarea; over++);
+ for(under = 0; (darea<<under) < sarea; under++);
+ uint cscale = clamp(under, over - 12, 12),
+ ascale = clamp(12 + under - over, 0, 24),
+ dscale = ascale + 12 - cscale,
+ area = ((ullong)darea<<ascale)/sarea;
+ dw *= wfrac;
+ dh *= hfrac;
+ for(uint y = 0; y < dh; y += hfrac)
+ {
+ const uint yn = y + hfrac - 1, yi = y>>12, h = (yn>>12) - yi, ylow = ((yn|(-int(h)>>24))&0xFFFU) + 1 - (y&0xFFFU), yhigh = (yn&0xFFFU) + 1;
+ const uchar *ysrc = &src[yi*stride];
+ for(uint x = 0; x < dw; x += wfrac, dst += BPP)
+ {
+ const uint xn = x + wfrac - 1, xi = x>>12, w = (xn>>12) - xi, xlow = ((w+0xFFFU)&0x1000U) - (x&0xFFFU), xhigh = (xn&0xFFFU) + 1;
+ const uchar *xsrc = &ysrc[xi*BPP], *xend = &xsrc[w*BPP];
+ uint t[BPP] = {0};
+ for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP)
+ loopi(BPP) t[i] += xcur[i];
+ loopi(BPP) t[i] = (ylow*(t[i] + ((xsrc[i]*xlow + xend[i]*xhigh)>>12)))>>cscale;
+ if(h)
+ {
+ xsrc += stride;
+ xend += stride;
+ for(uint hcur = h; --hcur; xsrc += stride, xend += stride)
+ {
+ uint c[BPP] = {0};
+ for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP)
+ loopi(BPP) c[i] += xcur[i];
+ loopi(BPP) t[i] += ((c[i]<<12) + xsrc[i]*xlow + xend[i]*xhigh)>>cscale;
+ }
+ uint c[BPP] = {0};
+ for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP)
+ loopi(BPP) c[i] += xcur[i];
+ loopi(BPP) t[i] += (yhigh*(c[i] + ((xsrc[i]*xlow + xend[i]*xhigh)>>12)))>>cscale;
+ }
+ loopi(BPP) dst[i] = (t[i] * area)>>dscale;
+ }
+ }
}
static void scaletexture(uchar * RESTRICT src, uint sw, uint sh, uint bpp, uint pitch, uchar * RESTRICT dst, uint dw, uint dh)
{
- if(sw == dw*2 && sh == dh*2)
- {
- switch(bpp)
- {
- case 1: return halvetexture<1>(src, sw, sh, pitch, dst);
- case 2: return halvetexture<2>(src, sw, sh, pitch, dst);
- case 3: return halvetexture<3>(src, sw, sh, pitch, dst);
- case 4: return halvetexture<4>(src, sw, sh, pitch, dst);
- }
- }
- else if(sw < dw || sh < dh || sw&(sw-1) || sh&(sh-1) || dw&(dw-1) || dh&(dh-1))
- {
- switch(bpp)
- {
- case 1: return scaletexture<1>(src, sw, sh, pitch, dst, dw, dh);
- case 2: return scaletexture<2>(src, sw, sh, pitch, dst, dw, dh);
- case 3: return scaletexture<3>(src, sw, sh, pitch, dst, dw, dh);
- case 4: return scaletexture<4>(src, sw, sh, pitch, dst, dw, dh);
- }
- }
- else
- {
- switch(bpp)
- {
- case 1: return shifttexture<1>(src, sw, sh, pitch, dst, dw, dh);
- case 2: return shifttexture<2>(src, sw, sh, pitch, dst, dw, dh);
- case 3: return shifttexture<3>(src, sw, sh, pitch, dst, dw, dh);
- case 4: return shifttexture<4>(src, sw, sh, pitch, dst, dw, dh);
- }
- }
+ if(sw == dw*2 && sh == dh*2)
+ {
+ switch(bpp)
+ {
+ case 1: return halvetexture<1>(src, sw, sh, pitch, dst);
+ case 2: return halvetexture<2>(src, sw, sh, pitch, dst);
+ case 3: return halvetexture<3>(src, sw, sh, pitch, dst);
+ case 4: return halvetexture<4>(src, sw, sh, pitch, dst);
+ }
+ }
+ else if(sw < dw || sh < dh || sw&(sw-1) || sh&(sh-1) || dw&(dw-1) || dh&(dh-1))
+ {
+ switch(bpp)
+ {
+ case 1: return scaletexture<1>(src, sw, sh, pitch, dst, dw, dh);
+ case 2: return scaletexture<2>(src, sw, sh, pitch, dst, dw, dh);
+ case 3: return scaletexture<3>(src, sw, sh, pitch, dst, dw, dh);
+ case 4: return scaletexture<4>(src, sw, sh, pitch, dst, dw, dh);
+ }
+ }
+ else
+ {
+ switch(bpp)
+ {
+ case 1: return shifttexture<1>(src, sw, sh, pitch, dst, dw, dh);
+ case 2: return shifttexture<2>(src, sw, sh, pitch, dst, dw, dh);
+ case 3: return shifttexture<3>(src, sw, sh, pitch, dst, dw, dh);
+ case 4: return shifttexture<4>(src, sw, sh, pitch, dst, dw, dh);
+ }
+ }
}
static void reorientnormals(uchar * RESTRICT src, int sw, int sh, int bpp, int stride, uchar * RESTRICT dst, bool flipx, bool flipy, bool swapxy)
{
- int stridex = bpp, stridey = bpp;
- if(swapxy) stridex *= sh; else stridey *= sw;
- if(flipx) { dst += (sw-1)*stridex; stridex = -stridex; }
- if(flipy) { dst += (sh-1)*stridey; stridey = -stridey; }
- uchar *srcrow = src;
- loopi(sh)
- {
- for(uchar *curdst = dst, *src = srcrow, *end = &srcrow[sw*bpp]; src < end;)
- {
- uchar nx = *src++, ny = *src++;
- if(flipx) nx = 255-nx;
- if(flipy) ny = 255-ny;
- if(swapxy) swap(nx, ny);
- curdst[0] = nx;
- curdst[1] = ny;
- curdst[2] = *src++;
- if(bpp > 3) curdst[3] = *src++;
- curdst += stridex;
- }
- srcrow += stride;
- dst += stridey;
- }
+ int stridex = bpp, stridey = bpp;
+ if(swapxy) stridex *= sh; else stridey *= sw;
+ if(flipx) { dst += (sw-1)*stridex; stridex = -stridex; }
+ if(flipy) { dst += (sh-1)*stridey; stridey = -stridey; }
+ uchar *srcrow = src;
+ loopi(sh)
+ {
+ for(uchar *curdst = dst, *src = srcrow, *end = &srcrow[sw*bpp]; src < end;)
+ {
+ uchar nx = *src++, ny = *src++;
+ if(flipx) nx = 255-nx;
+ if(flipy) ny = 255-ny;
+ if(swapxy) swap(nx, ny);
+ curdst[0] = nx;
+ curdst[1] = ny;
+ curdst[2] = *src++;
+ if(bpp > 3) curdst[3] = *src++;
+ curdst += stridex;
+ }
+ srcrow += stride;
+ dst += stridey;
+ }
}
template<int BPP>
static inline void reorienttexture(uchar * RESTRICT src, int sw, int sh, int stride, uchar * RESTRICT dst, bool flipx, bool flipy, bool swapxy)
{
- int stridex = BPP, stridey = BPP;
- if(swapxy) stridex *= sh; else stridey *= sw;
- if(flipx) { dst += (sw-1)*stridex; stridex = -stridex; }
- if(flipy) { dst += (sh-1)*stridey; stridey = -stridey; }
- uchar *srcrow = src;
- loopi(sh)
- {
- for(uchar *curdst = dst, *src = srcrow, *end = &srcrow[sw*BPP]; src < end;)
- {
- loopk(BPP) curdst[k] = *src++;
- curdst += stridex;
- }
- srcrow += stride;
- dst += stridey;
- }
+ int stridex = BPP, stridey = BPP;
+ if(swapxy) stridex *= sh; else stridey *= sw;
+ if(flipx) { dst += (sw-1)*stridex; stridex = -stridex; }
+ if(flipy) { dst += (sh-1)*stridey; stridey = -stridey; }
+ uchar *srcrow = src;
+ loopi(sh)
+ {
+ for(uchar *curdst = dst, *src = srcrow, *end = &srcrow[sw*BPP]; src < end;)
+ {
+ loopk(BPP) curdst[k] = *src++;
+ curdst += stridex;
+ }
+ srcrow += stride;
+ dst += stridey;
+ }
}
static void reorienttexture(uchar * RESTRICT src, int sw, int sh, int bpp, int stride, uchar * RESTRICT dst, bool flipx, bool flipy, bool swapxy)
{
- switch(bpp)
- {
- case 1: return reorienttexture<1>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
- case 2: return reorienttexture<2>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
- case 3: return reorienttexture<3>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
- case 4: return reorienttexture<4>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
- }
+ switch(bpp)
+ {
+ case 1: return reorienttexture<1>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
+ case 2: return reorienttexture<2>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
+ case 3: return reorienttexture<3>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
+ case 4: return reorienttexture<4>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
+ }
}
static void reorients3tc(GLenum format, int blocksize, int w, int h, uchar *src, uchar *dst, bool flipx, bool flipy, bool swapxy, bool normals = false)
{
- int bx1 = 0, by1 = 0, bx2 = min(w, 4), by2 = min(h, 4), bw = (w+3)/4, bh = (h+3)/4, stridex = blocksize, stridey = blocksize;
- if(swapxy) stridex *= bw; else stridey *= bh;
- if(flipx) { dst += (bw-1)*stridex; stridex = -stridex; bx1 += 4-bx2; bx2 = 4; }
- if(flipy) { dst += (bh-1)*stridey; stridey = -stridey; by1 += 4-by2; by2 = 4; }
- loopi(bh)
- {
- for(uchar *curdst = dst, *end = &src[bw*blocksize]; src < end; src += blocksize, curdst += stridex)
- {
- if(format == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT)
- {
- ullong salpha = lilswap(*(const ullong *)src), dalpha = 0;
- uint xmask = flipx ? 15 : 0, ymask = flipy ? 15 : 0, xshift = 2, yshift = 4;
- if(swapxy) swap(xshift, yshift);
- for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++)
- {
- dalpha |= ((salpha&15) << (((xmask^x)<<xshift) + ((ymask^y)<<yshift)));
- salpha >>= 4;
- }
- *(ullong *)curdst = lilswap(dalpha);
- src += 8;
- curdst += 8;
- }
- else if(format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
- {
- uchar alpha1 = src[0], alpha2 = src[1];
- ullong salpha = lilswap(*(const ushort *)&src[2]) + ((ullong)lilswap(*(const uint *)&src[4]) << 16), dalpha = 0;
- uint xmask = flipx ? 7 : 0, ymask = flipy ? 7 : 0, xshift = 0, yshift = 2;
- if(swapxy) swap(xshift, yshift);
- for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++)
- {
- dalpha |= ((salpha&7) << (3*((xmask^x)<<xshift) + ((ymask^y)<<yshift)));
- salpha >>= 3;
- }
- curdst[0] = alpha1;
- curdst[1] = alpha2;
- *(ushort *)&curdst[2] = lilswap(ushort(dalpha));
- *(ushort *)&curdst[4] = lilswap(ushort(dalpha>>16));
- *(ushort *)&curdst[6] = lilswap(ushort(dalpha>>32));
- src += 8;
- curdst += 8;
- }
-
- ushort color1 = lilswap(*(const ushort *)src), color2 = lilswap(*(const ushort *)&src[2]);
- uint sbits = lilswap(*(const uint *)&src[4]);
- if(normals)
- {
- ushort ncolor1 = color1, ncolor2 = color2;
- if(flipx)
- {
- ncolor1 = (ncolor1 & ~0xF800) | (0xF800 - (ncolor1 & 0xF800));
- ncolor2 = (ncolor2 & ~0xF800) | (0xF800 - (ncolor2 & 0xF800));
- }
- if(flipy)
- {
- ncolor1 = (ncolor1 & ~0x7E0) | (0x7E0 - (ncolor1 & 0x7E0));
- ncolor2 = (ncolor2 & ~0x7E0) | (0x7E0 - (ncolor2 & 0x7E0));
- }
- if(swapxy)
- {
- ncolor1 = (ncolor1 & 0x1F) | (((((ncolor1 >> 11) & 0x1F) * 0x3F) / 0x1F) << 5) | (((((ncolor1 >> 5) & 0x3F) * 0x1F) / 0x3F) << 11);
- ncolor2 = (ncolor2 & 0x1F) | (((((ncolor2 >> 11) & 0x1F) * 0x3F) / 0x1F) << 5) | (((((ncolor2 >> 5) & 0x3F) * 0x1F) / 0x3F) << 11);
- }
- if(color1 <= color2 && ncolor1 > ncolor2) { color1 = ncolor2; color2 = ncolor1; }
- else { color1 = ncolor1; color2 = ncolor2; }
- }
- uint dbits = 0, xmask = flipx ? 3 : 0, ymask = flipy ? 3 : 0, xshift = 1, yshift = 3;
- if(swapxy) swap(xshift, yshift);
- for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++)
- {
- dbits |= ((sbits&3) << (((xmask^x)<<xshift) + ((ymask^y)<<yshift)));
- sbits >>= 2;
- }
- *(ushort *)curdst = lilswap(color1);
- *(ushort *)&curdst[2] = lilswap(color2);
- *(uint *)&curdst[4] = lilswap(dbits);
-
- if(blocksize > 8) { src -= 8; curdst -= 8; }
- }
- dst += stridey;
- }
+ int bx1 = 0, by1 = 0, bx2 = min(w, 4), by2 = min(h, 4), bw = (w+3)/4, bh = (h+3)/4, stridex = blocksize, stridey = blocksize;
+ if(swapxy) stridex *= bw; else stridey *= bh;
+ if(flipx) { dst += (bw-1)*stridex; stridex = -stridex; bx1 += 4-bx2; bx2 = 4; }
+ if(flipy) { dst += (bh-1)*stridey; stridey = -stridey; by1 += 4-by2; by2 = 4; }
+ loopi(bh)
+ {
+ for(uchar *curdst = dst, *end = &src[bw*blocksize]; src < end; src += blocksize, curdst += stridex)
+ {
+ if(format == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT)
+ {
+ ullong salpha = lilswap(*(const ullong *)src), dalpha = 0;
+ uint xmask = flipx ? 15 : 0, ymask = flipy ? 15 : 0, xshift = 2, yshift = 4;
+ if(swapxy) swap(xshift, yshift);
+ for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++)
+ {
+ dalpha |= ((salpha&15) << (((xmask^x)<<xshift) + ((ymask^y)<<yshift)));
+ salpha >>= 4;
+ }
+ *(ullong *)curdst = lilswap(dalpha);
+ src += 8;
+ curdst += 8;
+ }
+ else if(format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
+ {
+ uchar alpha1 = src[0], alpha2 = src[1];
+ ullong salpha = lilswap(*(const ushort *)&src[2]) + ((ullong)lilswap(*(const uint *)&src[4]) << 16), dalpha = 0;
+ uint xmask = flipx ? 7 : 0, ymask = flipy ? 7 : 0, xshift = 0, yshift = 2;
+ if(swapxy) swap(xshift, yshift);
+ for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++)
+ {
+ dalpha |= ((salpha&7) << (3*((xmask^x)<<xshift) + ((ymask^y)<<yshift)));
+ salpha >>= 3;
+ }
+ curdst[0] = alpha1;
+ curdst[1] = alpha2;
+ *(ushort *)&curdst[2] = lilswap(ushort(dalpha));
+ *(ushort *)&curdst[4] = lilswap(ushort(dalpha>>16));
+ *(ushort *)&curdst[6] = lilswap(ushort(dalpha>>32));
+ src += 8;
+ curdst += 8;
+ }
+
+ ushort color1 = lilswap(*(const ushort *)src), color2 = lilswap(*(const ushort *)&src[2]);
+ uint sbits = lilswap(*(const uint *)&src[4]);
+ if(normals)
+ {
+ ushort ncolor1 = color1, ncolor2 = color2;
+ if(flipx)
+ {
+ ncolor1 = (ncolor1 & ~0xF800) | (0xF800 - (ncolor1 & 0xF800));
+ ncolor2 = (ncolor2 & ~0xF800) | (0xF800 - (ncolor2 & 0xF800));
+ }
+ if(flipy)
+ {
+ ncolor1 = (ncolor1 & ~0x7E0) | (0x7E0 - (ncolor1 & 0x7E0));
+ ncolor2 = (ncolor2 & ~0x7E0) | (0x7E0 - (ncolor2 & 0x7E0));
+ }
+ if(swapxy)
+ {
+ ncolor1 = (ncolor1 & 0x1F) | (((((ncolor1 >> 11) & 0x1F) * 0x3F) / 0x1F) << 5) | (((((ncolor1 >> 5) & 0x3F) * 0x1F) / 0x3F) << 11);
+ ncolor2 = (ncolor2 & 0x1F) | (((((ncolor2 >> 11) & 0x1F) * 0x3F) / 0x1F) << 5) | (((((ncolor2 >> 5) & 0x3F) * 0x1F) / 0x3F) << 11);
+ }
+ if(color1 <= color2 && ncolor1 > ncolor2) { color1 = ncolor2; color2 = ncolor1; }
+ else { color1 = ncolor1; color2 = ncolor2; }
+ }
+ uint dbits = 0, xmask = flipx ? 3 : 0, ymask = flipy ? 3 : 0, xshift = 1, yshift = 3;
+ if(swapxy) swap(xshift, yshift);
+ for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++)
+ {
+ dbits |= ((sbits&3) << (((xmask^x)<<xshift) + ((ymask^y)<<yshift)));
+ sbits >>= 2;
+ }
+ *(ushort *)curdst = lilswap(color1);
+ *(ushort *)&curdst[2] = lilswap(color2);
+ *(uint *)&curdst[4] = lilswap(dbits);
+
+ if(blocksize > 8) { src -= 8; curdst -= 8; }
+ }
+ dst += stridey;
+ }
}
static void reorientrgtc(GLenum format, int blocksize, int w, int h, uchar *src, uchar *dst, bool flipx, bool flipy, bool swapxy)
{
- int bx1 = 0, by1 = 0, bx2 = min(w, 4), by2 = min(h, 4), bw = (w+3)/4, bh = (h+3)/4, stridex = blocksize, stridey = blocksize;
- if(swapxy) stridex *= bw; else stridey *= bh;
- if(flipx) { dst += (bw-1)*stridex; stridex = -stridex; bx1 += 4-bx2; bx2 = 4; }
- if(flipy) { dst += (bh-1)*stridey; stridey = -stridey; by1 += 4-by2; by2 = 4; }
- stridex -= blocksize;
- loopi(bh)
- {
- for(uchar *curdst = dst, *end = &src[bw*blocksize]; src < end; curdst += stridex)
- {
- loopj(blocksize/8)
- {
- uchar val1 = src[0], val2 = src[1];
- ullong sval = lilswap(*(const ushort *)&src[2]) + ((ullong)lilswap(*(const uint *)&src[4] )<< 16), dval = 0;
- uint xmask = flipx ? 7 : 0, ymask = flipy ? 7 : 0, xshift = 0, yshift = 2;
- if(swapxy) swap(xshift, yshift);
- for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++)
- {
- dval |= ((sval&7) << (3*((xmask^x)<<xshift) + ((ymask^y)<<yshift)));
- sval >>= 3;
- }
- curdst[0] = val1;
- curdst[1] = val2;
- *(ushort *)&curdst[2] = lilswap(ushort(dval));
- *(ushort *)&curdst[4] = lilswap(ushort(dval>>16));
- *(ushort *)&curdst[6] = lilswap(ushort(dval>>32));
- src += 8;
- curdst += 8;
- }
- }
- dst += stridey;
- }
+ int bx1 = 0, by1 = 0, bx2 = min(w, 4), by2 = min(h, 4), bw = (w+3)/4, bh = (h+3)/4, stridex = blocksize, stridey = blocksize;
+ if(swapxy) stridex *= bw; else stridey *= bh;
+ if(flipx) { dst += (bw-1)*stridex; stridex = -stridex; bx1 += 4-bx2; bx2 = 4; }
+ if(flipy) { dst += (bh-1)*stridey; stridey = -stridey; by1 += 4-by2; by2 = 4; }
+ stridex -= blocksize;
+ loopi(bh)
+ {
+ for(uchar *curdst = dst, *end = &src[bw*blocksize]; src < end; curdst += stridex)
+ {
+ loopj(blocksize/8)
+ {
+ uchar val1 = src[0], val2 = src[1];
+ ullong sval = lilswap(*(const ushort *)&src[2]) + ((ullong)lilswap(*(const uint *)&src[4] )<< 16), dval = 0;
+ uint xmask = flipx ? 7 : 0, ymask = flipy ? 7 : 0, xshift = 0, yshift = 2;
+ if(swapxy) swap(xshift, yshift);
+ for(int y = by1; y < by2; y++) for(int x = bx1; x < bx2; x++)
+ {
+ dval |= ((sval&7) << (3*((xmask^x)<<xshift) + ((ymask^y)<<yshift)));
+ sval >>= 3;
+ }
+ curdst[0] = val1;
+ curdst[1] = val2;
+ *(ushort *)&curdst[2] = lilswap(ushort(dval));
+ *(ushort *)&curdst[4] = lilswap(ushort(dval>>16));
+ *(ushort *)&curdst[6] = lilswap(ushort(dval>>32));
+ src += 8;
+ curdst += 8;
+ }
+ }
+ dst += stridey;
+ }
}
#define writetex(t, body) do \
- { \
- uchar *dstrow = t.data; \
- loop(y, t.h) \
- { \
- for(uchar *dst = dstrow, *end = &dstrow[t.w*t.bpp]; dst < end; dst += t.bpp) \
- { \
- body; \
- } \
- dstrow += t.pitch; \
- } \
- } while(0)
+ { \
+ uchar *dstrow = t.data; \
+ loop(y, t.h) \
+ { \
+ for(uchar *dst = dstrow, *end = &dstrow[t.w*t.bpp]; dst < end; dst += t.bpp) \
+ { \
+ body; \
+ } \
+ dstrow += t.pitch; \
+ } \
+ } while(0)
#define readwritetex(t, s, body) do \
- { \
- uchar *dstrow = t.data, *srcrow = s.data; \
- loop(y, t.h) \
- { \
- for(uchar *dst = dstrow, *src = srcrow, *end = &srcrow[s.w*s.bpp]; src < end; dst += t.bpp, src += s.bpp) \
- { \
- body; \
- } \
- dstrow += t.pitch; \
- srcrow += s.pitch; \
- } \
- } while(0)
+ { \
+ uchar *dstrow = t.data, *srcrow = s.data; \
+ loop(y, t.h) \
+ { \
+ for(uchar *dst = dstrow, *src = srcrow, *end = &srcrow[s.w*s.bpp]; src < end; dst += t.bpp, src += s.bpp) \
+ { \
+ body; \
+ } \
+ dstrow += t.pitch; \
+ srcrow += s.pitch; \
+ } \
+ } while(0)
#define read2writetex(t, s1, src1, s2, src2, body) do \
- { \
- uchar *dstrow = t.data, *src1row = s1.data, *src2row = s2.data; \
- loop(y, t.h) \
- { \
- for(uchar *dst = dstrow, *end = &dstrow[t.w*t.bpp], *src1 = src1row, *src2 = src2row; dst < end; dst += t.bpp, src1 += s1.bpp, src2 += s2.bpp) \
- { \
- body; \
- } \
- dstrow += t.pitch; \
- src1row += s1.pitch; \
- src2row += s2.pitch; \
- } \
- } while(0)
+ { \
+ uchar *dstrow = t.data, *src1row = s1.data, *src2row = s2.data; \
+ loop(y, t.h) \
+ { \
+ for(uchar *dst = dstrow, *end = &dstrow[t.w*t.bpp], *src1 = src1row, *src2 = src2row; dst < end; dst += t.bpp, src1 += s1.bpp, src2 += s2.bpp) \
+ { \
+ body; \
+ } \
+ dstrow += t.pitch; \
+ src1row += s1.pitch; \
+ src2row += s2.pitch; \
+ } \
+ } while(0)
#define readwritergbtex(t, s, body) \
- { \
- if(t.bpp >= 3) readwritetex(t, s, body); \
- else \
- { \
- ImageData rgb(t.w, t.h, 3); \
- read2writetex(rgb, t, orig, s, src, { dst[0] = dst[1] = dst[2] = orig[0]; body; }); \
- t.replace(rgb); \
- } \
- }
+ { \
+ if(t.bpp >= 3) readwritetex(t, s, body); \
+ else \
+ { \
+ ImageData rgb(t.w, t.h, 3); \
+ read2writetex(rgb, t, orig, s, src, { dst[0] = dst[1] = dst[2] = orig[0]; body; }); \
+ t.replace(rgb); \
+ } \
+ }
void forcergbimage(ImageData &s)
{
- if(s.bpp >= 3) return;
- ImageData d(s.w, s.h, 3);
- readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; });
- s.replace(d);
+ if(s.bpp >= 3) return;
+ ImageData d(s.w, s.h, 3);
+ readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; });
+ s.replace(d);
}
#define readwritergbatex(t, s, body) \
- { \
- if(t.bpp >= 4) { readwritetex(t, s, body); } \
- else \
- { \
- ImageData rgba(t.w, t.h, 4); \
- if(t.bpp==3) read2writetex(rgba, t, orig, s, src, { dst[0] = orig[0]; dst[1] = orig[1]; dst[2] = orig[2]; body; }); \
- else read2writetex(rgba, t, orig, s, src, { dst[0] = dst[1] = dst[2] = orig[0]; body; }); \
- t.replace(rgba); \
- } \
- }
+ { \
+ if(t.bpp >= 4) { readwritetex(t, s, body); } \
+ else \
+ { \
+ ImageData rgba(t.w, t.h, 4); \
+ if(t.bpp==3) read2writetex(rgba, t, orig, s, src, { dst[0] = orig[0]; dst[1] = orig[1]; dst[2] = orig[2]; body; }); \
+ else read2writetex(rgba, t, orig, s, src, { dst[0] = dst[1] = dst[2] = orig[0]; body; }); \
+ t.replace(rgba); \
+ } \
+ }
void forcergbaimage(ImageData &s)
{
- if(s.bpp >= 4) return;
- ImageData d(s.w, s.h, 4);
- if(s.bpp==3) readwritetex(d, s, { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; });
- else readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; });
- s.replace(d);
+ if(s.bpp >= 4) return;
+ ImageData d(s.w, s.h, 4);
+ if(s.bpp==3) readwritetex(d, s, { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; });
+ else readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; });
+ s.replace(d);
}
void swizzleimage(ImageData &s)
{
- if(s.bpp==2)
- {
- ImageData d(s.w, s.h, 4);
- readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; dst[3] = src[1]; });
- s.replace(d);
- }
- else if(s.bpp==1)
- {
- ImageData d(s.w, s.h, 3);
- readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; });
- s.replace(d);
- }
+ if(s.bpp==2)
+ {
+ ImageData d(s.w, s.h, 4);
+ readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; dst[3] = src[1]; });
+ s.replace(d);
+ }
+ else if(s.bpp==1)
+ {
+ ImageData d(s.w, s.h, 3);
+ readwritetex(d, s, { dst[0] = dst[1] = dst[2] = src[0]; });
+ s.replace(d);
+ }
}
void texreorient(ImageData &s, bool flipx, bool flipy, bool swapxy, int type = TEX_DIFFUSE)
{
- ImageData d(swapxy ? s.h : s.w, swapxy ? s.w : s.h, s.bpp, s.levels, s.align, s.compressed);
- switch(s.compressed)
- {
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- [[fallthrough]];
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- [[fallthrough]];
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- [[fallthrough]];
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- {
- uchar *dst = d.data, *src = s.data;
- loopi(s.levels)
- {
- reorients3tc(s.compressed, s.bpp, max(s.w>>i, 1), max(s.h>>i, 1), src, dst, flipx, flipy, swapxy, type==TEX_NORMAL);
- src += s.calclevelsize(i);
- dst += d.calclevelsize(i);
- }
- break;
- }
- case GL_COMPRESSED_RED_RGTC1:
- [[fallthrough]];
- case GL_COMPRESSED_RG_RGTC2:
- [[fallthrough]];
- case GL_COMPRESSED_LUMINANCE_LATC1_EXT:
- [[fallthrough]];
- case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
- {
- uchar *dst = d.data, *src = s.data;
- loopi(s.levels)
- {
- reorientrgtc(s.compressed, s.bpp, max(s.w>>i, 1), max(s.h>>i, 1), src, dst, flipx, flipy, swapxy);
- src += s.calclevelsize(i);
- dst += d.calclevelsize(i);
- }
- break;
- }
- default:
- if(type==TEX_NORMAL && s.bpp >= 3) reorientnormals(s.data, s.w, s.h, s.bpp, s.pitch, d.data, flipx, flipy, swapxy);
- else reorienttexture(s.data, s.w, s.h, s.bpp, s.pitch, d.data, flipx, flipy, swapxy);
- break;
- }
- s.replace(d);
+ ImageData d(swapxy ? s.h : s.w, swapxy ? s.w : s.h, s.bpp, s.levels, s.align, s.compressed);
+ switch(s.compressed)
+ {
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ [[fallthrough]];
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ [[fallthrough]];
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+ [[fallthrough]];
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+ {
+ uchar *dst = d.data, *src = s.data;
+ loopi(s.levels)
+ {
+ reorients3tc(s.compressed, s.bpp, max(s.w>>i, 1), max(s.h>>i, 1), src, dst, flipx, flipy, swapxy, type==TEX_NORMAL);
+ src += s.calclevelsize(i);
+ dst += d.calclevelsize(i);
+ }
+ break;
+ }
+ case GL_COMPRESSED_RED_RGTC1:
+ [[fallthrough]];
+ case GL_COMPRESSED_RG_RGTC2:
+ [[fallthrough]];
+ case GL_COMPRESSED_LUMINANCE_LATC1_EXT:
+ [[fallthrough]];
+ case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
+ {
+ uchar *dst = d.data, *src = s.data;
+ loopi(s.levels)
+ {
+ reorientrgtc(s.compressed, s.bpp, max(s.w>>i, 1), max(s.h>>i, 1), src, dst, flipx, flipy, swapxy);
+ src += s.calclevelsize(i);
+ dst += d.calclevelsize(i);
+ }
+ break;
+ }
+ default:
+ if(type==TEX_NORMAL && s.bpp >= 3) reorientnormals(s.data, s.w, s.h, s.bpp, s.pitch, d.data, flipx, flipy, swapxy);
+ else reorienttexture(s.data, s.w, s.h, s.bpp, s.pitch, d.data, flipx, flipy, swapxy);
+ break;
+ }
+ s.replace(d);
}
extern const texrotation texrotations[8] =
{
- { false, false, false }, // 0: default
- { false, true, true }, // 1: 90 degrees
- { true, true, false }, // 2: 180 degrees
- { true, false, true }, // 3: 270 degrees
- { true, false, false }, // 4: flip X
- { false, true, false }, // 5: flip Y
- { false, false, true }, // 6: transpose
- { true, true, true }, // 7: flipped transpose
+ { false, false, false }, // 0: default
+ { false, true, true }, // 1: 90 degrees
+ { true, true, false }, // 2: 180 degrees
+ { true, false, true }, // 3: 270 degrees
+ { true, false, false }, // 4: flip X
+ { false, true, false }, // 5: flip Y
+ { false, false, true }, // 6: transpose
+ { true, true, true }, // 7: flipped transpose
};
void texrotate(ImageData &s, int numrots, int type = TEX_DIFFUSE)
{
- if(numrots>=1 && numrots<=7)
- {
- const texrotation &r = texrotations[numrots];
- texreorient(s, r.flipx, r.flipy, r.swapxy, type);
- }
+ if(numrots>=1 && numrots<=7)
+ {
+ const texrotation &r = texrotations[numrots];
+ texreorient(s, r.flipx, r.flipy, r.swapxy, type);
+ }
}
void texoffset(ImageData &s, int xoffset, int yoffset)
{
- xoffset = max(xoffset, 0);
- xoffset %= s.w;
- yoffset = max(yoffset, 0);
- yoffset %= s.h;
- if(!xoffset && !yoffset) return;
- ImageData d(s.w, s.h, s.bpp);
- uchar *src = s.data;
- loop(y, s.h)
- {
- uchar *dst = (uchar *)d.data+((y+yoffset)%d.h)*d.pitch;
- memcpy(dst+xoffset*s.bpp, src, (s.w-xoffset)*s.bpp);
- memcpy(dst, src+(s.w-xoffset)*s.bpp, xoffset*s.bpp);
- src += s.pitch;
- }
- s.replace(d);
+ xoffset = max(xoffset, 0);
+ xoffset %= s.w;
+ yoffset = max(yoffset, 0);
+ yoffset %= s.h;
+ if(!xoffset && !yoffset) return;
+ ImageData d(s.w, s.h, s.bpp);
+ uchar *src = s.data;
+ loop(y, s.h)
+ {
+ uchar *dst = (uchar *)d.data+((y+yoffset)%d.h)*d.pitch;
+ memcpy(dst+xoffset*s.bpp, src, (s.w-xoffset)*s.bpp);
+ memcpy(dst, src+(s.w-xoffset)*s.bpp, xoffset*s.bpp);
+ src += s.pitch;
+ }
+ s.replace(d);
}
void texmad(ImageData &s, const vec &mul, const vec &add)
{
- if(s.bpp < 3 && (mul.x != mul.y || mul.y != mul.z || add.x != add.y || add.y != add.z))
- swizzleimage(s);
- int maxk = min(int(s.bpp), 3);
- writetex(s,
- loopk(maxk) dst[k] = uchar(clamp(dst[k]*mul[k] + 255*add[k], 0.0f, 255.0f));
- );
+ if(s.bpp < 3 && (mul.x != mul.y || mul.y != mul.z || add.x != add.y || add.y != add.z))
+ swizzleimage(s);
+ int maxk = min(int(s.bpp), 3);
+ writetex(s,
+ loopk(maxk) dst[k] = uchar(clamp(dst[k]*mul[k] + 255*add[k], 0.0f, 255.0f));
+ );
}
void texcolorify(ImageData &s, const vec &color, vec weights)
{
- if(s.bpp < 3) return;
- if(weights.iszero()) weights = vec(0.21f, 0.72f, 0.07f);
- writetex(s,
- float lum = dst[0]*weights.x + dst[1]*weights.y + dst[2]*weights.z;
- loopk(3) dst[k] = uchar(clamp(lum*color[k], 0.0f, 255.0f));
- );
+ if(s.bpp < 3) return;
+ if(weights.iszero()) weights = vec(0.21f, 0.72f, 0.07f);
+ writetex(s,
+ float lum = dst[0]*weights.x + dst[1]*weights.y + dst[2]*weights.z;
+ loopk(3) dst[k] = uchar(clamp(lum*color[k], 0.0f, 255.0f));
+ );
}
void texcolormask(ImageData &s, const vec &color1, const vec &color2)
{
- if(s.bpp < 4) return;
- ImageData d(s.w, s.h, 3);
- readwritetex(d, s,
- vec color;
- color.lerp(color2, color1, src[3]/255.0f);
- loopk(3) dst[k] = uchar(clamp(color[k]*src[k], 0.0f, 255.0f));
- );
- s.replace(d);
+ if(s.bpp < 4) return;
+ ImageData d(s.w, s.h, 3);
+ readwritetex(d, s,
+ vec color;
+ color.lerp(color2, color1, src[3]/255.0f);
+ loopk(3) dst[k] = uchar(clamp(color[k]*src[k], 0.0f, 255.0f));
+ );
+ s.replace(d);
}
void texdup(ImageData &s, int srcchan, int dstchan)
{
- if(srcchan==dstchan || max(srcchan, dstchan) >= s.bpp) return;
- writetex(s, dst[dstchan] = dst[srcchan]);
+ if(srcchan==dstchan || max(srcchan, dstchan) >= s.bpp) return;
+ writetex(s, dst[dstchan] = dst[srcchan]);
}
void texmix(ImageData &s, int c1, int c2, int c3, int c4)
{
- int numchans = c1 < 0 ? 0 : (c2 < 0 ? 1 : (c3 < 0 ? 2 : (c4 < 0 ? 3 : 4)));
- if(numchans <= 0) return;
- ImageData d(s.w, s.h, numchans);
- readwritetex(d, s,
- switch(numchans)
- {
- case 4: dst[3] = src[c4];
- [[fallthrough]];
- case 3: dst[2] = src[c3];
- [[fallthrough]];
- case 2: dst[1] = src[c2];
- [[fallthrough]];
- case 1: dst[0] = src[c1]; break;
- default: break;
- }
- );
- s.replace(d);
+ int numchans = c1 < 0 ? 0 : (c2 < 0 ? 1 : (c3 < 0 ? 2 : (c4 < 0 ? 3 : 4)));
+ if(numchans <= 0) return;
+ ImageData d(s.w, s.h, numchans);
+ readwritetex(d, s,
+ switch(numchans)
+ {
+ case 4: dst[3] = src[c4];
+ [[fallthrough]];
+ case 3: dst[2] = src[c3];
+ [[fallthrough]];
+ case 2: dst[1] = src[c2];
+ [[fallthrough]];
+ case 1: dst[0] = src[c1]; break;
+ default: break;
+ }
+ );
+ s.replace(d);
}
void texgrey(ImageData &s)
{
- if(s.bpp <= 2) return;
- ImageData d(s.w, s.h, s.bpp >= 4 ? 2 : 1);
- if(s.bpp >= 4)
- {
- readwritetex(d, s,
- dst[0] = src[0];
- dst[1] = src[3];
- );
- }
- else
- {
- readwritetex(d, s, dst[0] = src[0]);
- }
- s.replace(d);
+ if(s.bpp <= 2) return;
+ ImageData d(s.w, s.h, s.bpp >= 4 ? 2 : 1);
+ if(s.bpp >= 4)
+ {
+ readwritetex(d, s,
+ dst[0] = src[0];
+ dst[1] = src[3];
+ );
+ }
+ else
+ {
+ readwritetex(d, s, dst[0] = src[0]);
+ }
+ s.replace(d);
}
void texpremul(ImageData &s)
{
- switch(s.bpp)
- {
- case 2:
- writetex(s,
- dst[0] = uchar((uint(dst[0])*uint(dst[1]))/255);
- );
- break;
- case 4:
- writetex(s,
- uint alpha = dst[3];
- dst[0] = uchar((uint(dst[0])*alpha)/255);
- dst[1] = uchar((uint(dst[1])*alpha)/255);
- dst[2] = uchar((uint(dst[2])*alpha)/255);
- );
- break;
- }
+ switch(s.bpp)
+ {
+ case 2:
+ writetex(s,
+ dst[0] = uchar((uint(dst[0])*uint(dst[1]))/255);
+ );
+ break;
+ case 4:
+ writetex(s,
+ uint alpha = dst[3];
+ dst[0] = uchar((uint(dst[0])*alpha)/255);
+ dst[1] = uchar((uint(dst[1])*alpha)/255);
+ dst[2] = uchar((uint(dst[2])*alpha)/255);
+ );
+ break;
+ }
}
void texagrad(ImageData &s, float x2, float y2, float x1, float y1)
{
- if(s.bpp != 2 && s.bpp != 4) return;
- y1 = 1 - y1;
- y2 = 1 - y2;
- float minx = 1, miny = 1, maxx = 1, maxy = 1;
- if(x1 != x2)
- {
- minx = (0 - x1) / (x2 - x1);
- maxx = (1 - x1) / (x2 - x1);
- }
- if(y1 != y2)
- {
- miny = (0 - y1) / (y2 - y1);
- maxy = (1 - y1) / (y2 - y1);
- }
- float dx = (maxx - minx)/max(s.w-1, 1),
- dy = (maxy - miny)/max(s.h-1, 1),
- cury = miny;
- for(uchar *dstrow = s.data + s.bpp - 1, *endrow = dstrow + s.h*s.pitch; dstrow < endrow; dstrow += s.pitch)
- {
- float curx = minx;
- for(uchar *dst = dstrow, *end = &dstrow[s.w*s.bpp]; dst < end; dst += s.bpp)
- {
- dst[0] = uchar(dst[0]*clamp(curx, 0.0f, 1.0f)*clamp(cury, 0.0f, 1.0f));
- curx += dx;
- }
- cury += dy;
- }
+ if(s.bpp != 2 && s.bpp != 4) return;
+ y1 = 1 - y1;
+ y2 = 1 - y2;
+ float minx = 1, miny = 1, maxx = 1, maxy = 1;
+ if(x1 != x2)
+ {
+ minx = (0 - x1) / (x2 - x1);
+ maxx = (1 - x1) / (x2 - x1);
+ }
+ if(y1 != y2)
+ {
+ miny = (0 - y1) / (y2 - y1);
+ maxy = (1 - y1) / (y2 - y1);
+ }
+ float dx = (maxx - minx)/max(s.w-1, 1),
+ dy = (maxy - miny)/max(s.h-1, 1),
+ cury = miny;
+ for(uchar *dstrow = s.data + s.bpp - 1, *endrow = dstrow + s.h*s.pitch; dstrow < endrow; dstrow += s.pitch)
+ {
+ float curx = minx;
+ for(uchar *dst = dstrow, *end = &dstrow[s.w*s.bpp]; dst < end; dst += s.bpp)
+ {
+ dst[0] = uchar(dst[0]*clamp(curx, 0.0f, 1.0f)*clamp(cury, 0.0f, 1.0f));
+ curx += dx;
+ }
+ cury += dy;
+ }
}
VAR(hwtexsize, 1, 0, 0);
@@ -628,384 +628,348 @@ extern int usetexcompress;
void setuptexcompress()
{
- if(!usetexcompress) return;
+ if(!usetexcompress) return;
- GLenum hint = GL_DONT_CARE;
- switch(texcompressquality)
- {
- case 1: hint = GL_NICEST; break;
- case 0: hint = GL_FASTEST; break;
- }
- glHint(GL_TEXTURE_COMPRESSION_HINT, hint);
+ GLenum hint = GL_DONT_CARE;
+ switch(texcompressquality)
+ {
+ case 1: hint = GL_NICEST; break;
+ case 0: hint = GL_FASTEST; break;
+ }
+ glHint(GL_TEXTURE_COMPRESSION_HINT, hint);
}
GLenum compressedformat(GLenum format, int w, int h, int force = 0)
{
- if(usetexcompress && texcompress && force >= 0 && (force || max(w, h) >= texcompress)) switch(format)
- {
- case GL_RGB5:
- case GL_RGB8:
- case GL_RGB: return usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB;
- case GL_RGB5_A1: return usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : GL_COMPRESSED_RGBA;
- case GL_RGBA: return usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA;
- case GL_RED:
- case GL_R8: return hasRGTC ? (usetexcompress > 1 ? GL_COMPRESSED_RED_RGTC1 : GL_COMPRESSED_RED) : (usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB);
- case GL_RG:
- case GL_RG8: return hasRGTC ? (usetexcompress > 1 ? GL_COMPRESSED_RG_RGTC2 : GL_COMPRESSED_RG) : (usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA);
- case GL_LUMINANCE:
- case GL_LUMINANCE8: return hasLATC ? (usetexcompress > 1 ? GL_COMPRESSED_LUMINANCE_LATC1_EXT : GL_COMPRESSED_LUMINANCE) : (usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB);
- case GL_LUMINANCE_ALPHA:
- case GL_LUMINANCE8_ALPHA8: return hasLATC ? (usetexcompress > 1 ? GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT : GL_COMPRESSED_LUMINANCE_ALPHA) : (usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA);
- }
- return format;
+ if(usetexcompress && texcompress && force >= 0 && (force || max(w, h) >= texcompress)) switch(format)
+ {
+ case GL_RGB5:
+ case GL_RGB8:
+ case GL_RGB: return usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB;
+ case GL_RGB5_A1: return usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : GL_COMPRESSED_RGBA;
+ case GL_RGBA: return usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA;
+ case GL_RED:
+ case GL_R8: return hasRGTC ? (usetexcompress > 1 ? GL_COMPRESSED_RED_RGTC1 : GL_COMPRESSED_RED) : (usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB);
+ case GL_RG:
+ case GL_RG8: return hasRGTC ? (usetexcompress > 1 ? GL_COMPRESSED_RG_RGTC2 : GL_COMPRESSED_RG) : (usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA);
+ case GL_LUMINANCE:
+ case GL_LUMINANCE8: return hasLATC ? (usetexcompress > 1 ? GL_COMPRESSED_LUMINANCE_LATC1_EXT : GL_COMPRESSED_LUMINANCE) : (usetexcompress > 1 ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB);
+ case GL_LUMINANCE_ALPHA:
+ case GL_LUMINANCE8_ALPHA8: return hasLATC ? (usetexcompress > 1 ? GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT : GL_COMPRESSED_LUMINANCE_ALPHA) : (usetexcompress > 1 ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA);
+ }
+ return format;
}
GLenum uncompressedformat(GLenum format)
{
- switch(format)
- {
- case GL_COMPRESSED_ALPHA:
- return GL_ALPHA;
- case GL_COMPRESSED_LUMINANCE:
- case GL_COMPRESSED_LUMINANCE_LATC1_EXT:
- return GL_LUMINANCE;
- case GL_COMPRESSED_LUMINANCE_ALPHA:
- case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
- return GL_LUMINANCE_ALPHA;
- case GL_COMPRESSED_RED:
- case GL_COMPRESSED_RED_RGTC1:
- return GL_RED;
- case GL_COMPRESSED_RG:
- case GL_COMPRESSED_RG_RGTC2:
- return GL_RG;
- case GL_COMPRESSED_RGB:
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- return GL_RGB;
- case GL_COMPRESSED_RGBA:
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- return GL_RGBA;
- }
- return GL_FALSE;
+ switch(format)
+ {
+ case GL_COMPRESSED_ALPHA:
+ return GL_ALPHA;
+ case GL_COMPRESSED_LUMINANCE:
+ case GL_COMPRESSED_LUMINANCE_LATC1_EXT:
+ return GL_LUMINANCE;
+ case GL_COMPRESSED_LUMINANCE_ALPHA:
+ case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
+ return GL_LUMINANCE_ALPHA;
+ case GL_COMPRESSED_RED:
+ case GL_COMPRESSED_RED_RGTC1:
+ return GL_RED;
+ case GL_COMPRESSED_RG:
+ case GL_COMPRESSED_RG_RGTC2:
+ return GL_RG;
+ case GL_COMPRESSED_RGB:
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ return GL_RGB;
+ case GL_COMPRESSED_RGBA:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+ return GL_RGBA;
+ }
+ return GL_FALSE;
}
int formatsize(GLenum format)
{
- switch(format)
- {
- case GL_RED:
- case GL_LUMINANCE:
- case GL_ALPHA: return 1;
- case GL_RG:
- case GL_LUMINANCE_ALPHA: return 2;
- case GL_RGB: return 3;
- case GL_RGBA: return 4;
- default: return 4;
- }
+ switch(format)
+ {
+ case GL_RED:
+ case GL_LUMINANCE:
+ case GL_ALPHA: return 1;
+ case GL_RG:
+ case GL_LUMINANCE_ALPHA: return 2;
+ case GL_RGB: return 3;
+ case GL_RGBA: return 4;
+ default: return 4;
+ }
}
VARFP(usenp2, 0, 0, 1, initwarning("texture quality", INIT_LOAD));
void resizetexture(int w, int h, bool mipmap, bool canreduce, GLenum target, int compress, int &tw, int &th)
{
- int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize,
- sizelimit = mipmap && maxtexsize ? min(maxtexsize, hwlimit) : hwlimit;
- if(compress > 0 && !usetexcompress)
- {
- w = max(w/compress, 1);
- h = max(h/compress, 1);
- }
- if(canreduce && texreduce)
- {
- w = max(w>>texreduce, 1);
- h = max(h>>texreduce, 1);
- }
- w = min(w, sizelimit);
- h = min(h, sizelimit);
- if(!usenp2 && (w&(w-1) || h&(h-1)))
- {
- tw = th = 1;
- while(tw < w) tw *= 2;
- while(th < h) th *= 2;
- if(w < tw - tw/4) tw /= 2;
- if(h < th - th/4) th /= 2;
- }
- else
- {
- tw = w;
- th = h;
- }
-}
-
-static GLuint mipmapfbo[2] = { 0, 0 };
-
-void cleanupmipmaps()
-{
- if(mipmapfbo[0]) { glDeleteFramebuffers_(2, mipmapfbo); memset(mipmapfbo, 0, sizeof(mipmapfbo)); }
-}
-
-VARFP(gpumipmap, 0, 0, 1, cleanupmipmaps());
+ int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize,
+ sizelimit = mipmap && maxtexsize ? min(maxtexsize, hwlimit) : hwlimit;
+ if(compress > 0 && !usetexcompress)
+ {
+ w = max(w/compress, 1);
+ h = max(h/compress, 1);
+ }
+ if(canreduce && texreduce)
+ {
+ w = max(w>>texreduce, 1);
+ h = max(h>>texreduce, 1);
+ }
+ w = min(w, sizelimit);
+ h = min(h, sizelimit);
+ if(!usenp2 && (w&(w-1) || h&(h-1)))
+ {
+ tw = th = 1;
+ while(tw < w) tw *= 2;
+ while(th < h) th *= 2;
+ if(w < tw - tw/4) tw /= 2;
+ if(h < th - th/4) th /= 2;
+ }
+ else
+ {
+ tw = w;
+ th = h;
+ }
+}
void uploadtexture(int tnum, GLenum target, GLenum internal, int tw, int th, GLenum format, GLenum type, void *pixels, int pw, int ph, int pitch, bool mipmap)
{
- int bpp = formatsize(format), row = 0, rowalign = 0;
- if(!pitch) pitch = pw*bpp;
- uchar *buf = NULL;
- if(pw!=tw || ph!=th)
- {
- buf = new uchar[tw*th*bpp];
- scaletexture((uchar *)pixels, pw, ph, bpp, pitch, buf, tw, th);
- }
- else if(tw*bpp != pitch)
- {
- row = pitch/bpp;
- rowalign = texalign(pixels, pitch, 1);
- while(rowalign > 0 && ((row*bpp + rowalign - 1)/rowalign)*rowalign != pitch) rowalign >>= 1;
- if(!rowalign)
- {
- row = 0;
- buf = new uchar[tw*th*bpp];
- loopi(th) memcpy(&buf[i*tw*bpp], &((uchar *)pixels)[i*pitch], tw*bpp);
- }
- }
- bool shouldgpumipmap = pixels && mipmap && max(tw, th) > 1 && gpumipmap && hasFBB && !uncompressedformat(internal);
- for(int level = 0, align = 0, mw = tw, mh = th;; level++)
- {
- uchar *src = buf ? buf : (uchar *)pixels;
- if(buf) pitch = mw*bpp;
- int srcalign = row > 0 ? rowalign : texalign(src, pitch, 1);
- if(align != srcalign) glPixelStorei(GL_UNPACK_ALIGNMENT, align = srcalign);
- if(row > 0) glPixelStorei(GL_UNPACK_ROW_LENGTH, row);
- glTexImage2D(target, level, internal, mw, mh, 0, format, type, src);
- if(row > 0) glPixelStorei(GL_UNPACK_ROW_LENGTH, row = 0);
- if(!mipmap || shouldgpumipmap || max(mw, mh) <= 1) break;
- int srcw = mw, srch = mh;
- if(mw > 1) mw /= 2;
- if(mh > 1) mh /= 2;
- if(src)
- {
- if(!buf) buf = new uchar[mw*mh*bpp];
- scaletexture(src, srcw, srch, bpp, pitch, buf, mw, mh);
- }
- }
- if(buf) delete[] buf;
- if(shouldgpumipmap)
- {
- GLint fbo = 0;
- if(!inbetweenframes || drawtex) glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
- for(int level = 1, mw = tw, mh = th; max(mw, mh) > 1; level++)
- {
- if(mw > 1) mw /= 2;
- if(mh > 1) mh /= 2;
- glTexImage2D(target, level, internal, mw, mh, 0, format, type, NULL);
- }
- if(!mipmapfbo[0]) glGenFramebuffers_(2, mipmapfbo);
- glBindFramebuffer_(GL_READ_FRAMEBUFFER, mipmapfbo[0]);
- glBindFramebuffer_(GL_DRAW_FRAMEBUFFER, mipmapfbo[1]);
- for(int level = 1, mw = tw, mh = th; max(mw, mh) > 1; level++)
- {
- int srcw = mw, srch = mh;
- if(mw > 1) mw /= 2;
- if(mh > 1) mh /= 2;
- glFramebufferTexture2D_(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, tnum, level - 1);
- glFramebufferTexture2D_(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, tnum, level);
- glBlitFramebuffer_(0, 0, srcw, srch, 0, 0, mw, mh, GL_COLOR_BUFFER_BIT, GL_LINEAR);
- }
- glFramebufferTexture2D_(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, 0, 0);
- glFramebufferTexture2D_(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, 0, 0);
- glBindFramebuffer_(GL_FRAMEBUFFER, fbo);
- }
+ int bpp = formatsize(format), row = 0, rowalign = 0;
+ if(!pitch) pitch = pw*bpp;
+ uchar *buf = NULL;
+ if(pw!=tw || ph!=th)
+ {
+ buf = new uchar[tw*th*bpp];
+ scaletexture((uchar *)pixels, pw, ph, bpp, pitch, buf, tw, th);
+ }
+ else if(tw*bpp != pitch)
+ {
+ row = pitch/bpp;
+ rowalign = texalign(pixels, pitch, 1);
+ while(rowalign > 0 && ((row*bpp + rowalign - 1)/rowalign)*rowalign != pitch) rowalign >>= 1;
+ if(!rowalign)
+ {
+ row = 0;
+ buf = new uchar[tw*th*bpp];
+ loopi(th) memcpy(&buf[i*tw*bpp], &((uchar *)pixels)[i*pitch], tw*bpp);
+ }
+ }
+ for(int level = 0, align = 0, mw = tw, mh = th;; level++)
+ {
+ uchar *src = buf ? buf : (uchar *)pixels;
+ if(buf) pitch = mw*bpp;
+ int srcalign = row > 0 ? rowalign : texalign(src, pitch, 1);
+ if(align != srcalign) glPixelStorei(GL_UNPACK_ALIGNMENT, align = srcalign);
+ if(row > 0) glPixelStorei(GL_UNPACK_ROW_LENGTH, row);
+ glTexImage2D(target, level, internal, mw, mh, 0, format, type, src);
+ if(row > 0) glPixelStorei(GL_UNPACK_ROW_LENGTH, row = 0);
+ if(!mipmap || max(mw, mh) <= 1) break;
+ int srcw = mw, srch = mh;
+ if(mw > 1) mw /= 2;
+ if(mh > 1) mh /= 2;
+ if(src)
+ {
+ if(!buf) buf = new uchar[mw*mh*bpp];
+ scaletexture(src, srcw, srch, bpp, pitch, buf, mw, mh);
+ }
+ }
+ if(buf) delete[] buf;
}
void uploadcompressedtexture(GLenum target, GLenum subtarget, GLenum format, int w, int h, uchar *data, int align, int blocksize, int levels, bool mipmap)
{
- int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize,
- sizelimit = levels > 1 && maxtexsize ? min(maxtexsize, hwlimit) : hwlimit;
- int level = 0;
- loopi(levels)
- {
- int size = ((w + align-1)/align) * ((h + align-1)/align) * blocksize;
- if(w <= sizelimit && h <= sizelimit)
- {
- glCompressedTexImage2D_(subtarget, level, format, w, h, 0, size, data);
- level++;
- if(!mipmap) break;
- }
- if(max(w, h) <= 1) break;
- if(w > 1) w /= 2;
- if(h > 1) h /= 2;
- data += size;
- }
+ int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize,
+ sizelimit = levels > 1 && maxtexsize ? min(maxtexsize, hwlimit) : hwlimit;
+ int level = 0;
+ loopi(levels)
+ {
+ int size = ((w + align-1)/align) * ((h + align-1)/align) * blocksize;
+ if(w <= sizelimit && h <= sizelimit)
+ {
+ glCompressedTexImage2D_(subtarget, level, format, w, h, 0, size, data);
+ level++;
+ if(!mipmap) break;
+ }
+ if(max(w, h) <= 1) break;
+ if(w > 1) w /= 2;
+ if(h > 1) h /= 2;
+ data += size;
+ }
}
GLenum textarget(GLenum subtarget)
{
- switch(subtarget)
- {
- case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
- case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
- case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
- case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
- case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
- case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
- return GL_TEXTURE_CUBE_MAP;
- }
- return subtarget;
+ switch(subtarget)
+ {
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ return GL_TEXTURE_CUBE_MAP;
+ }
+ return subtarget;
}
const GLint *swizzlemask(GLenum format)
{
- static const GLint luminance[4] = { GL_RED, GL_RED, GL_RED, GL_ONE };
- static const GLint luminancealpha[4] = { GL_RED, GL_RED, GL_RED, GL_GREEN };
- switch(format)
- {
- case GL_RED: return luminance;
- case GL_RG: return luminancealpha;
- }
- return NULL;
+ static const GLint luminance[4] = { GL_RED, GL_RED, GL_RED, GL_ONE };
+ static const GLint luminancealpha[4] = { GL_RED, GL_RED, GL_RED, GL_GREEN };
+ switch(format)
+ {
+ case GL_RED: return luminance;
+ case GL_RG: return luminancealpha;
+ }
+ return NULL;
}
void setuptexparameters(int tnum, void *pixels, int clamp, int filter, GLenum format, GLenum target, bool swizzle)
{
- glBindTexture(target, tnum);
- glTexParameteri(target, GL_TEXTURE_WRAP_S, clamp&1 ? GL_CLAMP_TO_EDGE : (clamp&0x100 ? GL_MIRRORED_REPEAT : GL_REPEAT));
- glTexParameteri(target, GL_TEXTURE_WRAP_T, clamp&2 ? GL_CLAMP_TO_EDGE : (clamp&0x200 ? GL_MIRRORED_REPEAT : GL_REPEAT));
- if(target==GL_TEXTURE_2D && hasAF && min(aniso, hwmaxaniso) > 0 && filter > 1) glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, min(aniso, hwmaxaniso));
- glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter && bilinear ? GL_LINEAR : GL_NEAREST);
- glTexParameteri(target, GL_TEXTURE_MIN_FILTER,
- filter > 1 ?
- (trilinear ?
- (bilinear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR) :
- (bilinear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST)) :
- (filter && bilinear ? GL_LINEAR : GL_NEAREST));
- if(swizzle && hasTRG && hasTSW)
- {
- const GLint *mask = swizzlemask(format);
- if(mask) glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, mask);
- }
+ glBindTexture(target, tnum);
+ glTexParameteri(target, GL_TEXTURE_WRAP_S, clamp&1 ? GL_CLAMP_TO_EDGE : (clamp&0x100 ? GL_MIRRORED_REPEAT : GL_REPEAT));
+ glTexParameteri(target, GL_TEXTURE_WRAP_T, clamp&2 ? GL_CLAMP_TO_EDGE : (clamp&0x200 ? GL_MIRRORED_REPEAT : GL_REPEAT));
+ if(target==GL_TEXTURE_2D && hasAF && min(aniso, hwmaxaniso) > 0 && filter > 1) glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, min(aniso, hwmaxaniso));
+ glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter && bilinear ? GL_LINEAR : GL_NEAREST);
+ glTexParameteri(target, GL_TEXTURE_MIN_FILTER,
+ filter > 1 ?
+ (trilinear ?
+ (bilinear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR) :
+ (bilinear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST)) :
+ (filter && bilinear ? GL_LINEAR : GL_NEAREST));
+ if(swizzle && hasTRG && hasTSW)
+ {
+ const GLint *mask = swizzlemask(format);
+ if(mask) glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, mask);
+ }
}
static GLenum textype(GLenum &component, GLenum &format)
{
- GLenum type = GL_UNSIGNED_BYTE;
- switch(component)
- {
- case GL_R16F:
- case GL_R32F:
- if(!format) format = GL_RED;
- type = GL_FLOAT;
- break;
-
- case GL_RG16F:
- case GL_RG32F:
- if(!format) format = GL_RG;
- type = GL_FLOAT;
- break;
-
- case GL_RGB16F:
- case GL_RGB32F:
- if(!format) format = GL_RGB;
- type = GL_FLOAT;
- break;
-
- case GL_RGBA16F:
- case GL_RGBA32F:
- if(!format) format = GL_RGBA;
- type = GL_FLOAT;
- break;
-
- case GL_DEPTH_COMPONENT16:
- case GL_DEPTH_COMPONENT24:
- case GL_DEPTH_COMPONENT32:
- if(!format) format = GL_DEPTH_COMPONENT;
- break;
-
- case GL_RGB5:
- case GL_RGB8:
- case GL_RGB10:
- case GL_RGB16:
- case GL_COMPRESSED_RGB:
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- if(!format) format = GL_RGB;
- break;
-
- case GL_RGB5_A1:
- case GL_RGBA8:
- case GL_RGB10_A2:
- case GL_RGBA16:
- case GL_COMPRESSED_RGBA:
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- if(!format) format = GL_RGBA;
- break;
-
- case GL_DEPTH_STENCIL:
- case GL_DEPTH24_STENCIL8:
- if(!format) format = GL_DEPTH_STENCIL;
- type = GL_UNSIGNED_INT_24_8;
- break;
-
- case GL_R8:
- case GL_R16:
- case GL_COMPRESSED_RED:
- case GL_COMPRESSED_RED_RGTC1:
- if(!format) format = GL_RED;
- break;
-
- case GL_RG8:
- case GL_RG16:
- case GL_COMPRESSED_RG:
- case GL_COMPRESSED_RG_RGTC2:
- if(!format) format = GL_RG;
- break;
-
- case GL_LUMINANCE8:
- case GL_LUMINANCE16:
- case GL_COMPRESSED_LUMINANCE:
- case GL_COMPRESSED_LUMINANCE_LATC1_EXT:
- if(!format) format = GL_LUMINANCE;
- break;
-
- case GL_LUMINANCE8_ALPHA8:
- case GL_LUMINANCE16_ALPHA16:
- case GL_COMPRESSED_LUMINANCE_ALPHA:
- case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
- if(!format) format = GL_LUMINANCE_ALPHA;
- break;
-
- case GL_ALPHA8:
- case GL_ALPHA16:
- case GL_COMPRESSED_ALPHA:
- if(!format) format = GL_ALPHA;
- break;
- }
- if(!format) format = component;
- return type;
+ GLenum type = GL_UNSIGNED_BYTE;
+ switch(component)
+ {
+ case GL_R16F:
+ case GL_R32F:
+ if(!format) format = GL_RED;
+ type = GL_FLOAT;
+ break;
+
+ case GL_RG16F:
+ case GL_RG32F:
+ if(!format) format = GL_RG;
+ type = GL_FLOAT;
+ break;
+
+ case GL_RGB16F:
+ case GL_RGB32F:
+ if(!format) format = GL_RGB;
+ type = GL_FLOAT;
+ break;
+
+ case GL_RGBA16F:
+ case GL_RGBA32F:
+ if(!format) format = GL_RGBA;
+ type = GL_FLOAT;
+ break;
+
+ case GL_DEPTH_COMPONENT16:
+ case GL_DEPTH_COMPONENT24:
+ case GL_DEPTH_COMPONENT32:
+ if(!format) format = GL_DEPTH_COMPONENT;
+ break;
+
+ case GL_RGB5:
+ case GL_RGB8:
+ case GL_RGB10:
+ case GL_RGB16:
+ case GL_COMPRESSED_RGB:
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ if(!format) format = GL_RGB;
+ break;
+
+ case GL_RGB5_A1:
+ case GL_RGBA8:
+ case GL_RGB10_A2:
+ case GL_RGBA16:
+ case GL_COMPRESSED_RGBA:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+ if(!format) format = GL_RGBA;
+ break;
+
+ case GL_DEPTH_STENCIL:
+ case GL_DEPTH24_STENCIL8:
+ if(!format) format = GL_DEPTH_STENCIL;
+ type = GL_UNSIGNED_INT_24_8;
+ break;
+
+ case GL_R8:
+ case GL_R16:
+ case GL_COMPRESSED_RED:
+ case GL_COMPRESSED_RED_RGTC1:
+ if(!format) format = GL_RED;
+ break;
+
+ case GL_RG8:
+ case GL_RG16:
+ case GL_COMPRESSED_RG:
+ case GL_COMPRESSED_RG_RGTC2:
+ if(!format) format = GL_RG;
+ break;
+
+ case GL_LUMINANCE8:
+ case GL_LUMINANCE16:
+ case GL_COMPRESSED_LUMINANCE:
+ case GL_COMPRESSED_LUMINANCE_LATC1_EXT:
+ if(!format) format = GL_LUMINANCE;
+ break;
+
+ case GL_LUMINANCE8_ALPHA8:
+ case GL_LUMINANCE16_ALPHA16:
+ case GL_COMPRESSED_LUMINANCE_ALPHA:
+ case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
+ if(!format) format = GL_LUMINANCE_ALPHA;
+ break;
+
+ case GL_ALPHA8:
+ case GL_ALPHA16:
+ case GL_COMPRESSED_ALPHA:
+ if(!format) format = GL_ALPHA;
+ break;
+ }
+ if(!format) format = component;
+ return type;
}
void createtexture(int tnum, int w, int h, void *pixels, int clamp, int filter, GLenum component, GLenum subtarget, int pw, int ph, int pitch, bool resize, GLenum format, bool swizzle)
{
- GLenum target = textarget(subtarget), type = textype(component, format);
- if(filter >= 0 && clamp >= 0) setuptexparameters(tnum, pixels, clamp, filter, format, target, swizzle);
- if(!pw) pw = w;
- if(!ph) ph = h;
- int tw = w, th = h;
- bool mipmap = filter > 1 && pixels;
- if(resize && pixels)
- {
- resizetexture(w, h, mipmap, false, target, 0, tw, th);
- if(mipmap) component = compressedformat(component, tw, th);
- }
- uploadtexture(tnum, subtarget, component, tw, th, format, type, pixels, pw, ph, pitch, mipmap);
+ GLenum target = textarget(subtarget), type = textype(component, format);
+ if(filter >= 0 && clamp >= 0) setuptexparameters(tnum, pixels, clamp, filter, format, target, swizzle);
+ if(!pw) pw = w;
+ if(!ph) ph = h;
+ int tw = w, th = h;
+ bool mipmap = filter > 1 && pixels;
+ if(resize && pixels)
+ {
+ resizetexture(w, h, mipmap, false, target, 0, tw, th);
+ if(mipmap) component = compressedformat(component, tw, th);
+ }
+ uploadtexture(tnum, subtarget, component, tw, th, format, type, pixels, pw, ph, pitch, mipmap);
}
void createcompressedtexture(int tnum, int w, int h, uchar *data, int align, int blocksize, int levels, int clamp, int filter, GLenum format, GLenum subtarget, bool swizzle = false)
{
- GLenum target = textarget(subtarget);
- if(filter >= 0 && clamp >= 0) setuptexparameters(tnum, data, clamp, filter, format, target);
- uploadcompressedtexture(target, subtarget, format, w, h, data, align, blocksize, levels, filter > 1);
+ GLenum target = textarget(subtarget);
+ if(filter >= 0 && clamp >= 0) setuptexparameters(tnum, data, clamp, filter, format, target);
+ uploadcompressedtexture(target, subtarget, format, w, h, data, align, blocksize, levels, filter > 1);
}
hashnameset<Texture> textures;
@@ -1014,112 +978,112 @@ Texture *notexture = NULL; // used as default, ensured to be loaded
static GLenum texformat(int bpp, bool swizzle = false)
{
- switch(bpp)
- {
- case 1: return hasTRG && (hasTSW || !glcompat || !swizzle) ? GL_RED : GL_LUMINANCE;
- case 2: return hasTRG && (hasTSW || !glcompat || !swizzle) ? GL_RG : GL_LUMINANCE_ALPHA;
- case 3: return GL_RGB;
- case 4: return GL_RGBA;
- default: return 0;
- }
+ switch(bpp)
+ {
+ case 1: return hasTRG && (hasTSW || !glcompat || !swizzle) ? GL_RED : GL_LUMINANCE;
+ case 2: return hasTRG && (hasTSW || !glcompat || !swizzle) ? GL_RG : GL_LUMINANCE_ALPHA;
+ case 3: return GL_RGB;
+ case 4: return GL_RGBA;
+ default: return 0;
+ }
}
static bool alphaformat(GLenum format)
{
- switch(format)
- {
- case GL_ALPHA:
- case GL_LUMINANCE_ALPHA:
- case GL_RG:
- case GL_RGBA:
- return true;
- default:
- return false;
- }
+ switch(format)
+ {
+ case GL_ALPHA:
+ case GL_LUMINANCE_ALPHA:
+ case GL_RG:
+ case GL_RGBA:
+ return true;
+ default:
+ return false;
+ }
}
int texalign(const void *data, int w, int bpp)
{
- int stride = w*bpp;
- if(stride&1) return 1;
- if(stride&2) return 2;
- return 4;
+ int stride = w*bpp;
+ if(stride&1) return 1;
+ if(stride&2) return 2;
+ return 4;
}
static Texture *newtexture(Texture *t, const char *rname, ImageData &s, int clamp = 0, bool mipit = true, bool canreduce = false, bool transient = false, int compress = 0)
{
- if(!t)
- {
- char *key = newstring(rname);
- t = &textures[key];
- t->name = key;
- }
-
- t->clamp = clamp;
- t->mipmap = mipit;
- t->type = Texture::IMAGE;
- if(transient) t->type |= Texture::TRANSIENT;
- if(clamp&0x300) t->type |= Texture::MIRROR;
- if(!s.data)
- {
- t->type |= Texture::STUB;
- t->w = t->h = t->xs = t->ys = t->bpp = 0;
- return t;
- }
-
- bool swizzle = !(clamp&0x10000);
- GLenum format;
- if(s.compressed)
- {
- format = uncompressedformat(s.compressed);
- t->bpp = formatsize(format);
- t->type |= Texture::COMPRESSED;
- }
- else
- {
- format = texformat(s.bpp, swizzle);
- t->bpp = s.bpp;
- if(swizzle && hasTRG && !hasTSW && swizzlemask(format))
- {
- swizzleimage(s);
- format = texformat(s.bpp, swizzle);
- t->bpp = s.bpp;
- }
- }
- if(alphaformat(format)) t->type |= Texture::ALPHA;
- t->w = t->xs = s.w;
- t->h = t->ys = s.h;
-
- int filter = !canreduce || reducefilter ? (mipit ? 2 : 1) : 0;
- glGenTextures(1, &t->id);
- if(s.compressed)
- {
- uchar *data = s.data;
- int levels = s.levels, level = 0;
- if(canreduce && texreduce) loopi(min(texreduce, s.levels-1))
- {
- data += s.calclevelsize(level++);
- levels--;
- if(t->w > 1) t->w /= 2;
- if(t->h > 1) t->h /= 2;
- }
- int sizelimit = mipit && maxtexsize ? min(maxtexsize, hwtexsize) : hwtexsize;
- while(t->w > sizelimit || t->h > sizelimit)
- {
- data += s.calclevelsize(level++);
- levels--;
- if(t->w > 1) t->w /= 2;
- if(t->h > 1) t->h /= 2;
- }
- createcompressedtexture(t->id, t->w, t->h, data, s.align, s.bpp, levels, clamp, filter, s.compressed, GL_TEXTURE_2D, swizzle);
- }
- else
- {
- resizetexture(t->w, t->h, mipit, canreduce, GL_TEXTURE_2D, compress, t->w, t->h);
- GLenum component = compressedformat(format, t->w, t->h, compress);
- createtexture(t->id, t->w, t->h, s.data, clamp, filter, component, GL_TEXTURE_2D, t->xs, t->ys, s.pitch, false, format, swizzle);
- }
- return t;
+ if(!t)
+ {
+ char *key = newstring(rname);
+ t = &textures[key];
+ t->name = key;
+ }
+
+ t->clamp = clamp;
+ t->mipmap = mipit;
+ t->type = Texture::IMAGE;
+ if(transient) t->type |= Texture::TRANSIENT;
+ if(clamp&0x300) t->type |= Texture::MIRROR;
+ if(!s.data)
+ {
+ t->type |= Texture::STUB;
+ t->w = t->h = t->xs = t->ys = t->bpp = 0;
+ return t;
+ }
+
+ bool swizzle = !(clamp&0x10000);
+ GLenum format;
+ if(s.compressed)
+ {
+ format = uncompressedformat(s.compressed);
+ t->bpp = formatsize(format);
+ t->type |= Texture::COMPRESSED;
+ }
+ else
+ {
+ format = texformat(s.bpp, swizzle);
+ t->bpp = s.bpp;
+ if(swizzle && hasTRG && !hasTSW && swizzlemask(format))
+ {
+ swizzleimage(s);
+ format = texformat(s.bpp, swizzle);
+ t->bpp = s.bpp;
+ }
+ }
+ if(alphaformat(format)) t->type |= Texture::ALPHA;
+ t->w = t->xs = s.w;
+ t->h = t->ys = s.h;
+
+ int filter = !canreduce || reducefilter ? (mipit ? 2 : 1) : 0;
+ glGenTextures(1, &t->id);
+ if(s.compressed)
+ {
+ uchar *data = s.data;
+ int levels = s.levels, level = 0;
+ if(canreduce && texreduce) loopi(min(texreduce, s.levels-1))
+ {
+ data += s.calclevelsize(level++);
+ levels--;
+ if(t->w > 1) t->w /= 2;
+ if(t->h > 1) t->h /= 2;
+ }
+ int sizelimit = mipit && maxtexsize ? min(maxtexsize, hwtexsize) : hwtexsize;
+ while(t->w > sizelimit || t->h > sizelimit)
+ {
+ data += s.calclevelsize(level++);
+ levels--;
+ if(t->w > 1) t->w /= 2;
+ if(t->h > 1) t->h /= 2;
+ }
+ createcompressedtexture(t->id, t->w, t->h, data, s.align, s.bpp, levels, clamp, filter, s.compressed, GL_TEXTURE_2D, swizzle);
+ }
+ else
+ {
+ resizetexture(t->w, t->h, mipit, canreduce, GL_TEXTURE_2D, compress, t->w, t->h);
+ GLenum component = compressedformat(format, t->w, t->h, compress);
+ createtexture(t->id, t->w, t->h, s.data, clamp, filter, component, GL_TEXTURE_2D, t->xs, t->ys, s.pitch, false, format, swizzle);
+ }
+ return t;
}
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
@@ -1132,435 +1096,435 @@ static Texture *newtexture(Texture *t, const char *rname, ImageData &s, int clam
SDL_Surface *wrapsurface(void *data, int width, int height, int bpp)
{
- switch(bpp)
- {
- case 3: return SDL_CreateRGBSurfaceFrom(data, width, height, 8*bpp, bpp*width, RGBMASKS);
- case 4: return SDL_CreateRGBSurfaceFrom(data, width, height, 8*bpp, bpp*width, RGBAMASKS);
- }
- return NULL;
+ switch(bpp)
+ {
+ case 3: return SDL_CreateRGBSurfaceFrom(data, width, height, 8*bpp, bpp*width, RGBMASKS);
+ case 4: return SDL_CreateRGBSurfaceFrom(data, width, height, 8*bpp, bpp*width, RGBAMASKS);
+ }
+ return NULL;
}
SDL_Surface *creatergbsurface(SDL_Surface *os)
{
- SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 24, RGBMASKS);
- if(ns) SDL_BlitSurface(os, NULL, ns, NULL);
- SDL_FreeSurface(os);
- return ns;
+ SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 24, RGBMASKS);
+ if(ns) SDL_BlitSurface(os, NULL, ns, NULL);
+ SDL_FreeSurface(os);
+ return ns;
}
SDL_Surface *creatergbasurface(SDL_Surface *os)
{
- SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 32, RGBAMASKS);
- if(ns)
- {
- SDL_SetSurfaceBlendMode(os, SDL_BLENDMODE_NONE);
- SDL_BlitSurface(os, NULL, ns, NULL);
- }
- SDL_FreeSurface(os);
- return ns;
+ SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 32, RGBAMASKS);
+ if(ns)
+ {
+ SDL_SetSurfaceBlendMode(os, SDL_BLENDMODE_NONE);
+ SDL_BlitSurface(os, NULL, ns, NULL);
+ }
+ SDL_FreeSurface(os);
+ return ns;
}
bool checkgrayscale(SDL_Surface *s)
{
- // gray scale images have 256 levels, no colorkey, and the palette is a ramp
- if(s->format->palette)
- {
- if(s->format->palette->ncolors != 256 || SDL_GetColorKey(s, NULL) >= 0) return false;
- const SDL_Color *colors = s->format->palette->colors;
- loopi(256) if(colors[i].r != i || colors[i].g != i || colors[i].b != i) return false;
- }
- return true;
+ // gray scale images have 256 levels, no colorkey, and the palette is a ramp
+ if(s->format->palette)
+ {
+ if(s->format->palette->ncolors != 256 || SDL_GetColorKey(s, NULL) >= 0) return false;
+ const SDL_Color *colors = s->format->palette->colors;
+ loopi(256) if(colors[i].r != i || colors[i].g != i || colors[i].b != i) return false;
+ }
+ return true;
}
SDL_Surface *fixsurfaceformat(SDL_Surface *s)
{
- if(!s) return NULL;
- if(!s->pixels || min(s->w, s->h) <= 0 || s->format->BytesPerPixel <= 0)
- {
- SDL_FreeSurface(s);
- return NULL;
- }
- static const uint rgbmasks[] = { RGBMASKS }, rgbamasks[] = { RGBAMASKS };
- switch(s->format->BytesPerPixel)
- {
- case 1:
- if(!checkgrayscale(s)) return SDL_GetColorKey(s, NULL) >= 0 ? creatergbasurface(s) : creatergbsurface(s);
- break;
- case 3:
- if(s->format->Rmask != rgbmasks[0] || s->format->Gmask != rgbmasks[1] || s->format->Bmask != rgbmasks[2])
- return creatergbsurface(s);
- break;
- case 4:
- if(s->format->Rmask != rgbamasks[0] || s->format->Gmask != rgbamasks[1] || s->format->Bmask != rgbamasks[2] || s->format->Amask != rgbamasks[3])
- return s->format->Amask ? creatergbasurface(s) : creatergbsurface(s);
- break;
- }
- return s;
+ if(!s) return NULL;
+ if(!s->pixels || min(s->w, s->h) <= 0 || s->format->BytesPerPixel <= 0)
+ {
+ SDL_FreeSurface(s);
+ return NULL;
+ }
+ static const uint rgbmasks[] = { RGBMASKS }, rgbamasks[] = { RGBAMASKS };
+ switch(s->format->BytesPerPixel)
+ {
+ case 1:
+ if(!checkgrayscale(s)) return SDL_GetColorKey(s, NULL) >= 0 ? creatergbasurface(s) : creatergbsurface(s);
+ break;
+ case 3:
+ if(s->format->Rmask != rgbmasks[0] || s->format->Gmask != rgbmasks[1] || s->format->Bmask != rgbmasks[2])
+ return creatergbsurface(s);
+ break;
+ case 4:
+ if(s->format->Rmask != rgbamasks[0] || s->format->Gmask != rgbamasks[1] || s->format->Bmask != rgbamasks[2] || s->format->Amask != rgbamasks[3])
+ return s->format->Amask ? creatergbasurface(s) : creatergbsurface(s);
+ break;
+ }
+ return s;
}
void texflip(ImageData &s)
{
- ImageData d(s.w, s.h, s.bpp);
- uchar *dst = d.data, *src = &s.data[s.pitch*s.h];
- loopi(s.h)
- {
- src -= s.pitch;
- memcpy(dst, src, s.bpp*s.w);
- dst += d.pitch;
- }
- s.replace(d);
+ ImageData d(s.w, s.h, s.bpp);
+ uchar *dst = d.data, *src = &s.data[s.pitch*s.h];
+ loopi(s.h)
+ {
+ src -= s.pitch;
+ memcpy(dst, src, s.bpp*s.w);
+ dst += d.pitch;
+ }
+ s.replace(d);
}
void texnormal(ImageData &s, int emphasis)
{
- ImageData d(s.w, s.h, 3);
- uchar *src = s.data, *dst = d.data;
- loop(y, s.h) loop(x, s.w)
- {
- vec normal(0.0f, 0.0f, 255.0f/emphasis);
- normal.x += src[y*s.pitch + ((x+s.w-1)%s.w)*s.bpp];
- normal.x -= src[y*s.pitch + ((x+1)%s.w)*s.bpp];
- normal.y += src[((y+s.h-1)%s.h)*s.pitch + x*s.bpp];
- normal.y -= src[((y+1)%s.h)*s.pitch + x*s.bpp];
- normal.normalize();
- *dst++ = uchar(127.5f + normal.x*127.5f);
- *dst++ = uchar(127.5f + normal.y*127.5f);
- *dst++ = uchar(127.5f + normal.z*127.5f);
- }
- s.replace(d);
+ ImageData d(s.w, s.h, 3);
+ uchar *src = s.data, *dst = d.data;
+ loop(y, s.h) loop(x, s.w)
+ {
+ vec normal(0.0f, 0.0f, 255.0f/emphasis);
+ normal.x += src[y*s.pitch + ((x+s.w-1)%s.w)*s.bpp];
+ normal.x -= src[y*s.pitch + ((x+1)%s.w)*s.bpp];
+ normal.y += src[((y+s.h-1)%s.h)*s.pitch + x*s.bpp];
+ normal.y -= src[((y+1)%s.h)*s.pitch + x*s.bpp];
+ normal.normalize();
+ *dst++ = uchar(127.5f + normal.x*127.5f);
+ *dst++ = uchar(127.5f + normal.y*127.5f);
+ *dst++ = uchar(127.5f + normal.z*127.5f);
+ }
+ s.replace(d);
}
template<int n, int bpp, bool normals>
static void blurtexture(int w, int h, uchar *dst, const uchar *src, int margin)
{
- static const int weights3x3[9] =
- {
- 0x10, 0x20, 0x10,
- 0x20, 0x40, 0x20,
- 0x10, 0x20, 0x10
- };
- static const int weights5x5[25] =
- {
- 0x05, 0x05, 0x09, 0x05, 0x05,
- 0x05, 0x0A, 0x14, 0x0A, 0x05,
- 0x09, 0x14, 0x28, 0x14, 0x09,
- 0x05, 0x0A, 0x14, 0x0A, 0x05,
- 0x05, 0x05, 0x09, 0x05, 0x05
- };
- const int *mat = n > 1 ? weights5x5 : weights3x3;
- int mstride = 2*n + 1,
- mstartoffset = n*(mstride + 1),
- stride = bpp*w,
- startoffset = n*bpp,
- nextoffset1 = stride + mstride*bpp,
- nextoffset2 = stride - mstride*bpp;
- src += margin*(stride + bpp);
- for(int y = margin; y < h-margin; y++)
- {
- for(int x = margin; x < w-margin; x++)
- {
- int dr = 0, dg = 0, db = 0;
- const uchar *p = src - startoffset;
- const int *m = mat + mstartoffset;
- for(int t = y; t >= y-n; t--, p -= nextoffset1, m -= mstride)
- {
- if(t < 0) p += stride;
- int a = 0;
- if(n > 1) { a += m[-2]; if(x >= 2) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp; }
- a += m[-1]; if(x >= 1) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp;
- int cr = p[0], cg = p[1], cb = p[2]; a += m[0]; dr += cr * a; dg += cg * a; db += cb * a; p += bpp;
- if(x+1 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[1]; dg += cg * m[1]; db += cb * m[1]; p += bpp;
- if(n > 1) { if(x+2 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[2]; dg += cg * m[2]; db += cb * m[2]; p += bpp; }
- }
- p = src - startoffset + stride;
- m = mat + mstartoffset + mstride;
- for(int t = y+1; t <= y+n; t++, p += nextoffset2, m += mstride)
- {
- if(t >= h) p -= stride;
- int a = 0;
- if(n > 1) { a += m[-2]; if(x >= 2) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp; }
- a += m[-1]; if(x >= 1) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp;
- int cr = p[0], cg = p[1], cb = p[2]; a += m[0]; dr += cr * a; dg += cg * a; db += cb * a; p += bpp;
- if(x+1 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[1]; dg += cg * m[1]; db += cb * m[1]; p += bpp;
- if(n > 1) { if(x+2 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[2]; dg += cg * m[2]; db += cb * m[2]; p += bpp; }
- }
- if(normals)
- {
- vec v(dr-0x7F80, dg-0x7F80, db-0x7F80);
- float mag = 127.5f/v.magnitude();
- dst[0] = uchar(v.x*mag + 127.5f);
- dst[1] = uchar(v.y*mag + 127.5f);
- dst[2] = uchar(v.z*mag + 127.5f);
- }
- else
- {
- dst[0] = dr>>8;
- dst[1] = dg>>8;
- dst[2] = db>>8;
- }
- if(bpp > 3) dst[3] = src[3];
- dst += bpp;
- src += bpp;
- }
- src += 2*margin*bpp;
- }
+ static const int weights3x3[9] =
+ {
+ 0x10, 0x20, 0x10,
+ 0x20, 0x40, 0x20,
+ 0x10, 0x20, 0x10
+ };
+ static const int weights5x5[25] =
+ {
+ 0x05, 0x05, 0x09, 0x05, 0x05,
+ 0x05, 0x0A, 0x14, 0x0A, 0x05,
+ 0x09, 0x14, 0x28, 0x14, 0x09,
+ 0x05, 0x0A, 0x14, 0x0A, 0x05,
+ 0x05, 0x05, 0x09, 0x05, 0x05
+ };
+ const int *mat = n > 1 ? weights5x5 : weights3x3;
+ int mstride = 2*n + 1,
+ mstartoffset = n*(mstride + 1),
+ stride = bpp*w,
+ startoffset = n*bpp,
+ nextoffset1 = stride + mstride*bpp,
+ nextoffset2 = stride - mstride*bpp;
+ src += margin*(stride + bpp);
+ for(int y = margin; y < h-margin; y++)
+ {
+ for(int x = margin; x < w-margin; x++)
+ {
+ int dr = 0, dg = 0, db = 0;
+ const uchar *p = src - startoffset;
+ const int *m = mat + mstartoffset;
+ for(int t = y; t >= y-n; t--, p -= nextoffset1, m -= mstride)
+ {
+ if(t < 0) p += stride;
+ int a = 0;
+ if(n > 1) { a += m[-2]; if(x >= 2) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp; }
+ a += m[-1]; if(x >= 1) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp;
+ int cr = p[0], cg = p[1], cb = p[2]; a += m[0]; dr += cr * a; dg += cg * a; db += cb * a; p += bpp;
+ if(x+1 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[1]; dg += cg * m[1]; db += cb * m[1]; p += bpp;
+ if(n > 1) { if(x+2 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[2]; dg += cg * m[2]; db += cb * m[2]; p += bpp; }
+ }
+ p = src - startoffset + stride;
+ m = mat + mstartoffset + mstride;
+ for(int t = y+1; t <= y+n; t++, p += nextoffset2, m += mstride)
+ {
+ if(t >= h) p -= stride;
+ int a = 0;
+ if(n > 1) { a += m[-2]; if(x >= 2) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp; }
+ a += m[-1]; if(x >= 1) { dr += p[0] * a; dg += p[1] * a; db += p[2] * a; a = 0; } p += bpp;
+ int cr = p[0], cg = p[1], cb = p[2]; a += m[0]; dr += cr * a; dg += cg * a; db += cb * a; p += bpp;
+ if(x+1 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[1]; dg += cg * m[1]; db += cb * m[1]; p += bpp;
+ if(n > 1) { if(x+2 < w) { cr = p[0]; cg = p[1]; cb = p[2]; } dr += cr * m[2]; dg += cg * m[2]; db += cb * m[2]; p += bpp; }
+ }
+ if(normals)
+ {
+ vec v(dr-0x7F80, dg-0x7F80, db-0x7F80);
+ float mag = 127.5f/v.magnitude();
+ dst[0] = uchar(v.x*mag + 127.5f);
+ dst[1] = uchar(v.y*mag + 127.5f);
+ dst[2] = uchar(v.z*mag + 127.5f);
+ }
+ else
+ {
+ dst[0] = dr>>8;
+ dst[1] = dg>>8;
+ dst[2] = db>>8;
+ }
+ if(bpp > 3) dst[3] = src[3];
+ dst += bpp;
+ src += bpp;
+ }
+ src += 2*margin*bpp;
+ }
}
void blurtexture(int n, int bpp, int w, int h, uchar *dst, const uchar *src, int margin)
{
- switch((clamp(n, 1, 2)<<4) | bpp)
- {
- case 0x13: blurtexture<1, 3, false>(w, h, dst, src, margin); break;
- case 0x23: blurtexture<2, 3, false>(w, h, dst, src, margin); break;
- case 0x14: blurtexture<1, 4, false>(w, h, dst, src, margin); break;
- case 0x24: blurtexture<2, 4, false>(w, h, dst, src, margin); break;
- }
+ switch((clamp(n, 1, 2)<<4) | bpp)
+ {
+ case 0x13: blurtexture<1, 3, false>(w, h, dst, src, margin); break;
+ case 0x23: blurtexture<2, 3, false>(w, h, dst, src, margin); break;
+ case 0x14: blurtexture<1, 4, false>(w, h, dst, src, margin); break;
+ case 0x24: blurtexture<2, 4, false>(w, h, dst, src, margin); break;
+ }
}
void blurnormals(int n, int w, int h, bvec *dst, const bvec *src, int margin)
{
- switch(clamp(n, 1, 2))
- {
- case 1: blurtexture<1, 3, true>(w, h, dst->v, src->v, margin); break;
- case 2: blurtexture<2, 3, true>(w, h, dst->v, src->v, margin); break;
- }
+ switch(clamp(n, 1, 2))
+ {
+ case 1: blurtexture<1, 3, true>(w, h, dst->v, src->v, margin); break;
+ case 2: blurtexture<2, 3, true>(w, h, dst->v, src->v, margin); break;
+ }
}
void texblur(ImageData &s, int n, int r)
{
- if(s.bpp < 3) return;
- loopi(r)
- {
- ImageData d(s.w, s.h, s.bpp);
- blurtexture(n, s.bpp, s.w, s.h, d.data, s.data);
- s.replace(d);
- }
+ if(s.bpp < 3) return;
+ loopi(r)
+ {
+ ImageData d(s.w, s.h, s.bpp);
+ blurtexture(n, s.bpp, s.w, s.h, d.data, s.data);
+ s.replace(d);
+ }
}
void scaleimage(ImageData &s, int w, int h)
{
- ImageData d(w, h, s.bpp);
- scaletexture(s.data, s.w, s.h, s.bpp, s.pitch, d.data, w, h);
- s.replace(d);
+ ImageData d(w, h, s.bpp);
+ scaletexture(s.data, s.w, s.h, s.bpp, s.pitch, d.data, w, h);
+ s.replace(d);
}
bool canloadsurface(const char *name)
{
- stream *f = openfile(name, "rb");
- if(!f) return false;
- delete f;
- return true;
+ stream *f = openfile(name, "rb");
+ if(!f) return false;
+ delete f;
+ return true;
}
SDL_Surface *loadsurface(const char *name)
{
- SDL_Surface *s = NULL;
- stream *z = openzipfile(name, "rb");
- if(z)
- {
- SDL_RWops *rw = z->rwops();
- if(rw)
- {
- char *ext = (char *)strrchr(name, '.');
- if(ext) ++ext;
- s = IMG_LoadTyped_RW(rw, 0, ext);
- SDL_FreeRW(rw);
- }
- delete z;
- }
- if(!s) s = IMG_Load(findfile(name, "rb"));
- return fixsurfaceformat(s);
+ SDL_Surface *s = NULL;
+ stream *z = openzipfile(name, "rb");
+ if(z)
+ {
+ SDL_RWops *rw = z->rwops();
+ if(rw)
+ {
+ char *ext = (char *)strrchr(name, '.');
+ if(ext) ++ext;
+ s = IMG_LoadTyped_RW(rw, 0, ext);
+ SDL_FreeRW(rw);
+ }
+ delete z;
+ }
+ if(!s) s = IMG_Load(findfile(name, "rb"));
+ return fixsurfaceformat(s);
}
static vec parsevec(const char *arg)
{
- vec v(0, 0, 0);
- int i = 0;
- for(; arg[0] && (!i || arg[0]=='/') && i<3; arg += strcspn(arg, "/,><"), i++)
- {
- if(i) arg++;
- v[i] = atof(arg);
- }
- if(i==1) v.y = v.z = v.x;
- return v;
+ vec v(0, 0, 0);
+ int i = 0;
+ for(; arg[0] && (!i || arg[0]=='/') && i<3; arg += strcspn(arg, "/,><"), i++)
+ {
+ if(i) arg++;
+ v[i] = atof(arg);
+ }
+ if(i==1) v.y = v.z = v.x;
+ return v;
}
static bool texturedata(ImageData &d, const char *tname, Slot::Tex *tex = NULL, bool msg = true, int *compress = NULL, int *wrap = NULL)
{
- const char *cmds = NULL, *file = tname;
-
- if(!tname)
- {
- if(!tex) return false;
- if(tex->name[0]=='<')
- {
- cmds = tex->name;
- file = strrchr(tex->name, '>');
- if(!file) { if(msg) conoutf(CON_ERROR, "could not load texture packages/%s", tex->name); return false; }
- file++;
- }
- else file = tex->name;
-
- static string pname;
- formatstring(pname, "packages/%s", file);
- file = path(pname);
- }
- else if(tname[0]=='<')
- {
- cmds = tname;
- file = strrchr(tname, '>');
- if(!file) { if(msg) conoutf(CON_ERROR, "could not load texture %s", tname); return false; }
- file++;
- }
-
- int flen = strlen(file);
- bool raw = !compress, guess = false;
- for(const char *pcmds = cmds; pcmds;)
- {
- #define PARSETEXCOMMANDS(cmds) \
- const char *cmd = NULL, *end = NULL, *arg[4] = { NULL, NULL, NULL, NULL }; \
- cmd = &cmds[1]; \
- end = strchr(cmd, '>'); \
- if(!end) break; \
- cmds = strchr(cmd, '<'); \
- size_t len = strcspn(cmd, ":,><"); \
- loopi(4) \
- { \
- arg[i] = strchr(i ? arg[i-1] : cmd, i ? ',' : ':'); \
- if(!arg[i] || arg[i] >= end) arg[i] = ""; \
- else arg[i]++; \
- }
- PARSETEXCOMMANDS(pcmds);
- if(matchstring(cmd, len, "thumbnail"))
- {
- raw = true;
- guess = flen >= 4 && !strchr(file+flen-4, '.');
- }
- else if(matchstring(cmd, len, "stub")) return canloadsurface(file);
- }
-
- if(msg) renderprogress(loadprogress, file);
-
- if(!d.data)
- {
- SDL_Surface *s = NULL;
- if(guess)
- {
- static const char *exts[] = {".jpg", ".png"};
- string ext;
- loopi(sizeof(exts)/sizeof(exts[0]))
- {
- copystring(ext, file);
- concatstring(ext, exts[i]);
- s = loadsurface(ext);
- if(s) break;
- }
- }
- else s = loadsurface(file);
- if(!s) { if(msg) conoutf(CON_ERROR, "could not load texture %s", file); return false; }
- int bpp = s->format->BitsPerPixel;
- if(bpp%8 || !texformat(bpp/8)) { SDL_FreeSurface(s); conoutf(CON_ERROR, "texture must be 8, 16, 24, or 32 bpp: %s", file); return false; }
- if(max(s->w, s->h) > (1<<12)) { SDL_FreeSurface(s); conoutf(CON_ERROR, "texture size exceeded %dx%d pixels: %s", 1<<12, 1<<12, file); return false; }
- d.wrap(s);
- }
-
- while(cmds)
- {
- PARSETEXCOMMANDS(cmds);
- if(d.compressed) goto compressed;
- if(matchstring(cmd, len, "mad")) texmad(d, parsevec(arg[0]), parsevec(arg[1]));
- else if(matchstring(cmd, len, "colorify")) texcolorify(d, parsevec(arg[0]), parsevec(arg[1]));
- else if(matchstring(cmd, len, "colormask")) texcolormask(d, parsevec(arg[0]), *arg[1] ? parsevec(arg[1]) : vec(1, 1, 1));
- else if(matchstring(cmd, len, "normal"))
- {
- int emphasis = atoi(arg[0]);
- texnormal(d, emphasis > 0 ? emphasis : 3);
- }
- else if(matchstring(cmd, len, "dup")) texdup(d, atoi(arg[0]), atoi(arg[1]));
- else if(matchstring(cmd, len, "offset")) texoffset(d, atoi(arg[0]), atoi(arg[1]));
- else if(matchstring(cmd, len, "rotate")) texrotate(d, atoi(arg[0]), tex ? tex->type : 0);
- else if(matchstring(cmd, len, "reorient")) texreorient(d, atoi(arg[0])>0, atoi(arg[1])>0, atoi(arg[2])>0, tex ? tex->type : TEX_DIFFUSE);
- else if(matchstring(cmd, len, "mix")) texmix(d, *arg[0] ? atoi(arg[0]) : -1, *arg[1] ? atoi(arg[1]) : -1, *arg[2] ? atoi(arg[2]) : -1, *arg[3] ? atoi(arg[3]) : -1);
- else if(matchstring(cmd, len, "grey")) texgrey(d);
- else if(matchstring(cmd, len, "blur"))
- {
- int emphasis = atoi(arg[0]), repeat = atoi(arg[1]);
- texblur(d, emphasis > 0 ? clamp(emphasis, 1, 2) : 1, repeat > 0 ? repeat : 1);
- }
- else if(matchstring(cmd, len, "premul")) texpremul(d);
- else if(matchstring(cmd, len, "agrad")) texagrad(d, atof(arg[0]), atof(arg[1]), atof(arg[2]), atof(arg[3]));
- else if(matchstring(cmd, len, "compress"))
- {
- int scale = atoi(arg[0]);
- if(scale <= 0) scale = 2;
- if(compress) *compress = scale;
- }
- else if(matchstring(cmd, len, "nocompress"))
- {
- if(compress) *compress = -1;
- }
- else if(matchstring(cmd, len, "thumbnail"))
- {
- int w = atoi(arg[0]), h = atoi(arg[1]);
- if(w <= 0 || w > (1<<12)) w = 64;
- if(h <= 0 || h > (1<<12)) h = w;
- if(d.w > w || d.h > h) scaleimage(d, w, h);
- }
- else
- compressed:
- if(matchstring(cmd, len, "mirror"))
- {
- if(wrap) *wrap |= 0x300;
- }
- else if(matchstring(cmd, len, "noswizzle"))
- {
- if(wrap) *wrap |= 0x10000;
- }
- }
-
- return true;
+ const char *cmds = NULL, *file = tname;
+
+ if(!tname)
+ {
+ if(!tex) return false;
+ if(tex->name[0]=='<')
+ {
+ cmds = tex->name;
+ file = strrchr(tex->name, '>');
+ if(!file) { if(msg) conoutf(CON_ERROR, "could not load texture packages/%s", tex->name); return false; }
+ file++;
+ }
+ else file = tex->name;
+
+ static string pname;
+ formatstring(pname, "packages/%s", file);
+ file = path(pname);
+ }
+ else if(tname[0]=='<')
+ {
+ cmds = tname;
+ file = strrchr(tname, '>');
+ if(!file) { if(msg) conoutf(CON_ERROR, "could not load texture %s", tname); return false; }
+ file++;
+ }
+
+ int flen = strlen(file);
+ bool raw = !compress, guess = false;
+ for(const char *pcmds = cmds; pcmds;)
+ {
+ #define PARSETEXCOMMANDS(cmds) \
+ const char *cmd = NULL, *end = NULL, *arg[4] = { NULL, NULL, NULL, NULL }; \
+ cmd = &cmds[1]; \
+ end = strchr(cmd, '>'); \
+ if(!end) break; \
+ cmds = strchr(cmd, '<'); \
+ size_t len = strcspn(cmd, ":,><"); \
+ loopi(4) \
+ { \
+ arg[i] = strchr(i ? arg[i-1] : cmd, i ? ',' : ':'); \
+ if(!arg[i] || arg[i] >= end) arg[i] = ""; \
+ else arg[i]++; \
+ }
+ PARSETEXCOMMANDS(pcmds);
+ if(matchstring(cmd, len, "thumbnail"))
+ {
+ raw = true;
+ guess = flen >= 4 && !strchr(file+flen-4, '.');
+ }
+ else if(matchstring(cmd, len, "stub")) return canloadsurface(file);
+ }
+
+ if(msg) renderprogress(loadprogress, file);
+
+ if(!d.data)
+ {
+ SDL_Surface *s = NULL;
+ if(guess)
+ {
+ static const char *exts[] = {".jpg", ".png"};
+ string ext;
+ loopi(sizeof(exts)/sizeof(exts[0]))
+ {
+ copystring(ext, file);
+ concatstring(ext, exts[i]);
+ s = loadsurface(ext);
+ if(s) break;
+ }
+ }
+ else s = loadsurface(file);
+ if(!s) { if(msg) conoutf(CON_ERROR, "could not load texture %s", file); return false; }
+ int bpp = s->format->BitsPerPixel;
+ if(bpp%8 || !texformat(bpp/8)) { SDL_FreeSurface(s); conoutf(CON_ERROR, "texture must be 8, 16, 24, or 32 bpp: %s", file); return false; }
+ if(max(s->w, s->h) > (1<<12)) { SDL_FreeSurface(s); conoutf(CON_ERROR, "texture size exceeded %dx%d pixels: %s", 1<<12, 1<<12, file); return false; }
+ d.wrap(s);
+ }
+
+ while(cmds)
+ {
+ PARSETEXCOMMANDS(cmds);
+ if(d.compressed) goto compressed;
+ if(matchstring(cmd, len, "mad")) texmad(d, parsevec(arg[0]), parsevec(arg[1]));
+ else if(matchstring(cmd, len, "colorify")) texcolorify(d, parsevec(arg[0]), parsevec(arg[1]));
+ else if(matchstring(cmd, len, "colormask")) texcolormask(d, parsevec(arg[0]), *arg[1] ? parsevec(arg[1]) : vec(1, 1, 1));
+ else if(matchstring(cmd, len, "normal"))
+ {
+ int emphasis = atoi(arg[0]);
+ texnormal(d, emphasis > 0 ? emphasis : 3);
+ }
+ else if(matchstring(cmd, len, "dup")) texdup(d, atoi(arg[0]), atoi(arg[1]));
+ else if(matchstring(cmd, len, "offset")) texoffset(d, atoi(arg[0]), atoi(arg[1]));
+ else if(matchstring(cmd, len, "rotate")) texrotate(d, atoi(arg[0]), tex ? tex->type : 0);
+ else if(matchstring(cmd, len, "reorient")) texreorient(d, atoi(arg[0])>0, atoi(arg[1])>0, atoi(arg[2])>0, tex ? tex->type : TEX_DIFFUSE);
+ else if(matchstring(cmd, len, "mix")) texmix(d, *arg[0] ? atoi(arg[0]) : -1, *arg[1] ? atoi(arg[1]) : -1, *arg[2] ? atoi(arg[2]) : -1, *arg[3] ? atoi(arg[3]) : -1);
+ else if(matchstring(cmd, len, "grey")) texgrey(d);
+ else if(matchstring(cmd, len, "blur"))
+ {
+ int emphasis = atoi(arg[0]), repeat = atoi(arg[1]);
+ texblur(d, emphasis > 0 ? clamp(emphasis, 1, 2) : 1, repeat > 0 ? repeat : 1);
+ }
+ else if(matchstring(cmd, len, "premul")) texpremul(d);
+ else if(matchstring(cmd, len, "agrad")) texagrad(d, atof(arg[0]), atof(arg[1]), atof(arg[2]), atof(arg[3]));
+ else if(matchstring(cmd, len, "compress"))
+ {
+ int scale = atoi(arg[0]);
+ if(scale <= 0) scale = 2;
+ if(compress) *compress = scale;
+ }
+ else if(matchstring(cmd, len, "nocompress"))
+ {
+ if(compress) *compress = -1;
+ }
+ else if(matchstring(cmd, len, "thumbnail"))
+ {
+ int w = atoi(arg[0]), h = atoi(arg[1]);
+ if(w <= 0 || w > (1<<12)) w = 64;
+ if(h <= 0 || h > (1<<12)) h = w;
+ if(d.w > w || d.h > h) scaleimage(d, w, h);
+ }
+ else
+ compressed:
+ if(matchstring(cmd, len, "mirror"))
+ {
+ if(wrap) *wrap |= 0x300;
+ }
+ else if(matchstring(cmd, len, "noswizzle"))
+ {
+ if(wrap) *wrap |= 0x10000;
+ }
+ }
+
+ return true;
}
uchar *loadalphamask(Texture *t)
{
- if(t->alphamask) return t->alphamask;
- if(!(t->type&Texture::ALPHA)) return NULL;
- ImageData s;
- if(!texturedata(s, t->name, NULL, false) || !s.data || s.compressed) return NULL;
- t->alphamask = new uchar[s.h * ((s.w+7)/8)];
- uchar *srcrow = s.data, *dst = t->alphamask-1;
- loop(y, s.h)
- {
- uchar *src = srcrow+s.bpp-1;
- loop(x, s.w)
- {
- int offset = x%8;
- if(!offset) *++dst = 0;
- if(*src) *dst |= 1<<offset;
- src += s.bpp;
- }
- srcrow += s.pitch;
- }
- return t->alphamask;
+ if(t->alphamask) return t->alphamask;
+ if(!(t->type&Texture::ALPHA)) return NULL;
+ ImageData s;
+ if(!texturedata(s, t->name, NULL, false) || !s.data || s.compressed) return NULL;
+ t->alphamask = new uchar[s.h * ((s.w+7)/8)];
+ uchar *srcrow = s.data, *dst = t->alphamask-1;
+ loop(y, s.h)
+ {
+ uchar *src = srcrow+s.bpp-1;
+ loop(x, s.w)
+ {
+ int offset = x%8;
+ if(!offset) *++dst = 0;
+ if(*src) *dst |= 1<<offset;
+ src += s.bpp;
+ }
+ srcrow += s.pitch;
+ }
+ return t->alphamask;
}
Texture *textureload(const char *name, int clamp, bool mipit, bool msg)
{
- string tname;
- copystring(tname, name);
- Texture *t = textures.access(path(tname));
- if(t) return t;
- int compress = 0;
- ImageData s;
- if(texturedata(s, tname, NULL, msg, &compress, &clamp)) return newtexture(NULL, tname, s, clamp, mipit, false, false, compress);
- return notexture;
+ string tname;
+ copystring(tname, name);
+ Texture *t = textures.access(path(tname));
+ if(t) return t;
+ int compress = 0;
+ ImageData s;
+ if(texturedata(s, tname, NULL, msg, &compress, &clamp)) return newtexture(NULL, tname, s, clamp, mipit, false, false, compress);
+ return notexture;
}
bool settexture(const char *name, int clamp)
{
- Texture *t = textureload(name, clamp, true, false);
- glBindTexture(GL_TEXTURE_2D, t->id);
- return t != notexture;
+ Texture *t = textureload(name, clamp, true, false);
+ glBindTexture(GL_TEXTURE_2D, t->id);
+ return t != notexture;
}
vector<VSlot *> vslots;
@@ -1571,30 +1535,30 @@ VSlot dummyvslot(&dummyslot);
void texturereset(int *n)
{
- if(!(identflags&IDF_OVERRIDDEN) && !game::allowedittoggle()) return;
- resetslotshader();
- int limit = clamp(*n, 0, slots.length());
- for(int i = limit; i < slots.length(); i++)
- {
- Slot *s = slots[i];
- for(VSlot *vs = s->variants; vs; vs = vs->next) vs->slot = &dummyslot;
- delete s;
- }
- slots.setsize(limit);
- while(vslots.length())
- {
- VSlot *vs = vslots.last();
- if(vs->slot != &dummyslot || vs->changed) break;
- delete vslots.pop();
- }
+ if(!(identflags&IDF_OVERRIDDEN) && !game::allowedittoggle()) return;
+ resetslotshader();
+ int limit = clamp(*n, 0, slots.length());
+ for(int i = limit; i < slots.length(); i++)
+ {
+ Slot *s = slots[i];
+ for(VSlot *vs = s->variants; vs; vs = vs->next) vs->slot = &dummyslot;
+ delete s;
+ }
+ slots.setsize(limit);
+ while(vslots.length())
+ {
+ VSlot *vs = vslots.last();
+ if(vs->slot != &dummyslot || vs->changed) break;
+ delete vslots.pop();
+ }
}
COMMAND(texturereset, "i");
void materialreset()
{
- if(!(identflags&IDF_OVERRIDDEN) && !game::allowedittoggle()) return;
- loopi((MATF_VOLUME|MATF_INDEX)+1) materialslots[i].reset();
+ if(!(identflags&IDF_OVERRIDDEN) && !game::allowedittoggle()) return;
+ loopi((MATF_VOLUME|MATF_INDEX)+1) materialslots[i].reset();
}
COMMAND(materialreset, "");
@@ -1604,1042 +1568,1005 @@ static bool markingvslots = false;
void clearslots()
{
- resetslotshader();
- slots.deletecontents();
- vslots.deletecontents();
- loopi((MATF_VOLUME|MATF_INDEX)+1) materialslots[i].reset();
- clonedvslots = 0;
+ resetslotshader();
+ slots.deletecontents();
+ vslots.deletecontents();
+ loopi((MATF_VOLUME|MATF_INDEX)+1) materialslots[i].reset();
+ clonedvslots = 0;
}
static void assignvslot(VSlot &vs);
static inline void assignvslotlayer(VSlot &vs)
{
- if(vs.layer && vslots.inrange(vs.layer))
- {
- VSlot &layer = *vslots[vs.layer];
- if(layer.index < 0) assignvslot(layer);
- }
+ if(vs.layer && vslots.inrange(vs.layer))
+ {
+ VSlot &layer = *vslots[vs.layer];
+ if(layer.index < 0) assignvslot(layer);
+ }
}
static void assignvslot(VSlot &vs)
{
- vs.index = compactedvslots++;
- assignvslotlayer(vs);
+ vs.index = compactedvslots++;
+ assignvslotlayer(vs);
}
void compactvslot(int &index)
{
- if(vslots.inrange(index))
- {
- VSlot &vs = *vslots[index];
- if(vs.index < 0) assignvslot(vs);
- if(!markingvslots) index = vs.index;
- }
+ if(vslots.inrange(index))
+ {
+ VSlot &vs = *vslots[index];
+ if(vs.index < 0) assignvslot(vs);
+ if(!markingvslots) index = vs.index;
+ }
}
void compactvslot(VSlot &vs)
{
- if(vs.index < 0) assignvslot(vs);
+ if(vs.index < 0) assignvslot(vs);
}
void compactvslots(cube *c, int n)
{
- if((compactvslotsprogress++&0xFFF)==0) renderprogress(min(float(compactvslotsprogress)/allocnodes, 1.0f), markingvslots ? "marking slots..." : "compacting slots...");
- loopi(n)
- {
- if(c[i].children) compactvslots(c[i].children);
- else loopj(6) if(vslots.inrange(c[i].texture[j]))
- {
- VSlot &vs = *vslots[c[i].texture[j]];
- if(vs.index < 0) assignvslot(vs);
- if(!markingvslots) c[i].texture[j] = vs.index;
- }
- }
+ if((compactvslotsprogress++&0xFFF)==0) renderprogress(min(float(compactvslotsprogress)/allocnodes, 1.0f), markingvslots ? "marking slots..." : "compacting slots...");
+ loopi(n)
+ {
+ if(c[i].children) compactvslots(c[i].children);
+ else loopj(6) if(vslots.inrange(c[i].texture[j]))
+ {
+ VSlot &vs = *vslots[c[i].texture[j]];
+ if(vs.index < 0) assignvslot(vs);
+ if(!markingvslots) c[i].texture[j] = vs.index;
+ }
+ }
}
int compactvslots()
{
- clonedvslots = 0;
- markingvslots = false;
- compactedvslots = 0;
- compactvslotsprogress = 0;
- loopv(vslots) vslots[i]->index = -1;
- loopv(slots) slots[i]->variants->index = compactedvslots++;
- loopv(slots) assignvslotlayer(*slots[i]->variants);
- loopv(vslots)
- {
- VSlot &vs = *vslots[i];
- if(!vs.changed && vs.index < 0) { markingvslots = true; break; }
- }
- compactvslots(worldroot);
- int total = compactedvslots;
- compacteditvslots();
- loopv(vslots)
- {
- VSlot *vs = vslots[i];
- if(vs->changed) continue;
- while(vs->next)
- {
- if(vs->next->index < 0) vs->next = vs->next->next;
- else vs = vs->next;
- }
- }
- if(markingvslots)
- {
- markingvslots = false;
- compactedvslots = 0;
- compactvslotsprogress = 0;
- int lastdiscard = 0;
- loopv(vslots)
- {
- VSlot &vs = *vslots[i];
- if(vs.changed || (vs.index < 0 && !vs.next)) vs.index = -1;
- else
- {
- while(lastdiscard < i)
- {
- VSlot &ds = *vslots[lastdiscard++];
- if(!ds.changed && ds.index < 0) ds.index = compactedvslots++;
- }
- vs.index = compactedvslots++;
- }
- }
- compactvslots(worldroot);
- total = compactedvslots;
- compacteditvslots();
- }
- compactmruvslots();
- loopv(vslots)
- {
- VSlot &vs = *vslots[i];
- if(vs.index >= 0 && vs.layer && vslots.inrange(vs.layer)) vs.layer = vslots[vs.layer]->index;
- }
- loopv(vslots)
- {
- while(vslots[i]->index >= 0 && vslots[i]->index != i)
- swap(vslots[i], vslots[vslots[i]->index]);
- }
- for(int i = compactedvslots; i < vslots.length(); i++) delete vslots[i];
- vslots.setsize(compactedvslots);
- return total;
+ clonedvslots = 0;
+ markingvslots = false;
+ compactedvslots = 0;
+ compactvslotsprogress = 0;
+ loopv(vslots) vslots[i]->index = -1;
+ loopv(slots) slots[i]->variants->index = compactedvslots++;
+ loopv(slots) assignvslotlayer(*slots[i]->variants);
+ loopv(vslots)
+ {
+ VSlot &vs = *vslots[i];
+ if(!vs.changed && vs.index < 0) { markingvslots = true; break; }
+ }
+ compactvslots(worldroot);
+ int total = compactedvslots;
+ compacteditvslots();
+ loopv(vslots)
+ {
+ VSlot *vs = vslots[i];
+ if(vs->changed) continue;
+ while(vs->next)
+ {
+ if(vs->next->index < 0) vs->next = vs->next->next;
+ else vs = vs->next;
+ }
+ }
+ if(markingvslots)
+ {
+ markingvslots = false;
+ compactedvslots = 0;
+ compactvslotsprogress = 0;
+ int lastdiscard = 0;
+ loopv(vslots)
+ {
+ VSlot &vs = *vslots[i];
+ if(vs.changed || (vs.index < 0 && !vs.next)) vs.index = -1;
+ else
+ {
+ while(lastdiscard < i)
+ {
+ VSlot &ds = *vslots[lastdiscard++];
+ if(!ds.changed && ds.index < 0) ds.index = compactedvslots++;
+ }
+ vs.index = compactedvslots++;
+ }
+ }
+ compactvslots(worldroot);
+ total = compactedvslots;
+ compacteditvslots();
+ }
+ compactmruvslots();
+ loopv(vslots)
+ {
+ VSlot &vs = *vslots[i];
+ if(vs.index >= 0 && vs.layer && vslots.inrange(vs.layer)) vs.layer = vslots[vs.layer]->index;
+ }
+ loopv(vslots)
+ {
+ while(vslots[i]->index >= 0 && vslots[i]->index != i)
+ swap(vslots[i], vslots[vslots[i]->index]);
+ }
+ for(int i = compactedvslots; i < vslots.length(); i++) delete vslots[i];
+ vslots.setsize(compactedvslots);
+ return total;
}
ICOMMAND(compactvslots, "", (),
{
- extern int nompedit;
- if(nompedit && multiplayer()) return;
- compactvslots();
- allchanged();
+ extern int nompedit;
+ if(nompedit && multiplayer()) return;
+ compactvslots();
+ allchanged();
});
static Slot &loadslot(Slot &s, bool forceload);
static void clampvslotoffset(VSlot &dst, Slot *slot = NULL)
{
- if(!slot) slot = dst.slot;
- if(slot && slot->sts.inrange(0))
- {
- if(!slot->loaded) loadslot(*slot, false);
- Texture *t = slot->sts[0].t;
- int xs = t->xs, ys = t->ys;
- if(t->type & Texture::MIRROR) { xs *= 2; ys *= 2; }
- if(texrotations[dst.rotation].swapxy) swap(xs, ys);
- dst.offset.x %= xs; if(dst.offset.x < 0) dst.offset.x += xs;
- dst.offset.y %= ys; if(dst.offset.y < 0) dst.offset.y += ys;
- }
- else dst.offset.max(0);
+ if(!slot) slot = dst.slot;
+ if(slot && slot->sts.inrange(0))
+ {
+ if(!slot->loaded) loadslot(*slot, false);
+ Texture *t = slot->sts[0].t;
+ int xs = t->xs, ys = t->ys;
+ if(t->type & Texture::MIRROR) { xs *= 2; ys *= 2; }
+ if(texrotations[dst.rotation].swapxy) swap(xs, ys);
+ dst.offset.x %= xs; if(dst.offset.x < 0) dst.offset.x += xs;
+ dst.offset.y %= ys; if(dst.offset.y < 0) dst.offset.y += ys;
+ }
+ else dst.offset.max(0);
}
static void propagatevslot(VSlot &dst, const VSlot &src, int diff, bool edit = false)
{
- if(diff & (1<<VSLOT_SHPARAM)) loopv(src.params) dst.params.add(src.params[i]);
- if(diff & (1<<VSLOT_SCALE)) dst.scale = src.scale;
- if(diff & (1<<VSLOT_ROTATION))
- {
- dst.rotation = src.rotation;
- if(edit && !dst.offset.iszero()) clampvslotoffset(dst);
- }
- if(diff & (1<<VSLOT_OFFSET))
- {
- dst.offset = src.offset;
- if(edit) clampvslotoffset(dst);
- }
- if(diff & (1<<VSLOT_SCROLL)) dst.scroll = src.scroll;
- if(diff & (1<<VSLOT_LAYER)) dst.layer = src.layer;
- if(diff & (1<<VSLOT_ALPHA))
- {
- dst.alphafront = src.alphafront;
- dst.alphaback = src.alphaback;
- }
- if(diff & (1<<VSLOT_COLOR)) dst.colorscale = src.colorscale;
+ if(diff & (1<<VSLOT_SHPARAM)) loopv(src.params) dst.params.add(src.params[i]);
+ if(diff & (1<<VSLOT_SCALE)) dst.scale = src.scale;
+ if(diff & (1<<VSLOT_ROTATION))
+ {
+ dst.rotation = src.rotation;
+ if(edit && !dst.offset.iszero()) clampvslotoffset(dst);
+ }
+ if(diff & (1<<VSLOT_OFFSET))
+ {
+ dst.offset = src.offset;
+ if(edit) clampvslotoffset(dst);
+ }
+ if(diff & (1<<VSLOT_SCROLL)) dst.scroll = src.scroll;
+ if(diff & (1<<VSLOT_LAYER)) dst.layer = src.layer;
+ if(diff & (1<<VSLOT_ALPHA))
+ {
+ dst.alphafront = src.alphafront;
+ dst.alphaback = src.alphaback;
+ }
+ if(diff & (1<<VSLOT_COLOR)) dst.colorscale = src.colorscale;
}
static void propagatevslot(VSlot *root, int changed)
{
- for(VSlot *vs = root->next; vs; vs = vs->next)
- {
- int diff = changed & ~vs->changed;
- if(diff) propagatevslot(*vs, *root, diff);
- }
+ for(VSlot *vs = root->next; vs; vs = vs->next)
+ {
+ int diff = changed & ~vs->changed;
+ if(diff) propagatevslot(*vs, *root, diff);
+ }
}
static void mergevslot(VSlot &dst, const VSlot &src, int diff, Slot *slot = NULL)
{
- if(diff & (1<<VSLOT_SHPARAM)) loopv(src.params)
- {
- const SlotShaderParam &sp = src.params[i];
- loopvj(dst.params)
- {
- SlotShaderParam &dp = dst.params[j];
- if(sp.name == dp.name)
- {
- memcpy(dp.val, sp.val, sizeof(dp.val));
- goto nextparam;
- }
- }
- dst.params.add(sp);
- nextparam:;
- }
- if(diff & (1<<VSLOT_SCALE))
- {
- dst.scale = clamp(dst.scale*src.scale, 1/8.0f, 8.0f);
- }
- if(diff & (1<<VSLOT_ROTATION))
- {
- dst.rotation = clamp(dst.rotation + src.rotation, 0, 7);
- if(!dst.offset.iszero()) clampvslotoffset(dst, slot);
- }
- if(diff & (1<<VSLOT_OFFSET))
- {
- dst.offset.add(src.offset);
- clampvslotoffset(dst, slot);
- }
- if(diff & (1<<VSLOT_SCROLL)) dst.scroll.add(src.scroll);
- if(diff & (1<<VSLOT_LAYER)) dst.layer = src.layer;
- if(diff & (1<<VSLOT_ALPHA))
- {
- dst.alphafront = src.alphafront;
- dst.alphaback = src.alphaback;
- }
- if(diff & (1<<VSLOT_COLOR)) dst.colorscale.mul(src.colorscale);
+ if(diff & (1<<VSLOT_SHPARAM)) loopv(src.params)
+ {
+ const SlotShaderParam &sp = src.params[i];
+ loopvj(dst.params)
+ {
+ SlotShaderParam &dp = dst.params[j];
+ if(sp.name == dp.name)
+ {
+ memcpy(dp.val, sp.val, sizeof(dp.val));
+ goto nextparam;
+ }
+ }
+ dst.params.add(sp);
+ nextparam:;
+ }
+ if(diff & (1<<VSLOT_SCALE))
+ {
+ dst.scale = clamp(dst.scale*src.scale, 1/8.0f, 8.0f);
+ }
+ if(diff & (1<<VSLOT_ROTATION))
+ {
+ dst.rotation = clamp(dst.rotation + src.rotation, 0, 7);
+ if(!dst.offset.iszero()) clampvslotoffset(dst, slot);
+ }
+ if(diff & (1<<VSLOT_OFFSET))
+ {
+ dst.offset.add(src.offset);
+ clampvslotoffset(dst, slot);
+ }
+ if(diff & (1<<VSLOT_SCROLL)) dst.scroll.add(src.scroll);
+ if(diff & (1<<VSLOT_LAYER)) dst.layer = src.layer;
+ if(diff & (1<<VSLOT_ALPHA))
+ {
+ dst.alphafront = src.alphafront;
+ dst.alphaback = src.alphaback;
+ }
+ if(diff & (1<<VSLOT_COLOR)) dst.colorscale.mul(src.colorscale);
}
void mergevslot(VSlot &dst, const VSlot &src, const VSlot &delta)
{
- dst.changed = src.changed | delta.changed;
- propagatevslot(dst, src, (1<<VSLOT_NUM)-1);
- mergevslot(dst, delta, delta.changed, src.slot);
+ dst.changed = src.changed | delta.changed;
+ propagatevslot(dst, src, (1<<VSLOT_NUM)-1);
+ mergevslot(dst, delta, delta.changed, src.slot);
}
static VSlot *reassignvslot(Slot &owner, VSlot *vs)
{
- owner.variants = vs;
- while(vs)
- {
- vs->slot = &owner;
- vs->linked = false;
- vs = vs->next;
- }
- return owner.variants;
+ owner.variants = vs;
+ while(vs)
+ {
+ vs->slot = &owner;
+ vs->linked = false;
+ vs = vs->next;
+ }
+ return owner.variants;
}
static VSlot *emptyvslot(Slot &owner)
{
- int offset = 0;
- loopvrev(slots) if(slots[i]->variants) { offset = slots[i]->variants->index + 1; break; }
- for(int i = offset; i < vslots.length(); i++) if(!vslots[i]->changed) return reassignvslot(owner, vslots[i]);
- return vslots.add(new VSlot(&owner, vslots.length()));
+ int offset = 0;
+ loopvrev(slots) if(slots[i]->variants) { offset = slots[i]->variants->index + 1; break; }
+ for(int i = offset; i < vslots.length(); i++) if(!vslots[i]->changed) return reassignvslot(owner, vslots[i]);
+ return vslots.add(new VSlot(&owner, vslots.length()));
}
static bool comparevslot(const VSlot &dst, const VSlot &src, int diff)
{
- if(diff & (1<<VSLOT_SHPARAM))
- {
- if(src.params.length() != dst.params.length()) return false;
- loopv(src.params)
- {
- const SlotShaderParam &sp = src.params[i], &dp = dst.params[i];
- if(sp.name != dp.name || memcmp(sp.val, dp.val, sizeof(sp.val))) return false;
- }
- }
- if(diff & (1<<VSLOT_SCALE) && dst.scale != src.scale) return false;
- if(diff & (1<<VSLOT_ROTATION) && dst.rotation != src.rotation) return false;
- if(diff & (1<<VSLOT_OFFSET) && dst.offset != src.offset) return false;
- if(diff & (1<<VSLOT_SCROLL) && dst.scroll != src.scroll) return false;
- if(diff & (1<<VSLOT_LAYER) && dst.layer != src.layer) return false;
- if(diff & (1<<VSLOT_ALPHA) && (dst.alphafront != src.alphafront || dst.alphaback != src.alphaback)) return false;
- if(diff & (1<<VSLOT_COLOR) && dst.colorscale != src.colorscale) return false;
- return true;
+ if(diff & (1<<VSLOT_SHPARAM))
+ {
+ if(src.params.length() != dst.params.length()) return false;
+ loopv(src.params)
+ {
+ const SlotShaderParam &sp = src.params[i], &dp = dst.params[i];
+ if(sp.name != dp.name || memcmp(sp.val, dp.val, sizeof(sp.val))) return false;
+ }
+ }
+ if(diff & (1<<VSLOT_SCALE) && dst.scale != src.scale) return false;
+ if(diff & (1<<VSLOT_ROTATION) && dst.rotation != src.rotation) return false;
+ if(diff & (1<<VSLOT_OFFSET) && dst.offset != src.offset) return false;
+ if(diff & (1<<VSLOT_SCROLL) && dst.scroll != src.scroll) return false;
+ if(diff & (1<<VSLOT_LAYER) && dst.layer != src.layer) return false;
+ if(diff & (1<<VSLOT_ALPHA) && (dst.alphafront != src.alphafront || dst.alphaback != src.alphaback)) return false;
+ if(diff & (1<<VSLOT_COLOR) && dst.colorscale != src.colorscale) return false;
+ return true;
}
void packvslot(vector<uchar> &buf, const VSlot &src)
{
- if(src.changed & (1<<VSLOT_SHPARAM))
- {
- loopv(src.params)
- {
- const SlotShaderParam &p = src.params[i];
- buf.put(VSLOT_SHPARAM);
- sendstring(p.name, buf);
- loopj(4) putfloat(buf, p.val[j]);
- }
- }
- if(src.changed & (1<<VSLOT_SCALE))
- {
- buf.put(VSLOT_SCALE);
- putfloat(buf, src.scale);
- }
- if(src.changed & (1<<VSLOT_ROTATION))
- {
- buf.put(VSLOT_ROTATION);
- putint(buf, src.rotation);
- }
- if(src.changed & (1<<VSLOT_OFFSET))
- {
- buf.put(VSLOT_OFFSET);
- putint(buf, src.offset.x);
- putint(buf, src.offset.y);
- }
- if(src.changed & (1<<VSLOT_SCROLL))
- {
- buf.put(VSLOT_SCROLL);
- putfloat(buf, src.scroll.x);
- putfloat(buf, src.scroll.y);
- }
- if(src.changed & (1<<VSLOT_LAYER))
- {
- buf.put(VSLOT_LAYER);
- putuint(buf, vslots.inrange(src.layer) && !vslots[src.layer]->changed ? src.layer : 0);
- }
- if(src.changed & (1<<VSLOT_ALPHA))
- {
- buf.put(VSLOT_ALPHA);
- putfloat(buf, src.alphafront);
- putfloat(buf, src.alphaback);
- }
- if(src.changed & (1<<VSLOT_COLOR))
- {
- buf.put(VSLOT_COLOR);
- putfloat(buf, src.colorscale.r);
- putfloat(buf, src.colorscale.g);
- putfloat(buf, src.colorscale.b);
- }
- buf.put(0xFF);
+ if(src.changed & (1<<VSLOT_SHPARAM))
+ {
+ loopv(src.params)
+ {
+ const SlotShaderParam &p = src.params[i];
+ buf.put(VSLOT_SHPARAM);
+ sendstring(p.name, buf);
+ loopj(4) putfloat(buf, p.val[j]);
+ }
+ }
+ if(src.changed & (1<<VSLOT_SCALE))
+ {
+ buf.put(VSLOT_SCALE);
+ putfloat(buf, src.scale);
+ }
+ if(src.changed & (1<<VSLOT_ROTATION))
+ {
+ buf.put(VSLOT_ROTATION);
+ putint(buf, src.rotation);
+ }
+ if(src.changed & (1<<VSLOT_OFFSET))
+ {
+ buf.put(VSLOT_OFFSET);
+ putint(buf, src.offset.x);
+ putint(buf, src.offset.y);
+ }
+ if(src.changed & (1<<VSLOT_SCROLL))
+ {
+ buf.put(VSLOT_SCROLL);
+ putfloat(buf, src.scroll.x);
+ putfloat(buf, src.scroll.y);
+ }
+ if(src.changed & (1<<VSLOT_LAYER))
+ {
+ buf.put(VSLOT_LAYER);
+ putuint(buf, vslots.inrange(src.layer) && !vslots[src.layer]->changed ? src.layer : 0);
+ }
+ if(src.changed & (1<<VSLOT_ALPHA))
+ {
+ buf.put(VSLOT_ALPHA);
+ putfloat(buf, src.alphafront);
+ putfloat(buf, src.alphaback);
+ }
+ if(src.changed & (1<<VSLOT_COLOR))
+ {
+ buf.put(VSLOT_COLOR);
+ putfloat(buf, src.colorscale.r);
+ putfloat(buf, src.colorscale.g);
+ putfloat(buf, src.colorscale.b);
+ }
+ buf.put(0xFF);
}
void packvslot(vector<uchar> &buf, int index)
{
- if(vslots.inrange(index)) packvslot(buf, *vslots[index]);
- else buf.put(0xFF);
+ if(vslots.inrange(index)) packvslot(buf, *vslots[index]);
+ else buf.put(0xFF);
}
void packvslot(vector<uchar> &buf, const VSlot *vs)
{
- if(vs) packvslot(buf, *vs);
- else buf.put(0xFF);
+ if(vs) packvslot(buf, *vs);
+ else buf.put(0xFF);
}
bool unpackvslot(ucharbuf &buf, VSlot &dst, bool delta)
{
- while(buf.remaining())
- {
- int changed = buf.get();
- if(changed >= 0x80) break;
- switch(changed)
- {
- case VSLOT_SHPARAM:
- {
- string name;
- getstring(name, buf);
- SlotShaderParam p = { name[0] ? getshaderparamname(name) : NULL, -1, { 0, 0, 0, 0 } };
- loopi(4) p.val[i] = getfloat(buf);
- if(p.name) dst.params.add(p);
- break;
- }
- case VSLOT_SCALE:
- dst.scale = getfloat(buf);
- if(dst.scale <= 0) dst.scale = 1;
- else if(!delta) dst.scale = clamp(dst.scale, 1/8.0f, 8.0f);
- break;
- case VSLOT_ROTATION:
- dst.rotation = getint(buf);
- if(!delta) dst.rotation = clamp(dst.rotation, 0, 7);
- break;
- case VSLOT_OFFSET:
- dst.offset.x = getint(buf);
- dst.offset.y = getint(buf);
- if(!delta) dst.offset.max(0);
- break;
- case VSLOT_SCROLL:
- dst.scroll.x = getfloat(buf);
- dst.scroll.y = getfloat(buf);
- break;
- case VSLOT_LAYER:
- {
- int tex = getuint(buf);
- dst.layer = vslots.inrange(tex) ? tex : 0;
- break;
- }
- case VSLOT_ALPHA:
- dst.alphafront = clamp(getfloat(buf), 0.0f, 1.0f);
- dst.alphaback = clamp(getfloat(buf), 0.0f, 1.0f);
- break;
- case VSLOT_COLOR:
- dst.colorscale.r = clamp(getfloat(buf), 0.0f, 2.0f);
- dst.colorscale.g = clamp(getfloat(buf), 0.0f, 2.0f);
- dst.colorscale.b = clamp(getfloat(buf), 0.0f, 2.0f);
- break;
- default:
- return false;
- }
- dst.changed |= 1<<changed;
- }
- if(buf.overread()) return false;
- return true;
+ while(buf.remaining())
+ {
+ int changed = buf.get();
+ if(changed >= 0x80) break;
+ switch(changed)
+ {
+ case VSLOT_SHPARAM:
+ {
+ string name;
+ getstring(name, buf);
+ SlotShaderParam p = { name[0] ? getshaderparamname(name) : NULL, -1, { 0, 0, 0, 0 } };
+ loopi(4) p.val[i] = getfloat(buf);
+ if(p.name) dst.params.add(p);
+ break;
+ }
+ case VSLOT_SCALE:
+ dst.scale = getfloat(buf);
+ if(dst.scale <= 0) dst.scale = 1;
+ else if(!delta) dst.scale = clamp(dst.scale, 1/8.0f, 8.0f);
+ break;
+ case VSLOT_ROTATION:
+ dst.rotation = getint(buf);
+ if(!delta) dst.rotation = clamp(dst.rotation, 0, 7);
+ break;
+ case VSLOT_OFFSET:
+ dst.offset.x = getint(buf);
+ dst.offset.y = getint(buf);
+ if(!delta) dst.offset.max(0);
+ break;
+ case VSLOT_SCROLL:
+ dst.scroll.x = getfloat(buf);
+ dst.scroll.y = getfloat(buf);
+ break;
+ case VSLOT_LAYER:
+ {
+ int tex = getuint(buf);
+ dst.layer = vslots.inrange(tex) ? tex : 0;
+ break;
+ }
+ case VSLOT_ALPHA:
+ dst.alphafront = clamp(getfloat(buf), 0.0f, 1.0f);
+ dst.alphaback = clamp(getfloat(buf), 0.0f, 1.0f);
+ break;
+ case VSLOT_COLOR:
+ dst.colorscale.r = clamp(getfloat(buf), 0.0f, 2.0f);
+ dst.colorscale.g = clamp(getfloat(buf), 0.0f, 2.0f);
+ dst.colorscale.b = clamp(getfloat(buf), 0.0f, 2.0f);
+ break;
+ default:
+ return false;
+ }
+ dst.changed |= 1<<changed;
+ }
+ if(buf.overread()) return false;
+ return true;
}
VSlot *findvslot(Slot &slot, const VSlot &src, const VSlot &delta)
{
- for(VSlot *dst = slot.variants; dst; dst = dst->next)
- {
- if((!dst->changed || dst->changed == (src.changed | delta.changed)) &&
- comparevslot(*dst, src, src.changed & ~delta.changed) &&
- comparevslot(*dst, delta, delta.changed))
- return dst;
- }
- return NULL;
+ for(VSlot *dst = slot.variants; dst; dst = dst->next)
+ {
+ if((!dst->changed || dst->changed == (src.changed | delta.changed)) &&
+ comparevslot(*dst, src, src.changed & ~delta.changed) &&
+ comparevslot(*dst, delta, delta.changed))
+ return dst;
+ }
+ return NULL;
}
static VSlot *clonevslot(const VSlot &src, const VSlot &delta)
{
- VSlot *dst = vslots.add(new VSlot(src.slot, vslots.length()));
- dst->changed = src.changed | delta.changed;
- propagatevslot(*dst, src, ((1<<VSLOT_NUM)-1) & ~delta.changed);
- propagatevslot(*dst, delta, delta.changed, true);
- return dst;
+ VSlot *dst = vslots.add(new VSlot(src.slot, vslots.length()));
+ dst->changed = src.changed | delta.changed;
+ propagatevslot(*dst, src, ((1<<VSLOT_NUM)-1) & ~delta.changed);
+ propagatevslot(*dst, delta, delta.changed, true);
+ return dst;
}
VARP(autocompactvslots, 0, 256, 0x10000);
VSlot *editvslot(const VSlot &src, const VSlot &delta)
{
- VSlot *exists = findvslot(*src.slot, src, delta);
- if(exists) return exists;
- if(vslots.length()>=0x10000)
- {
- compactvslots();
- allchanged();
- if(vslots.length()>=0x10000) return NULL;
- }
- if(autocompactvslots && ++clonedvslots >= autocompactvslots)
- {
- compactvslots();
- allchanged();
- }
- return clonevslot(src, delta);
+ VSlot *exists = findvslot(*src.slot, src, delta);
+ if(exists) return exists;
+ if(vslots.length()>=0x10000)
+ {
+ compactvslots();
+ allchanged();
+ if(vslots.length()>=0x10000) return NULL;
+ }
+ if(autocompactvslots && ++clonedvslots >= autocompactvslots)
+ {
+ compactvslots();
+ allchanged();
+ }
+ return clonevslot(src, delta);
}
static void fixinsidefaces(cube *c, const ivec &o, int size, int tex)
{
- loopi(8)
- {
- ivec co(i, o, size);
- if(c[i].children) fixinsidefaces(c[i].children, co, size>>1, tex);
- else loopj(6) if(!visibletris(c[i], j, co, size))
- c[i].texture[j] = tex;
- }
+ loopi(8)
+ {
+ ivec co(i, o, size);
+ if(c[i].children) fixinsidefaces(c[i].children, co, size>>1, tex);
+ else loopj(6) if(!visibletris(c[i], j, co, size))
+ c[i].texture[j] = tex;
+ }
}
ICOMMAND(fixinsidefaces, "i", (int *tex),
{
- extern int nompedit;
- if(noedit(true) || (nompedit && multiplayer())) return;
- fixinsidefaces(worldroot, ivec(0, 0, 0), worldsize>>1, *tex && vslots.inrange(*tex) ? *tex : DEFAULT_GEOM);
- allchanged();
+ extern int nompedit;
+ if(noedit(true) || (nompedit && multiplayer())) return;
+ fixinsidefaces(worldroot, ivec(0, 0, 0), worldsize>>1, *tex && vslots.inrange(*tex) ? *tex : DEFAULT_GEOM);
+ allchanged();
});
const struct slottex
{
- const char *name;
- int id;
+ const char *name;
+ int id;
} slottexs[] =
{
- {"c", TEX_DIFFUSE},
- {"u", TEX_UNKNOWN},
- {"d", TEX_DECAL},
- {"n", TEX_NORMAL},
- {"g", TEX_GLOW},
- {"s", TEX_SPEC},
- {"z", TEX_DEPTH},
- {"a", TEX_ALPHA},
- {"e", TEX_ENVMAP}
+ {"c", TEX_DIFFUSE},
+ {"u", TEX_UNKNOWN},
+ {"d", TEX_DECAL},
+ {"n", TEX_NORMAL},
+ {"g", TEX_GLOW},
+ {"s", TEX_SPEC},
+ {"z", TEX_DEPTH},
+ {"a", TEX_ALPHA},
+ {"e", TEX_ENVMAP}
};
int findslottex(const char *name)
{
- loopi(sizeof(slottexs)/sizeof(slottex))
- {
- if(!strcmp(slottexs[i].name, name)) return slottexs[i].id;
- }
- return -1;
+ loopi(sizeof(slottexs)/sizeof(slottex))
+ {
+ if(!strcmp(slottexs[i].name, name)) return slottexs[i].id;
+ }
+ return -1;
}
void texture(char *type, char *name, int *rot, int *xoffset, int *yoffset, float *scale)
{
- if(slots.length()>=0x10000) return;
- static int lastmatslot = -1;
- int tnum = findslottex(type), matslot = findmaterial(type);
- if(tnum<0) tnum = atoi(type);
- if(tnum==TEX_DIFFUSE) lastmatslot = matslot;
- else if(lastmatslot>=0) matslot = lastmatslot;
- else if(slots.empty()) return;
- Slot &s = matslot>=0 ? materialslots[matslot] : *(tnum!=TEX_DIFFUSE ? slots.last() : slots.add(new Slot(slots.length())));
- s.loaded = false;
- s.texmask |= 1<<tnum;
- if(s.sts.length()>=8) conoutf(CON_WARN, "warning: too many textures in slot %d", slots.length()-1);
- Slot::Tex &st = s.sts.add();
- st.type = tnum;
- st.combined = -1;
- st.t = NULL;
- copystring(st.name, name);
- path(st.name);
- if(tnum==TEX_DIFFUSE)
- {
- setslotshader(s);
- VSlot &vs = matslot >= 0 ? materialslots[matslot] : *emptyvslot(s);
- vs.reset();
- vs.rotation = clamp(*rot, 0, 7);
- vs.offset = ivec2(*xoffset, *yoffset).max(0);
- vs.scale = *scale <= 0 ? 1 : *scale;
- propagatevslot(&vs, (1<<VSLOT_NUM)-1);
- }
+ if(slots.length()>=0x10000) return;
+ static int lastmatslot = -1;
+ int tnum = findslottex(type), matslot = findmaterial(type);
+ if(tnum<0) tnum = atoi(type);
+ if(tnum==TEX_DIFFUSE) lastmatslot = matslot;
+ else if(lastmatslot>=0) matslot = lastmatslot;
+ else if(slots.empty()) return;
+ Slot &s = matslot>=0 ? materialslots[matslot] : *(tnum!=TEX_DIFFUSE ? slots.last() : slots.add(new Slot(slots.length())));
+ s.loaded = false;
+ s.texmask |= 1<<tnum;
+ if(s.sts.length()>=8) conoutf(CON_WARN, "warning: too many textures in slot %d", slots.length()-1);
+ Slot::Tex &st = s.sts.add();
+ st.type = tnum;
+ st.combined = -1;
+ st.t = NULL;
+ copystring(st.name, name);
+ path(st.name);
+ if(tnum==TEX_DIFFUSE)
+ {
+ setslotshader(s);
+ VSlot &vs = matslot >= 0 ? materialslots[matslot] : *emptyvslot(s);
+ vs.reset();
+ vs.rotation = clamp(*rot, 0, 7);
+ vs.offset = ivec2(*xoffset, *yoffset).max(0);
+ vs.scale = *scale <= 0 ? 1 : *scale;
+ propagatevslot(&vs, (1<<VSLOT_NUM)-1);
+ }
}
COMMAND(texture, "ssiiif");
void texscroll(float *scrollS, float *scrollT)
{
- if(slots.empty()) return;
- Slot &s = *slots.last();
- s.variants->scroll = vec2(*scrollS, *scrollT).div(1000.0f);
- propagatevslot(s.variants, 1<<VSLOT_SCROLL);
+ if(slots.empty()) return;
+ Slot &s = *slots.last();
+ s.variants->scroll = vec2(*scrollS, *scrollT).div(1000.0f);
+ propagatevslot(s.variants, 1<<VSLOT_SCROLL);
}
COMMAND(texscroll, "ff");
void texoffset_(int *xoffset, int *yoffset)
{
- if(slots.empty()) return;
- Slot &s = *slots.last();
- s.variants->offset = ivec2(*xoffset, *yoffset).max(0);
- propagatevslot(s.variants, 1<<VSLOT_OFFSET);
+ if(slots.empty()) return;
+ Slot &s = *slots.last();
+ s.variants->offset = ivec2(*xoffset, *yoffset).max(0);
+ propagatevslot(s.variants, 1<<VSLOT_OFFSET);
}
COMMANDN(texoffset, texoffset_, "ii");
void texrotate_(int *rot)
{
- if(slots.empty()) return;
- Slot &s = *slots.last();
- s.variants->rotation = clamp(*rot, 0, 7);
- propagatevslot(s.variants, 1<<VSLOT_ROTATION);
+ if(slots.empty()) return;
+ Slot &s = *slots.last();
+ s.variants->rotation = clamp(*rot, 0, 7);
+ propagatevslot(s.variants, 1<<VSLOT_ROTATION);
}
COMMANDN(texrotate, texrotate_, "i");
void texscale(float *scale)
{
- if(slots.empty()) return;
- Slot &s = *slots.last();
- s.variants->scale = *scale <= 0 ? 1 : *scale;
- propagatevslot(s.variants, 1<<VSLOT_SCALE);
+ if(slots.empty()) return;
+ Slot &s = *slots.last();
+ s.variants->scale = *scale <= 0 ? 1 : *scale;
+ propagatevslot(s.variants, 1<<VSLOT_SCALE);
}
COMMAND(texscale, "f");
void texlayer(int *layer, char *name, int *mode, float *scale)
{
- if(slots.empty()) return;
- Slot &s = *slots.last();
- s.variants->layer = *layer < 0 ? max(slots.length()-1+*layer, 0) : *layer;
- s.layermaskname = name[0] ? newstring(path(makerelpath("packages", name))) : NULL;
- s.layermaskmode = *mode;
- s.layermaskscale = *scale <= 0 ? 1 : *scale;
- propagatevslot(s.variants, 1<<VSLOT_LAYER);
+ if(slots.empty()) return;
+ Slot &s = *slots.last();
+ s.variants->layer = *layer < 0 ? max(slots.length()-1+*layer, 0) : *layer;
+ s.layermaskname = name[0] ? newstring(path(makerelpath("packages", name))) : NULL;
+ s.layermaskmode = *mode;
+ s.layermaskscale = *scale <= 0 ? 1 : *scale;
+ propagatevslot(s.variants, 1<<VSLOT_LAYER);
}
COMMAND(texlayer, "isif");
void texalpha(float *front, float *back)
{
- if(slots.empty()) return;
- Slot &s = *slots.last();
- s.variants->alphafront = clamp(*front, 0.0f, 1.0f);
- s.variants->alphaback = clamp(*back, 0.0f, 1.0f);
- propagatevslot(s.variants, 1<<VSLOT_ALPHA);
+ if(slots.empty()) return;
+ Slot &s = *slots.last();
+ s.variants->alphafront = clamp(*front, 0.0f, 1.0f);
+ s.variants->alphaback = clamp(*back, 0.0f, 1.0f);
+ propagatevslot(s.variants, 1<<VSLOT_ALPHA);
}
COMMAND(texalpha, "ff");
void texcolor(float *r, float *g, float *b)
{
- if(slots.empty()) return;
- Slot &s = *slots.last();
- s.variants->colorscale = vec(clamp(*r, 0.0f, 1.0f), clamp(*g, 0.0f, 1.0f), clamp(*b, 0.0f, 1.0f));
- propagatevslot(s.variants, 1<<VSLOT_COLOR);
+ if(slots.empty()) return;
+ Slot &s = *slots.last();
+ s.variants->colorscale = vec(clamp(*r, 0.0f, 1.0f), clamp(*g, 0.0f, 1.0f), clamp(*b, 0.0f, 1.0f));
+ propagatevslot(s.variants, 1<<VSLOT_COLOR);
}
COMMAND(texcolor, "fff");
static int findtextype(Slot &s, int type, int last = -1)
{
- for(int i = last+1; i<s.sts.length(); i++) if((type&(1<<s.sts[i].type)) && s.sts[i].combined<0) return i;
- return -1;
+ for(int i = last+1; i<s.sts.length(); i++) if((type&(1<<s.sts[i].type)) && s.sts[i].combined<0) return i;
+ return -1;
}
static void addglow(ImageData &c, ImageData &g, const vec &glowcolor)
{
- if(g.bpp < 3)
- {
- readwritergbtex(c, g,
- loopk(3) dst[k] = clamp(int(dst[k]) + int(src[0]*glowcolor[k]), 0, 255);
- );
- }
- else
- {
- readwritergbtex(c, g,
- loopk(3) dst[k] = clamp(int(dst[k]) + int(src[k]*glowcolor[k]), 0, 255);
- );
- }
+ if(g.bpp < 3) { readwritergbtex(c, g, loopk(3) dst[k] = clamp(int(dst[k]) + int(src[0]*glowcolor[k]), 0, 255); ); }
+ else { readwritergbtex(c, g, loopk(3) dst[k] = clamp(int(dst[k]) + int(src[k]*glowcolor[k]), 0, 255); ); }
}
static void mergespec(ImageData &c, ImageData &s)
{
- if(s.bpp < 3)
- {
- readwritergbatex(c, s,
- dst[3] = src[0];
- );
- }
- else
- {
- readwritergbatex(c, s,
- dst[3] = (int(src[0]) + int(src[1]) + int(src[2]))/3;
- );
- }
+ if(s.bpp < 3) { readwritergbatex(c, s, dst[3] = src[0]; ); }
+ else { readwritergbatex(c, s, dst[3] = (int(src[0]) + int(src[1]) + int(src[2]))/3; ); }
}
static void mergedepth(ImageData &c, ImageData &z)
{
- readwritergbatex(c, z,
- dst[3] = src[0];
- );
+ readwritergbatex(c, z, dst[3] = src[0]; );
}
static void mergealpha(ImageData &c, ImageData &s)
{
- if(s.bpp < 3)
- {
- readwritergbatex(c, s,
- dst[3] = src[0];
- );
- }
- else if(s.bpp == 3)
- {
- readwritergbatex(c, s,
- dst[3] = (int(src[0]) + int(src[1]) + int(src[2]))/3;
- );
- }
- else
- {
- readwritergbatex(c, s,
- dst[3] = src[3];
- );
- }
+ if(s.bpp < 3) { readwritergbatex(c, s, dst[3] = src[0]; ); }
+ else if(s.bpp == 3) { readwritergbatex(c, s, dst[3] = (int(src[0]) + int(src[1]) + int(src[2]))/3; ); }
+ else { readwritergbatex(c, s, dst[3] = src[3]; ); }
}
static void addname(vector<char> &key, Slot &slot, Slot::Tex &t, bool combined = false, const char *prefix = NULL)
{
- if(combined) key.add('&');
- if(prefix) { while(*prefix) key.add(*prefix++); }
- defformatstring(tname, "packages/%s", t.name);
- for(const char *s = path(tname); *s; key.add(*s++));
+ if(combined) key.add('&');
+ if(prefix) { while(*prefix) key.add(*prefix++); }
+ defformatstring(tname, "packages/%s", t.name);
+ for(const char *s = path(tname); *s; key.add(*s++));
}
static void texcombine(Slot &s, int index, Slot::Tex &t, bool forceload = false)
{
- vector<char> key;
- addname(key, s, t);
- int texmask = 0;
- if(!forceload) switch(t.type)
- {
- case TEX_DIFFUSE:
- case TEX_NORMAL:
- {
- int i = findtextype(s, t.type==TEX_DIFFUSE ? (s.texmask&(1<<TEX_SPEC) ? 1<<TEX_SPEC : 1<<TEX_ALPHA) : (s.texmask&(1<<TEX_DEPTH) ? 1<<TEX_DEPTH : 1<<TEX_ALPHA));
- if(i<0) break;
- texmask |= 1<<s.sts[i].type;
- s.sts[i].combined = index;
- addname(key, s, s.sts[i], true);
- break;
- }
- }
- key.add('\0');
- t.t = textures.access(key.getbuf());
- if(t.t) return;
- int compress = 0, wrap = 0;
- ImageData ts;
- if(!texturedata(ts, NULL, &t, true, &compress, &wrap)) { t.t = notexture; return; }
- if(!ts.compressed) switch(t.type)
- {
- case TEX_DIFFUSE:
- case TEX_NORMAL:
- loopv(s.sts)
- {
- Slot::Tex &a = s.sts[i];
- if(a.combined!=index) continue;
- ImageData as;
- if(!texturedata(as, NULL, &a)) continue;
- //if(ts.bpp!=4) forcergbaimage(ts);
- if(as.w!=ts.w || as.h!=ts.h) scaleimage(as, ts.w, ts.h);
- switch(a.type)
- {
- case TEX_SPEC: mergespec(ts, as); break;
- case TEX_DEPTH: mergedepth(ts, as); break;
- case TEX_ALPHA: mergealpha(ts, as); break;
- }
- break; // only one combination
- }
- break;
- }
- t.t = newtexture(NULL, key.getbuf(), ts, wrap, true, true, true, compress);
+ vector<char> key;
+ addname(key, s, t);
+ int texmask = 0;
+ if(!forceload) switch(t.type)
+ {
+ case TEX_DIFFUSE:
+ case TEX_NORMAL:
+ {
+ int i = findtextype(s, t.type==TEX_DIFFUSE ? (s.texmask&(1<<TEX_SPEC) ? 1<<TEX_SPEC : 1<<TEX_ALPHA) : (s.texmask&(1<<TEX_DEPTH) ? 1<<TEX_DEPTH : 1<<TEX_ALPHA));
+ if(i<0) break;
+ texmask |= 1<<s.sts[i].type;
+ s.sts[i].combined = index;
+ addname(key, s, s.sts[i], true);
+ break;
+ }
+ }
+ key.add('\0');
+ t.t = textures.access(key.getbuf());
+ if(t.t) return;
+ int compress = 0, wrap = 0;
+ ImageData ts;
+ if(!texturedata(ts, NULL, &t, true, &compress, &wrap)) { t.t = notexture; return; }
+ if(!ts.compressed) switch(t.type)
+ {
+ case TEX_DIFFUSE:
+ case TEX_NORMAL:
+ loopv(s.sts)
+ {
+ Slot::Tex &a = s.sts[i];
+ if(a.combined!=index) continue;
+ ImageData as;
+ if(!texturedata(as, NULL, &a)) continue;
+ //if(ts.bpp!=4) forcergbaimage(ts);
+ if(as.w!=ts.w || as.h!=ts.h) scaleimage(as, ts.w, ts.h);
+ switch(a.type)
+ {
+ case TEX_SPEC: mergespec(ts, as); break;
+ case TEX_DEPTH: mergedepth(ts, as); break;
+ case TEX_ALPHA: mergealpha(ts, as); break;
+ }
+ break; // only one combination
+ }
+ break;
+ }
+ t.t = newtexture(NULL, key.getbuf(), ts, wrap, true, true, true, compress);
}
static Slot &loadslot(Slot &s, bool forceload)
{
- linkslotshader(s);
- loopv(s.sts)
- {
- Slot::Tex &t = s.sts[i];
- if(t.combined >= 0) continue;
- switch(t.type)
- {
- case TEX_ENVMAP:
- t.t = cubemapload(t.name);
- break;
-
- default:
- texcombine(s, i, t, forceload);
- break;
- }
- }
- s.loaded = true;
- return s;
+ linkslotshader(s);
+ loopv(s.sts)
+ {
+ Slot::Tex &t = s.sts[i];
+ if(t.combined >= 0) continue;
+ switch(t.type)
+ {
+ case TEX_ENVMAP:
+ t.t = cubemapload(t.name);
+ break;
+
+ default:
+ texcombine(s, i, t, forceload);
+ break;
+ }
+ }
+ s.loaded = true;
+ return s;
}
MSlot &lookupmaterialslot(int index, bool load)
{
- if(materialslots[index].sts.empty() && index&MATF_INDEX) index &= ~MATF_INDEX;
- MSlot &s = materialslots[index];
- if(load && !s.linked)
- {
- if(!s.loaded) loadslot(s, true);
- linkvslotshader(s);
- s.linked = true;
- }
- return s;
+ if(materialslots[index].sts.empty() && index&MATF_INDEX) index &= ~MATF_INDEX;
+ MSlot &s = materialslots[index];
+ if(load && !s.linked)
+ {
+ if(!s.loaded) loadslot(s, true);
+ linkvslotshader(s);
+ s.linked = true;
+ }
+ return s;
}
Slot &lookupslot(int index, bool load)
{
- Slot &s = slots.inrange(index) ? *slots[index] : (slots.inrange(DEFAULT_GEOM) ? *slots[DEFAULT_GEOM] : dummyslot);
- return s.loaded || !load ? s : loadslot(s, false);
+ Slot &s = slots.inrange(index) ? *slots[index] : (slots.inrange(DEFAULT_GEOM) ? *slots[DEFAULT_GEOM] : dummyslot);
+ return s.loaded || !load ? s : loadslot(s, false);
}
VSlot &lookupvslot(int index, bool load)
{
- VSlot &s = vslots.inrange(index) && vslots[index]->slot ? *vslots[index] : (slots.inrange(DEFAULT_GEOM) && slots[DEFAULT_GEOM]->variants ? *slots[DEFAULT_GEOM]->variants : dummyvslot);
- if(load && !s.linked)
- {
- if(!s.slot->loaded) loadslot(*s.slot, false);
- linkvslotshader(s);
- s.linked = true;
- }
- return s;
+ VSlot &s = vslots.inrange(index) && vslots[index]->slot ? *vslots[index] : (slots.inrange(DEFAULT_GEOM) && slots[DEFAULT_GEOM]->variants ? *slots[DEFAULT_GEOM]->variants : dummyvslot);
+ if(load && !s.linked)
+ {
+ if(!s.slot->loaded) loadslot(*s.slot, false);
+ linkvslotshader(s);
+ s.linked = true;
+ }
+ return s;
}
void linkslotshaders()
{
- loopv(slots) if(slots[i]->loaded) linkslotshader(*slots[i]);
- loopv(vslots) if(vslots[i]->linked) linkvslotshader(*vslots[i]);
- loopi((MATF_VOLUME|MATF_INDEX)+1) if(materialslots[i].loaded)
- {
- linkslotshader(materialslots[i]);
- linkvslotshader(materialslots[i]);
- }
+ loopv(slots) if(slots[i]->loaded) linkslotshader(*slots[i]);
+ loopv(vslots) if(vslots[i]->linked) linkvslotshader(*vslots[i]);
+ loopi((MATF_VOLUME|MATF_INDEX)+1) if(materialslots[i].loaded)
+ {
+ linkslotshader(materialslots[i]);
+ linkvslotshader(materialslots[i]);
+ }
}
Texture *loadthumbnail(Slot &slot)
{
- if(slot.thumbnail) return slot.thumbnail;
- if(!slot.variants)
- {
- slot.thumbnail = notexture;
- return slot.thumbnail;
- }
- VSlot &vslot = *slot.variants;
- linkslotshader(slot, false);
- linkvslotshader(vslot, false);
- vector<char> name;
- if(vslot.colorscale == vec(1, 1, 1)) addname(name, slot, slot.sts[0], false, "<thumbnail>");
- else
- {
- defformatstring(prefix, "<thumbnail:%.2f/%.2f/%.2f>", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z);
- addname(name, slot, slot.sts[0], false, prefix);
- }
- int glow = -1;
- if(slot.texmask&(1<<TEX_GLOW))
- {
- loopvj(slot.sts) if(slot.sts[j].type==TEX_GLOW) { glow = j; break; }
- if(glow >= 0)
- {
- defformatstring(prefix, "<glow:%.2f/%.2f/%.2f>", vslot.glowcolor.x, vslot.glowcolor.y, vslot.glowcolor.z);
- addname(name, slot, slot.sts[glow], true, prefix);
- }
- }
- VSlot *layer = vslot.layer ? &lookupvslot(vslot.layer, false) : NULL;
- if(layer)
- {
- if(layer->colorscale == vec(1, 1, 1)) addname(name, *layer->slot, layer->slot->sts[0], true, "<layer>");
- else
- {
- defformatstring(prefix, "<layer:%.2f/%.2f/%.2f>", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z);
- addname(name, *layer->slot, layer->slot->sts[0], true, prefix);
- }
- }
- name.add('\0');
- Texture *t = textures.access(path(name.getbuf()));
- if(t) slot.thumbnail = t;
- else
- {
- ImageData s, g, l;
- texturedata(s, NULL, &slot.sts[0], false);
- if(glow >= 0) texturedata(g, NULL, &slot.sts[glow], false);
- if(layer) texturedata(l, NULL, &layer->slot->sts[0], false);
- if(!s.data) t = slot.thumbnail = notexture;
- else
- {
- if(vslot.colorscale != vec(1, 1, 1)) texmad(s, vslot.colorscale, vec(0, 0, 0));
- int xs = s.w, ys = s.h;
- if(s.w > 64 || s.h > 64) scaleimage(s, min(s.w, 64), min(s.h, 64));
- if(g.data)
- {
- if(g.w != s.w || g.h != s.h) scaleimage(g, s.w, s.h);
- addglow(s, g, vslot.glowcolor);
- }
- if(l.data)
- {
- if(layer->colorscale != vec(1, 1, 1)) texmad(l, layer->colorscale, vec(0, 0, 0));
- if(l.w != s.w/2 || l.h != s.h/2) scaleimage(l, s.w/2, s.h/2);
- forcergbimage(s);
- forcergbimage(l);
- uchar *dstrow = &s.data[s.pitch*l.h + s.bpp*l.w], *srcrow = l.data;
- loop(y, l.h)
- {
- for(uchar *dst = dstrow, *src = srcrow, *end = &srcrow[l.w*l.bpp]; src < end; dst += s.bpp, src += l.bpp)
- loopk(3) dst[k] = src[k];
- dstrow += s.pitch;
- srcrow += l.pitch;
- }
- }
- if(s.bpp < 3) forcergbimage(s);
- t = newtexture(NULL, name.getbuf(), s, 0, false, false, true);
- t->xs = xs;
- t->ys = ys;
- slot.thumbnail = t;
- }
- }
- return t;
+ if(slot.thumbnail) return slot.thumbnail;
+ if(!slot.variants)
+ {
+ slot.thumbnail = notexture;
+ return slot.thumbnail;
+ }
+ VSlot &vslot = *slot.variants;
+ linkslotshader(slot, false);
+ linkvslotshader(vslot, false);
+ vector<char> name;
+ if(vslot.colorscale == vec(1, 1, 1)) addname(name, slot, slot.sts[0], false, "<thumbnail>");
+ else
+ {
+ defformatstring(prefix, "<thumbnail:%.2f/%.2f/%.2f>", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z);
+ addname(name, slot, slot.sts[0], false, prefix);
+ }
+ int glow = -1;
+ if(slot.texmask&(1<<TEX_GLOW))
+ {
+ loopvj(slot.sts) if(slot.sts[j].type==TEX_GLOW) { glow = j; break; }
+ if(glow >= 0)
+ {
+ defformatstring(prefix, "<glow:%.2f/%.2f/%.2f>", vslot.glowcolor.x, vslot.glowcolor.y, vslot.glowcolor.z);
+ addname(name, slot, slot.sts[glow], true, prefix);
+ }
+ }
+ VSlot *layer = vslot.layer ? &lookupvslot(vslot.layer, false) : NULL;
+ if(layer)
+ {
+ if(layer->colorscale == vec(1, 1, 1)) addname(name, *layer->slot, layer->slot->sts[0], true, "<layer>");
+ else
+ {
+ defformatstring(prefix, "<layer:%.2f/%.2f/%.2f>", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z);
+ addname(name, *layer->slot, layer->slot->sts[0], true, prefix);
+ }
+ }
+ name.add('\0');
+ Texture *t = textures.access(path(name.getbuf()));
+ if(t) slot.thumbnail = t;
+ else
+ {
+ ImageData s, g, l;
+ texturedata(s, NULL, &slot.sts[0], false);
+ if(glow >= 0) texturedata(g, NULL, &slot.sts[glow], false);
+ if(layer) texturedata(l, NULL, &layer->slot->sts[0], false);
+ if(!s.data) t = slot.thumbnail = notexture;
+ else
+ {
+ if(vslot.colorscale != vec(1, 1, 1)) texmad(s, vslot.colorscale, vec(0, 0, 0));
+ int xs = s.w, ys = s.h;
+ if(s.w > 64 || s.h > 64) scaleimage(s, min(s.w, 64), min(s.h, 64));
+ if(g.data)
+ {
+ if(g.w != s.w || g.h != s.h) scaleimage(g, s.w, s.h);
+ addglow(s, g, vslot.glowcolor);
+ }
+ if(l.data)
+ {
+ if(layer->colorscale != vec(1, 1, 1)) texmad(l, layer->colorscale, vec(0, 0, 0));
+ if(l.w != s.w/2 || l.h != s.h/2) scaleimage(l, s.w/2, s.h/2);
+ forcergbimage(s);
+ forcergbimage(l);
+ uchar *dstrow = &s.data[s.pitch*l.h + s.bpp*l.w], *srcrow = l.data;
+ loop(y, l.h)
+ {
+ for(uchar *dst = dstrow, *src = srcrow, *end = &srcrow[l.w*l.bpp]; src < end; dst += s.bpp, src += l.bpp)
+ loopk(3) dst[k] = src[k];
+ dstrow += s.pitch;
+ srcrow += l.pitch;
+ }
+ }
+ if(s.bpp < 3) forcergbimage(s);
+ t = newtexture(NULL, name.getbuf(), s, 0, false, false, true);
+ t->xs = xs;
+ t->ys = ys;
+ slot.thumbnail = t;
+ }
+ }
+ return t;
}
void loadlayermasks()
{
- loopv(slots)
- {
- Slot &slot = *slots[i];
- if(slot.loaded && slot.layermaskname && !slot.layermask)
- {
- slot.layermask = new ImageData;
- texturedata(*slot.layermask, slot.layermaskname);
- if(!slot.layermask->data) DELETEP(slot.layermask);
- }
- }
+ loopv(slots)
+ {
+ Slot &slot = *slots[i];
+ if(slot.loaded && slot.layermaskname && !slot.layermask)
+ {
+ slot.layermask = new ImageData;
+ texturedata(*slot.layermask, slot.layermaskname);
+ if(!slot.layermask->data) DELETEP(slot.layermask);
+ }
+ }
}
// environment mapped reflections
void forcecubemapload(GLuint tex)
{
- extern int ati_cubemap_bug;
- if(!ati_cubemap_bug || !tex) return;
-
- SETSHADER(cubemap);
- GLenum depthtest = glIsEnabled(GL_DEPTH_TEST), blend = glIsEnabled(GL_BLEND);
- if(depthtest) glDisable(GL_DEPTH_TEST);
- glBindTexture(GL_TEXTURE_CUBE_MAP, tex);
- if(!blend) glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- gle::defvertex(2);
- gle::deftexcoord0(3);
- gle::defcolor(4);
- gle::begin(GL_LINES);
- loopi(2)
- {
- gle::attribf(i*1e-3f, 0);
- gle::attribf(0, 0, 1);
- gle::attribf(1, 1, 1, 0);
- }
- gle::end();
- if(!blend) glDisable(GL_BLEND);
- if(depthtest) glEnable(GL_DEPTH_TEST);
+ extern int ati_cubemap_bug;
+ if(!ati_cubemap_bug || !tex) return;
+
+ SETSHADER(cubemap);
+ GLenum depthtest = glIsEnabled(GL_DEPTH_TEST), blend = glIsEnabled(GL_BLEND);
+ if(depthtest) glDisable(GL_DEPTH_TEST);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, tex);
+ if(!blend) glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ gle::defvertex(2);
+ gle::deftexcoord0(3);
+ gle::defcolor(4);
+ gle::begin(GL_LINES);
+ loopi(2)
+ {
+ gle::attribf(i*1e-3f, 0);
+ gle::attribf(0, 0, 1);
+ gle::attribf(1, 1, 1, 0);
+ }
+ gle::end();
+ if(!blend) glDisable(GL_BLEND);
+ if(depthtest) glEnable(GL_DEPTH_TEST);
}
extern const cubemapside cubemapsides[6] =
{
- { GL_TEXTURE_CUBE_MAP_NEGATIVE_X, "lf", true, true, true },
- { GL_TEXTURE_CUBE_MAP_POSITIVE_X, "rt", false, false, true },
- { GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, "ft", true, false, false },
- { GL_TEXTURE_CUBE_MAP_POSITIVE_Y, "bk", false, true, false },
- { GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, "dn", false, false, true },
- { GL_TEXTURE_CUBE_MAP_POSITIVE_Z, "up", false, false, true },
+ { GL_TEXTURE_CUBE_MAP_NEGATIVE_X, "lf", true, true, true },
+ { GL_TEXTURE_CUBE_MAP_POSITIVE_X, "rt", false, false, true },
+ { GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, "ft", true, false, false },
+ { GL_TEXTURE_CUBE_MAP_POSITIVE_Y, "bk", false, true, false },
+ { GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, "dn", false, false, true },
+ { GL_TEXTURE_CUBE_MAP_POSITIVE_Z, "up", false, false, true },
};
VARFP(envmapsize, 4, 7, 10, setupmaterials());
Texture *cubemaploadwildcard(Texture *t, const char *name, bool mipit, bool msg, bool transient = false)
{
- string tname;
- if(!name) copystring(tname, t->name);
- else
- {
- copystring(tname, name);
- t = textures.access(path(tname));
- if(t)
- {
- if(!transient && t->type&Texture::TRANSIENT) t->type &= ~Texture::TRANSIENT;
- return t;
- }
- }
- char *wildcard = strchr(tname, '*');
- ImageData surface[6];
- string sname;
- if(!wildcard) copystring(sname, tname);
- int tsize = 0, compress = 0;
- loopi(6)
- {
- if(wildcard)
- {
- copystring(sname, stringslice(tname, wildcard));
- concatstring(sname, cubemapsides[i].name);
- concatstring(sname, wildcard+1);
- }
- ImageData &s = surface[i];
- texturedata(s, sname, NULL, msg, &compress);
- if(!s.data) return NULL;
- if(s.w != s.h)
- {
- if(msg) conoutf(CON_ERROR, "cubemap texture %s does not have square size", sname);
- return NULL;
- }
- if(s.compressed ? s.compressed!=surface[0].compressed || s.w!=surface[0].w || s.h!=surface[0].h || s.levels!=surface[0].levels : surface[0].compressed || s.bpp!=surface[0].bpp)
- {
- if(msg) conoutf(CON_ERROR, "cubemap texture %s doesn't match other sides' format", sname);
- return NULL;
- }
- tsize = max(tsize, max(s.w, s.h));
- }
- if(name)
- {
- char *key = newstring(tname);
- t = &textures[key];
- t->name = key;
- }
- t->type = Texture::CUBEMAP;
- if(transient) t->type |= Texture::TRANSIENT;
- GLenum format;
- if(surface[0].compressed)
- {
- format = uncompressedformat(surface[0].compressed);
- t->bpp = formatsize(format);
- t->type |= Texture::COMPRESSED;
- }
- else
- {
- format = texformat(surface[0].bpp, true);
- t->bpp = surface[0].bpp;
- if(hasTRG && !hasTSW && swizzlemask(format))
- {
- loopi(6) swizzleimage(surface[i]);
- format = texformat(surface[0].bpp, true);
- t->bpp = surface[0].bpp;
- }
- }
- if(alphaformat(format)) t->type |= Texture::ALPHA;
- t->mipmap = mipit;
- t->clamp = 3;
- t->xs = t->ys = tsize;
- t->w = t->h = min(1<<envmapsize, tsize);
- resizetexture(t->w, t->h, mipit, false, GL_TEXTURE_CUBE_MAP, compress, t->w, t->h);
- GLenum component = format;
- if(!surface[0].compressed)
- {
- component = compressedformat(format, t->w, t->h, compress);
- switch(component)
- {
- case GL_RGB: component = GL_RGB5; break;
- }
- }
- glGenTextures(1, &t->id);
- loopi(6)
- {
- ImageData &s = surface[i];
- const cubemapside &side = cubemapsides[i];
- texreorient(s, side.flipx, side.flipy, side.swapxy);
- if(s.compressed)
- {
- int w = s.w, h = s.h, levels = s.levels, level = 0;
- uchar *data = s.data;
- while(levels > 1 && (w > t->w || h > t->h))
- {
- data += s.calclevelsize(level++);
- levels--;
- if(w > 1) w /= 2;
- if(h > 1) h /= 2;
- }
- createcompressedtexture(t->id, w, h, data, s.align, s.bpp, levels, i ? -1 : 3, mipit ? 2 : 1, s.compressed, side.target, true);
- }
- else
- {
- createtexture(t->id, t->w, t->h, s.data, i ? -1 : 3, mipit ? 2 : 1, component, side.target, s.w, s.h, s.pitch, false, format, true);
- }
- }
- forcecubemapload(t->id);
- return t;
+ string tname;
+ if(!name) copystring(tname, t->name);
+ else
+ {
+ copystring(tname, name);
+ t = textures.access(path(tname));
+ if(t)
+ {
+ if(!transient && t->type&Texture::TRANSIENT) t->type &= ~Texture::TRANSIENT;
+ return t;
+ }
+ }
+ char *wildcard = strchr(tname, '*');
+ ImageData surface[6];
+ string sname;
+ if(!wildcard) copystring(sname, tname);
+ int tsize = 0, compress = 0;
+ loopi(6)
+ {
+ if(wildcard)
+ {
+ copystring(sname, stringslice(tname, wildcard));
+ concatstring(sname, cubemapsides[i].name);
+ concatstring(sname, wildcard+1);
+ }
+ ImageData &s = surface[i];
+ texturedata(s, sname, NULL, msg, &compress);
+ if(!s.data) return NULL;
+ if(s.w != s.h)
+ {
+ if(msg) conoutf(CON_ERROR, "cubemap texture %s does not have square size", sname);
+ return NULL;
+ }
+ if(s.compressed ? s.compressed!=surface[0].compressed || s.w!=surface[0].w || s.h!=surface[0].h || s.levels!=surface[0].levels : surface[0].compressed || s.bpp!=surface[0].bpp)
+ {
+ if(msg) conoutf(CON_ERROR, "cubemap texture %s doesn't match other sides' format", sname);
+ return NULL;
+ }
+ tsize = max(tsize, max(s.w, s.h));
+ }
+ if(name)
+ {
+ char *key = newstring(tname);
+ t = &textures[key];
+ t->name = key;
+ }
+ t->type = Texture::CUBEMAP;
+ if(transient) t->type |= Texture::TRANSIENT;
+ GLenum format;
+ if(surface[0].compressed)
+ {
+ format = uncompressedformat(surface[0].compressed);
+ t->bpp = formatsize(format);
+ t->type |= Texture::COMPRESSED;
+ }
+ else
+ {
+ format = texformat(surface[0].bpp, true);
+ t->bpp = surface[0].bpp;
+ if(hasTRG && !hasTSW && swizzlemask(format))
+ {
+ loopi(6) swizzleimage(surface[i]);
+ format = texformat(surface[0].bpp, true);
+ t->bpp = surface[0].bpp;
+ }
+ }
+ if(alphaformat(format)) t->type |= Texture::ALPHA;
+ t->mipmap = mipit;
+ t->clamp = 3;
+ t->xs = t->ys = tsize;
+ t->w = t->h = min(1<<envmapsize, tsize);
+ resizetexture(t->w, t->h, mipit, false, GL_TEXTURE_CUBE_MAP, compress, t->w, t->h);
+ GLenum component = format;
+ if(!surface[0].compressed)
+ {
+ component = compressedformat(format, t->w, t->h, compress);
+ switch(component)
+ {
+ case GL_RGB: component = GL_RGB5; break;
+ }
+ }
+ glGenTextures(1, &t->id);
+ loopi(6)
+ {
+ ImageData &s = surface[i];
+ const cubemapside &side = cubemapsides[i];
+ texreorient(s, side.flipx, side.flipy, side.swapxy);
+ if(s.compressed)
+ {
+ int w = s.w, h = s.h, levels = s.levels, level = 0;
+ uchar *data = s.data;
+ while(levels > 1 && (w > t->w || h > t->h))
+ {
+ data += s.calclevelsize(level++);
+ levels--;
+ if(w > 1) w /= 2;
+ if(h > 1) h /= 2;
+ }
+ createcompressedtexture(t->id, w, h, data, s.align, s.bpp, levels, i ? -1 : 3, mipit ? 2 : 1, s.compressed, side.target, true);
+ }
+ else
+ {
+ createtexture(t->id, t->w, t->h, s.data, i ? -1 : 3, mipit ? 2 : 1, component, side.target, s.w, s.h, s.pitch, false, format, true);
+ }
+ }
+ forcecubemapload(t->id);
+ return t;
}
Texture *cubemapload(const char *name, bool mipit, bool msg, bool transient)
{
- string pname;
- copystring(pname, makerelpath("packages", name));
- path(pname);
- Texture *t = NULL;
- if(!strchr(pname, '*'))
- {
- defformatstring(jpgname, "%s_*.jpg", pname);
- t = cubemaploadwildcard(NULL, jpgname, mipit, false, transient);
- if(!t)
- {
- defformatstring(pngname, "%s_*.png", pname);
- t = cubemaploadwildcard(NULL, pngname, mipit, false, transient);
- if(!t && msg) conoutf(CON_ERROR, "could not load envmap %s", name);
- }
- }
- else t = cubemaploadwildcard(NULL, pname, mipit, msg, transient);
- return t;
+ string pname;
+ copystring(pname, makerelpath("packages", name));
+ path(pname);
+ Texture *t = NULL;
+ if(!strchr(pname, '*'))
+ {
+ defformatstring(jpgname, "%s_*.jpg", pname);
+ t = cubemaploadwildcard(NULL, jpgname, mipit, false, transient);
+ if(!t)
+ {
+ defformatstring(pngname, "%s_*.png", pname);
+ t = cubemaploadwildcard(NULL, pngname, mipit, false, transient);
+ if(!t && msg) conoutf(CON_ERROR, "could not load envmap %s", name);
+ }
+ }
+ else t = cubemaploadwildcard(NULL, pname, mipit, msg, transient);
+ return t;
}
VARR(envmapradius, 0, 128, 10000);
@@ -2647,16 +2574,16 @@ VARR(envmapbb, 0, 0, 1);
struct envmap
{
- int radius, size, blur;
- vec o;
- GLuint tex;
+ int radius, size, blur;
+ vec o;
+ GLuint tex;
- envmap() : radius(-1), size(0), blur(0), o(0, 0, 0), tex(0) {}
+ envmap() : radius(-1), size(0), blur(0), o(0, 0, 0), tex(0) {}
- void clear()
- {
- if(tex) { glDeleteTextures(1, &tex); tex = 0; }
- }
+ void clear()
+ {
+ if(tex) { glDeleteTextures(1, &tex); tex = 0; }
+ }
};
static vector<envmap> envmaps;
@@ -2664,581 +2591,455 @@ static Texture *skyenvmap = NULL;
void clearenvmaps()
{
- if(skyenvmap)
- {
- if(skyenvmap->type&Texture::TRANSIENT) cleanuptexture(skyenvmap);
- skyenvmap = NULL;
- }
- loopv(envmaps) envmaps[i].clear();
- envmaps.shrink(0);
+ if(skyenvmap)
+ {
+ if(skyenvmap->type&Texture::TRANSIENT) cleanuptexture(skyenvmap);
+ skyenvmap = NULL;
+ }
+ loopv(envmaps) envmaps[i].clear();
+ envmaps.shrink(0);
}
VAR(aaenvmap, 0, 2, 4);
GLuint genenvmap(const vec &o, int envmapsize, int blur, bool onlysky)
{
- int rendersize = 1<<(envmapsize+aaenvmap), sizelimit = min(hwcubetexsize, min(screenw, screenh));
- if(maxtexsize) sizelimit = min(sizelimit, maxtexsize);
- while(rendersize > sizelimit) rendersize /= 2;
- int texsize = min(rendersize, 1<<envmapsize);
- if(!aaenvmap) rendersize = texsize;
- GLuint tex;
- glGenTextures(1, &tex);
- glViewport(0, 0, rendersize, rendersize);
- float yaw = 0, pitch = 0;
- uchar *pixels = new uchar[3*rendersize*rendersize*2];
- glPixelStorei(GL_PACK_ALIGNMENT, texalign(pixels, rendersize, 3));
- loopi(6)
- {
- const cubemapside &side = cubemapsides[i];
- switch(side.target)
- {
- case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: // lf
- yaw = 90; pitch = 0; break;
- case GL_TEXTURE_CUBE_MAP_POSITIVE_X: // rt
- yaw = 270; pitch = 0; break;
- case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: // ft
- yaw = 180; pitch = 0; break;
- case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: // bk
- yaw = 0; pitch = 0; break;
- case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: // dn
- yaw = 270; pitch = -90; break;
- case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: // up
- yaw = 270; pitch = 90; break;
- }
- glFrontFace((side.flipx==side.flipy)!=side.swapxy ? GL_CW : GL_CCW);
- drawcubemap(rendersize, o, yaw, pitch, side, onlysky);
- uchar *src = pixels, *dst = &pixels[3*rendersize*rendersize];
- glReadPixels(0, 0, rendersize, rendersize, GL_RGB, GL_UNSIGNED_BYTE, src);
- if(rendersize > texsize)
- {
- scaletexture(src, rendersize, rendersize, 3, 3*rendersize, dst, texsize, texsize);
- swap(src, dst);
- }
- if(blur > 0)
- {
- blurtexture(blur, 3, texsize, texsize, dst, src);
- swap(src, dst);
- }
- createtexture(tex, texsize, texsize, src, 3, 2, GL_RGB5, side.target);
- }
- glFrontFace(GL_CW);
- delete[] pixels;
- glViewport(0, 0, screenw, screenh);
- clientkeepalive();
- forcecubemapload(tex);
- return tex;
+ int rendersize = 1<<(envmapsize+aaenvmap), sizelimit = min(hwcubetexsize, min(screenw, screenh));
+ if(maxtexsize) sizelimit = min(sizelimit, maxtexsize);
+ while(rendersize > sizelimit) rendersize /= 2;
+ int texsize = min(rendersize, 1<<envmapsize);
+ if(!aaenvmap) rendersize = texsize;
+ GLuint tex;
+ glGenTextures(1, &tex);
+ glViewport(0, 0, rendersize, rendersize);
+ float yaw = 0, pitch = 0;
+ uchar *pixels = new uchar[3*rendersize*rendersize*2];
+ glPixelStorei(GL_PACK_ALIGNMENT, texalign(pixels, rendersize, 3));
+ loopi(6)
+ {
+ const cubemapside &side = cubemapsides[i];
+ switch(side.target)
+ {
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: // lf
+ yaw = 90; pitch = 0; break;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X: // rt
+ yaw = 270; pitch = 0; break;
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: // ft
+ yaw = 180; pitch = 0; break;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: // bk
+ yaw = 0; pitch = 0; break;
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: // dn
+ yaw = 270; pitch = -90; break;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: // up
+ yaw = 270; pitch = 90; break;
+ }
+ glFrontFace((side.flipx==side.flipy)!=side.swapxy ? GL_CW : GL_CCW);
+ drawcubemap(rendersize, o, yaw, pitch, side, onlysky);
+ uchar *src = pixels, *dst = &pixels[3*rendersize*rendersize];
+ glReadPixels(0, 0, rendersize, rendersize, GL_RGB, GL_UNSIGNED_BYTE, src);
+ if(rendersize > texsize)
+ {
+ scaletexture(src, rendersize, rendersize, 3, 3*rendersize, dst, texsize, texsize);
+ swap(src, dst);
+ }
+ if(blur > 0)
+ {
+ blurtexture(blur, 3, texsize, texsize, dst, src);
+ swap(src, dst);
+ }
+ createtexture(tex, texsize, texsize, src, 3, 2, GL_RGB5, side.target);
+ }
+ glFrontFace(GL_CW);
+ delete[] pixels;
+ glViewport(0, 0, screenw, screenh);
+ clientkeepalive();
+ forcecubemapload(tex);
+ return tex;
}
void initenvmaps()
{
- clearenvmaps();
- skyenvmap = NULL;
- if(shouldrenderskyenvmap()) envmaps.add().size = 1;
- else if(skybox[0]) skyenvmap = cubemapload(skybox, true, false, true);
- const vector<extentity *> &ents = entities::getents();
- loopv(ents)
- {
- const extentity &ent = *ents[i];
- if(ent.type != ET_ENVMAP) continue;
- envmap &em = envmaps.add();
- em.radius = ent.attr1 ? clamp(int(ent.attr1), 0, 10000) : envmapradius;
- em.size = ent.attr2 ? clamp(int(ent.attr2), 4, 9) : 0;
- em.blur = ent.attr3 ? clamp(int(ent.attr3), 1, 2) : 0;
- em.o = ent.o;
- }
+ clearenvmaps();
+ skyenvmap = NULL;
+ if(shouldrenderskyenvmap()) envmaps.add().size = 1;
+ else if(skybox[0]) skyenvmap = cubemapload(skybox, true, false, true);
+ const vector<extentity *> &ents = entities::getents();
+ loopv(ents)
+ {
+ const extentity &ent = *ents[i];
+ if(ent.type != ET_ENVMAP) continue;
+ envmap &em = envmaps.add();
+ em.radius = ent.attr1 ? clamp(int(ent.attr1), 0, 10000) : envmapradius;
+ em.size = ent.attr2 ? clamp(int(ent.attr2), 4, 9) : 0;
+ em.blur = ent.attr3 ? clamp(int(ent.attr3), 1, 2) : 0;
+ em.o = ent.o;
+ }
}
void genenvmaps()
{
- if(envmaps.empty()) return;
- renderprogress(0, "generating environment maps...");
- int lastprogress = SDL_GetTicks();
- loopv(envmaps)
- {
- envmap &em = envmaps[i];
- em.tex = genenvmap(em.o, em.size ? min(em.size, envmapsize) : envmapsize, em.blur, em.radius < 0);
- if(renderedframe) continue;
- int millis = SDL_GetTicks();
- if(millis - lastprogress >= 250)
- {
- renderprogress(float(i+1)/envmaps.length(), "generating environment maps...", 0, true);
- lastprogress = millis;
- }
- }
+ if(envmaps.empty()) return;
+ renderprogress(0, "generating environment maps...");
+ int lastprogress = SDL_GetTicks();
+ loopv(envmaps)
+ {
+ envmap &em = envmaps[i];
+ em.tex = genenvmap(em.o, em.size ? min(em.size, envmapsize) : envmapsize, em.blur, em.radius < 0);
+ if(renderedframe) continue;
+ int millis = SDL_GetTicks();
+ if(millis - lastprogress >= 250)
+ {
+ renderprogress(float(i+1)/envmaps.length(), "generating environment maps...", 0, true);
+ lastprogress = millis;
+ }
+ }
}
ushort closestenvmap(const vec &o)
{
- ushort minemid = EMID_SKY;
- float mindist = 1e16f;
- loopv(envmaps)
- {
- envmap &em = envmaps[i];
- float dist;
- if(envmapbb)
- {
- if(!o.insidebb(vec(em.o).sub(em.radius), vec(em.o).add(em.radius))) continue;
- dist = em.o.dist(o);
- }
- else
- {
- dist = em.o.dist(o);
- if(dist > em.radius) continue;
- }
- if(dist < mindist)
- {
- minemid = EMID_RESERVED + i;
- mindist = dist;
- }
- }
- return minemid;
+ ushort minemid = EMID_SKY;
+ float mindist = 1e16f;
+ loopv(envmaps)
+ {
+ envmap &em = envmaps[i];
+ float dist;
+ if(envmapbb)
+ {
+ if(!o.insidebb(vec(em.o).sub(em.radius), vec(em.o).add(em.radius))) continue;
+ dist = em.o.dist(o);
+ }
+ else
+ {
+ dist = em.o.dist(o);
+ if(dist > em.radius) continue;
+ }
+ if(dist < mindist)
+ {
+ minemid = EMID_RESERVED + i;
+ mindist = dist;
+ }
+ }
+ return minemid;
}
ushort closestenvmap(int orient, const ivec &co, int size)
{
- vec loc(co);
- int dim = dimension(orient);
- if(dimcoord(orient)) loc[dim] += size;
- loc[R[dim]] += size/2;
- loc[C[dim]] += size/2;
- return closestenvmap(loc);
+ vec loc(co);
+ int dim = dimension(orient);
+ if(dimcoord(orient)) loc[dim] += size;
+ loc[R[dim]] += size/2;
+ loc[C[dim]] += size/2;
+ return closestenvmap(loc);
}
static inline GLuint lookupskyenvmap()
{
- return envmaps.length() && envmaps[0].radius < 0 ? envmaps[0].tex : (skyenvmap ? skyenvmap->id : 0);
+ return envmaps.length() && envmaps[0].radius < 0 ? envmaps[0].tex : (skyenvmap ? skyenvmap->id : 0);
}
GLuint lookupenvmap(Slot &slot)
{
- loopv(slot.sts) if(slot.sts[i].type==TEX_ENVMAP && slot.sts[i].t) return slot.sts[i].t->id;
- return lookupskyenvmap();
+ loopv(slot.sts) if(slot.sts[i].type==TEX_ENVMAP && slot.sts[i].t) return slot.sts[i].t->id;
+ return lookupskyenvmap();
}
GLuint lookupenvmap(ushort emid)
{
- if(emid==EMID_SKY || emid==EMID_CUSTOM) return skyenvmap ? skyenvmap->id : 0;
- if(emid==EMID_NONE || !envmaps.inrange(emid-EMID_RESERVED)) return 0;
- GLuint tex = envmaps[emid-EMID_RESERVED].tex;
- return tex ? tex : lookupskyenvmap();
+ if(emid==EMID_SKY || emid==EMID_CUSTOM) return skyenvmap ? skyenvmap->id : 0;
+ if(emid==EMID_NONE || !envmaps.inrange(emid-EMID_RESERVED)) return 0;
+ GLuint tex = envmaps[emid-EMID_RESERVED].tex;
+ return tex ? tex : lookupskyenvmap();
}
void cleanuptexture(Texture *t)
{
- DELETEA(t->alphamask);
- if(t->id) { glDeleteTextures(1, &t->id); t->id = 0; }
- if(t->type&Texture::TRANSIENT) textures.remove(t->name);
+ DELETEA(t->alphamask);
+ if(t->id) { glDeleteTextures(1, &t->id); t->id = 0; }
+ if(t->type&Texture::TRANSIENT) textures.remove(t->name);
}
void cleanuptextures()
{
- cleanupmipmaps();
- clearenvmaps();
- loopv(slots) slots[i]->cleanup();
- loopv(vslots) vslots[i]->cleanup();
- loopi((MATF_VOLUME|MATF_INDEX)+1) materialslots[i].cleanup();
- enumerate(textures, Texture, tex, cleanuptexture(&tex));
+ clearenvmaps();
+ loopv(slots) slots[i]->cleanup();
+ loopv(vslots) vslots[i]->cleanup();
+ loopi((MATF_VOLUME|MATF_INDEX)+1) materialslots[i].cleanup();
+ enumerate(textures, Texture, tex, cleanuptexture(&tex));
}
bool reloadtexture(const char *name)
{
- Texture *t = textures.access(path(name, true));
- if(t) return reloadtexture(*t);
- return true;
+ Texture *t = textures.access(path(name, true));
+ if(t) return reloadtexture(*t);
+ return true;
}
bool reloadtexture(Texture &tex)
{
- if(tex.id) return true;
- switch(tex.type&Texture::TYPE)
- {
- case Texture::IMAGE:
- {
- int compress = 0;
- ImageData s;
- if(!texturedata(s, tex.name, NULL, true, &compress) || !newtexture(&tex, NULL, s, tex.clamp, tex.mipmap, false, false, compress)) return false;
- break;
- }
+ if(tex.id) return true;
+ switch(tex.type&Texture::TYPE)
+ {
+ case Texture::IMAGE:
+ {
+ int compress = 0;
+ ImageData s;
+ if(!texturedata(s, tex.name, NULL, true, &compress) || !newtexture(&tex, NULL, s, tex.clamp, tex.mipmap, false, false, compress)) return false;
+ break;
+ }
- case Texture::CUBEMAP:
- if(!cubemaploadwildcard(&tex, NULL, tex.mipmap, true)) return false;
- break;
- }
- return true;
+ case Texture::CUBEMAP:
+ if(!cubemaploadwildcard(&tex, NULL, tex.mipmap, true)) return false;
+ break;
+ }
+ return true;
}
void reloadtex(char *name)
{
- Texture *t = textures.access(path(name, true));
- if(!t) { conoutf(CON_ERROR, "texture %s is not loaded", name); return; }
- if(t->type&Texture::TRANSIENT) { conoutf(CON_ERROR, "can't reload transient texture %s", name); return; }
- DELETEA(t->alphamask);
- Texture oldtex = *t;
- t->id = 0;
- if(!reloadtexture(*t))
- {
- if(t->id) glDeleteTextures(1, &t->id);
- *t = oldtex;
- conoutf(CON_ERROR, "failed to reload texture %s", name);
- }
+ Texture *t = textures.access(path(name, true));
+ if(!t) { conoutf(CON_ERROR, "texture %s is not loaded", name); return; }
+ if(t->type&Texture::TRANSIENT) { conoutf(CON_ERROR, "can't reload transient texture %s", name); return; }
+ DELETEA(t->alphamask);
+ Texture oldtex = *t;
+ t->id = 0;
+ if(!reloadtexture(*t))
+ {
+ if(t->id) glDeleteTextures(1, &t->id);
+ *t = oldtex;
+ conoutf(CON_ERROR, "failed to reload texture %s", name);
+ }
}
COMMAND(reloadtex, "s");
void reloadtextures()
{
- int reloaded = 0;
- enumerate(textures, Texture, tex,
- {
- loadprogress = float(++reloaded)/textures.numelems;
- reloadtexture(tex);
- });
- loadprogress = 0;
+ int reloaded = 0;
+ enumerate(textures, Texture, tex,
+ {
+ loadprogress = float(++reloaded)/textures.numelems;
+ reloadtexture(tex);
+ });
+ loadprogress = 0;
}
void writepngchunk(stream *f, const char *type, uchar *data = NULL, uint len = 0)
{
- f->putbig<uint>(len);
- f->write(type, 4);
- f->write(data, len);
+ f->putbig<uint>(len);
+ f->write(type, 4);
+ f->write(data, len);
- uint crc = crc32(0, Z_NULL, 0);
- crc = crc32(crc, (const Bytef *)type, 4);
- if(data) crc = crc32(crc, data, len);
- f->putbig<uint>(crc);
+ uint crc = crc32(0, Z_NULL, 0);
+ crc = crc32(crc, (const Bytef *)type, 4);
+ if(data) crc = crc32(crc, data, len);
+ f->putbig<uint>(crc);
}
VARP(compresspng, 0, 9, 9);
void savepng(const char *filename, ImageData &image, bool flip)
{
- uchar ctype = 0;
- switch(image.bpp)
- {
- case 1: ctype = 0; break;
- case 2: ctype = 4; break;
- case 3: ctype = 2; break;
- case 4: ctype = 6; break;
- default: conoutf(CON_ERROR, "failed saving png to %s", filename); return;
- }
- stream *f = openfile(filename, "wb");
- if(!f) { conoutf(CON_ERROR, "could not write to %s", filename); return; }
-
- uchar signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
- f->write(signature, sizeof(signature));
-
- struct pngihdr
- {
- uint width, height;
- uchar bitdepth, colortype, compress, filter, interlace;
- } ihdr = { bigswap<uint>(image.w), bigswap<uint>(image.h), 8, ctype, 0, 0, 0 };
- writepngchunk(f, "IHDR", (uchar *)&ihdr, 13);
-
- stream::offset idat = f->tell();
- uint len = 0;
- f->write("\0\0\0\0IDAT", 8);
- uint crc = crc32(0, Z_NULL, 0);
- crc = crc32(crc, (const Bytef *)"IDAT", 4);
-
- z_stream z;
- z.zalloc = NULL;
- z.zfree = NULL;
- z.opaque = NULL;
-
- if(deflateInit(&z, compresspng) != Z_OK)
- goto error;
-
- uchar buf[1<<12];
- z.next_out = (Bytef *)buf;
- z.avail_out = sizeof(buf);
-
- loopi(image.h)
- {
- uchar filter = 0;
- loopj(2)
- {
- z.next_in = j ? (Bytef *)image.data + (flip ? image.h-i-1 : i)*image.pitch : (Bytef *)&filter;
- z.avail_in = j ? image.w*image.bpp : 1;
- while(z.avail_in > 0)
- {
- if(deflate(&z, Z_NO_FLUSH) != Z_OK) goto cleanuperror;
- #define FLUSHZ do { \
- int flush = sizeof(buf) - z.avail_out; \
- crc = crc32(crc, buf, flush); \
- len += flush; \
- f->write(buf, flush); \
- z.next_out = (Bytef *)buf; \
- z.avail_out = sizeof(buf); \
- } while(0)
- FLUSHZ;
- }
- }
- }
-
- for(;;)
- {
- int err = deflate(&z, Z_FINISH);
- if(err != Z_OK && err != Z_STREAM_END) goto cleanuperror;
- FLUSHZ;
- if(err == Z_STREAM_END) break;
- }
-
- deflateEnd(&z);
-
- f->seek(idat, SEEK_SET);
- f->putbig<uint>(len);
- f->seek(0, SEEK_END);
- f->putbig<uint>(crc);
-
- writepngchunk(f, "IEND");
-
- delete f;
- return;
+ uchar ctype = 0;
+ switch(image.bpp)
+ {
+ case 1: ctype = 0; break;
+ case 2: ctype = 4; break;
+ case 3: ctype = 2; break;
+ case 4: ctype = 6; break;
+ default: conoutf(CON_ERROR, "failed saving png to %s", filename); return;
+ }
+ stream *f = openfile(filename, "wb");
+ if(!f) { conoutf(CON_ERROR, "could not write to %s", filename); return; }
+
+ uchar signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+ f->write(signature, sizeof(signature));
+
+ struct pngihdr
+ {
+ uint width, height;
+ uchar bitdepth, colortype, compress, filter, interlace;
+ } ihdr = { bigswap<uint>(image.w), bigswap<uint>(image.h), 8, ctype, 0, 0, 0 };
+ writepngchunk(f, "IHDR", (uchar *)&ihdr, 13);
+
+ stream::offset idat = f->tell();
+ uint len = 0;
+ f->write("\0\0\0\0IDAT", 8);
+ uint crc = crc32(0, Z_NULL, 0);
+ crc = crc32(crc, (const Bytef *)"IDAT", 4);
+
+ z_stream z;
+ z.zalloc = NULL;
+ z.zfree = NULL;
+ z.opaque = NULL;
+
+ if(deflateInit(&z, compresspng) != Z_OK)
+ goto error;
+
+ uchar buf[1<<12];
+ z.next_out = (Bytef *)buf;
+ z.avail_out = sizeof(buf);
+
+ loopi(image.h)
+ {
+ uchar filter = 0;
+ loopj(2)
+ {
+ z.next_in = j ? (Bytef *)image.data + (flip ? image.h-i-1 : i)*image.pitch : (Bytef *)&filter;
+ z.avail_in = j ? image.w*image.bpp : 1;
+ while(z.avail_in > 0)
+ {
+ if(deflate(&z, Z_NO_FLUSH) != Z_OK) goto cleanuperror;
+ #define FLUSHZ do { \
+ int flush = sizeof(buf) - z.avail_out; \
+ crc = crc32(crc, buf, flush); \
+ len += flush; \
+ f->write(buf, flush); \
+ z.next_out = (Bytef *)buf; \
+ z.avail_out = sizeof(buf); \
+ } while(0)
+ FLUSHZ;
+ }
+ }
+ }
+
+ for(;;)
+ {
+ int err = deflate(&z, Z_FINISH);
+ if(err != Z_OK && err != Z_STREAM_END) goto cleanuperror;
+ FLUSHZ;
+ if(err == Z_STREAM_END) break;
+ }
+
+ deflateEnd(&z);
+
+ f->seek(idat, SEEK_SET);
+ f->putbig<uint>(len);
+ f->seek(0, SEEK_END);
+ f->putbig<uint>(crc);
+
+ writepngchunk(f, "IEND");
+
+ delete f;
+ return;
cleanuperror:
- deflateEnd(&z);
+ deflateEnd(&z);
error:
- delete f;
+ delete f;
- conoutf(CON_ERROR, "failed saving png to %s", filename);
+ conoutf(CON_ERROR, "failed saving png to %s", filename);
}
-struct tgaheader
-{
- uchar identsize;
- uchar cmaptype;
- uchar imagetype;
- uchar cmaporigin[2];
- uchar cmapsize[2];
- uchar cmapentrysize;
- uchar xorigin[2];
- uchar yorigin[2];
- uchar width[2];
- uchar height[2];
- uchar pixelsize;
- uchar descbyte;
-};
-
-VARP(compresstga, 0, 1, 1);
-
-void savetga(const char *filename, ImageData &image, bool flip)
-{
- switch(image.bpp)
- {
- case 3: case 4: break;
- default: conoutf(CON_ERROR, "failed saving tga to %s", filename); return;
- }
-
- stream *f = openfile(filename, "wb");
- if(!f) { conoutf(CON_ERROR, "could not write to %s", filename); return; }
-
- tgaheader hdr;
- memset(&hdr, 0, sizeof(hdr));
- hdr.pixelsize = image.bpp*8;
- hdr.width[0] = image.w&0xFF;
- hdr.width[1] = (image.w>>8)&0xFF;
- hdr.height[0] = image.h&0xFF;
- hdr.height[1] = (image.h>>8)&0xFF;
- hdr.imagetype = compresstga ? 10 : 2;
- f->write(&hdr, sizeof(hdr));
-
- uchar buf[128*4];
- loopi(image.h)
- {
- uchar *src = image.data + (flip ? i : image.h - i - 1)*image.pitch;
- for(int remaining = image.w; remaining > 0;)
- {
- int raw = 1;
- if(compresstga)
- {
- int run = 1;
- for(uchar *scan = src; run < min(remaining, 128); run++)
- {
- scan += image.bpp;
- if(src[0]!=scan[0] || src[1]!=scan[1] || src[2]!=scan[2] || (image.bpp==4 && src[3]!=scan[3])) break;
- }
- if(run > 1)
- {
- f->putchar(0x80 | (run-1));
- f->putchar(src[2]); f->putchar(src[1]); f->putchar(src[0]);
- if(image.bpp==4) f->putchar(src[3]);
- src += run*image.bpp;
- remaining -= run;
- if(remaining <= 0) break;
- }
- for(uchar *scan = src; raw < min(remaining, 128); raw++)
- {
- scan += image.bpp;
- if(src[0]==scan[0] && src[1]==scan[1] && src[2]==scan[2] && (image.bpp!=4 || src[3]==scan[3])) break;
- }
- f->putchar(raw - 1);
- }
- else raw = min(remaining, 128);
- uchar *dst = buf;
- loopj(raw)
- {
- dst[0] = src[2];
- dst[1] = src[1];
- dst[2] = src[0];
- if(image.bpp==4) dst[3] = src[3];
- dst += image.bpp;
- src += image.bpp;
- }
- f->write(buf, raw*image.bpp);
- remaining -= raw;
- }
- }
-
- delete f;
-}
-
-enum
-{
- IMG_BMP = 0,
- IMG_TGA = 1,
- IMG_PNG = 2,
- IMG_JPG = 3,
- NUMIMG
-};
+enum { IMG_BMP, IMG_PNG, IMG_JPG, NUMIMG };
VARP(screenshotquality, 0, 97, 100);
VARP(screenshotformat, 0, IMG_PNG, NUMIMG-1);
-const char *imageexts[NUMIMG] = { ".bmp", ".tga", ".png", ".jpg" };
+const char *imageexts[NUMIMG] = { ".bmp", ".png", ".jpg" };
int guessimageformat(const char *filename, int format = IMG_BMP)
{
- int len = strlen(filename);
- loopi(NUMIMG)
- {
- int extlen = strlen(imageexts[i]);
- if(len >= extlen && !strcasecmp(&filename[len-extlen], imageexts[i])) return i;
- }
- return format;
+ int len = strlen(filename);
+ loopi(NUMIMG)
+ {
+ int extlen = strlen(imageexts[i]);
+ if(len >= extlen && !strcasecmp(&filename[len-extlen], imageexts[i])) return i;
+ }
+ return format;
}
void saveimage(const char *filename, int format, ImageData &image, bool flip = false)
{
- switch(format)
- {
- case IMG_PNG: savepng(filename, image, flip); break;
- case IMG_TGA: savetga(filename, image, flip); break;
- default:
- {
- ImageData flipped(image.w, image.h, image.bpp, image.data);
- if(flip) texflip(flipped);
- SDL_Surface *s = wrapsurface(flipped.data, flipped.w, flipped.h, flipped.bpp);
- if(!s) break;
- stream *f = openfile(filename, "wb");
- if(f)
- {
- switch(format) {
- case IMG_JPG:
+ switch(format)
+ {
+ case IMG_PNG: savepng(filename, image, flip); break;
+ default:
+ {
+ ImageData flipped(image.w, image.h, image.bpp, image.data);
+ if(flip) texflip(flipped);
+ SDL_Surface *s = wrapsurface(flipped.data, flipped.w, flipped.h, flipped.bpp);
+ if(!s) break;
+ stream *f = openfile(filename, "wb");
+ if(f)
+ {
+ switch(format) {
+ case IMG_JPG:
#if SDL_IMAGE_VERSION_ATLEAST(2, 0, 2)
- IMG_SaveJPG_RW(s, f->rwops(), 1, screenshotquality);
+ IMG_SaveJPG_RW(s, f->rwops(), 1, screenshotquality);
#else
- conoutf(CON_ERROR, "JPG screenshot support requires SDL_image 2.0.2");
+ conoutf(CON_ERROR, "JPG screenshot support requires SDL_image 2.0.2");
#endif
- break;
- default: SDL_SaveBMP_RW(s, f->rwops(), 1); break;
- }
- delete f;
- }
- SDL_FreeSurface(s);
- break;
- }
- }
+ break;
+ default: SDL_SaveBMP_RW(s, f->rwops(), 1); break;
+ }
+ delete f;
+ }
+ SDL_FreeSurface(s);
+ break;
+ }
+ }
}
bool loadimage(const char *filename, ImageData &image)
{
- SDL_Surface *s = loadsurface(path(filename, true));
- if(!s) return false;
- image.wrap(s);
- return true;
+ SDL_Surface *s = loadsurface(path(filename, true));
+ if(!s) return false;
+ image.wrap(s);
+ return true;
}
SVARP(screenshotdir, "screenshot");
void screenshot(char *filename)
{
- static string buf;
- int format = -1, dirlen = 0;
- copystring(buf, screenshotdir);
- if(screenshotdir[0])
- {
- dirlen = strlen(buf);
- if(buf[dirlen] != '/' && buf[dirlen] != '\\' && dirlen+1 < (int)sizeof(buf)) { buf[dirlen++] = '/'; buf[dirlen] = '\0'; }
- const char *dir = findfile(buf, "w");
- if(!fileexists(dir, "w")) createdir(dir);
- }
- if(filename[0])
- {
- concatstring(buf, filename);
- format = guessimageformat(buf, -1);
- }
- else
- {
- string sstime;
- time_t t = time(NULL);
- size_t len = strftime(sstime, sizeof(sstime), "%Y-%m-%d_%H.%M.%S", localtime(&t));
- sstime[min(len, sizeof(sstime)-1)] = '\0';
- concatstring(buf, sstime);
-
- const char *map = game::getclientmap(), *ssinfo = game::getscreenshotinfo();
- if(map && map[0])
- {
- concatstring(buf, "_");
- concatstring(buf, map);
- }
- if(ssinfo && ssinfo[0])
- {
- concatstring(buf, "_");
- concatstring(buf, ssinfo);
- }
-
- for(char *s = &buf[dirlen]; *s; s++) if(iscubespace(*s) || *s == '/' || *s == '\\') *s = '-';
- }
- if(format < 0)
- {
- format = screenshotformat;
- concatstring(buf, imageexts[format]);
- }
-
- ImageData image(screenw, screenh, 3);
- glPixelStorei(GL_PACK_ALIGNMENT, texalign(image.data, screenw, 3));
- glReadPixels(0, 0, screenw, screenh, GL_RGB, GL_UNSIGNED_BYTE, image.data);
- saveimage(path(buf), format, image, true);
+ static string buf;
+ int format = -1, dirlen = 0;
+ copystring(buf, screenshotdir);
+ if(screenshotdir[0])
+ {
+ dirlen = strlen(buf);
+ if(buf[dirlen] != '/' && buf[dirlen] != '\\' && dirlen+1 < (int)sizeof(buf)) { buf[dirlen++] = '/'; buf[dirlen] = '\0'; }
+ const char *dir = findfile(buf, "w");
+ if(!fileexists(dir, "w")) createdir(dir);
+ }
+ if(filename[0])
+ {
+ concatstring(buf, filename);
+ format = guessimageformat(buf, -1);
+ }
+ else
+ {
+ string sstime;
+ time_t t = time(NULL);
+ size_t len = strftime(sstime, sizeof(sstime), "%Y-%m-%d_%H.%M.%S", localtime(&t));
+ sstime[min(len, sizeof(sstime)-1)] = '\0';
+ concatstring(buf, sstime);
+
+ const char *map = game::getclientmap(), *ssinfo = game::getscreenshotinfo();
+ if(map && map[0])
+ {
+ concatstring(buf, "_");
+ concatstring(buf, map);
+ }
+ if(ssinfo && ssinfo[0])
+ {
+ concatstring(buf, "_");
+ concatstring(buf, ssinfo);
+ }
+
+ for(char *s = &buf[dirlen]; *s; s++) if(iscubespace(*s) || *s == '/' || *s == '\\') *s = '-';
+ }
+ if(format < 0)
+ {
+ format = screenshotformat;
+ concatstring(buf, imageexts[format]);
+ }
+
+ ImageData image(screenw, screenh, 3);
+ glPixelStorei(GL_PACK_ALIGNMENT, texalign(image.data, screenw, 3));
+ glReadPixels(0, 0, screenw, screenh, GL_RGB, GL_UNSIGNED_BYTE, image.data);
+ saveimage(path(buf), format, image, true);
}
COMMAND(screenshot, "s");
-
-void flipnormalmapy(char *destfile, char *normalfile) // jpg/png /tga-> tga
-{
- ImageData ns;
- if(!loadimage(normalfile, ns)) return;
- ImageData d(ns.w, ns.h, 3);
- readwritetex(d, ns,
- dst[0] = src[0];
- dst[1] = 255 - src[1];
- dst[2] = src[2];
- );
- saveimage(destfile, guessimageformat(destfile, IMG_TGA), d);
-}
-
-void mergenormalmaps(char *heightfile, char *normalfile) // jpg/png/tga + tga -> tga
-{
- ImageData hs, ns;
- if(!loadimage(heightfile, hs) || !loadimage(normalfile, ns) || hs.w != ns.w || hs.h != ns.h) return;
- ImageData d(ns.w, ns.h, 3);
- read2writetex(d, hs, srch, ns, srcn,
- *(bvec *)dst = bvec(((bvec *)srcn)->tonormal().mul(2).add(((bvec *)srch)->tonormal()).normalize());
- );
- saveimage(normalfile, guessimageformat(normalfile, IMG_TGA), d);
-}
-
-COMMAND(flipnormalmapy, "ss");
-COMMAND(mergenormalmaps, "ss");
-
diff --git a/src/engine/texture.h b/src/engine/texture.h
index b39ba80..0b1335a 100644
--- a/src/engine/texture.h
+++ b/src/engine/texture.h
@@ -1,99 +1,99 @@
struct GlobalShaderParamState
{
- const char *name;
- union
- {
- float fval[32];
- int ival[32];
- uint uval[32];
- uchar buf[32*sizeof(float)];
- };
- int version;
-
- static int nextversion;
-
- void resetversions();
-
- void changed()
- {
- if(++nextversion < 0) resetversions();
- version = nextversion;
- }
+ const char *name;
+ union
+ {
+ float fval[32];
+ int ival[32];
+ uint uval[32];
+ uchar buf[32*sizeof(float)];
+ };
+ int version;
+
+ static int nextversion;
+
+ void resetversions();
+
+ void changed()
+ {
+ if(++nextversion < 0) resetversions();
+ version = nextversion;
+ }
};
struct ShaderParamBinding
{
- int loc, size;
- GLenum format;
+ int loc, size;
+ GLenum format;
};
struct GlobalShaderParamUse : ShaderParamBinding
{
- GlobalShaderParamState *param;
- int version;
-
- void flush()
- {
- if(version == param->version) return;
- switch(format)
- {
- case GL_BOOL:
- case GL_FLOAT: glUniform1fv_(loc, size, param->fval); break;
- case GL_BOOL_VEC2:
- case GL_FLOAT_VEC2: glUniform2fv_(loc, size, param->fval); break;
- case GL_BOOL_VEC3:
- case GL_FLOAT_VEC3: glUniform3fv_(loc, size, param->fval); break;
- case GL_BOOL_VEC4:
- case GL_FLOAT_VEC4: glUniform4fv_(loc, size, param->fval); break;
- case GL_INT: glUniform1iv_(loc, size, param->ival); break;
- case GL_INT_VEC2: glUniform2iv_(loc, size, param->ival); break;
- case GL_INT_VEC3: glUniform3iv_(loc, size, param->ival); break;
- case GL_INT_VEC4: glUniform4iv_(loc, size, param->ival); break;
- case GL_FLOAT_MAT2: glUniformMatrix2fv_(loc, 1, GL_FALSE, param->fval); break;
- case GL_FLOAT_MAT3: glUniformMatrix3fv_(loc, 1, GL_FALSE, param->fval); break;
- case GL_FLOAT_MAT4: glUniformMatrix4fv_(loc, 1, GL_FALSE, param->fval); break;
- }
- version = param->version;
- }
+ GlobalShaderParamState *param;
+ int version;
+
+ void flush()
+ {
+ if(version == param->version) return;
+ switch(format)
+ {
+ case GL_BOOL:
+ case GL_FLOAT: glUniform1fv_(loc, size, param->fval); break;
+ case GL_BOOL_VEC2:
+ case GL_FLOAT_VEC2: glUniform2fv_(loc, size, param->fval); break;
+ case GL_BOOL_VEC3:
+ case GL_FLOAT_VEC3: glUniform3fv_(loc, size, param->fval); break;
+ case GL_BOOL_VEC4:
+ case GL_FLOAT_VEC4: glUniform4fv_(loc, size, param->fval); break;
+ case GL_INT: glUniform1iv_(loc, size, param->ival); break;
+ case GL_INT_VEC2: glUniform2iv_(loc, size, param->ival); break;
+ case GL_INT_VEC3: glUniform3iv_(loc, size, param->ival); break;
+ case GL_INT_VEC4: glUniform4iv_(loc, size, param->ival); break;
+ case GL_FLOAT_MAT2: glUniformMatrix2fv_(loc, 1, GL_FALSE, param->fval); break;
+ case GL_FLOAT_MAT3: glUniformMatrix3fv_(loc, 1, GL_FALSE, param->fval); break;
+ case GL_FLOAT_MAT4: glUniformMatrix4fv_(loc, 1, GL_FALSE, param->fval); break;
+ }
+ version = param->version;
+ }
};
struct LocalShaderParamState : ShaderParamBinding
{
- const char *name;
+ const char *name;
};
struct SlotShaderParam
{
- const char *name;
- int loc;
- float val[4];
+ const char *name;
+ int loc;
+ float val[4];
};
struct SlotShaderParamState : LocalShaderParamState
{
- float val[4];
-
- SlotShaderParamState() {}
- SlotShaderParamState(const SlotShaderParam &p)
- {
- name = p.name;
- loc = -1;
- size = 1;
- format = GL_FLOAT_VEC4;
- memcpy(val, p.val, sizeof(val));
- }
+ float val[4];
+
+ SlotShaderParamState() {}
+ SlotShaderParamState(const SlotShaderParam &p)
+ {
+ name = p.name;
+ loc = -1;
+ size = 1;
+ format = GL_FLOAT_VEC4;
+ memcpy(val, p.val, sizeof(val));
+ }
};
enum
{
- SHADER_DEFAULT = 0,
- SHADER_NORMALSLMS = 1<<0,
- SHADER_ENVMAP = 1<<1,
- SHADER_OPTION = 1<<3,
+ SHADER_DEFAULT = 0,
+ SHADER_NORMALSLMS = 1<<0,
+ SHADER_ENVMAP = 1<<1,
+ SHADER_OPTION = 1<<3,
- SHADER_INVALID = 1<<8,
- SHADER_DEFERRED = 1<<9
+ SHADER_INVALID = 1<<8,
+ SHADER_DEFERRED = 1<<9
};
#define MAXSHADERDETAIL 3
@@ -106,319 +106,319 @@ struct VSlot;
struct UniformLoc
{
- const char *name, *blockname;
- int loc, version, binding, stride, offset, size;
- void *data;
- UniformLoc(const char *name = NULL, const char *blockname = NULL, int binding = -1, int stride = -1) : name(name), blockname(blockname), loc(-1), version(-1), binding(binding), stride(stride), offset(-1), size(-1), data(NULL) {}
+ const char *name, *blockname;
+ int loc, version, binding, stride, offset, size;
+ void *data;
+ UniformLoc(const char *name = NULL, const char *blockname = NULL, int binding = -1, int stride = -1) : name(name), blockname(blockname), loc(-1), version(-1), binding(binding), stride(stride), offset(-1), size(-1), data(NULL) {}
};
struct AttribLoc
{
- const char *name;
- int loc;
- AttribLoc(const char *name = NULL, int loc = -1) : name(name), loc(loc) {}
+ const char *name;
+ int loc;
+ AttribLoc(const char *name = NULL, int loc = -1) : name(name), loc(loc) {}
};
struct Shader
{
- static Shader *lastshader;
-
- char *name, *vsstr, *psstr, *defer;
- int type;
- GLuint program, vsobj, psobj;
- vector<SlotShaderParamState> defaultparams;
- vector<GlobalShaderParamUse> globalparams;
- vector<LocalShaderParamState> localparams;
- vector<uchar> localparamremap;
- Shader *detailshader, *variantshader, *altshader, *fastshader[MAXSHADERDETAIL];
- vector<Shader *> variants;
- ushort *variantrows;
- bool standard, forced, used;
- Shader *reusevs, *reuseps;
- vector<UniformLoc> uniformlocs;
- vector<AttribLoc> attriblocs;
- const void *owner;
-
- Shader() : name(NULL), vsstr(NULL), psstr(NULL), defer(NULL), type(SHADER_DEFAULT), program(0), vsobj(0), psobj(0), detailshader(NULL), variantshader(NULL), altshader(NULL), variantrows(NULL), standard(false), forced(false), used(false), reusevs(NULL), reuseps(NULL), owner(NULL)
- {
- loopi(MAXSHADERDETAIL) fastshader[i] = this;
- }
-
- ~Shader()
- {
- DELETEA(name);
- DELETEA(vsstr);
- DELETEA(psstr);
- DELETEA(defer);
- DELETEA(variantrows);
- }
-
- void fixdetailshader(bool force = true, bool recurse = true);
- void allocparams(Slot *slot = NULL);
- void setslotparams(Slot &slot);
- void setslotparams(Slot &slot, VSlot &vslot);
- void bindprograms();
-
- void flushparams(Slot *slot = NULL)
- {
- if(!used) { allocparams(slot); used = true; }
- loopv(globalparams) globalparams[i].flush();
- }
-
- void force();
-
- bool invalid() const { return (type&SHADER_INVALID)!=0; }
- bool deferred() const { return (type&SHADER_DEFERRED)!=0; }
- bool loaded() const { return detailshader!=NULL; }
-
- static bool isnull(const Shader *s);
-
- bool isnull() const { return isnull(this); }
-
- int numvariants(int row) const
- {
- if(row < 0 || row >= MAXVARIANTROWS || !variantrows) return 0;
- return variantrows[row+1] - variantrows[row];
- }
-
- Shader *getvariant(int col, int row) const
- {
- if(row < 0 || row >= MAXVARIANTROWS || col < 0 || !variantrows) return NULL;
- int start = variantrows[row], end = variantrows[row+1];
- return col < end - start ? variants[start + col] : NULL;
- }
-
- bool hasoption(int row)
- {
- if(detailshader)
- {
- Shader *s = getvariant(0, row);
- if(s) return (s->type&SHADER_OPTION)!=0;
- }
- return false;
- }
-
- void addvariant(int row, Shader *s)
- {
- if(row < 0 || row >= MAXVARIANTROWS || variants.length() >= USHRT_MAX) return;
- if(!variantrows) { variantrows = new ushort[MAXVARIANTROWS+1]; memset(variantrows, 0, (MAXVARIANTROWS+1)*sizeof(ushort)); }
- variants.insert(variantrows[row+1], s);
- for(int i = row+1; i <= MAXVARIANTROWS; ++i) ++variantrows[i];
- }
-
- void setvariant_(int col, int row, Shader *fallbackshader)
- {
- Shader *s = fallbackshader;
- if(detailshader->variantrows)
- {
- int start = detailshader->variantrows[row], end = detailshader->variantrows[row+1];
- for(col = min(start + col, end-1); col >= start; --col)
- {
- if(!detailshader->variants[col]->invalid()) { s = detailshader->variants[col]; break; }
- }
- }
- if(lastshader!=s) s->bindprograms();
- }
-
- void setvariant(int col, int row, Shader *fallbackshader)
- {
- if(isnull() || !loaded()) return;
- setvariant_(col, row, fallbackshader);
- lastshader->flushparams();
- }
-
- void setvariant(int col, int row)
- {
- if(isnull() || !loaded()) return;
- setvariant_(col, row, detailshader);
- lastshader->flushparams();
- }
-
- void setvariant(int col, int row, Slot &slot, VSlot &vslot, Shader *fallbackshader)
- {
- if(isnull() || !loaded()) return;
- setvariant_(col, row, fallbackshader);
- lastshader->flushparams(&slot);
- lastshader->setslotparams(slot, vslot);
- }
-
- void setvariant(int col, int row, Slot &slot, VSlot &vslot)
- {
- if(isnull() || !loaded()) return;
- setvariant_(col, row, detailshader);
- lastshader->flushparams(&slot);
- lastshader->setslotparams(slot, vslot);
- }
-
- void set_()
- {
- if(lastshader!=detailshader) detailshader->bindprograms();
- }
-
- void set()
- {
- if(isnull() || !loaded()) return;
- set_();
- lastshader->flushparams();
- }
-
- void set(Slot &slot, VSlot &vslot)
- {
- if(isnull() || !loaded()) return;
- set_();
- lastshader->flushparams(&slot);
- lastshader->setslotparams(slot, vslot);
- }
-
- bool compile();
- void cleanup(bool invalid = false);
-
- static int uniformlocversion();
+ static Shader *lastshader;
+
+ char *name, *vsstr, *psstr, *defer;
+ int type;
+ GLuint program, vsobj, psobj;
+ vector<SlotShaderParamState> defaultparams;
+ vector<GlobalShaderParamUse> globalparams;
+ vector<LocalShaderParamState> localparams;
+ vector<uchar> localparamremap;
+ Shader *detailshader, *variantshader, *altshader, *fastshader[MAXSHADERDETAIL];
+ vector<Shader *> variants;
+ ushort *variantrows;
+ bool standard, forced, used;
+ Shader *reusevs, *reuseps;
+ vector<UniformLoc> uniformlocs;
+ vector<AttribLoc> attriblocs;
+ const void *owner;
+
+ Shader() : name(NULL), vsstr(NULL), psstr(NULL), defer(NULL), type(SHADER_DEFAULT), program(0), vsobj(0), psobj(0), detailshader(NULL), variantshader(NULL), altshader(NULL), variantrows(NULL), standard(false), forced(false), used(false), reusevs(NULL), reuseps(NULL), owner(NULL)
+ {
+ loopi(MAXSHADERDETAIL) fastshader[i] = this;
+ }
+
+ ~Shader()
+ {
+ DELETEA(name);
+ DELETEA(vsstr);
+ DELETEA(psstr);
+ DELETEA(defer);
+ DELETEA(variantrows);
+ }
+
+ void fixdetailshader(bool force = true, bool recurse = true);
+ void allocparams(Slot *slot = NULL);
+ void setslotparams(Slot &slot);
+ void setslotparams(Slot &slot, VSlot &vslot);
+ void bindprograms();
+
+ void flushparams(Slot *slot = NULL)
+ {
+ if(!used) { allocparams(slot); used = true; }
+ loopv(globalparams) globalparams[i].flush();
+ }
+
+ void force();
+
+ bool invalid() const { return (type&SHADER_INVALID)!=0; }
+ bool deferred() const { return (type&SHADER_DEFERRED)!=0; }
+ bool loaded() const { return detailshader!=NULL; }
+
+ static bool isnull(const Shader *s);
+
+ bool isnull() const { return isnull(this); }
+
+ int numvariants(int row) const
+ {
+ if(row < 0 || row >= MAXVARIANTROWS || !variantrows) return 0;
+ return variantrows[row+1] - variantrows[row];
+ }
+
+ Shader *getvariant(int col, int row) const
+ {
+ if(row < 0 || row >= MAXVARIANTROWS || col < 0 || !variantrows) return NULL;
+ int start = variantrows[row], end = variantrows[row+1];
+ return col < end - start ? variants[start + col] : NULL;
+ }
+
+ bool hasoption(int row)
+ {
+ if(detailshader)
+ {
+ Shader *s = getvariant(0, row);
+ if(s) return (s->type&SHADER_OPTION)!=0;
+ }
+ return false;
+ }
+
+ void addvariant(int row, Shader *s)
+ {
+ if(row < 0 || row >= MAXVARIANTROWS || variants.length() >= USHRT_MAX) return;
+ if(!variantrows) { variantrows = new ushort[MAXVARIANTROWS+1]; memset(variantrows, 0, (MAXVARIANTROWS+1)*sizeof(ushort)); }
+ variants.insert(variantrows[row+1], s);
+ for(int i = row+1; i <= MAXVARIANTROWS; ++i) ++variantrows[i];
+ }
+
+ void setvariant_(int col, int row, Shader *fallbackshader)
+ {
+ Shader *s = fallbackshader;
+ if(detailshader->variantrows)
+ {
+ int start = detailshader->variantrows[row], end = detailshader->variantrows[row+1];
+ for(col = min(start + col, end-1); col >= start; --col)
+ {
+ if(!detailshader->variants[col]->invalid()) { s = detailshader->variants[col]; break; }
+ }
+ }
+ if(lastshader!=s) s->bindprograms();
+ }
+
+ void setvariant(int col, int row, Shader *fallbackshader)
+ {
+ if(isnull() || !loaded()) return;
+ setvariant_(col, row, fallbackshader);
+ lastshader->flushparams();
+ }
+
+ void setvariant(int col, int row)
+ {
+ if(isnull() || !loaded()) return;
+ setvariant_(col, row, detailshader);
+ lastshader->flushparams();
+ }
+
+ void setvariant(int col, int row, Slot &slot, VSlot &vslot, Shader *fallbackshader)
+ {
+ if(isnull() || !loaded()) return;
+ setvariant_(col, row, fallbackshader);
+ lastshader->flushparams(&slot);
+ lastshader->setslotparams(slot, vslot);
+ }
+
+ void setvariant(int col, int row, Slot &slot, VSlot &vslot)
+ {
+ if(isnull() || !loaded()) return;
+ setvariant_(col, row, detailshader);
+ lastshader->flushparams(&slot);
+ lastshader->setslotparams(slot, vslot);
+ }
+
+ void set_()
+ {
+ if(lastshader!=detailshader) detailshader->bindprograms();
+ }
+
+ void set()
+ {
+ if(isnull() || !loaded()) return;
+ set_();
+ lastshader->flushparams();
+ }
+
+ void set(Slot &slot, VSlot &vslot)
+ {
+ if(isnull() || !loaded()) return;
+ set_();
+ lastshader->flushparams(&slot);
+ lastshader->setslotparams(slot, vslot);
+ }
+
+ bool compile();
+ void cleanup(bool invalid = false);
+
+ static int uniformlocversion();
};
struct GlobalShaderParam
{
- const char *name;
- GlobalShaderParamState *param;
-
- GlobalShaderParam(const char *name) : name(name), param(NULL) {}
-
- GlobalShaderParamState *resolve()
- {
- extern GlobalShaderParamState *getglobalparam(const char *name);
- if(!param) param = getglobalparam(name);
- param->changed();
- return param;
- }
-
- void setf(float x = 0, float y = 0, float z = 0, float w = 0)
- {
- GlobalShaderParamState *g = resolve();
- g->fval[0] = x;
- g->fval[1] = y;
- g->fval[2] = z;
- g->fval[3] = w;
- }
- void set(const vec &v, float w = 0) { setf(v.x, v.y, v.z, w); }
- void set(const vec2 &v, float z = 0, float w = 0) { setf(v.x, v.y, z, w); }
- void set(const vec4 &v) { setf(v.x, v.y, v.z, v.w); }
- void set(const plane &p) { setf(p.x, p.y, p.z, p.offset); }
- void set(const matrix2 &m) { memcpy(resolve()->fval, m.a.v, sizeof(m)); }
- void set(const matrix3 &m) { memcpy(resolve()->fval, m.a.v, sizeof(m)); }
- void set(const matrix4 &m) { memcpy(resolve()->fval, m.a.v, sizeof(m)); }
-
- template<class T>
- void setv(const T *v, int n = 1) { memcpy(resolve()->buf, v, n*sizeof(T)); }
-
- void seti(int x = 0, int y = 0, int z = 0, int w = 0)
- {
- GlobalShaderParamState *g = resolve();
- g->ival[0] = x;
- g->ival[1] = y;
- g->ival[2] = z;
- g->ival[3] = w;
- }
- void set(const ivec &v, int w = 0) { seti(v.x, v.y, v.z, w); }
- void set(const ivec2 &v, int z = 0, int w = 0) { seti(v.x, v.y, z, w); }
- void set(const ivec4 &v) { seti(v.x, v.y, v.z, v.w); }
-
- void setu(uint x = 0, uint y = 0, uint z = 0, uint w = 0)
- {
- GlobalShaderParamState *g = resolve();
- g->uval[0] = x;
- g->uval[1] = y;
- g->uval[2] = z;
- g->uval[3] = w;
- }
-
- template<class T>
- T *reserve(int n = 1) { return (T *)resolve()->buf; }
+ const char *name;
+ GlobalShaderParamState *param;
+
+ GlobalShaderParam(const char *name) : name(name), param(NULL) {}
+
+ GlobalShaderParamState *resolve()
+ {
+ extern GlobalShaderParamState *getglobalparam(const char *name);
+ if(!param) param = getglobalparam(name);
+ param->changed();
+ return param;
+ }
+
+ void setf(float x = 0, float y = 0, float z = 0, float w = 0)
+ {
+ GlobalShaderParamState *g = resolve();
+ g->fval[0] = x;
+ g->fval[1] = y;
+ g->fval[2] = z;
+ g->fval[3] = w;
+ }
+ void set(const vec &v, float w = 0) { setf(v.x, v.y, v.z, w); }
+ void set(const vec2 &v, float z = 0, float w = 0) { setf(v.x, v.y, z, w); }
+ void set(const vec4 &v) { setf(v.x, v.y, v.z, v.w); }
+ void set(const plane &p) { setf(p.x, p.y, p.z, p.offset); }
+ void set(const matrix2 &m) { memcpy(resolve()->fval, m.a.v, sizeof(m)); }
+ void set(const matrix3 &m) { memcpy(resolve()->fval, m.a.v, sizeof(m)); }
+ void set(const matrix4 &m) { memcpy(resolve()->fval, m.a.v, sizeof(m)); }
+
+ template<class T>
+ void setv(const T *v, int n = 1) { memcpy(resolve()->buf, v, n*sizeof(T)); }
+
+ void seti(int x = 0, int y = 0, int z = 0, int w = 0)
+ {
+ GlobalShaderParamState *g = resolve();
+ g->ival[0] = x;
+ g->ival[1] = y;
+ g->ival[2] = z;
+ g->ival[3] = w;
+ }
+ void set(const ivec &v, int w = 0) { seti(v.x, v.y, v.z, w); }
+ void set(const ivec2 &v, int z = 0, int w = 0) { seti(v.x, v.y, z, w); }
+ void set(const ivec4 &v) { seti(v.x, v.y, v.z, v.w); }
+
+ void setu(uint x = 0, uint y = 0, uint z = 0, uint w = 0)
+ {
+ GlobalShaderParamState *g = resolve();
+ g->uval[0] = x;
+ g->uval[1] = y;
+ g->uval[2] = z;
+ g->uval[3] = w;
+ }
+
+ template<class T>
+ T *reserve(int n = 1) { return (T *)resolve()->buf; }
};
struct LocalShaderParam
{
- const char *name;
- int loc;
-
- LocalShaderParam(const char *name) : name(name), loc(-1) {}
-
- LocalShaderParamState *resolve()
- {
- Shader *s = Shader::lastshader;
- if(!s) return NULL;
- if(!s->localparamremap.inrange(loc))
- {
- extern int getlocalparam(const char *name);
- if(loc == -1) loc = getlocalparam(name);
- if(!s->localparamremap.inrange(loc)) return NULL;
- }
- uchar remap = s->localparamremap[loc];
- return s->localparams.inrange(remap) ? &s->localparams[remap] : NULL;
- }
-
- void setf(float x = 0, float y = 0, float z = 0, float w = 0)
- {
- ShaderParamBinding *b = resolve();
- if(b) switch(b->format)
- {
- case GL_BOOL:
- case GL_FLOAT: glUniform1f_(b->loc, x); break;
- case GL_BOOL_VEC2:
- case GL_FLOAT_VEC2: glUniform2f_(b->loc, x, y); break;
- case GL_BOOL_VEC3:
- case GL_FLOAT_VEC3: glUniform3f_(b->loc, x, y, z); break;
- case GL_BOOL_VEC4:
- case GL_FLOAT_VEC4: glUniform4f_(b->loc, x, y, z, w); break;
- case GL_INT: glUniform1i_(b->loc, int(x)); break;
- case GL_INT_VEC2: glUniform2i_(b->loc, int(x), int(y)); break;
- case GL_INT_VEC3: glUniform3i_(b->loc, int(x), int(y), int(z)); break;
- case GL_INT_VEC4: glUniform4i_(b->loc, int(x), int(y), int(z), int(w)); break;
- }
- }
- void set(const vec &v, float w = 0) { setf(v.x, v.y, v.z, w); }
- void set(const vec2 &v, float z = 0, float w = 0) { setf(v.x, v.y, z, w); }
- void set(const vec4 &v) { setf(v.x, v.y, v.z, v.w); }
- void set(const plane &p) { setf(p.x, p.y, p.z, p.offset); }
- void setv(const float *f, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform1fv_(b->loc, n, f); }
- void setv(const vec *v, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform3fv_(b->loc, n, v->v); }
- void setv(const vec2 *v, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform2fv_(b->loc, n, v->v); }
- void setv(const vec4 *v, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform4fv_(b->loc, n, v->v); }
- void setv(const plane *p, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform4fv_(b->loc, n, p->v); }
- void setv(const matrix2 *m, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniformMatrix2fv_(b->loc, n, GL_FALSE, m->a.v); }
- void setv(const matrix3 *m, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniformMatrix3fv_(b->loc, n, GL_FALSE, m->a.v); }
- void setv(const matrix4 *m, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniformMatrix4fv_(b->loc, n, GL_FALSE, m->a.v); }
- void set(const matrix2 &m) { setv(&m); }
- void set(const matrix3 &m) { setv(&m); }
- void set(const matrix4 &m) { setv(&m); }
-
- template<class T>
- void sett(T x, T y, T z, T w)
- {
- ShaderParamBinding *b = resolve();
- if(b) switch(b->format)
- {
- case GL_FLOAT: glUniform1f_(b->loc, x); break;
- case GL_FLOAT_VEC2: glUniform2f_(b->loc, x, y); break;
- case GL_FLOAT_VEC3: glUniform3f_(b->loc, x, y, z); break;
- case GL_FLOAT_VEC4: glUniform4f_(b->loc, x, y, z, w); break;
- case GL_BOOL:
- case GL_INT: glUniform1i_(b->loc, x); break;
- case GL_BOOL_VEC2:
- case GL_INT_VEC2: glUniform2i_(b->loc, x, y); break;
- case GL_BOOL_VEC3:
- case GL_INT_VEC3: glUniform3i_(b->loc, x, y, z); break;
- case GL_BOOL_VEC4:
- case GL_INT_VEC4: glUniform4i_(b->loc, x, y, z, w); break;
- }
- }
- void seti(int x = 0, int y = 0, int z = 0, int w = 0) { sett<int>(x, y, z, w); }
- void set(const ivec &v, int w = 0) { seti(v.x, v.y, v.z, w); }
- void set(const ivec2 &v, int z = 0, int w = 0) { seti(v.x, v.y, z, w); }
- void set(const ivec4 &v) { seti(v.x, v.y, v.z, v.w); }
- void setv(const int *i, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform1iv_(b->loc, n, i); }
- void setv(const ivec *v, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform3iv_(b->loc, n, v->v); }
- void setv(const ivec2 *v, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform2iv_(b->loc, n, v->v); }
- void setv(const ivec4 *v, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform4iv_(b->loc, n, v->v); }
+ const char *name;
+ int loc;
+
+ LocalShaderParam(const char *name) : name(name), loc(-1) {}
+
+ LocalShaderParamState *resolve()
+ {
+ Shader *s = Shader::lastshader;
+ if(!s) return NULL;
+ if(!s->localparamremap.inrange(loc))
+ {
+ extern int getlocalparam(const char *name);
+ if(loc == -1) loc = getlocalparam(name);
+ if(!s->localparamremap.inrange(loc)) return NULL;
+ }
+ uchar remap = s->localparamremap[loc];
+ return s->localparams.inrange(remap) ? &s->localparams[remap] : NULL;
+ }
+
+ void setf(float x = 0, float y = 0, float z = 0, float w = 0)
+ {
+ ShaderParamBinding *b = resolve();
+ if(b) switch(b->format)
+ {
+ case GL_BOOL:
+ case GL_FLOAT: glUniform1f_(b->loc, x); break;
+ case GL_BOOL_VEC2:
+ case GL_FLOAT_VEC2: glUniform2f_(b->loc, x, y); break;
+ case GL_BOOL_VEC3:
+ case GL_FLOAT_VEC3: glUniform3f_(b->loc, x, y, z); break;
+ case GL_BOOL_VEC4:
+ case GL_FLOAT_VEC4: glUniform4f_(b->loc, x, y, z, w); break;
+ case GL_INT: glUniform1i_(b->loc, int(x)); break;
+ case GL_INT_VEC2: glUniform2i_(b->loc, int(x), int(y)); break;
+ case GL_INT_VEC3: glUniform3i_(b->loc, int(x), int(y), int(z)); break;
+ case GL_INT_VEC4: glUniform4i_(b->loc, int(x), int(y), int(z), int(w)); break;
+ }
+ }
+ void set(const vec &v, float w = 0) { setf(v.x, v.y, v.z, w); }
+ void set(const vec2 &v, float z = 0, float w = 0) { setf(v.x, v.y, z, w); }
+ void set(const vec4 &v) { setf(v.x, v.y, v.z, v.w); }
+ void set(const plane &p) { setf(p.x, p.y, p.z, p.offset); }
+ void setv(const float *f, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform1fv_(b->loc, n, f); }
+ void setv(const vec *v, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform3fv_(b->loc, n, v->v); }
+ void setv(const vec2 *v, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform2fv_(b->loc, n, v->v); }
+ void setv(const vec4 *v, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform4fv_(b->loc, n, v->v); }
+ void setv(const plane *p, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform4fv_(b->loc, n, p->v); }
+ void setv(const matrix2 *m, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniformMatrix2fv_(b->loc, n, GL_FALSE, m->a.v); }
+ void setv(const matrix3 *m, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniformMatrix3fv_(b->loc, n, GL_FALSE, m->a.v); }
+ void setv(const matrix4 *m, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniformMatrix4fv_(b->loc, n, GL_FALSE, m->a.v); }
+ void set(const matrix2 &m) { setv(&m); }
+ void set(const matrix3 &m) { setv(&m); }
+ void set(const matrix4 &m) { setv(&m); }
+
+ template<class T>
+ void sett(T x, T y, T z, T w)
+ {
+ ShaderParamBinding *b = resolve();
+ if(b) switch(b->format)
+ {
+ case GL_FLOAT: glUniform1f_(b->loc, x); break;
+ case GL_FLOAT_VEC2: glUniform2f_(b->loc, x, y); break;
+ case GL_FLOAT_VEC3: glUniform3f_(b->loc, x, y, z); break;
+ case GL_FLOAT_VEC4: glUniform4f_(b->loc, x, y, z, w); break;
+ case GL_BOOL:
+ case GL_INT: glUniform1i_(b->loc, x); break;
+ case GL_BOOL_VEC2:
+ case GL_INT_VEC2: glUniform2i_(b->loc, x, y); break;
+ case GL_BOOL_VEC3:
+ case GL_INT_VEC3: glUniform3i_(b->loc, x, y, z); break;
+ case GL_BOOL_VEC4:
+ case GL_INT_VEC4: glUniform4i_(b->loc, x, y, z, w); break;
+ }
+ }
+ void seti(int x = 0, int y = 0, int z = 0, int w = 0) { sett<int>(x, y, z, w); }
+ void set(const ivec &v, int w = 0) { seti(v.x, v.y, v.z, w); }
+ void set(const ivec2 &v, int z = 0, int w = 0) { seti(v.x, v.y, z, w); }
+ void set(const ivec4 &v) { seti(v.x, v.y, v.z, v.w); }
+ void setv(const int *i, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform1iv_(b->loc, n, i); }
+ void setv(const ivec *v, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform3iv_(b->loc, n, v->v); }
+ void setv(const ivec2 *v, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform2iv_(b->loc, n, v->v); }
+ void setv(const ivec4 *v, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform4iv_(b->loc, n, v->v); }
};
#define LOCALPARAM(name, vals) do { static LocalShaderParam param( #name ); param.set(vals); } while(0)
@@ -431,106 +431,106 @@ struct LocalShaderParam
#define GLOBALPARAMV(name, vals, num) do { static GlobalShaderParam param( #name ); param.setv(vals, num); } while(0)
#define SETSHADER(name, ...) \
- do { \
- static Shader *name##shader = NULL; \
- if(!name##shader) name##shader = lookupshaderbyname(#name); \
- name##shader->set(__VA_ARGS__); \
- } while(0)
+ do { \
+ static Shader *name##shader = NULL; \
+ if(!name##shader) name##shader = lookupshaderbyname(#name); \
+ name##shader->set(__VA_ARGS__); \
+ } while(0)
#define SETVARIANT(name, ...) \
- do { \
- static Shader *name##shader = NULL; \
- if(!name##shader) name##shader = lookupshaderbyname(#name); \
- name##shader->setvariant(__VA_ARGS__); \
- } while(0)
+ do { \
+ static Shader *name##shader = NULL; \
+ if(!name##shader) name##shader = lookupshaderbyname(#name); \
+ name##shader->setvariant(__VA_ARGS__); \
+ } while(0)
struct ImageData
{
- int w, h, bpp, levels, align, pitch;
- GLenum compressed;
- uchar *data;
- void *owner;
- void (*freefunc)(void *);
-
- ImageData()
- : data(NULL), owner(NULL), freefunc(NULL)
- {}
-
-
- ImageData(int nw, int nh, int nbpp, int nlevels = 1, int nalign = 0, GLenum ncompressed = GL_FALSE)
- {
- setdata(NULL, nw, nh, nbpp, nlevels, nalign, ncompressed);
- }
-
- ImageData(int nw, int nh, int nbpp, uchar *data)
- : owner(NULL), freefunc(NULL)
- {
- setdata(data, nw, nh, nbpp);
- }
-
- ImageData(SDL_Surface *s) { wrap(s); }
- ~ImageData() { cleanup(); }
-
- void setdata(uchar *ndata, int nw, int nh, int nbpp, int nlevels = 1, int nalign = 0, GLenum ncompressed = GL_FALSE)
- {
- w = nw;
- h = nh;
- bpp = nbpp;
- levels = nlevels;
- align = nalign;
- pitch = align ? 0 : w*bpp;
- compressed = ncompressed;
- data = ndata ? ndata : new uchar[calcsize()];
- if(!ndata) { owner = this; freefunc = NULL; }
- }
-
- int calclevelsize(int level) const { return ((max(w>>level, 1)+align-1)/align)*((max(h>>level, 1)+align-1)/align)*bpp; }
-
- int calcsize() const
- {
- if(!align) return w*h*bpp;
- int lw = w, lh = h,
- size = 0;
- loopi(levels)
- {
- if(lw<=0) lw = 1;
- if(lh<=0) lh = 1;
- size += ((lw+align-1)/align)*((lh+align-1)/align)*bpp;
- if(lw*lh==1) break;
- lw >>= 1;
- lh >>= 1;
- }
- return size;
- }
-
- void disown()
- {
- data = NULL;
- owner = NULL;
- freefunc = NULL;
- }
-
- void cleanup()
- {
- if(owner==this) delete[] data;
- else if(freefunc) (*freefunc)(owner);
- disown();
- }
-
- void replace(ImageData &d)
- {
- cleanup();
- *this = d;
- if(owner == &d) owner = this;
- d.disown();
- }
-
- void wrap(SDL_Surface *s)
- {
- setdata((uchar *)s->pixels, s->w, s->h, s->format->BytesPerPixel);
- pitch = s->pitch;
- owner = s;
- freefunc = (void (*)(void *))SDL_FreeSurface;
- }
+ int w, h, bpp, levels, align, pitch;
+ GLenum compressed;
+ uchar *data;
+ void *owner;
+ void (*freefunc)(void *);
+
+ ImageData()
+ : data(NULL), owner(NULL), freefunc(NULL)
+ {}
+
+
+ ImageData(int nw, int nh, int nbpp, int nlevels = 1, int nalign = 0, GLenum ncompressed = GL_FALSE)
+ {
+ setdata(NULL, nw, nh, nbpp, nlevels, nalign, ncompressed);
+ }
+
+ ImageData(int nw, int nh, int nbpp, uchar *data)
+ : owner(NULL), freefunc(NULL)
+ {
+ setdata(data, nw, nh, nbpp);
+ }
+
+ ImageData(SDL_Surface *s) { wrap(s); }
+ ~ImageData() { cleanup(); }
+
+ void setdata(uchar *ndata, int nw, int nh, int nbpp, int nlevels = 1, int nalign = 0, GLenum ncompressed = GL_FALSE)
+ {
+ w = nw;
+ h = nh;
+ bpp = nbpp;
+ levels = nlevels;
+ align = nalign;
+ pitch = align ? 0 : w*bpp;
+ compressed = ncompressed;
+ data = ndata ? ndata : new uchar[calcsize()];
+ if(!ndata) { owner = this; freefunc = NULL; }
+ }
+
+ int calclevelsize(int level) const { return ((max(w>>level, 1)+align-1)/align)*((max(h>>level, 1)+align-1)/align)*bpp; }
+
+ int calcsize() const
+ {
+ if(!align) return w*h*bpp;
+ int lw = w, lh = h,
+ size = 0;
+ loopi(levels)
+ {
+ if(lw<=0) lw = 1;
+ if(lh<=0) lh = 1;
+ size += ((lw+align-1)/align)*((lh+align-1)/align)*bpp;
+ if(lw*lh==1) break;
+ lw >>= 1;
+ lh >>= 1;
+ }
+ return size;
+ }
+
+ void disown()
+ {
+ data = NULL;
+ owner = NULL;
+ freefunc = NULL;
+ }
+
+ void cleanup()
+ {
+ if(owner==this) delete[] data;
+ else if(freefunc) (*freefunc)(owner);
+ disown();
+ }
+
+ void replace(ImageData &d)
+ {
+ cleanup();
+ *this = d;
+ if(owner == &d) owner = this;
+ d.disown();
+ }
+
+ void wrap(SDL_Surface *s)
+ {
+ setdata((uchar *)s->pixels, s->w, s->h, s->format->BytesPerPixel);
+ pitch = s->pitch;
+ owner = s;
+ freefunc = (void (*)(void *))SDL_FreeSurface;
+ }
};
// management of texture slots
@@ -539,190 +539,190 @@ struct ImageData
struct Texture
{
- enum
- {
- IMAGE = 0,
- CUBEMAP = 1,
- TYPE = 0xFF,
-
- STUB = 1<<8,
- TRANSIENT = 1<<9,
- COMPRESSED = 1<<10,
- ALPHA = 1<<11,
- MIRROR = 1<<12,
- FLAGS = 0xFF00
- };
-
- char *name;
- int type, w, h, xs, ys, bpp, clamp;
- bool mipmap, canreduce;
- GLuint id;
- uchar *alphamask;
-
- Texture() : alphamask(NULL) {}
+ enum
+ {
+ IMAGE = 0,
+ CUBEMAP = 1,
+ TYPE = 0xFF,
+
+ STUB = 1<<8,
+ TRANSIENT = 1<<9,
+ COMPRESSED = 1<<10,
+ ALPHA = 1<<11,
+ MIRROR = 1<<12,
+ FLAGS = 0xFF00
+ };
+
+ char *name;
+ int type, w, h, xs, ys, bpp, clamp;
+ bool mipmap, canreduce;
+ GLuint id;
+ uchar *alphamask;
+
+ Texture() : alphamask(NULL) {}
};
enum
{
- TEX_DIFFUSE = 0,
- TEX_UNKNOWN,
- TEX_DECAL,
- TEX_NORMAL,
- TEX_GLOW,
- TEX_SPEC,
- TEX_DEPTH,
- TEX_ALPHA,
- TEX_ENVMAP
+ TEX_DIFFUSE = 0,
+ TEX_UNKNOWN,
+ TEX_DECAL,
+ TEX_NORMAL,
+ TEX_GLOW,
+ TEX_SPEC,
+ TEX_DEPTH,
+ TEX_ALPHA,
+ TEX_ENVMAP
};
enum
{
- VSLOT_SHPARAM = 0,
- VSLOT_SCALE,
- VSLOT_ROTATION,
- VSLOT_OFFSET,
- VSLOT_SCROLL,
- VSLOT_LAYER,
- VSLOT_ALPHA,
- VSLOT_COLOR,
- VSLOT_NUM
+ VSLOT_SHPARAM = 0,
+ VSLOT_SCALE,
+ VSLOT_ROTATION,
+ VSLOT_OFFSET,
+ VSLOT_SCROLL,
+ VSLOT_LAYER,
+ VSLOT_ALPHA,
+ VSLOT_COLOR,
+ VSLOT_NUM
};
struct VSlot
{
- Slot *slot;
- VSlot *next;
- int index, changed;
- vector<SlotShaderParam> params;
- bool linked;
- float scale;
- int rotation;
- ivec2 offset;
- vec2 scroll;
- int layer;
- float alphafront, alphaback;
- vec colorscale;
- vec glowcolor;
-
- VSlot(Slot *slot = NULL, int index = -1) : slot(slot), next(NULL), index(index), changed(0)
- {
- reset();
- if(slot) addvariant(slot);
- }
-
- void addvariant(Slot *slot);
-
- void reset()
- {
- params.shrink(0);
- linked = false;
- scale = 1;
- rotation = 0;
- offset = ivec2(0, 0);
- scroll = vec2(0, 0);
- layer = 0;
- alphafront = 0.5f;
- alphaback = 0;
- colorscale = vec(1, 1, 1);
- glowcolor = vec(1, 1, 1);
- }
-
- void cleanup()
- {
- linked = false;
- }
+ Slot *slot;
+ VSlot *next;
+ int index, changed;
+ vector<SlotShaderParam> params;
+ bool linked;
+ float scale;
+ int rotation;
+ ivec2 offset;
+ vec2 scroll;
+ int layer;
+ float alphafront, alphaback;
+ vec colorscale;
+ vec glowcolor;
+
+ VSlot(Slot *slot = NULL, int index = -1) : slot(slot), next(NULL), index(index), changed(0)
+ {
+ reset();
+ if(slot) addvariant(slot);
+ }
+
+ void addvariant(Slot *slot);
+
+ void reset()
+ {
+ params.shrink(0);
+ linked = false;
+ scale = 1;
+ rotation = 0;
+ offset = ivec2(0, 0);
+ scroll = vec2(0, 0);
+ layer = 0;
+ alphafront = 0.5f;
+ alphaback = 0;
+ colorscale = vec(1, 1, 1);
+ glowcolor = vec(1, 1, 1);
+ }
+
+ void cleanup()
+ {
+ linked = false;
+ }
};
struct Slot
{
- struct Tex
- {
- int type;
- Texture *t;
- string name;
- int combined;
- };
-
- int index;
- vector<Tex> sts;
- Shader *shader;
- vector<SlotShaderParam> params;
- VSlot *variants;
- bool loaded;
- uint texmask;
- Texture *thumbnail;
- char *layermaskname;
- int layermaskmode;
- float layermaskscale;
- ImageData *layermask;
-
- Slot(int index = -1) : index(index), variants(NULL), layermaskname(NULL), layermask(NULL) { reset(); }
-
- void reset()
- {
- sts.shrink(0);
- shader = NULL;
- params.shrink(0);
- loaded = false;
- texmask = 0;
- thumbnail = NULL;
- DELETEA(layermaskname);
- layermaskmode = 0;
- layermaskscale = 1;
- if(layermask) DELETEP(layermask);
- }
-
- void cleanup()
- {
- loaded = false;
- thumbnail = NULL;
- loopv(sts)
- {
- Tex &t = sts[i];
- t.t = NULL;
- t.combined = -1;
- }
- }
+ struct Tex
+ {
+ int type;
+ Texture *t;
+ string name;
+ int combined;
+ };
+
+ int index;
+ vector<Tex> sts;
+ Shader *shader;
+ vector<SlotShaderParam> params;
+ VSlot *variants;
+ bool loaded;
+ uint texmask;
+ Texture *thumbnail;
+ char *layermaskname;
+ int layermaskmode;
+ float layermaskscale;
+ ImageData *layermask;
+
+ Slot(int index = -1) : index(index), variants(NULL), layermaskname(NULL), layermask(NULL) { reset(); }
+
+ void reset()
+ {
+ sts.shrink(0);
+ shader = NULL;
+ params.shrink(0);
+ loaded = false;
+ texmask = 0;
+ thumbnail = NULL;
+ DELETEA(layermaskname);
+ layermaskmode = 0;
+ layermaskscale = 1;
+ if(layermask) DELETEP(layermask);
+ }
+
+ void cleanup()
+ {
+ loaded = false;
+ thumbnail = NULL;
+ loopv(sts)
+ {
+ Tex &t = sts[i];
+ t.t = NULL;
+ t.combined = -1;
+ }
+ }
};
inline void VSlot::addvariant(Slot *slot)
{
- if(!slot->variants) slot->variants = this;
- else
- {
- VSlot *prev = slot->variants;
- while(prev->next) prev = prev->next;
- prev->next = this;
- }
+ if(!slot->variants) slot->variants = this;
+ else
+ {
+ VSlot *prev = slot->variants;
+ while(prev->next) prev = prev->next;
+ prev->next = this;
+ }
}
struct MSlot : Slot, VSlot
{
- MSlot() : VSlot(this) {}
-
- void reset()
- {
- Slot::reset();
- VSlot::reset();
- }
-
- void cleanup()
- {
- Slot::cleanup();
- VSlot::cleanup();
- }
+ MSlot() : VSlot(this) {}
+
+ void reset()
+ {
+ Slot::reset();
+ VSlot::reset();
+ }
+
+ void cleanup()
+ {
+ Slot::cleanup();
+ VSlot::cleanup();
+ }
};
struct texrotation
{
- bool flipx, flipy, swapxy;
+ bool flipx, flipy, swapxy;
};
struct cubemapside
{
- GLenum target;
- const char *name;
- bool flipx, flipy, swapxy;
+ GLenum target;
+ const char *name;
+ bool flipx, flipy, swapxy;
};
extern const texrotation texrotations[8];
@@ -755,7 +755,6 @@ extern void setupblurkernel(int radius, float sigma, float *weights, float *offs
extern void setblurshader(int pass, int size, int radius, float *weights, float *offsets);
extern void savepng(const char *filename, ImageData &image, bool flip = false);
-extern void savetga(const char *filename, ImageData &image, bool flip = false);
extern bool loadimage(const char *filename, ImageData &image);
extern MSlot &lookupmaterialslot(int slot, bool load = true);
diff --git a/src/engine/vertmodel.h b/src/engine/vertmodel.h
index eb09001..e9af1b0 100644
--- a/src/engine/vertmodel.h
+++ b/src/engine/vertmodel.h
@@ -1,490 +1,490 @@
struct vertmodel : animmodel
{
- struct vert { vec pos, norm; };
- struct vvert { vec pos; vec2 tc; };
- struct vvertn : vvert { vec norm; };
- struct vvertbump : vvert { squat tangent; };
- struct tcvert { vec2 tc; };
- struct bumpvert { vec4 tangent; };
- struct tri { ushort vert[3]; };
-
- struct vbocacheentry
- {
- GLuint vbuf;
- animstate as;
- int millis;
-
- vbocacheentry() : vbuf(0) { as.cur.fr1 = as.prev.fr1 = -1; }
- };
-
- struct vertmesh : mesh
- {
- vert *verts;
- tcvert *tcverts;
- bumpvert *bumpverts;
- tri *tris;
- int numverts, numtris;
-
- int voffset, eoffset, elen;
- ushort minvert, maxvert;
-
- vertmesh() : verts(0), tcverts(0), bumpverts(0), tris(0)
- {
- }
-
- virtual ~vertmesh()
- {
- DELETEA(verts);
- DELETEA(tcverts);
- DELETEA(bumpverts);
- DELETEA(tris);
- }
-
- void smoothnorms(float limit = 0, bool areaweight = true)
- {
- if(((vertmeshgroup *)group)->numframes == 1) mesh::smoothnorms(verts, numverts, tris, numtris, limit, areaweight);
- else buildnorms(areaweight);
- }
-
- void buildnorms(bool areaweight = true)
- {
- mesh::buildnorms(verts, numverts, tris, numtris, areaweight, ((vertmeshgroup *)group)->numframes);
- }
-
- void calctangents(bool areaweight = true)
- {
- if(bumpverts) return;
- bumpverts = new bumpvert[((vertmeshgroup *)group)->numframes*numverts];
- mesh::calctangents(bumpverts, verts, tcverts, numverts, tris, numtris, areaweight, ((vertmeshgroup *)group)->numframes);
- }
-
- void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m)
- {
- loopj(numverts)
- {
- vec v = m.transform(verts[j].pos);
- loopi(3)
- {
- bbmin[i] = min(bbmin[i], v[i]);
- bbmax[i] = max(bbmax[i], v[i]);
- }
- }
- }
-
- void genBIH(BIH::mesh &m)
- {
- m.tris = (const BIH::tri *)tris;
- m.numtris = numtris;
- m.pos = (const uchar *)&verts->pos;
- m.posstride = sizeof(vert);
- m.tc = (const uchar *)&tcverts->tc;
- m.tcstride = sizeof(tcvert);
- }
-
- static inline void assignvert(vvertn &vv, int j, tcvert &tc, vert &v)
- {
- vv.pos = v.pos;
- vv.norm = v.norm;
- vv.tc = tc.tc;
- }
-
- inline void assignvert(vvertbump &vv, int j, tcvert &tc, vert &v)
- {
- vv.pos = v.pos;
- vv.tc = tc.tc;
- vv.tangent = bumpverts[j].tangent;
- }
-
- template<class T>
- int genvbo(vector<ushort> &idxs, int offset, vector<T> &vverts, int *htdata, int htlen)
- {
- voffset = offset;
- eoffset = idxs.length();
- minvert = 0xFFFF;
- loopi(numtris)
- {
- tri &t = tris[i];
- loopj(3)
- {
- int index = t.vert[j];
- tcvert &tc = tcverts[index];
- vert &v = verts[index];
- T vv;
- assignvert(vv, index, tc, v);
- int htidx = hthash(v.pos)&(htlen-1);
- loopk(htlen)
- {
- int &vidx = htdata[(htidx+k)&(htlen-1)];
- if(vidx < 0) { vidx = idxs.add(ushort(vverts.length())); vverts.add(vv); break; }
- else if(!memcmp(&vverts[vidx], &vv, sizeof(vv))) { minvert = min(minvert, idxs.add(ushort(vidx))); break; }
- }
- }
- }
- minvert = min(minvert, ushort(voffset));
- maxvert = max(minvert, ushort(vverts.length()-1));
- elen = idxs.length()-eoffset;
- return vverts.length()-voffset;
- }
-
- int genvbo(vector<ushort> &idxs, int offset)
- {
- voffset = offset;
- eoffset = idxs.length();
- loopi(numtris)
- {
- tri &t = tris[i];
- loopj(3) idxs.add(voffset+t.vert[j]);
- }
- minvert = voffset;
- maxvert = voffset + numverts-1;
- elen = idxs.length()-eoffset;
- return numverts;
- }
-
- template<class T>
- static inline void fillvert(T &vv, int j, tcvert &tc, vert &v)
- {
- vv.tc = tc.tc;
- }
-
- template<class T>
- void fillverts(T *vdata)
- {
- vdata += voffset;
- loopi(numverts) fillvert(vdata[i], i, tcverts[i], verts[i]);
- }
-
- void interpverts(const animstate &as, bool tangents, void * RESTRICT vdata, skin &s)
- {
- const vert * RESTRICT vert1 = &verts[as.cur.fr1 * numverts],
- * RESTRICT vert2 = &verts[as.cur.fr2 * numverts],
- * RESTRICT pvert1 = as.interp<1 ? &verts[as.prev.fr1 * numverts] : NULL,
- * RESTRICT pvert2 = as.interp<1 ? &verts[as.prev.fr2 * numverts] : NULL;
- #define ipvert(attrib) v.attrib.lerp(vert1[i].attrib, vert2[i].attrib, as.cur.t)
- #define ipbvert(attrib, type) v.attrib.lerp(bvert1[i].attrib, bvert2[i].attrib, as.cur.t)
- #define ipvertp(attrib) v.attrib.lerp(pvert1[i].attrib, pvert2[i].attrib, as.prev.t).lerp(vec().lerp(vert1[i].attrib, vert2[i].attrib, as.cur.t), as.interp)
- #define ipbvertp(attrib, type) v.attrib.lerp(type().lerp(bpvert1[i].attrib, bpvert2[i].attrib, as.prev.t), type().lerp(bvert1[i].attrib, bvert2[i].attrib, as.cur.t), as.interp)
- #define iploop(type, body) \
- loopi(numverts) \
- { \
- type &v = ((type * RESTRICT)vdata)[i]; \
- body; \
- }
- if(tangents)
- {
- const bumpvert * RESTRICT bvert1 = &bumpverts[as.cur.fr1 * numverts],
- * RESTRICT bvert2 = &bumpverts[as.cur.fr2 * numverts],
- * RESTRICT bpvert1 = as.interp<1 ? &bumpverts[as.prev.fr1 * numverts] : NULL,
- * RESTRICT bpvert2 = as.interp<1 ? &bumpverts[as.prev.fr2 * numverts] : NULL;
- if(as.interp<1) iploop(vvertbump, { ipvertp(pos); ipbvertp(tangent, vec4); })
- else iploop(vvertbump, { ipvert(pos); ipbvert(tangent, vec4); })
- }
- else
- {
- if(as.interp<1) iploop(vvertn, { ipvertp(pos); ipvertp(norm); })
- else iploop(vvertn, { ipvert(pos); ipvert(norm); })
- }
- #undef iploop
- #undef ipvert
- #undef ipbvert
- #undef ipvertp
- #undef ipbvertp
- }
-
- void render(const animstate *as, skin &s, vbocacheentry &vc)
- {
- if(!Shader::lastshader) return;
- glDrawRangeElements_(GL_TRIANGLES, minvert, maxvert, elen, GL_UNSIGNED_SHORT, &((vertmeshgroup *)group)->edata[eoffset]);
- glde++;
- xtravertsva += numverts;
- }
- };
-
- struct tag
- {
- char *name;
- matrix4x3 transform;
-
- tag() : name(NULL) {}
- ~tag() { DELETEA(name); }
- };
-
- struct vertmeshgroup : meshgroup
- {
- int numframes;
- tag *tags;
- int numtags;
-
- static const int MAXVBOCACHE = 16;
- vbocacheentry vbocache[MAXVBOCACHE];
-
- ushort *edata;
- GLuint ebuf;
- bool vtangents;
- int vlen, vertsize;
- uchar *vdata;
-
- vertmeshgroup() : numframes(0), tags(NULL), numtags(0), edata(NULL), ebuf(0), vtangents(false), vlen(0), vertsize(0), vdata(NULL)
- {
- }
-
- virtual ~vertmeshgroup()
- {
- DELETEA(tags);
- if(ebuf) glDeleteBuffers_(1, &ebuf);
- loopi(MAXVBOCACHE)
- {
- if(vbocache[i].vbuf) glDeleteBuffers_(1, &vbocache[i].vbuf);
- }
- DELETEA(vdata);
- }
-
- int findtag(const char *name)
- {
- loopi(numtags) if(!strcmp(tags[i].name, name)) return i;
- return -1;
- }
-
- int totalframes() const { return numframes; }
-
- void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n)
- {
- n.mul(m, tags[numtags + i].transform);
- n.posttranslate(m.transformnormal(p->translate), p->model->scale);
- }
-
- void calctagmatrix(part *p, int i, const animstate &as, matrix4 &matrix)
- {
- const matrix4x3 &tag1 = tags[as.cur.fr1*numtags + i].transform,
- &tag2 = tags[as.cur.fr2*numtags + i].transform;
- matrix4x3 tag;
- tag.lerp(tag1, tag2, as.cur.t);
- if(as.interp<1)
- {
- const matrix4x3 &tag1p = tags[as.prev.fr1*numtags + i].transform,
- &tag2p = tags[as.prev.fr2*numtags + i].transform;
- matrix4x3 tagp;
- tagp.lerp(tag1p, tag2p, as.prev.t);
- tag.lerp(tagp, tag, as.interp);
- }
- tag.d.add(p->translate).mul(p->model->scale);
- matrix = matrix4(tag);
- }
-
- void genvbo(bool tangents, vbocacheentry &vc)
- {
- if(!vc.vbuf) glGenBuffers_(1, &vc.vbuf);
- if(ebuf) return;
-
- vector<ushort> idxs;
-
- if(tangents) loopv(meshes) ((vertmesh *)meshes[i])->calctangents();
-
- vtangents = tangents;
- vertsize = tangents ? sizeof(vvertbump) : sizeof(vvertn);
- vlen = 0;
- if(numframes>1)
- {
- loopv(meshes) vlen += ((vertmesh *)meshes[i])->genvbo(idxs, vlen);
- DELETEA(vdata);
- vdata = new uchar[vlen*vertsize];
- #define FILLVDATA(type) do { \
- loopv(meshes) ((vertmesh *)meshes[i])->fillverts((type *)vdata); \
- } while(0)
- if(tangents) FILLVDATA(vvertbump);
- else FILLVDATA(vvertn);
- #undef FILLVDATA
- }
- else
- {
- gle::bindvbo(vc.vbuf);
- #define GENVBO(type) do { \
- vector<type> vverts; \
- loopv(meshes) vlen += ((vertmesh *)meshes[i])->genvbo(idxs, vlen, vverts, htdata, htlen); \
- glBufferData_(GL_ARRAY_BUFFER, vverts.length()*sizeof(type), vverts.getbuf(), GL_STATIC_DRAW); \
- } while(0)
- int numverts = 0, htlen = 128;
- loopv(meshes) numverts += ((vertmesh *)meshes[i])->numverts;
- while(htlen < numverts) htlen *= 2;
- if(numverts*4 > htlen*3) htlen *= 2;
- int *htdata = new int[htlen];
- memset(htdata, -1, htlen*sizeof(int));
- if(tangents) GENVBO(vvertbump);
- else GENVBO(vvertn);
- delete[] htdata;
- #undef GENVBO
- gle::clearvbo();
- }
-
- glGenBuffers_(1, &ebuf);
- gle::bindebo(ebuf);
- glBufferData_(GL_ELEMENT_ARRAY_BUFFER, idxs.length()*sizeof(ushort), idxs.getbuf(), GL_STATIC_DRAW);
- gle::clearebo();
- }
-
- void bindvbo(const animstate *as, vbocacheentry &vc)
- {
- vvert *vverts = 0;
- bindpos(ebuf, vc.vbuf, &vverts->pos, vertsize);
- if(as->cur.anim&ANIM_NOSKIN)
- {
- if(enabletc) disabletc();
- if(enablenormals) disablenormals();
- if(enabletangents) disabletangents();
- }
- else
- {
- if(vtangents)
- {
- if(enablenormals) disablenormals();
- vvertbump *vvertbumps = 0;
- bindtangents(&vvertbumps->tangent, vertsize);
- }
- else
- {
- if(enabletangents) disabletangents();
- vvertn *vvertns = 0;
- bindnormals(&vvertns->norm, vertsize);
- }
-
- bindtc(&vverts->tc, vertsize);
- }
- if(enablebones) disablebones();
- }
-
- void cleanup()
- {
- loopi(MAXVBOCACHE)
- {
- vbocacheentry &c = vbocache[i];
- if(c.vbuf) { glDeleteBuffers_(1, &c.vbuf); c.vbuf = 0; }
- c.as.cur.fr1 = -1;
- }
- if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; }
- }
-
- void preload(part *p)
- {
- if(numframes > 1) return;
- bool tangents = p->tangents();
- if(tangents!=vtangents) cleanup();
- if(!vbocache->vbuf) genvbo(tangents, *vbocache);
- }
-
- void render(const animstate *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p)
- {
- if(as->cur.anim&ANIM_NORENDER)
- {
- loopv(p->links) calctagmatrix(p, p->links[i].tag, *as, p->links[i].matrix);
- return;
- }
-
- bool tangents = p->tangents();
- if(tangents!=vtangents) { cleanup(); disablevbo(); }
- vbocacheentry *vc = NULL;
- if(numframes<=1) vc = vbocache;
- else
- {
- loopi(MAXVBOCACHE)
- {
- vbocacheentry &c = vbocache[i];
- if(!c.vbuf) continue;
- if(c.as==*as) { vc = &c; break; }
- }
- if(!vc) loopi(MAXVBOCACHE) { vc = &vbocache[i]; if(!vc->vbuf || vc->millis < lastmillis) break; }
- }
- if(!vc->vbuf) genvbo(tangents, *vc);
- if(numframes>1)
- {
- if(vc->as!=*as)
- {
- vc->as = *as;
- vc->millis = lastmillis;
- loopv(meshes)
- {
- vertmesh &m = *(vertmesh *)meshes[i];
- m.interpverts(*as, tangents, vdata + m.voffset*vertsize, p->skins[i]);
- }
- gle::bindvbo(vc->vbuf);
- glBufferData_(GL_ARRAY_BUFFER, vlen*vertsize, vdata, GL_STREAM_DRAW);
- }
- vc->millis = lastmillis;
- }
-
- bindvbo(as, *vc);
- loopv(meshes)
- {
- vertmesh *m = (vertmesh *)meshes[i];
- p->skins[i].bind(m, as);
- m->render(as, p->skins[i], *vc);
- }
-
- loopv(p->links) calctagmatrix(p, p->links[i].tag, *as, p->links[i].matrix);
- }
- };
-
- vertmodel(const char *name) : animmodel(name)
- {
- }
+ struct vert { vec pos, norm; };
+ struct vvert { vec pos; vec2 tc; };
+ struct vvertn : vvert { vec norm; };
+ struct vvertbump : vvert { squat tangent; };
+ struct tcvert { vec2 tc; };
+ struct bumpvert { vec4 tangent; };
+ struct tri { ushort vert[3]; };
+
+ struct vbocacheentry
+ {
+ GLuint vbuf;
+ animstate as;
+ int millis;
+
+ vbocacheentry() : vbuf(0) { as.cur.fr1 = as.prev.fr1 = -1; }
+ };
+
+ struct vertmesh : mesh
+ {
+ vert *verts;
+ tcvert *tcverts;
+ bumpvert *bumpverts;
+ tri *tris;
+ int numverts, numtris;
+
+ int voffset, eoffset, elen;
+ ushort minvert, maxvert;
+
+ vertmesh() : verts(0), tcverts(0), bumpverts(0), tris(0)
+ {
+ }
+
+ virtual ~vertmesh()
+ {
+ DELETEA(verts);
+ DELETEA(tcverts);
+ DELETEA(bumpverts);
+ DELETEA(tris);
+ }
+
+ void smoothnorms(float limit = 0, bool areaweight = true)
+ {
+ if(((vertmeshgroup *)group)->numframes == 1) mesh::smoothnorms(verts, numverts, tris, numtris, limit, areaweight);
+ else buildnorms(areaweight);
+ }
+
+ void buildnorms(bool areaweight = true)
+ {
+ mesh::buildnorms(verts, numverts, tris, numtris, areaweight, ((vertmeshgroup *)group)->numframes);
+ }
+
+ void calctangents(bool areaweight = true)
+ {
+ if(bumpverts) return;
+ bumpverts = new bumpvert[((vertmeshgroup *)group)->numframes*numverts];
+ mesh::calctangents(bumpverts, verts, tcverts, numverts, tris, numtris, areaweight, ((vertmeshgroup *)group)->numframes);
+ }
+
+ void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m)
+ {
+ loopj(numverts)
+ {
+ vec v = m.transform(verts[j].pos);
+ loopi(3)
+ {
+ bbmin[i] = min(bbmin[i], v[i]);
+ bbmax[i] = max(bbmax[i], v[i]);
+ }
+ }
+ }
+
+ void genBIH(BIH::mesh &m)
+ {
+ m.tris = (const BIH::tri *)tris;
+ m.numtris = numtris;
+ m.pos = (const uchar *)&verts->pos;
+ m.posstride = sizeof(vert);
+ m.tc = (const uchar *)&tcverts->tc;
+ m.tcstride = sizeof(tcvert);
+ }
+
+ static inline void assignvert(vvertn &vv, int j, tcvert &tc, vert &v)
+ {
+ vv.pos = v.pos;
+ vv.norm = v.norm;
+ vv.tc = tc.tc;
+ }
+
+ inline void assignvert(vvertbump &vv, int j, tcvert &tc, vert &v)
+ {
+ vv.pos = v.pos;
+ vv.tc = tc.tc;
+ vv.tangent = bumpverts[j].tangent;
+ }
+
+ template<class T>
+ int genvbo(vector<ushort> &idxs, int offset, vector<T> &vverts, int *htdata, int htlen)
+ {
+ voffset = offset;
+ eoffset = idxs.length();
+ minvert = 0xFFFF;
+ loopi(numtris)
+ {
+ tri &t = tris[i];
+ loopj(3)
+ {
+ int index = t.vert[j];
+ tcvert &tc = tcverts[index];
+ vert &v = verts[index];
+ T vv;
+ assignvert(vv, index, tc, v);
+ int htidx = hthash(v.pos)&(htlen-1);
+ loopk(htlen)
+ {
+ int &vidx = htdata[(htidx+k)&(htlen-1)];
+ if(vidx < 0) { vidx = idxs.add(ushort(vverts.length())); vverts.add(vv); break; }
+ else if(!memcmp(&vverts[vidx], &vv, sizeof(vv))) { minvert = min(minvert, idxs.add(ushort(vidx))); break; }
+ }
+ }
+ }
+ minvert = min(minvert, ushort(voffset));
+ maxvert = max(minvert, ushort(vverts.length()-1));
+ elen = idxs.length()-eoffset;
+ return vverts.length()-voffset;
+ }
+
+ int genvbo(vector<ushort> &idxs, int offset)
+ {
+ voffset = offset;
+ eoffset = idxs.length();
+ loopi(numtris)
+ {
+ tri &t = tris[i];
+ loopj(3) idxs.add(voffset+t.vert[j]);
+ }
+ minvert = voffset;
+ maxvert = voffset + numverts-1;
+ elen = idxs.length()-eoffset;
+ return numverts;
+ }
+
+ template<class T>
+ static inline void fillvert(T &vv, int j, tcvert &tc, vert &v)
+ {
+ vv.tc = tc.tc;
+ }
+
+ template<class T>
+ void fillverts(T *vdata)
+ {
+ vdata += voffset;
+ loopi(numverts) fillvert(vdata[i], i, tcverts[i], verts[i]);
+ }
+
+ void interpverts(const animstate &as, bool tangents, void * RESTRICT vdata, skin &s)
+ {
+ const vert * RESTRICT vert1 = &verts[as.cur.fr1 * numverts],
+ * RESTRICT vert2 = &verts[as.cur.fr2 * numverts],
+ * RESTRICT pvert1 = as.interp<1 ? &verts[as.prev.fr1 * numverts] : NULL,
+ * RESTRICT pvert2 = as.interp<1 ? &verts[as.prev.fr2 * numverts] : NULL;
+ #define ipvert(attrib) v.attrib.lerp(vert1[i].attrib, vert2[i].attrib, as.cur.t)
+ #define ipbvert(attrib, type) v.attrib.lerp(bvert1[i].attrib, bvert2[i].attrib, as.cur.t)
+ #define ipvertp(attrib) v.attrib.lerp(pvert1[i].attrib, pvert2[i].attrib, as.prev.t).lerp(vec().lerp(vert1[i].attrib, vert2[i].attrib, as.cur.t), as.interp)
+ #define ipbvertp(attrib, type) v.attrib.lerp(type().lerp(bpvert1[i].attrib, bpvert2[i].attrib, as.prev.t), type().lerp(bvert1[i].attrib, bvert2[i].attrib, as.cur.t), as.interp)
+ #define iploop(type, body) \
+ loopi(numverts) \
+ { \
+ type &v = ((type * RESTRICT)vdata)[i]; \
+ body; \
+ }
+ if(tangents)
+ {
+ const bumpvert * RESTRICT bvert1 = &bumpverts[as.cur.fr1 * numverts],
+ * RESTRICT bvert2 = &bumpverts[as.cur.fr2 * numverts],
+ * RESTRICT bpvert1 = as.interp<1 ? &bumpverts[as.prev.fr1 * numverts] : NULL,
+ * RESTRICT bpvert2 = as.interp<1 ? &bumpverts[as.prev.fr2 * numverts] : NULL;
+ if(as.interp<1) iploop(vvertbump, { ipvertp(pos); ipbvertp(tangent, vec4); })
+ else iploop(vvertbump, { ipvert(pos); ipbvert(tangent, vec4); })
+ }
+ else
+ {
+ if(as.interp<1) iploop(vvertn, { ipvertp(pos); ipvertp(norm); })
+ else iploop(vvertn, { ipvert(pos); ipvert(norm); })
+ }
+ #undef iploop
+ #undef ipvert
+ #undef ipbvert
+ #undef ipvertp
+ #undef ipbvertp
+ }
+
+ void render(const animstate *as, skin &s, vbocacheentry &vc)
+ {
+ if(!Shader::lastshader) return;
+ glDrawRangeElements_(GL_TRIANGLES, minvert, maxvert, elen, GL_UNSIGNED_SHORT, &((vertmeshgroup *)group)->edata[eoffset]);
+ glde++;
+ xtravertsva += numverts;
+ }
+ };
+
+ struct tag
+ {
+ char *name;
+ matrix4x3 transform;
+
+ tag() : name(NULL) {}
+ ~tag() { DELETEA(name); }
+ };
+
+ struct vertmeshgroup : meshgroup
+ {
+ int numframes;
+ tag *tags;
+ int numtags;
+
+ static const int MAXVBOCACHE = 16;
+ vbocacheentry vbocache[MAXVBOCACHE];
+
+ ushort *edata;
+ GLuint ebuf;
+ bool vtangents;
+ int vlen, vertsize;
+ uchar *vdata;
+
+ vertmeshgroup() : numframes(0), tags(NULL), numtags(0), edata(NULL), ebuf(0), vtangents(false), vlen(0), vertsize(0), vdata(NULL)
+ {
+ }
+
+ virtual ~vertmeshgroup()
+ {
+ DELETEA(tags);
+ if(ebuf) glDeleteBuffers_(1, &ebuf);
+ loopi(MAXVBOCACHE)
+ {
+ if(vbocache[i].vbuf) glDeleteBuffers_(1, &vbocache[i].vbuf);
+ }
+ DELETEA(vdata);
+ }
+
+ int findtag(const char *name)
+ {
+ loopi(numtags) if(!strcmp(tags[i].name, name)) return i;
+ return -1;
+ }
+
+ int totalframes() const { return numframes; }
+
+ void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n)
+ {
+ n.mul(m, tags[numtags + i].transform);
+ n.posttranslate(m.transformnormal(p->translate), p->model->scale);
+ }
+
+ void calctagmatrix(part *p, int i, const animstate &as, matrix4 &matrix)
+ {
+ const matrix4x3 &tag1 = tags[as.cur.fr1*numtags + i].transform,
+ &tag2 = tags[as.cur.fr2*numtags + i].transform;
+ matrix4x3 tag;
+ tag.lerp(tag1, tag2, as.cur.t);
+ if(as.interp<1)
+ {
+ const matrix4x3 &tag1p = tags[as.prev.fr1*numtags + i].transform,
+ &tag2p = tags[as.prev.fr2*numtags + i].transform;
+ matrix4x3 tagp;
+ tagp.lerp(tag1p, tag2p, as.prev.t);
+ tag.lerp(tagp, tag, as.interp);
+ }
+ tag.d.add(p->translate).mul(p->model->scale);
+ matrix = matrix4(tag);
+ }
+
+ void genvbo(bool tangents, vbocacheentry &vc)
+ {
+ if(!vc.vbuf) glGenBuffers_(1, &vc.vbuf);
+ if(ebuf) return;
+
+ vector<ushort> idxs;
+
+ if(tangents) loopv(meshes) ((vertmesh *)meshes[i])->calctangents();
+
+ vtangents = tangents;
+ vertsize = tangents ? sizeof(vvertbump) : sizeof(vvertn);
+ vlen = 0;
+ if(numframes>1)
+ {
+ loopv(meshes) vlen += ((vertmesh *)meshes[i])->genvbo(idxs, vlen);
+ DELETEA(vdata);
+ vdata = new uchar[vlen*vertsize];
+ #define FILLVDATA(type) do { \
+ loopv(meshes) ((vertmesh *)meshes[i])->fillverts((type *)vdata); \
+ } while(0)
+ if(tangents) FILLVDATA(vvertbump);
+ else FILLVDATA(vvertn);
+ #undef FILLVDATA
+ }
+ else
+ {
+ gle::bindvbo(vc.vbuf);
+ #define GENVBO(type) do { \
+ vector<type> vverts; \
+ loopv(meshes) vlen += ((vertmesh *)meshes[i])->genvbo(idxs, vlen, vverts, htdata, htlen); \
+ glBufferData_(GL_ARRAY_BUFFER, vverts.length()*sizeof(type), vverts.getbuf(), GL_STATIC_DRAW); \
+ } while(0)
+ int numverts = 0, htlen = 128;
+ loopv(meshes) numverts += ((vertmesh *)meshes[i])->numverts;
+ while(htlen < numverts) htlen *= 2;
+ if(numverts*4 > htlen*3) htlen *= 2;
+ int *htdata = new int[htlen];
+ memset(htdata, -1, htlen*sizeof(int));
+ if(tangents) GENVBO(vvertbump);
+ else GENVBO(vvertn);
+ delete[] htdata;
+ #undef GENVBO
+ gle::clearvbo();
+ }
+
+ glGenBuffers_(1, &ebuf);
+ gle::bindebo(ebuf);
+ glBufferData_(GL_ELEMENT_ARRAY_BUFFER, idxs.length()*sizeof(ushort), idxs.getbuf(), GL_STATIC_DRAW);
+ gle::clearebo();
+ }
+
+ void bindvbo(const animstate *as, vbocacheentry &vc)
+ {
+ vvert *vverts = 0;
+ bindpos(ebuf, vc.vbuf, &vverts->pos, vertsize);
+ if(as->cur.anim&ANIM_NOSKIN)
+ {
+ if(enabletc) disabletc();
+ if(enablenormals) disablenormals();
+ if(enabletangents) disabletangents();
+ }
+ else
+ {
+ if(vtangents)
+ {
+ if(enablenormals) disablenormals();
+ vvertbump *vvertbumps = 0;
+ bindtangents(&vvertbumps->tangent, vertsize);
+ }
+ else
+ {
+ if(enabletangents) disabletangents();
+ vvertn *vvertns = 0;
+ bindnormals(&vvertns->norm, vertsize);
+ }
+
+ bindtc(&vverts->tc, vertsize);
+ }
+ if(enablebones) disablebones();
+ }
+
+ void cleanup()
+ {
+ loopi(MAXVBOCACHE)
+ {
+ vbocacheentry &c = vbocache[i];
+ if(c.vbuf) { glDeleteBuffers_(1, &c.vbuf); c.vbuf = 0; }
+ c.as.cur.fr1 = -1;
+ }
+ if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; }
+ }
+
+ void preload(part *p)
+ {
+ if(numframes > 1) return;
+ bool tangents = p->tangents();
+ if(tangents!=vtangents) cleanup();
+ if(!vbocache->vbuf) genvbo(tangents, *vbocache);
+ }
+
+ void render(const animstate *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p)
+ {
+ if(as->cur.anim&ANIM_NORENDER)
+ {
+ loopv(p->links) calctagmatrix(p, p->links[i].tag, *as, p->links[i].matrix);
+ return;
+ }
+
+ bool tangents = p->tangents();
+ if(tangents!=vtangents) { cleanup(); disablevbo(); }
+ vbocacheentry *vc = NULL;
+ if(numframes<=1) vc = vbocache;
+ else
+ {
+ loopi(MAXVBOCACHE)
+ {
+ vbocacheentry &c = vbocache[i];
+ if(!c.vbuf) continue;
+ if(c.as==*as) { vc = &c; break; }
+ }
+ if(!vc) loopi(MAXVBOCACHE) { vc = &vbocache[i]; if(!vc->vbuf || vc->millis < lastmillis) break; }
+ }
+ if(!vc->vbuf) genvbo(tangents, *vc);
+ if(numframes>1)
+ {
+ if(vc->as!=*as)
+ {
+ vc->as = *as;
+ vc->millis = lastmillis;
+ loopv(meshes)
+ {
+ vertmesh &m = *(vertmesh *)meshes[i];
+ m.interpverts(*as, tangents, vdata + m.voffset*vertsize, p->skins[i]);
+ }
+ gle::bindvbo(vc->vbuf);
+ glBufferData_(GL_ARRAY_BUFFER, vlen*vertsize, vdata, GL_STREAM_DRAW);
+ }
+ vc->millis = lastmillis;
+ }
+
+ bindvbo(as, *vc);
+ loopv(meshes)
+ {
+ vertmesh *m = (vertmesh *)meshes[i];
+ p->skins[i].bind(m, as);
+ m->render(as, p->skins[i], *vc);
+ }
+
+ loopv(p->links) calctagmatrix(p, p->links[i].tag, *as, p->links[i].matrix);
+ }
+ };
+
+ vertmodel(const char *name) : animmodel(name)
+ {
+ }
};
template<class MDL> struct vertloader : modelloader<MDL, vertmodel>
{
- vertloader(const char *name) : modelloader<MDL, vertmodel>(name) {}
+ vertloader(const char *name) : modelloader<MDL, vertmodel>(name) {}
};
template<class MDL> struct vertcommands : modelcommands<MDL, struct MDL::vertmesh>
{
- typedef struct MDL::part part;
- typedef struct MDL::skin skin;
-
- static void loadpart(char *model, float *smooth)
- {
- if(!MDL::loading) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
- defformatstring(filename, "%s/%s", MDL::dir, model);
- part &mdl = MDL::loading->addpart();
- if(mdl.index) mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0;
- mdl.meshes = MDL::loading->sharemeshes(path(filename), double(*smooth > 0 ? cos(clamp(*smooth, 0.0f, 180.0f)*RAD) : 2));
- if(!mdl.meshes) conoutf(CON_ERROR, "could not load %s", filename);
- else mdl.initskins();
- }
-
- static void setpitch(float *pitchscale, float *pitchoffset, float *pitchmin, float *pitchmax)
- {
- if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
- part &mdl = *MDL::loading->parts.last();
-
- mdl.pitchscale = *pitchscale;
- mdl.pitchoffset = *pitchoffset;
- if(*pitchmin || *pitchmax)
- {
- mdl.pitchmin = *pitchmin;
- mdl.pitchmax = *pitchmax;
- }
- else
- {
- mdl.pitchmin = -360*fabs(mdl.pitchscale) + mdl.pitchoffset;
- mdl.pitchmax = 360*fabs(mdl.pitchscale) + mdl.pitchoffset;
- }
- }
-
- static void setanim(char *anim, int *frame, int *range, float *speed, int *priority)
- {
- if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
- vector<int> anims;
- findanims(anim, anims);
- if(anims.empty()) conoutf(CON_ERROR, "could not find animation %s", anim);
- else loopv(anims)
- {
- MDL::loading->parts.last()->setanim(0, anims[i], *frame, *range, *speed, *priority);
- }
- }
-
- vertcommands()
- {
- if(MDL::multiparted()) this->modelcommand(loadpart, "load", "sf");
- this->modelcommand(setpitch, "pitch", "ffff");
- if(MDL::animated()) this->modelcommand(setanim, "anim", "siiff");
- }
+ typedef struct MDL::part part;
+ typedef struct MDL::skin skin;
+
+ static void loadpart(char *model, float *smooth)
+ {
+ if(!MDL::loading) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+ defformatstring(filename, "%s/%s", MDL::dir, model);
+ part &mdl = MDL::loading->addpart();
+ if(mdl.index) mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0;
+ mdl.meshes = MDL::loading->sharemeshes(path(filename), double(*smooth > 0 ? cos(clamp(*smooth, 0.0f, 180.0f)*RAD) : 2));
+ if(!mdl.meshes) conoutf(CON_ERROR, "could not load %s", filename);
+ else mdl.initskins();
+ }
+
+ static void setpitch(float *pitchscale, float *pitchoffset, float *pitchmin, float *pitchmax)
+ {
+ if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+ part &mdl = *MDL::loading->parts.last();
+
+ mdl.pitchscale = *pitchscale;
+ mdl.pitchoffset = *pitchoffset;
+ if(*pitchmin || *pitchmax)
+ {
+ mdl.pitchmin = *pitchmin;
+ mdl.pitchmax = *pitchmax;
+ }
+ else
+ {
+ mdl.pitchmin = -360*fabs(mdl.pitchscale) + mdl.pitchoffset;
+ mdl.pitchmax = 360*fabs(mdl.pitchscale) + mdl.pitchoffset;
+ }
+ }
+
+ static void setanim(char *anim, int *frame, int *range, float *speed, int *priority)
+ {
+ if(!MDL::loading || MDL::loading->parts.empty()) { conoutf(CON_ERROR, "not loading an %s", MDL::formatname()); return; }
+ vector<int> anims;
+ findanims(anim, anims);
+ if(anims.empty()) conoutf(CON_ERROR, "could not find animation %s", anim);
+ else loopv(anims)
+ {
+ MDL::loading->parts.last()->setanim(0, anims[i], *frame, *range, *speed, *priority);
+ }
+ }
+
+ vertcommands()
+ {
+ if(MDL::multiparted()) this->modelcommand(loadpart, "load", "sf");
+ this->modelcommand(setpitch, "pitch", "ffff");
+ if(MDL::animated()) this->modelcommand(setanim, "anim", "siiff");
+ }
};
diff --git a/src/engine/water.cpp b/src/engine/water.cpp
index ff184cc..06c6e36 100644
--- a/src/engine/water.cpp
+++ b/src/engine/water.cpp
@@ -14,236 +14,236 @@ static float whoffset, whphase;
static inline float vertwangle(int v1, int v2)
{
- static const float whscale = 59.0f/23.0f/(2*M_PI);
- v1 &= wsize-1;
- v2 &= wsize-1;
- return v1*v2*whscale+whoffset;
+ static const float whscale = 59.0f/23.0f/(2*M_PI);
+ v1 &= wsize-1;
+ v2 &= wsize-1;
+ return v1*v2*whscale+whoffset;
}
static inline float vertwphase(float angle)
{
- float s = angle - int(angle) - 0.5f;
- s *= 8 - fabs(s)*16;
- return WATER_AMPLITUDE*s-WATER_OFFSET;
+ float s = angle - int(angle) - 0.5f;
+ s *= 8 - fabs(s)*16;
+ return WATER_AMPLITUDE*s-WATER_OFFSET;
}
static inline void vertw(int v1, int v2, int v3)
{
- float h = vertwphase(vertwangle(v1, v2));
- gle::attribf(v1, v2, v3+h);
+ float h = vertwphase(vertwangle(v1, v2));
+ gle::attribf(v1, v2, v3+h);
}
static inline void vertwq(float v1, float v2, float v3)
{
- gle::attribf(v1, v2, v3+whphase);
+ gle::attribf(v1, v2, v3+whphase);
}
static inline void vertwn(float v1, float v2, float v3)
{
- float h = -WATER_OFFSET;
- gle::attribf(v1, v2, v3+h);
+ float h = -WATER_OFFSET;
+ gle::attribf(v1, v2, v3+h);
}
struct waterstrip
{
- int x1, y1, x2, y2, z;
- ushort size, subdiv;
-
- int numverts() const { return 2*((y2-y1)/subdiv + 1)*((x2-x1)/subdiv); }
-
- void save()
- {
- x1 = wx1;
- y1 = wy1;
- x2 = wx2;
- y2 = wy2;
- z = wz;
- size = wsize;
- subdiv = wsubdiv;
- }
-
- void restore()
- {
- wx1 = x1;
- wy1 = y1;
- wx2 = x2;
- wy2 = y2;
- wz = z;
- wsize = size;
- wsubdiv = subdiv;
- }
+ int x1, y1, x2, y2, z;
+ ushort size, subdiv;
+
+ int numverts() const { return 2*((y2-y1)/subdiv + 1)*((x2-x1)/subdiv); }
+
+ void save()
+ {
+ x1 = wx1;
+ y1 = wy1;
+ x2 = wx2;
+ y2 = wy2;
+ z = wz;
+ size = wsize;
+ subdiv = wsubdiv;
+ }
+
+ void restore()
+ {
+ wx1 = x1;
+ wy1 = y1;
+ wx2 = x2;
+ wy2 = y2;
+ wz = z;
+ wsize = size;
+ wsubdiv = subdiv;
+ }
};
vector<waterstrip> waterstrips;
void flushwaterstrips()
{
- if(gle::attribbuf.length()) xtraverts += gle::end();
- gle::defvertex();
- int numverts = 0;
- loopv(waterstrips) numverts += waterstrips[i].numverts();
- gle::begin(GL_TRIANGLE_STRIP, numverts);
- loopv(waterstrips)
- {
- waterstrips[i].restore();
- for(int x = wx1; x < wx2; x += wsubdiv)
- {
- for(int y = wy1; y <= wy2; y += wsubdiv)
- {
- vertw(x, y, wz);
- vertw(x+wsubdiv, y, wz);
- }
- x += wsubdiv;
- if(x >= wx2) break;
- for(int y = wy2; y >= wy1; y -= wsubdiv)
- {
- vertw(x, y, wz);
- vertw(x+wsubdiv, y, wz);
- }
- }
- gle::multidraw();
- }
- waterstrips.setsize(0);
- wsize = 0;
- xtraverts += gle::end();
+ if(gle::attribbuf.length()) xtraverts += gle::end();
+ gle::defvertex();
+ int numverts = 0;
+ loopv(waterstrips) numverts += waterstrips[i].numverts();
+ gle::begin(GL_TRIANGLE_STRIP, numverts);
+ loopv(waterstrips)
+ {
+ waterstrips[i].restore();
+ for(int x = wx1; x < wx2; x += wsubdiv)
+ {
+ for(int y = wy1; y <= wy2; y += wsubdiv)
+ {
+ vertw(x, y, wz);
+ vertw(x+wsubdiv, y, wz);
+ }
+ x += wsubdiv;
+ if(x >= wx2) break;
+ for(int y = wy2; y >= wy1; y -= wsubdiv)
+ {
+ vertw(x, y, wz);
+ vertw(x+wsubdiv, y, wz);
+ }
+ }
+ gle::multidraw();
+ }
+ waterstrips.setsize(0);
+ wsize = 0;
+ xtraverts += gle::end();
}
void flushwater(int mat = MAT_WATER, bool force = true)
{
- if(wsize)
- {
- if(wsubdiv >= wsize)
- {
- if(gle::attribbuf.empty()) { gle::defvertex(); gle::begin(GL_QUADS); }
- vertwq(wx1, wy1, wz);
- vertwq(wx2, wy1, wz);
- vertwq(wx2, wy2, wz);
- vertwq(wx1, wy2, wz);
- }
- else waterstrips.add().save();
- wsize = 0;
- }
-
- if(force)
- {
- if(gle::attribbuf.length()) xtraverts += gle::end();
- if(waterstrips.length()) flushwaterstrips();
- }
+ if(wsize)
+ {
+ if(wsubdiv >= wsize)
+ {
+ if(gle::attribbuf.empty()) { gle::defvertex(); gle::begin(GL_QUADS); }
+ vertwq(wx1, wy1, wz);
+ vertwq(wx2, wy1, wz);
+ vertwq(wx2, wy2, wz);
+ vertwq(wx1, wy2, wz);
+ }
+ else waterstrips.add().save();
+ wsize = 0;
+ }
+
+ if(force)
+ {
+ if(gle::attribbuf.length()) xtraverts += gle::end();
+ if(waterstrips.length()) flushwaterstrips();
+ }
}
void rendervertwater(int subdiv, int xo, int yo, int z, int size, int mat)
{
- if(wsize == size && wsubdiv == subdiv && wz == z)
- {
- if(wx2 == xo)
- {
- if(wy1 == yo && wy2 == yo + size) { wx2 += size; return; }
- }
- else if(wy2 == yo && wx1 == xo && wx2 == xo + size) { wy2 += size; return; }
- }
-
- flushwater(mat, false);
-
- wx1 = xo;
- wy1 = yo;
- wx2 = xo + size,
- wy2 = yo + size;
- wz = z;
- wsize = size;
- wsubdiv = subdiv;
-
- ASSERT((wx1 & (subdiv - 1)) == 0);
- ASSERT((wy1 & (subdiv - 1)) == 0);
+ if(wsize == size && wsubdiv == subdiv && wz == z)
+ {
+ if(wx2 == xo)
+ {
+ if(wy1 == yo && wy2 == yo + size) { wx2 += size; return; }
+ }
+ else if(wy2 == yo && wx1 == xo && wx2 == xo + size) { wy2 += size; return; }
+ }
+
+ flushwater(mat, false);
+
+ wx1 = xo;
+ wy1 = yo;
+ wx2 = xo + size,
+ wy2 = yo + size;
+ wz = z;
+ wsize = size;
+ wsubdiv = subdiv;
+
+ ASSERT((wx1 & (subdiv - 1)) == 0);
+ ASSERT((wy1 & (subdiv - 1)) == 0);
}
int calcwatersubdiv(int x, int y, int z, int size)
{
- float dist;
- if(camera1->o.x >= x && camera1->o.x < x + size &&
- camera1->o.y >= y && camera1->o.y < y + size)
- dist = fabs(camera1->o.z - float(z));
- else
- dist = vec(x + size/2, y + size/2, z + size/2).dist(camera1->o) - size*1.42f/2;
- int subdiv = watersubdiv + int(dist) / (32 << waterlod);
- return subdiv >= 31 ? INT_MAX : 1<<subdiv;
+ float dist;
+ if(camera1->o.x >= x && camera1->o.x < x + size &&
+ camera1->o.y >= y && camera1->o.y < y + size)
+ dist = fabs(camera1->o.z - float(z));
+ else
+ dist = vec(x + size/2, y + size/2, z + size/2).dist(camera1->o) - size*1.42f/2;
+ int subdiv = watersubdiv + int(dist) / (32 << waterlod);
+ return subdiv >= 31 ? INT_MAX : 1<<subdiv;
}
int renderwaterlod(int x, int y, int z, int size, int mat)
{
- if(size <= (32 << waterlod))
- {
- int subdiv = calcwatersubdiv(x, y, z, size);
- if(subdiv < size * 2) rendervertwater(min(subdiv, size), x, y, z, size, mat);
- return subdiv;
- }
- else
- {
- int subdiv = calcwatersubdiv(x, y, z, size);
- if(subdiv >= size)
- {
- if(subdiv < size * 2) rendervertwater(size, x, y, z, size, mat);
- return subdiv;
- }
- int childsize = size / 2,
- subdiv1 = renderwaterlod(x, y, z, childsize, mat),
- subdiv2 = renderwaterlod(x + childsize, y, z, childsize, mat),
- subdiv3 = renderwaterlod(x + childsize, y + childsize, z, childsize, mat),
- subdiv4 = renderwaterlod(x, y + childsize, z, childsize, mat),
- minsubdiv = subdiv1;
- minsubdiv = min(minsubdiv, subdiv2);
- minsubdiv = min(minsubdiv, subdiv3);
- minsubdiv = min(minsubdiv, subdiv4);
- if(minsubdiv < size * 2)
- {
- if(minsubdiv >= size) rendervertwater(size, x, y, z, size, mat);
- else
- {
- if(subdiv1 >= size) rendervertwater(childsize, x, y, z, childsize, mat);
- if(subdiv2 >= size) rendervertwater(childsize, x + childsize, y, z, childsize, mat);
- if(subdiv3 >= size) rendervertwater(childsize, x + childsize, y + childsize, z, childsize, mat);
- if(subdiv4 >= size) rendervertwater(childsize, x, y + childsize, z, childsize, mat);
- }
- }
- return minsubdiv;
- }
+ if(size <= (32 << waterlod))
+ {
+ int subdiv = calcwatersubdiv(x, y, z, size);
+ if(subdiv < size * 2) rendervertwater(min(subdiv, size), x, y, z, size, mat);
+ return subdiv;
+ }
+ else
+ {
+ int subdiv = calcwatersubdiv(x, y, z, size);
+ if(subdiv >= size)
+ {
+ if(subdiv < size * 2) rendervertwater(size, x, y, z, size, mat);
+ return subdiv;
+ }
+ int childsize = size / 2,
+ subdiv1 = renderwaterlod(x, y, z, childsize, mat),
+ subdiv2 = renderwaterlod(x + childsize, y, z, childsize, mat),
+ subdiv3 = renderwaterlod(x + childsize, y + childsize, z, childsize, mat),
+ subdiv4 = renderwaterlod(x, y + childsize, z, childsize, mat),
+ minsubdiv = subdiv1;
+ minsubdiv = min(minsubdiv, subdiv2);
+ minsubdiv = min(minsubdiv, subdiv3);
+ minsubdiv = min(minsubdiv, subdiv4);
+ if(minsubdiv < size * 2)
+ {
+ if(minsubdiv >= size) rendervertwater(size, x, y, z, size, mat);
+ else
+ {
+ if(subdiv1 >= size) rendervertwater(childsize, x, y, z, childsize, mat);
+ if(subdiv2 >= size) rendervertwater(childsize, x + childsize, y, z, childsize, mat);
+ if(subdiv3 >= size) rendervertwater(childsize, x + childsize, y + childsize, z, childsize, mat);
+ if(subdiv4 >= size) rendervertwater(childsize, x, y + childsize, z, childsize, mat);
+ }
+ }
+ return minsubdiv;
+ }
}
void renderflatwater(int x, int y, int z, int rsize, int csize, int mat)
{
- if(gle::attribbuf.empty()) { gle::defvertex(); gle::begin(GL_QUADS); }
- vertwn(x, y, z);
- vertwn(x+rsize, y, z);
- vertwn(x+rsize, y+csize, z);
- vertwn(x, y+csize, z);
+ if(gle::attribbuf.empty()) { gle::defvertex(); gle::begin(GL_QUADS); }
+ vertwn(x, y, z);
+ vertwn(x+rsize, y, z);
+ vertwn(x+rsize, y+csize, z);
+ vertwn(x, y+csize, z);
}
VARFP(vertwater, 0, 1, 1, allchanged());
static inline void renderwater(const materialsurface &m, int mat = MAT_WATER)
{
- if(!vertwater || drawtex == DRAWTEX_MINIMAP) renderflatwater(m.o.x, m.o.y, m.o.z, m.rsize, m.csize, mat);
- else if(renderwaterlod(m.o.x, m.o.y, m.o.z, m.csize, mat) >= int(m.csize) * 2)
- rendervertwater(m.csize, m.o.x, m.o.y, m.o.z, m.csize, mat);
+ if(!vertwater || drawtex == DRAWTEX_MINIMAP) renderflatwater(m.o.x, m.o.y, m.o.z, m.rsize, m.csize, mat);
+ else if(renderwaterlod(m.o.x, m.o.y, m.o.z, m.csize, mat) >= int(m.csize) * 2)
+ rendervertwater(m.csize, m.o.x, m.o.y, m.o.z, m.csize, mat);
}
void setuplava(Texture *tex, float scale)
{
- float xk = TEX_SCALE/(tex->xs*scale);
- float yk = TEX_SCALE/(tex->ys*scale);
- float scroll = lastmillis/1000.0f;
- LOCALPARAMF(lavatexgen, xk, yk, scroll, scroll);
- gle::normal(vec(0, 0, 1));
- whoffset = fmod(float(lastmillis/2000.0f/(2*M_PI)), 1.0f);
- whphase = vertwphase(whoffset);
+ float xk = TEX_SCALE/(tex->xs*scale);
+ float yk = TEX_SCALE/(tex->ys*scale);
+ float scroll = lastmillis/1000.0f;
+ LOCALPARAMF(lavatexgen, xk, yk, scroll, scroll);
+ gle::normal(vec(0, 0, 1));
+ whoffset = fmod(float(lastmillis/2000.0f/(2*M_PI)), 1.0f);
+ whphase = vertwphase(whoffset);
}
void renderlava(const materialsurface &m)
{
- renderwater(m, MAT_LAVA);
+ renderwater(m, MAT_LAVA);
}
void flushlava()
{
- flushwater(MAT_LAVA);
+ flushwater(MAT_LAVA);
}
/* reflective/refractive water */
@@ -252,32 +252,32 @@ void flushlava()
struct Reflection
{
- GLuint tex, refracttex;
- int material, height, depth, age;
- bool init;
- matrix4 projmat;
- occludequery *query, *prevquery;
- vector<materialsurface *> matsurfs;
-
- Reflection() : tex(0), refracttex(0), material(-1), height(-1), depth(0), age(0), init(false), query(NULL), prevquery(NULL)
- {}
+ GLuint tex, refracttex;
+ int material, height, depth, age;
+ bool init;
+ matrix4 projmat;
+ occludequery *query, *prevquery;
+ vector<materialsurface *> matsurfs;
+
+ Reflection() : tex(0), refracttex(0), material(-1), height(-1), depth(0), age(0), init(false), query(NULL), prevquery(NULL)
+ {}
};
VARP(reflectdist, 0, 2000, 10000);
#define WATERVARS(name) \
- bvec name##color(0x14, 0x46, 0x50), name##fallcolor(0, 0, 0); \
- HVARFR(name##colour, 0, 0x144650, 0xFFFFFF, \
- { \
- if(!name##colour) name##colour = 0x144650; \
- name##color = bvec((name##colour>>16)&0xFF, (name##colour>>8)&0xFF, name##colour&0xFF); \
- }); \
- VARR(name##fog, 0, 150, 10000); \
- VARR(name##spec, 0, 150, 1000); \
- HVARFR(name##fallcolour, 0, 0, 0xFFFFFF, \
- { \
- name##fallcolor = bvec((name##fallcolour>>16)&0xFF, (name##fallcolour>>8)&0xFF, name##fallcolour&0xFF); \
- });
+ bvec name##color(0x14, 0x46, 0x50), name##fallcolor(0, 0, 0); \
+ HVARFR(name##colour, 0, 0x144650, 0xFFFFFF, \
+ { \
+ if(!name##colour) name##colour = 0x144650; \
+ name##color = bvec((name##colour>>16)&0xFF, (name##colour>>8)&0xFF, name##colour&0xFF); \
+ }); \
+ VARR(name##fog, 0, 150, 10000); \
+ VARR(name##spec, 0, 150, 1000); \
+ HVARFR(name##fallcolour, 0, 0, 0xFFFFFF, \
+ { \
+ name##fallcolor = bvec((name##fallcolour>>16)&0xFF, (name##fallcolour>>8)&0xFF, name##fallcolour&0xFF); \
+ });
WATERVARS(water)
WATERVARS(water2)
@@ -292,13 +292,13 @@ GETMATIDXVAR(water, fog, int)
GETMATIDXVAR(water, spec, int)
#define LAVAVARS(name) \
- bvec name##color(0xFF, 0x40, 0x00); \
- HVARFR(name##colour, 0, 0xFF4000, 0xFFFFFF, \
- { \
- if(!name##colour) name##colour = 0xFF4000; \
- name##color = bvec((name##colour>>16)&0xFF, (name##colour>>8)&0xFF, name##colour&0xFF); \
- }); \
- VARR(name##fog, 0, 50, 10000);
+ bvec name##color(0xFF, 0x40, 0x00); \
+ HVARFR(name##colour, 0, 0xFF4000, 0xFFFFFF, \
+ { \
+ if(!name##colour) name##colour = 0xFF4000; \
+ name##color = bvec((name##colour>>16)&0xFF, (name##colour>>8)&0xFF, name##colour&0xFF); \
+ }); \
+ VARR(name##fog, 0, 50, 10000);
LAVAVARS(lava)
LAVAVARS(lava2)
@@ -311,13 +311,13 @@ GETMATIDXVAR(lava, fog, int)
void setprojtexmatrix(Reflection &ref)
{
- if(ref.init)
- {
- ref.init = false;
- (ref.projmat = camprojmatrix).projective();
- }
+ if(ref.init)
+ {
+ ref.init = false;
+ (ref.projmat = camprojmatrix).projective();
+ }
- LOCALPARAM(watermatrix, ref.projmat);
+ LOCALPARAM(watermatrix, ref.projmat);
}
Reflection reflections[MAXREFLECTIONS];
@@ -331,379 +331,379 @@ VARFP(waterfade, 0, 1, 1, { cleanreflections(); preloadwatershaders(); });
void preloadwatershaders(bool force)
{
- static bool needwater = false;
- if(force) needwater = true;
- if(!needwater) return;
+ static bool needwater = false;
+ if(force) needwater = true;
+ if(!needwater) return;
- useshaderbyname("waterglare");
+ useshaderbyname("waterglare");
- if(waterenvmap && !waterreflect)
- useshaderbyname(waterrefract ? (waterfade ? "waterenvfade" : "waterenvrefract") : "waterenv");
- else useshaderbyname(waterrefract ? (waterfade ? "waterfade" : "waterrefract") : (waterreflect ? "waterreflect" : "water"));
+ if(waterenvmap && !waterreflect)
+ useshaderbyname(waterrefract ? (waterfade ? "waterenvfade" : "waterenvrefract") : "waterenv");
+ else useshaderbyname(waterrefract ? (waterfade ? "waterfade" : "waterrefract") : (waterreflect ? "waterreflect" : "water"));
- useshaderbyname(waterrefract ? (waterfade ? "underwaterfade" : "underwaterrefract") : "underwater");
+ useshaderbyname(waterrefract ? (waterfade ? "underwaterfade" : "underwaterrefract") : "underwater");
- extern int waterfallenv;
- useshaderbyname(waterfallenv ? "waterfallenv" : "waterfall");
- if(waterfallrefract) useshaderbyname(waterfallenv ? "waterfallenvrefract" : "waterfallrefract");
+ extern int waterfallenv;
+ useshaderbyname(waterfallenv ? "waterfallenv" : "waterfall");
+ if(waterfallrefract) useshaderbyname(waterfallenv ? "waterfallenvrefract" : "waterfallrefract");
}
void renderwater()
{
- if(editmode && showmat && !drawtex) return;
- if(!rplanes) return;
-
- glDisable(GL_CULL_FACE);
-
- if(!glaring && drawtex != DRAWTEX_MINIMAP)
- {
- if(waterrefract)
- {
- if(waterfade)
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
- }
- else
- {
- glDepthMask(GL_FALSE);
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_SRC_ALPHA);
- }
- }
-
- GLOBALPARAM(camera, camera1->o);
- GLOBALPARAMF(millis, lastmillis/1000.0f);
-
- #define SETWATERSHADER(which, name) \
- do { \
- static Shader *name##shader = NULL; \
- if(!name##shader) name##shader = lookupshaderbyname(#name); \
- which##shader = name##shader; \
- } while(0)
-
- Shader *aboveshader = NULL;
- if(glaring) SETWATERSHADER(above, waterglare);
- else if(drawtex == DRAWTEX_MINIMAP) aboveshader = notextureshader;
- else if(waterenvmap && !waterreflect)
- {
- if(waterrefract)
- {
- if(waterfade) SETWATERSHADER(above, waterenvfade);
- else SETWATERSHADER(above, waterenvrefract);
- }
- else SETWATERSHADER(above, waterenv);
- }
- else if(waterrefract)
- {
- if(waterfade) SETWATERSHADER(above, waterfade);
- else SETWATERSHADER(above, waterrefract);
- }
- else if(waterreflect) SETWATERSHADER(above, waterreflect);
- else SETWATERSHADER(above, water);
-
- Shader *belowshader = NULL;
- if(!glaring && drawtex != DRAWTEX_MINIMAP)
- {
- if(waterrefract)
- {
- if(waterfade) SETWATERSHADER(below, underwaterfade);
- else SETWATERSHADER(below, underwaterrefract);
- }
- else SETWATERSHADER(below, underwater);
- }
-
- vec ambient(max(skylightcolor[0], ambientcolor[0]), max(skylightcolor[1], ambientcolor[1]), max(skylightcolor[2], ambientcolor[2]));
- float offset = -WATER_OFFSET;
- loopi(MAXREFLECTIONS)
- {
- Reflection &ref = reflections[i];
- if(ref.height<0 || ref.age || ref.matsurfs.empty()) continue;
- if(!glaring && oqfrags && oqwater && ref.query && ref.query->owner==&ref)
- {
- if(!ref.prevquery || ref.prevquery->owner!=&ref || checkquery(ref.prevquery))
- {
- if(checkquery(ref.query)) continue;
- }
- }
-
- bool below = camera1->o.z < ref.height+offset;
- if(below)
- {
- if(!belowshader) continue;
- belowshader->set();
- }
- else aboveshader->set();
-
- if(!glaring && drawtex != DRAWTEX_MINIMAP)
- {
- if(waterreflect || waterrefract)
- {
- if(waterreflect || !waterenvmap) glBindTexture(GL_TEXTURE_2D, waterreflect ? ref.tex : ref.refracttex);
- setprojtexmatrix(ref);
- }
-
- if(waterrefract)
- {
- glActiveTexture_(GL_TEXTURE3);
- glBindTexture(GL_TEXTURE_2D, ref.refracttex);
- if(waterfade)
- {
- float fadeheight = ref.height+offset+(below ? -2 : 2);
- LOCALPARAMF(waterheight, fadeheight);
- }
- }
- }
-
- MSlot &mslot = lookupmaterialslot(ref.material);
- glActiveTexture_(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, mslot.sts.inrange(2) ? mslot.sts[2].t->id : notexture->id);
- glActiveTexture_(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, mslot.sts.inrange(3) ? mslot.sts[3].t->id : notexture->id);
- glActiveTexture_(GL_TEXTURE0);
- if(!glaring && waterenvmap && !waterreflect && drawtex != DRAWTEX_MINIMAP)
- {
- glBindTexture(GL_TEXTURE_CUBE_MAP, lookupenvmap(mslot));
- }
-
- whoffset = fmod(float(lastmillis/600.0f/(2*M_PI)), 1.0f);
- whphase = vertwphase(whoffset);
-
- gle::color(getwatercolor(ref.material));
- int wfog = getwaterfog(ref.material), wspec = getwaterspec(ref.material);
-
- const entity *lastlight = (const entity *)-1;
- int lastdepth = -1;
- loopvj(ref.matsurfs)
- {
- materialsurface &m = *ref.matsurfs[j];
-
- entity *light = (m.light && m.light->type==ET_LIGHT ? m.light : NULL);
- if(light!=lastlight)
- {
- flushwater();
- vec lightpos = light ? light->o : vec(worldsize/2, worldsize/2, worldsize);
- float lightrad = light && light->attr1 ? light->attr1 : worldsize*8.0f;
- vec lightcol = (light ? vec(light->attr2, light->attr3, light->attr4) : vec(ambient)).div(255.0f).mul(wspec/100.0f);
- LOCALPARAM(lightpos, lightpos);
- LOCALPARAM(lightcolor, lightcol);
- LOCALPARAMF(lightradius, lightrad);
- lastlight = light;
- }
-
- if(!glaring && !waterrefract && m.depth!=lastdepth)
- {
- flushwater();
- float depth = !wfog ? 1.0f : min(0.75f*m.depth/wfog, 0.95f);
- depth = max(depth, !below && (waterreflect || waterenvmap) ? 0.3f : 0.6f);
- LOCALPARAMF(depth, depth, 1.0f-depth);
- lastdepth = m.depth;
- }
-
- renderwater(m);
- }
- flushwater();
- }
-
- if(!glaring && drawtex != DRAWTEX_MINIMAP)
- {
- if(waterrefract)
- {
- if(waterfade) glDisable(GL_BLEND);
- }
- else
- {
- glDepthMask(GL_TRUE);
- glDisable(GL_BLEND);
- }
- }
-
- glEnable(GL_CULL_FACE);
+ if(editmode && showmat && !drawtex) return;
+ if(!rplanes) return;
+
+ glDisable(GL_CULL_FACE);
+
+ if(!glaring && drawtex != DRAWTEX_MINIMAP)
+ {
+ if(waterrefract)
+ {
+ if(waterfade)
+ {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+ else
+ {
+ glDepthMask(GL_FALSE);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_SRC_ALPHA);
+ }
+ }
+
+ GLOBALPARAM(camera, camera1->o);
+ GLOBALPARAMF(millis, lastmillis/1000.0f);
+
+ #define SETWATERSHADER(which, name) \
+ do { \
+ static Shader *name##shader = NULL; \
+ if(!name##shader) name##shader = lookupshaderbyname(#name); \
+ which##shader = name##shader; \
+ } while(0)
+
+ Shader *aboveshader = NULL;
+ if(glaring) SETWATERSHADER(above, waterglare);
+ else if(drawtex == DRAWTEX_MINIMAP) aboveshader = notextureshader;
+ else if(waterenvmap && !waterreflect)
+ {
+ if(waterrefract)
+ {
+ if(waterfade) SETWATERSHADER(above, waterenvfade);
+ else SETWATERSHADER(above, waterenvrefract);
+ }
+ else SETWATERSHADER(above, waterenv);
+ }
+ else if(waterrefract)
+ {
+ if(waterfade) SETWATERSHADER(above, waterfade);
+ else SETWATERSHADER(above, waterrefract);
+ }
+ else if(waterreflect) SETWATERSHADER(above, waterreflect);
+ else SETWATERSHADER(above, water);
+
+ Shader *belowshader = NULL;
+ if(!glaring && drawtex != DRAWTEX_MINIMAP)
+ {
+ if(waterrefract)
+ {
+ if(waterfade) SETWATERSHADER(below, underwaterfade);
+ else SETWATERSHADER(below, underwaterrefract);
+ }
+ else SETWATERSHADER(below, underwater);
+ }
+
+ vec ambient(max(skylightcolor[0], ambientcolor[0]), max(skylightcolor[1], ambientcolor[1]), max(skylightcolor[2], ambientcolor[2]));
+ float offset = -WATER_OFFSET;
+ loopi(MAXREFLECTIONS)
+ {
+ Reflection &ref = reflections[i];
+ if(ref.height<0 || ref.age || ref.matsurfs.empty()) continue;
+ if(!glaring && oqfrags && oqwater && ref.query && ref.query->owner==&ref)
+ {
+ if(!ref.prevquery || ref.prevquery->owner!=&ref || checkquery(ref.prevquery))
+ {
+ if(checkquery(ref.query)) continue;
+ }
+ }
+
+ bool below = camera1->o.z < ref.height+offset;
+ if(below)
+ {
+ if(!belowshader) continue;
+ belowshader->set();
+ }
+ else aboveshader->set();
+
+ if(!glaring && drawtex != DRAWTEX_MINIMAP)
+ {
+ if(waterreflect || waterrefract)
+ {
+ if(waterreflect || !waterenvmap) glBindTexture(GL_TEXTURE_2D, waterreflect ? ref.tex : ref.refracttex);
+ setprojtexmatrix(ref);
+ }
+
+ if(waterrefract)
+ {
+ glActiveTexture_(GL_TEXTURE3);
+ glBindTexture(GL_TEXTURE_2D, ref.refracttex);
+ if(waterfade)
+ {
+ float fadeheight = ref.height+offset+(below ? -2 : 2);
+ LOCALPARAMF(waterheight, fadeheight);
+ }
+ }
+ }
+
+ MSlot &mslot = lookupmaterialslot(ref.material);
+ glActiveTexture_(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mslot.sts.inrange(2) ? mslot.sts[2].t->id : notexture->id);
+ glActiveTexture_(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, mslot.sts.inrange(3) ? mslot.sts[3].t->id : notexture->id);
+ glActiveTexture_(GL_TEXTURE0);
+ if(!glaring && waterenvmap && !waterreflect && drawtex != DRAWTEX_MINIMAP)
+ {
+ glBindTexture(GL_TEXTURE_CUBE_MAP, lookupenvmap(mslot));
+ }
+
+ whoffset = fmod(float(lastmillis/600.0f/(2*M_PI)), 1.0f);
+ whphase = vertwphase(whoffset);
+
+ gle::color(getwatercolor(ref.material));
+ int wfog = getwaterfog(ref.material), wspec = getwaterspec(ref.material);
+
+ const entity *lastlight = (const entity *)-1;
+ int lastdepth = -1;
+ loopvj(ref.matsurfs)
+ {
+ materialsurface &m = *ref.matsurfs[j];
+
+ entity *light = (m.light && m.light->type==ET_LIGHT ? m.light : NULL);
+ if(light!=lastlight)
+ {
+ flushwater();
+ vec lightpos = light ? light->o : vec(worldsize/2, worldsize/2, worldsize);
+ float lightrad = light && light->attr1 ? light->attr1 : worldsize*8.0f;
+ vec lightcol = (light ? vec(light->attr2, light->attr3, light->attr4) : vec(ambient)).div(255.0f).mul(wspec/100.0f);
+ LOCALPARAM(lightpos, lightpos);
+ LOCALPARAM(lightcolor, lightcol);
+ LOCALPARAMF(lightradius, lightrad);
+ lastlight = light;
+ }
+
+ if(!glaring && !waterrefract && m.depth!=lastdepth)
+ {
+ flushwater();
+ float depth = !wfog ? 1.0f : min(0.75f*m.depth/wfog, 0.95f);
+ depth = max(depth, !below && (waterreflect || waterenvmap) ? 0.3f : 0.6f);
+ LOCALPARAMF(depth, depth, 1.0f-depth);
+ lastdepth = m.depth;
+ }
+
+ renderwater(m);
+ }
+ flushwater();
+ }
+
+ if(!glaring && drawtex != DRAWTEX_MINIMAP)
+ {
+ if(waterrefract)
+ {
+ if(waterfade) glDisable(GL_BLEND);
+ }
+ else
+ {
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+ }
+ }
+
+ glEnable(GL_CULL_FACE);
}
void setupwaterfallrefract()
{
- glBindTexture(GL_TEXTURE_2D, waterfallrefraction.refracttex ? waterfallrefraction.refracttex : notexture->id);
- setprojtexmatrix(waterfallrefraction);
+ glBindTexture(GL_TEXTURE_2D, waterfallrefraction.refracttex ? waterfallrefraction.refracttex : notexture->id);
+ setprojtexmatrix(waterfallrefraction);
}
void cleanreflection(Reflection &ref)
{
- ref.material = -1;
- ref.height = -1;
- ref.init = false;
- ref.query = ref.prevquery = NULL;
- ref.matsurfs.setsize(0);
- if(ref.tex)
- {
- glDeleteTextures(1, &ref.tex);
- ref.tex = 0;
- }
- if(ref.refracttex)
- {
- glDeleteTextures(1, &ref.refracttex);
- ref.refracttex = 0;
- }
+ ref.material = -1;
+ ref.height = -1;
+ ref.init = false;
+ ref.query = ref.prevquery = NULL;
+ ref.matsurfs.setsize(0);
+ if(ref.tex)
+ {
+ glDeleteTextures(1, &ref.tex);
+ ref.tex = 0;
+ }
+ if(ref.refracttex)
+ {
+ glDeleteTextures(1, &ref.refracttex);
+ ref.refracttex = 0;
+ }
}
void cleanreflections()
{
- loopi(MAXREFLECTIONS) cleanreflection(reflections[i]);
- cleanreflection(waterfallrefraction);
- if(reflectionfb)
- {
- glDeleteFramebuffers_(1, &reflectionfb);
- reflectionfb = 0;
- }
- if(reflectiondb)
- {
- glDeleteRenderbuffers_(1, &reflectiondb);
- reflectiondb = 0;
- }
+ loopi(MAXREFLECTIONS) cleanreflection(reflections[i]);
+ cleanreflection(waterfallrefraction);
+ if(reflectionfb)
+ {
+ glDeleteFramebuffers_(1, &reflectionfb);
+ reflectionfb = 0;
+ }
+ if(reflectiondb)
+ {
+ glDeleteRenderbuffers_(1, &reflectiondb);
+ reflectiondb = 0;
+ }
}
VARFP(reflectsize, 6, 8, 11, cleanreflections());
void genwatertex(GLuint &tex, GLuint &fb, GLuint &db, bool refract = false)
{
- static const GLenum colorfmts[] = { GL_RGBA, GL_RGBA8, GL_RGB, GL_RGB8, GL_FALSE },
- depthfmts[] = { GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_FALSE };
- static GLenum reflectfmt = GL_FALSE, refractfmt = GL_FALSE, depthfmt = GL_FALSE;
- static bool usingalpha = false;
- bool needsalpha = refract && waterrefract && waterfade;
- if(refract && usingalpha!=needsalpha)
- {
- usingalpha = needsalpha;
- refractfmt = GL_FALSE;
- }
- int size = 1<<reflectsize;
- while(size>hwtexsize) size /= 2;
-
- glGenTextures(1, &tex);
- char *buf = new char[size*size*4];
- memset(buf, 0, size*size*4);
-
- GLenum &colorfmt = refract ? refractfmt : reflectfmt;
- if(colorfmt && fb && db)
- {
- createtexture(tex, size, size, buf, 3, 1, colorfmt);
- delete[] buf;
- return;
- }
-
- if(!fb) glGenFramebuffers_(1, &fb);
- int find = needsalpha ? 0 : 2;
- do
- {
- createtexture(tex, size, size, buf, 3, 1, colorfmt ? colorfmt : colorfmts[find]);
- glBindFramebuffer_(GL_FRAMEBUFFER, fb);
- glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
- if(glCheckFramebufferStatus_(GL_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE) break;
- }
- while(!colorfmt && colorfmts[++find]);
- if(!colorfmt) colorfmt = colorfmts[find];
-
- delete[] buf;
-
- if(!db) { glGenRenderbuffers_(1, &db); depthfmt = GL_FALSE; }
- if(!depthfmt) glBindRenderbuffer_(GL_RENDERBUFFER, db);
- find = 0;
- do
- {
- if(!depthfmt) glRenderbufferStorage_(GL_RENDERBUFFER, depthfmts[find], size, size);
- glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, db);
- if(glCheckFramebufferStatus_(GL_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE) break;
- }
- while(!depthfmt && depthfmts[++find]);
- if(!depthfmt)
- {
- glBindRenderbuffer_(GL_RENDERBUFFER, 0);
- depthfmt = depthfmts[find];
- }
-
- glBindFramebuffer_(GL_FRAMEBUFFER, 0);
+ static const GLenum colorfmts[] = { GL_RGBA, GL_RGBA8, GL_RGB, GL_RGB8, GL_FALSE },
+ depthfmts[] = { GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_FALSE };
+ static GLenum reflectfmt = GL_FALSE, refractfmt = GL_FALSE, depthfmt = GL_FALSE;
+ static bool usingalpha = false;
+ bool needsalpha = refract && waterrefract && waterfade;
+ if(refract && usingalpha!=needsalpha)
+ {
+ usingalpha = needsalpha;
+ refractfmt = GL_FALSE;
+ }
+ int size = 1<<reflectsize;
+ while(size>hwtexsize) size /= 2;
+
+ glGenTextures(1, &tex);
+ char *buf = new char[size*size*4];
+ memset(buf, 0, size*size*4);
+
+ GLenum &colorfmt = refract ? refractfmt : reflectfmt;
+ if(colorfmt && fb && db)
+ {
+ createtexture(tex, size, size, buf, 3, 1, colorfmt);
+ delete[] buf;
+ return;
+ }
+
+ if(!fb) glGenFramebuffers_(1, &fb);
+ int find = needsalpha ? 0 : 2;
+ do
+ {
+ createtexture(tex, size, size, buf, 3, 1, colorfmt ? colorfmt : colorfmts[find]);
+ glBindFramebuffer_(GL_FRAMEBUFFER, fb);
+ glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
+ if(glCheckFramebufferStatus_(GL_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE) break;
+ }
+ while(!colorfmt && colorfmts[++find]);
+ if(!colorfmt) colorfmt = colorfmts[find];
+
+ delete[] buf;
+
+ if(!db) { glGenRenderbuffers_(1, &db); depthfmt = GL_FALSE; }
+ if(!depthfmt) glBindRenderbuffer_(GL_RENDERBUFFER, db);
+ find = 0;
+ do
+ {
+ if(!depthfmt) glRenderbufferStorage_(GL_RENDERBUFFER, depthfmts[find], size, size);
+ glFramebufferRenderbuffer_(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, db);
+ if(glCheckFramebufferStatus_(GL_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE) break;
+ }
+ while(!depthfmt && depthfmts[++find]);
+ if(!depthfmt)
+ {
+ glBindRenderbuffer_(GL_RENDERBUFFER, 0);
+ depthfmt = depthfmts[find];
+ }
+
+ glBindFramebuffer_(GL_FRAMEBUFFER, 0);
}
void addwaterfallrefraction(materialsurface &m)
{
- Reflection &ref = waterfallrefraction;
- if(ref.age>=0)
- {
- ref.age = -1;
- ref.init = false;
- ref.matsurfs.setsize(0);
- ref.material = MAT_WATER;
- ref.height = INT_MAX;
- }
- ref.matsurfs.add(&m);
-
- if(!ref.refracttex) genwatertex(ref.refracttex, reflectionfb, reflectiondb);
+ Reflection &ref = waterfallrefraction;
+ if(ref.age>=0)
+ {
+ ref.age = -1;
+ ref.init = false;
+ ref.matsurfs.setsize(0);
+ ref.material = MAT_WATER;
+ ref.height = INT_MAX;
+ }
+ ref.matsurfs.add(&m);
+
+ if(!ref.refracttex) genwatertex(ref.refracttex, reflectionfb, reflectiondb);
}
void addreflection(materialsurface &m)
{
- int mat = m.material, height = m.o.z;
- Reflection *ref = NULL, *oldest = NULL;
- loopi(MAXREFLECTIONS)
- {
- Reflection &r = reflections[i];
- if(r.height<0)
- {
- if(!ref) ref = &r;
- }
- else if(r.height==height && r.material==mat)
- {
- r.matsurfs.add(&m);
- r.depth = max(r.depth, int(m.depth));
- if(r.age<0) return;
- ref = &r;
- break;
- }
- else if(!oldest || r.age>oldest->age) oldest = &r;
- }
- if(!ref)
- {
- if(!oldest || oldest->age<0) return;
- ref = oldest;
- }
- if(ref->height!=height || ref->material!=mat)
- {
- ref->material = mat;
- ref->height = height;
- ref->prevquery = NULL;
- }
- rplanes++;
- ref->age = -1;
- ref->init = false;
- ref->matsurfs.setsize(0);
- ref->matsurfs.add(&m);
- ref->depth = m.depth;
- if(drawtex == DRAWTEX_MINIMAP) return;
-
- if(waterreflect && !ref->tex) genwatertex(ref->tex, reflectionfb, reflectiondb);
- if(waterrefract && !ref->refracttex) genwatertex(ref->refracttex, reflectionfb, reflectiondb, true);
+ int mat = m.material, height = m.o.z;
+ Reflection *ref = NULL, *oldest = NULL;
+ loopi(MAXREFLECTIONS)
+ {
+ Reflection &r = reflections[i];
+ if(r.height<0)
+ {
+ if(!ref) ref = &r;
+ }
+ else if(r.height==height && r.material==mat)
+ {
+ r.matsurfs.add(&m);
+ r.depth = max(r.depth, int(m.depth));
+ if(r.age<0) return;
+ ref = &r;
+ break;
+ }
+ else if(!oldest || r.age>oldest->age) oldest = &r;
+ }
+ if(!ref)
+ {
+ if(!oldest || oldest->age<0) return;
+ ref = oldest;
+ }
+ if(ref->height!=height || ref->material!=mat)
+ {
+ ref->material = mat;
+ ref->height = height;
+ ref->prevquery = NULL;
+ }
+ rplanes++;
+ ref->age = -1;
+ ref->init = false;
+ ref->matsurfs.setsize(0);
+ ref->matsurfs.add(&m);
+ ref->depth = m.depth;
+ if(drawtex == DRAWTEX_MINIMAP) return;
+
+ if(waterreflect && !ref->tex) genwatertex(ref->tex, reflectionfb, reflectiondb);
+ if(waterrefract && !ref->refracttex) genwatertex(ref->refracttex, reflectionfb, reflectiondb, true);
}
static void drawmaterialquery(const materialsurface &m, float offset, float border = 0, float reflect = -1)
{
- if(gle::attribbuf.empty())
- {
- gle::defvertex();
- gle::begin(GL_QUADS);
- }
- float x = m.o.x, y = m.o.y, z = m.o.z, csize = m.csize + border, rsize = m.rsize + border;
- if(reflect >= 0) z = 2*reflect - z;
- switch(m.orient)
- {
+ if(gle::attribbuf.empty())
+ {
+ gle::defvertex();
+ gle::begin(GL_QUADS);
+ }
+ float x = m.o.x, y = m.o.y, z = m.o.z, csize = m.csize + border, rsize = m.rsize + border;
+ if(reflect >= 0) z = 2*reflect - z;
+ switch(m.orient)
+ {
#define GENFACEORIENT(orient, v0, v1, v2, v3) \
- case orient: v0 v1 v2 v3 break;
+ case orient: v0 v1 v2 v3 break;
#define GENFACEVERT(orient, vert, mx,my,mz, sx,sy,sz) \
- gle::attribf(mx sx, my sy, mz sz);
- GENFACEVERTS(x, x, y, y, z, z, - border, + csize, - border, + rsize, + offset, - offset)
+ gle::attribf(mx sx, my sy, mz sz);
+ GENFACEVERTS(x, x, y, y, z, z, - border, + csize, - border, + rsize, + offset, - offset)
#undef GENFACEORIENT
#undef GENFACEVERT
- }
+ }
}
extern void drawreflection(float z, bool refract, int fogdepth = -1, const bvec &col = bvec(0, 0, 0));
@@ -712,96 +712,96 @@ int rplanes = 0;
void queryreflection(Reflection &ref, bool init)
{
- if(init)
- {
- nocolorshader->set();
- glDepthMask(GL_FALSE);
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
- glDisable(GL_CULL_FACE);
- }
- startquery(ref.query);
- loopvj(ref.matsurfs)
- {
- materialsurface &m = *ref.matsurfs[j];
- float offset = 0.1f;
- if(m.orient==O_TOP)
- {
- offset = WATER_OFFSET +
- (vertwater ? WATER_AMPLITUDE*(camera1->pitch > 0 || m.depth < WATER_AMPLITUDE+0.5f ? -1 : 1) : 0);
- if(fabs(m.o.z-offset - camera1->o.z) < 0.5f && m.depth > WATER_AMPLITUDE+1.5f)
- offset += camera1->pitch > 0 ? -1 : 1;
- }
- drawmaterialquery(m, offset);
- }
- xtraverts += gle::end();
- endquery(ref.query);
+ if(init)
+ {
+ nocolorshader->set();
+ glDepthMask(GL_FALSE);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ glDisable(GL_CULL_FACE);
+ }
+ startquery(ref.query);
+ loopvj(ref.matsurfs)
+ {
+ materialsurface &m = *ref.matsurfs[j];
+ float offset = 0.1f;
+ if(m.orient==O_TOP)
+ {
+ offset = WATER_OFFSET +
+ (vertwater ? WATER_AMPLITUDE*(camera1->pitch > 0 || m.depth < WATER_AMPLITUDE+0.5f ? -1 : 1) : 0);
+ if(fabs(m.o.z-offset - camera1->o.z) < 0.5f && m.depth > WATER_AMPLITUDE+1.5f)
+ offset += camera1->pitch > 0 ? -1 : 1;
+ }
+ drawmaterialquery(m, offset);
+ }
+ xtraverts += gle::end();
+ endquery(ref.query);
}
void queryreflections()
{
- rplanes = 0;
-
- static int lastsize = 0;
- int size = 1<<reflectsize;
- while(size>hwtexsize) size /= 2;
- if(size!=lastsize) { if(lastsize) cleanreflections(); lastsize = size; }
-
- for(vtxarray *va = visibleva; va; va = va->next)
- {
- if(!va->matsurfs || va->occluded >= OCCLUDE_BB || va->curvfc >= VFC_FOGGED) continue;
- int lastmat = -1;
- loopi(va->matsurfs)
- {
- materialsurface &m = va->matbuf[i];
- if(m.material != lastmat)
- {
- if((m.material&MATF_VOLUME) != MAT_WATER || m.orient == O_BOTTOM) { i += m.skip; continue; }
- if(m.orient != O_TOP)
- {
- if(!waterfallrefract || !getwaterfog(m.material)) { i += m.skip; continue; }
- }
- lastmat = m.material;
- }
- if(m.orient==O_TOP) addreflection(m);
- else addwaterfallrefraction(m);
- }
- }
-
- loopi(MAXREFLECTIONS)
- {
- Reflection &ref = reflections[i];
- ++ref.age;
- }
- if(waterfallrefract)
- {
- Reflection &ref = waterfallrefraction;
- ++ref.age;
- }
-
- if((editmode && showmat && !drawtex) || !oqfrags || !oqwater || drawtex == DRAWTEX_MINIMAP) return;
-
- int refs = 0;
- if(waterreflect || waterrefract) loopi(MAXREFLECTIONS)
- {
- Reflection &ref = reflections[i];
- ref.prevquery = oqwater > 1 ? ref.query : NULL;
- ref.query = ref.height>=0 && !ref.age && ref.matsurfs.length() ? newquery(&ref) : NULL;
- if(ref.query) queryreflection(ref, !refs++);
- }
- if(waterfallrefract)
- {
- Reflection &ref = waterfallrefraction;
- ref.prevquery = oqwater > 1 ? ref.query : NULL;
- ref.query = ref.height>=0 && !ref.age && ref.matsurfs.length() ? newquery(&ref) : NULL;
- if(ref.query) queryreflection(ref, !refs++);
- }
-
- if(refs)
- {
- glDepthMask(GL_TRUE);
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- glEnable(GL_CULL_FACE);
- }
+ rplanes = 0;
+
+ static int lastsize = 0;
+ int size = 1<<reflectsize;
+ while(size>hwtexsize) size /= 2;
+ if(size!=lastsize) { if(lastsize) cleanreflections(); lastsize = size; }
+
+ for(vtxarray *va = visibleva; va; va = va->next)
+ {
+ if(!va->matsurfs || va->occluded >= OCCLUDE_BB || va->curvfc >= VFC_FOGGED) continue;
+ int lastmat = -1;
+ loopi(va->matsurfs)
+ {
+ materialsurface &m = va->matbuf[i];
+ if(m.material != lastmat)
+ {
+ if((m.material&MATF_VOLUME) != MAT_WATER || m.orient == O_BOTTOM) { i += m.skip; continue; }
+ if(m.orient != O_TOP)
+ {
+ if(!waterfallrefract || !getwaterfog(m.material)) { i += m.skip; continue; }
+ }
+ lastmat = m.material;
+ }
+ if(m.orient==O_TOP) addreflection(m);
+ else addwaterfallrefraction(m);
+ }
+ }
+
+ loopi(MAXREFLECTIONS)
+ {
+ Reflection &ref = reflections[i];
+ ++ref.age;
+ }
+ if(waterfallrefract)
+ {
+ Reflection &ref = waterfallrefraction;
+ ++ref.age;
+ }
+
+ if((editmode && showmat && !drawtex) || !oqfrags || !oqwater || drawtex == DRAWTEX_MINIMAP) return;
+
+ int refs = 0;
+ if(waterreflect || waterrefract) loopi(MAXREFLECTIONS)
+ {
+ Reflection &ref = reflections[i];
+ ref.prevquery = oqwater > 1 ? ref.query : NULL;
+ ref.query = ref.height>=0 && !ref.age && ref.matsurfs.length() ? newquery(&ref) : NULL;
+ if(ref.query) queryreflection(ref, !refs++);
+ }
+ if(waterfallrefract)
+ {
+ Reflection &ref = waterfallrefraction;
+ ref.prevquery = oqwater > 1 ? ref.query : NULL;
+ ref.query = ref.height>=0 && !ref.age && ref.matsurfs.length() ? newquery(&ref) : NULL;
+ if(ref.query) queryreflection(ref, !refs++);
+ }
+
+ if(refs)
+ {
+ glDepthMask(GL_TRUE);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glEnable(GL_CULL_FACE);
+ }
glFlush();
}
@@ -817,41 +817,41 @@ VAR(maskreflect, 0, 2, 16);
void maskreflection(Reflection &ref, float offset, bool reflect, bool clear = false)
{
- const bvec &wcol = getwatercolor(ref.material);
- vec color = wcol.tocolor();
- if(!maskreflect)
- {
- if(clear) glClearColor(color.r, color.g, color.b, 1);
- glClear(GL_DEPTH_BUFFER_BIT | (clear ? GL_COLOR_BUFFER_BIT : 0));
- return;
- }
- glClearDepth(0);
- glClear(GL_DEPTH_BUFFER_BIT);
- glClearDepth(1);
- glDepthRange(1, 1);
- glDepthFunc(GL_ALWAYS);
- glDisable(GL_CULL_FACE);
- if(clear)
- {
- notextureshader->set();
- gle::color(color);
- }
- else
- {
- nocolorshader->set();
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
- }
- float reflectheight = reflect ? ref.height + offset : -1;
- loopv(ref.matsurfs)
- {
- materialsurface &m = *ref.matsurfs[i];
- drawmaterialquery(m, -offset, maskreflect, reflectheight);
- }
- xtraverts += gle::end();
- if(!clear) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- glEnable(GL_CULL_FACE);
- glDepthFunc(GL_LESS);
- glDepthRange(0, 1);
+ const bvec &wcol = getwatercolor(ref.material);
+ vec color = wcol.tocolor();
+ if(!maskreflect)
+ {
+ if(clear) glClearColor(color.r, color.g, color.b, 1);
+ glClear(GL_DEPTH_BUFFER_BIT | (clear ? GL_COLOR_BUFFER_BIT : 0));
+ return;
+ }
+ glClearDepth(0);
+ glClear(GL_DEPTH_BUFFER_BIT);
+ glClearDepth(1);
+ glDepthRange(1, 1);
+ glDepthFunc(GL_ALWAYS);
+ glDisable(GL_CULL_FACE);
+ if(clear)
+ {
+ notextureshader->set();
+ gle::color(color);
+ }
+ else
+ {
+ nocolorshader->set();
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ }
+ float reflectheight = reflect ? ref.height + offset : -1;
+ loopv(ref.matsurfs)
+ {
+ materialsurface &m = *ref.matsurfs[i];
+ drawmaterialquery(m, -offset, maskreflect, reflectheight);
+ }
+ xtraverts += gle::end();
+ if(!clear) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glEnable(GL_CULL_FACE);
+ glDepthFunc(GL_LESS);
+ glDepthRange(0, 1);
}
VAR(reflectscissor, 0, 1, 1);
@@ -859,195 +859,195 @@ VAR(reflectvfc, 0, 1, 1);
static bool calcscissorbox(Reflection &ref, int size, vec &clipmin, vec &clipmax, int &sx, int &sy, int &sw, int &sh)
{
- materialsurface &m0 = *ref.matsurfs[0];
- int dim = dimension(m0.orient), r = R[dim], c = C[dim];
- ivec bbmin = m0.o, bbmax = bbmin;
- bbmax[r] += m0.rsize;
- bbmax[c] += m0.csize;
- loopvj(ref.matsurfs)
- {
- materialsurface &m = *ref.matsurfs[j];
- bbmin[r] = min(bbmin[r], m.o[r]);
- bbmin[c] = min(bbmin[c], m.o[c]);
- bbmax[r] = max(bbmax[r], m.o[r] + m.rsize);
- bbmax[c] = max(bbmax[c], m.o[c] + m.csize);
- bbmin[dim] = min(bbmin[dim], m.o[dim]);
- bbmax[dim] = max(bbmax[dim], m.o[dim]);
- }
-
- vec4 v[8];
- float sx1 = 1, sy1 = 1, sx2 = -1, sy2 = -1;
- loopi(8)
- {
- vec4 &p = v[i];
- camprojmatrix.transform(vec(i&1 ? bbmax.x : bbmin.x, i&2 ? bbmax.y : bbmin.y, (i&4 ? bbmax.z + WATER_AMPLITUDE : bbmin.z - WATER_AMPLITUDE) - WATER_OFFSET), p);
- if(p.z >= -p.w)
- {
- float x = p.x / p.w, y = p.y / p.w;
- sx1 = min(sx1, x);
- sy1 = min(sy1, y);
- sx2 = max(sx2, x);
- sy2 = max(sy2, y);
- }
- }
- if(sx1 >= sx2 || sy1 >= sy2) return false;
- loopi(8)
- {
- const vec4 &p = v[i];
- if(p.z >= -p.w) continue;
- loopj(3)
- {
- const vec4 &o = v[i^(1<<j)];
- if(o.z <= -o.w) continue;
- float t = (p.z + p.w)/(p.z + p.w - o.z - o.w),
- w = p.w + t*(o.w - p.w),
- x = (p.x + t*(o.x - p.x))/w,
- y = (p.y + t*(o.y - p.y))/w;
- sx1 = min(sx1, x);
- sy1 = min(sy1, y);
- sx2 = max(sx2, x);
- sy2 = max(sy2, y);
- }
- }
- if(sx1 <= -1 && sy1 <= -1 && sx2 >= 1 && sy2 >= 1) return false;
- sx1 = max(sx1, -1.0f);
- sy1 = max(sy1, -1.0f);
- sx2 = min(sx2, 1.0f);
- sy2 = min(sy2, 1.0f);
- if(reflectvfc)
- {
- clipmin.x = clamp(clipmin.x, sx1, sx2);
- clipmin.y = clamp(clipmin.y, sy1, sy2);
- clipmax.x = clamp(clipmax.x, sx1, sx2);
- clipmax.y = clamp(clipmax.y, sy1, sy2);
- }
- sx = int(floor((sx1+1)*0.5f*size));
- sy = int(floor((sy1+1)*0.5f*size));
- sw = max(int(ceil((sx2+1)*0.5f*size)) - sx, 0);
- sh = max(int(ceil((sy2+1)*0.5f*size)) - sy, 0);
- return true;
+ materialsurface &m0 = *ref.matsurfs[0];
+ int dim = dimension(m0.orient), r = R[dim], c = C[dim];
+ ivec bbmin = m0.o, bbmax = bbmin;
+ bbmax[r] += m0.rsize;
+ bbmax[c] += m0.csize;
+ loopvj(ref.matsurfs)
+ {
+ materialsurface &m = *ref.matsurfs[j];
+ bbmin[r] = min(bbmin[r], m.o[r]);
+ bbmin[c] = min(bbmin[c], m.o[c]);
+ bbmax[r] = max(bbmax[r], m.o[r] + m.rsize);
+ bbmax[c] = max(bbmax[c], m.o[c] + m.csize);
+ bbmin[dim] = min(bbmin[dim], m.o[dim]);
+ bbmax[dim] = max(bbmax[dim], m.o[dim]);
+ }
+
+ vec4 v[8];
+ float sx1 = 1, sy1 = 1, sx2 = -1, sy2 = -1;
+ loopi(8)
+ {
+ vec4 &p = v[i];
+ camprojmatrix.transform(vec(i&1 ? bbmax.x : bbmin.x, i&2 ? bbmax.y : bbmin.y, (i&4 ? bbmax.z + WATER_AMPLITUDE : bbmin.z - WATER_AMPLITUDE) - WATER_OFFSET), p);
+ if(p.z >= -p.w)
+ {
+ float x = p.x / p.w, y = p.y / p.w;
+ sx1 = min(sx1, x);
+ sy1 = min(sy1, y);
+ sx2 = max(sx2, x);
+ sy2 = max(sy2, y);
+ }
+ }
+ if(sx1 >= sx2 || sy1 >= sy2) return false;
+ loopi(8)
+ {
+ const vec4 &p = v[i];
+ if(p.z >= -p.w) continue;
+ loopj(3)
+ {
+ const vec4 &o = v[i^(1<<j)];
+ if(o.z <= -o.w) continue;
+ float t = (p.z + p.w)/(p.z + p.w - o.z - o.w),
+ w = p.w + t*(o.w - p.w),
+ x = (p.x + t*(o.x - p.x))/w,
+ y = (p.y + t*(o.y - p.y))/w;
+ sx1 = min(sx1, x);
+ sy1 = min(sy1, y);
+ sx2 = max(sx2, x);
+ sy2 = max(sy2, y);
+ }
+ }
+ if(sx1 <= -1 && sy1 <= -1 && sx2 >= 1 && sy2 >= 1) return false;
+ sx1 = max(sx1, -1.0f);
+ sy1 = max(sy1, -1.0f);
+ sx2 = min(sx2, 1.0f);
+ sy2 = min(sy2, 1.0f);
+ if(reflectvfc)
+ {
+ clipmin.x = clamp(clipmin.x, sx1, sx2);
+ clipmin.y = clamp(clipmin.y, sy1, sy2);
+ clipmax.x = clamp(clipmax.x, sx1, sx2);
+ clipmax.y = clamp(clipmax.y, sy1, sy2);
+ }
+ sx = int(floor((sx1+1)*0.5f*size));
+ sy = int(floor((sy1+1)*0.5f*size));
+ sw = max(int(ceil((sx2+1)*0.5f*size)) - sx, 0);
+ sh = max(int(ceil((sy2+1)*0.5f*size)) - sy, 0);
+ return true;
}
VARR(refractclear, 0, 0, 1);
void drawreflections()
{
- if((editmode && showmat && !drawtex) || drawtex == DRAWTEX_MINIMAP) return;
-
- static int lastdrawn = 0;
- int refs = 0, n = lastdrawn;
- float offset = -WATER_OFFSET;
- int size = 1<<reflectsize;
- while(size>hwtexsize) size /= 2;
-
- if(waterreflect || waterrefract) loopi(MAXREFLECTIONS)
- {
- Reflection &ref = reflections[++n%MAXREFLECTIONS];
- if(ref.height<0 || ref.age || ref.matsurfs.empty()) continue;
- if(oqfrags && oqwater && ref.query && ref.query->owner==&ref)
- {
- if(!ref.prevquery || ref.prevquery->owner!=&ref || checkquery(ref.prevquery))
- {
- if(checkquery(ref.query)) continue;
- }
- }
-
- if(!refs)
- {
- glViewport(0, 0, size, size);
- glBindFramebuffer_(GL_FRAMEBUFFER, reflectionfb);
- }
- refs++;
- ref.init = true;
- lastdrawn = n;
-
- vec clipmin(-1, -1, -1), clipmax(1, 1, 1);
- int sx, sy, sw, sh;
- bool scissor = reflectscissor && calcscissorbox(ref, size, clipmin, clipmax, sx, sy, sw, sh);
- if(scissor) glScissor(sx, sy, sw, sh);
- else
- {
- sx = sy = 0;
- sw = sh = size;
- }
-
- const bvec &wcol = getwatercolor(ref.material);
- int wfog = getwaterfog(ref.material);
-
- if(waterreflect && ref.tex && camera1->o.z >= ref.height+offset)
- {
- glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ref.tex, 0);
- if(scissor) glEnable(GL_SCISSOR_TEST);
- maskreflection(ref, offset, true);
- savevfcP();
- setvfcP(ref.height+offset, clipmin, clipmax);
- drawreflection(ref.height+offset, false);
- restorevfcP();
- if(scissor) glDisable(GL_SCISSOR_TEST);
- }
-
- if(waterrefract && ref.refracttex)
- {
- glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ref.refracttex, 0);
- if(scissor) glEnable(GL_SCISSOR_TEST);
- maskreflection(ref, offset, false, refractclear || !wfog || (ref.depth>=10000 && camera1->o.z >= ref.height + offset));
- if(wfog || waterfade)
- {
- savevfcP();
- setvfcP(-1, clipmin, clipmax);
- drawreflection(ref.height+offset, true, wfog, wcol);
- restorevfcP();
- }
- if(scissor) glDisable(GL_SCISSOR_TEST);
- }
-
- if(refs>=maxreflect) break;
- }
-
- if(waterfallrefract && waterfallrefraction.refracttex)
- {
- Reflection &ref = waterfallrefraction;
-
- if(ref.height<0 || ref.age || ref.matsurfs.empty()) goto nowaterfall;
- if(oqfrags && oqwater && ref.query && ref.query->owner==&ref)
- {
- if(!ref.prevquery || ref.prevquery->owner!=&ref || checkquery(ref.prevquery))
- {
- if(checkquery(ref.query)) goto nowaterfall;
- }
- }
-
- if(!refs)
- {
- glViewport(0, 0, size, size);
- glBindFramebuffer_(GL_FRAMEBUFFER, reflectionfb);
- }
- refs++;
- ref.init = true;
-
- vec clipmin(-1, -1, -1), clipmax(1, 1, 1);
- int sx, sy, sw, sh;
- bool scissor = reflectscissor && calcscissorbox(ref, size, clipmin, clipmax, sx, sy, sw, sh);
- if(scissor) glScissor(sx, sy, sw, sh);
- else
- {
- sx = sy = 0;
- sw = sh = size;
- }
-
- glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ref.refracttex, 0);
- if(scissor) glEnable(GL_SCISSOR_TEST);
- maskreflection(ref, -0.1f, false);
- savevfcP();
- setvfcP(-1, clipmin, clipmax);
- drawreflection(-1, true);
- restorevfcP();
- if(scissor) glDisable(GL_SCISSOR_TEST);
- }
+ if((editmode && showmat && !drawtex) || drawtex == DRAWTEX_MINIMAP) return;
+
+ static int lastdrawn = 0;
+ int refs = 0, n = lastdrawn;
+ float offset = -WATER_OFFSET;
+ int size = 1<<reflectsize;
+ while(size>hwtexsize) size /= 2;
+
+ if(waterreflect || waterrefract) loopi(MAXREFLECTIONS)
+ {
+ Reflection &ref = reflections[++n%MAXREFLECTIONS];
+ if(ref.height<0 || ref.age || ref.matsurfs.empty()) continue;
+ if(oqfrags && oqwater && ref.query && ref.query->owner==&ref)
+ {
+ if(!ref.prevquery || ref.prevquery->owner!=&ref || checkquery(ref.prevquery))
+ {
+ if(checkquery(ref.query)) continue;
+ }
+ }
+
+ if(!refs)
+ {
+ glViewport(0, 0, size, size);
+ glBindFramebuffer_(GL_FRAMEBUFFER, reflectionfb);
+ }
+ refs++;
+ ref.init = true;
+ lastdrawn = n;
+
+ vec clipmin(-1, -1, -1), clipmax(1, 1, 1);
+ int sx, sy, sw, sh;
+ bool scissor = reflectscissor && calcscissorbox(ref, size, clipmin, clipmax, sx, sy, sw, sh);
+ if(scissor) glScissor(sx, sy, sw, sh);
+ else
+ {
+ sx = sy = 0;
+ sw = sh = size;
+ }
+
+ const bvec &wcol = getwatercolor(ref.material);
+ int wfog = getwaterfog(ref.material);
+
+ if(waterreflect && ref.tex && camera1->o.z >= ref.height+offset)
+ {
+ glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ref.tex, 0);
+ if(scissor) glEnable(GL_SCISSOR_TEST);
+ maskreflection(ref, offset, true);
+ savevfcP();
+ setvfcP(ref.height+offset, clipmin, clipmax);
+ drawreflection(ref.height+offset, false);
+ restorevfcP();
+ if(scissor) glDisable(GL_SCISSOR_TEST);
+ }
+
+ if(waterrefract && ref.refracttex)
+ {
+ glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ref.refracttex, 0);
+ if(scissor) glEnable(GL_SCISSOR_TEST);
+ maskreflection(ref, offset, false, refractclear || !wfog || (ref.depth>=10000 && camera1->o.z >= ref.height + offset));
+ if(wfog || waterfade)
+ {
+ savevfcP();
+ setvfcP(-1, clipmin, clipmax);
+ drawreflection(ref.height+offset, true, wfog, wcol);
+ restorevfcP();
+ }
+ if(scissor) glDisable(GL_SCISSOR_TEST);
+ }
+
+ if(refs>=maxreflect) break;
+ }
+
+ if(waterfallrefract && waterfallrefraction.refracttex)
+ {
+ Reflection &ref = waterfallrefraction;
+
+ if(ref.height<0 || ref.age || ref.matsurfs.empty()) goto nowaterfall;
+ if(oqfrags && oqwater && ref.query && ref.query->owner==&ref)
+ {
+ if(!ref.prevquery || ref.prevquery->owner!=&ref || checkquery(ref.prevquery))
+ {
+ if(checkquery(ref.query)) goto nowaterfall;
+ }
+ }
+
+ if(!refs)
+ {
+ glViewport(0, 0, size, size);
+ glBindFramebuffer_(GL_FRAMEBUFFER, reflectionfb);
+ }
+ refs++;
+ ref.init = true;
+
+ vec clipmin(-1, -1, -1), clipmax(1, 1, 1);
+ int sx, sy, sw, sh;
+ bool scissor = reflectscissor && calcscissorbox(ref, size, clipmin, clipmax, sx, sy, sw, sh);
+ if(scissor) glScissor(sx, sy, sw, sh);
+ else
+ {
+ sx = sy = 0;
+ sw = sh = size;
+ }
+
+ glFramebufferTexture2D_(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ref.refracttex, 0);
+ if(scissor) glEnable(GL_SCISSOR_TEST);
+ maskreflection(ref, -0.1f, false);
+ savevfcP();
+ setvfcP(-1, clipmin, clipmax);
+ drawreflection(-1, true);
+ restorevfcP();
+ if(scissor) glDisable(GL_SCISSOR_TEST);
+ }
nowaterfall:
- if(!refs) return;
- glViewport(0, 0, screenw, screenh);
- glBindFramebuffer_(GL_FRAMEBUFFER, 0);
+ if(!refs) return;
+ glViewport(0, 0, screenw, screenh);
+ glBindFramebuffer_(GL_FRAMEBUFFER, 0);
}
diff --git a/src/engine/world.cpp b/src/engine/world.cpp
index 7f5baed..af998c3 100644
--- a/src/engine/world.cpp
+++ b/src/engine/world.cpp
@@ -12,261 +12,261 @@ VAR(entselradius, 0, 2, 10);
static inline void mmboundbox(const entity &e, model *m, vec &center, vec &radius)
{
- m->boundbox(center, radius);
- rotatebb(center, radius, e.attr1);
+ m->boundbox(center, radius);
+ rotatebb(center, radius, e.attr1);
}
static inline void mmcollisionbox(const entity &e, model *m, vec &center, vec &radius)
{
- m->collisionbox(center, radius);
- rotatebb(center, radius, e.attr1);
+ m->collisionbox(center, radius);
+ rotatebb(center, radius, e.attr1);
}
bool getentboundingbox(const extentity &e, ivec &o, ivec &r)
{
- switch(e.type)
- {
- case ET_EMPTY:
- return false;
- case ET_MAPMODEL:
- {
- model *m = loadmapmodel(e.attr2);
- if(m)
- {
- vec center, radius;
- mmboundbox(e, m, center, radius);
- center.add(e.o);
- radius.max(entselradius);
- o = ivec(vec(center).sub(radius));
- r = ivec(vec(center).add(radius).add(1));
- break;
- }
- }
- [[fallthrough]];
- // invisible mapmodels use entselradius
- default:
- o = ivec(vec(e.o).sub(entselradius));
- r = ivec(vec(e.o).add(entselradius+1));
- break;
- }
- return true;
+ switch(e.type)
+ {
+ case ET_EMPTY:
+ return false;
+ case ET_MAPMODEL:
+ {
+ model *m = loadmapmodel(e.attr2);
+ if(m)
+ {
+ vec center, radius;
+ mmboundbox(e, m, center, radius);
+ center.add(e.o);
+ radius.max(entselradius);
+ o = ivec(vec(center).sub(radius));
+ r = ivec(vec(center).add(radius).add(1));
+ break;
+ }
+ }
+ [[fallthrough]];
+ // invisible mapmodels use entselradius
+ default:
+ o = ivec(vec(e.o).sub(entselradius));
+ r = ivec(vec(e.o).add(entselradius+1));
+ break;
+ }
+ return true;
}
enum
{
- MODOE_ADD = 1<<0,
- MODOE_UPDATEBB = 1<<1,
- MODOE_LIGHTENT = 1<<2
+ MODOE_ADD = 1<<0,
+ MODOE_UPDATEBB = 1<<1,
+ MODOE_LIGHTENT = 1<<2
};
void modifyoctaentity(int flags, int id, extentity &e, cube *c, const ivec &cor, int size, const ivec &bo, const ivec &br, int leafsize, vtxarray *lastva = NULL)
{
- loopoctabox(cor, size, bo, br)
- {
- ivec o(i, cor, size);
- vtxarray *va = c[i].ext && c[i].ext->va ? c[i].ext->va : lastva;
- if(c[i].children != NULL && size > leafsize)
- modifyoctaentity(flags, id, e, c[i].children, o, size>>1, bo, br, leafsize, va);
- else if(flags&MODOE_ADD)
- {
- if(!c[i].ext || !c[i].ext->ents) ext(c[i]).ents = new octaentities(o, size);
- octaentities &oe = *c[i].ext->ents;
- switch(e.type)
- {
- case ET_MAPMODEL:
- if(loadmapmodel(e.attr2))
- {
- if(va)
- {
- va->bbmin.x = -1;
- if(oe.mapmodels.empty()) va->mapmodels.add(&oe);
- }
- oe.mapmodels.add(id);
- oe.bbmin.min(bo).max(oe.o);
- oe.bbmax.max(br).min(ivec(oe.o).add(oe.size));
- break;
- }
- // invisible mapmodel
- [[fallthrough]];
- default:
- oe.other.add(id);
- break;
- }
-
- }
- else if(c[i].ext && c[i].ext->ents)
- {
- octaentities &oe = *c[i].ext->ents;
- switch(e.type)
- {
- case ET_MAPMODEL:
- if(loadmapmodel(e.attr2))
- {
- oe.mapmodels.removeobj(id);
- if(va)
- {
- va->bbmin.x = -1;
- if(oe.mapmodels.empty()) va->mapmodels.removeobj(&oe);
- }
- oe.bbmin = oe.bbmax = oe.o;
- oe.bbmin.add(oe.size);
- loopvj(oe.mapmodels)
- {
- extentity &e = *entities::getents()[oe.mapmodels[j]];
- ivec eo, er;
- if(getentboundingbox(e, eo, er))
- {
- oe.bbmin.min(eo);
- oe.bbmax.max(er);
- }
- }
- oe.bbmin.max(oe.o);
- oe.bbmax.min(ivec(oe.o).add(oe.size));
- break;
- }
- [[fallthrough]];
- // invisible mapmodel
- default:
- oe.other.removeobj(id);
- break;
- }
- if(oe.mapmodels.empty() && oe.other.empty())
- freeoctaentities(c[i]);
- }
- if(c[i].ext && c[i].ext->ents) c[i].ext->ents->query = NULL;
- if(va && va!=lastva)
- {
- if(lastva)
- {
- if(va->bbmin.x < 0) lastva->bbmin.x = -1;
- }
- else if(flags&MODOE_UPDATEBB) updatevabb(va);
- }
- }
+ loopoctabox(cor, size, bo, br)
+ {
+ ivec o(i, cor, size);
+ vtxarray *va = c[i].ext && c[i].ext->va ? c[i].ext->va : lastva;
+ if(c[i].children != NULL && size > leafsize)
+ modifyoctaentity(flags, id, e, c[i].children, o, size>>1, bo, br, leafsize, va);
+ else if(flags&MODOE_ADD)
+ {
+ if(!c[i].ext || !c[i].ext->ents) ext(c[i]).ents = new octaentities(o, size);
+ octaentities &oe = *c[i].ext->ents;
+ switch(e.type)
+ {
+ case ET_MAPMODEL:
+ if(loadmapmodel(e.attr2))
+ {
+ if(va)
+ {
+ va->bbmin.x = -1;
+ if(oe.mapmodels.empty()) va->mapmodels.add(&oe);
+ }
+ oe.mapmodels.add(id);
+ oe.bbmin.min(bo).max(oe.o);
+ oe.bbmax.max(br).min(ivec(oe.o).add(oe.size));
+ break;
+ }
+ // invisible mapmodel
+ [[fallthrough]];
+ default:
+ oe.other.add(id);
+ break;
+ }
+
+ }
+ else if(c[i].ext && c[i].ext->ents)
+ {
+ octaentities &oe = *c[i].ext->ents;
+ switch(e.type)
+ {
+ case ET_MAPMODEL:
+ if(loadmapmodel(e.attr2))
+ {
+ oe.mapmodels.removeobj(id);
+ if(va)
+ {
+ va->bbmin.x = -1;
+ if(oe.mapmodels.empty()) va->mapmodels.removeobj(&oe);
+ }
+ oe.bbmin = oe.bbmax = oe.o;
+ oe.bbmin.add(oe.size);
+ loopvj(oe.mapmodels)
+ {
+ extentity &e = *entities::getents()[oe.mapmodels[j]];
+ ivec eo, er;
+ if(getentboundingbox(e, eo, er))
+ {
+ oe.bbmin.min(eo);
+ oe.bbmax.max(er);
+ }
+ }
+ oe.bbmin.max(oe.o);
+ oe.bbmax.min(ivec(oe.o).add(oe.size));
+ break;
+ }
+ [[fallthrough]];
+ // invisible mapmodel
+ default:
+ oe.other.removeobj(id);
+ break;
+ }
+ if(oe.mapmodels.empty() && oe.other.empty())
+ freeoctaentities(c[i]);
+ }
+ if(c[i].ext && c[i].ext->ents) c[i].ext->ents->query = NULL;
+ if(va && va!=lastva)
+ {
+ if(lastva)
+ {
+ if(va->bbmin.x < 0) lastva->bbmin.x = -1;
+ }
+ else if(flags&MODOE_UPDATEBB) updatevabb(va);
+ }
+ }
}
vector<int> outsideents;
static bool modifyoctaent(int flags, int id, extentity &e)
{
- if(flags&MODOE_ADD ? e.flags&EF_OCTA : !(e.flags&EF_OCTA)) return false;
-
- ivec o, r;
- if(!getentboundingbox(e, o, r)) return false;
-
- if(!insideworld(e.o))
- {
- int idx = outsideents.find(id);
- if(flags&MODOE_ADD)
- {
- if(idx < 0) outsideents.add(id);
- }
- else if(idx >= 0) outsideents.removeunordered(idx);
- }
- else
- {
- int leafsize = octaentsize, limit = max(r.x - o.x, max(r.y - o.y, r.z - o.z));
- while(leafsize < limit) leafsize *= 2;
- int diff = ~(leafsize-1) & ((o.x^r.x)|(o.y^r.y)|(o.z^r.z));
- if(diff && (limit > octaentsize/2 || diff < leafsize*2)) leafsize *= 2;
- modifyoctaentity(flags, id, e, worldroot, ivec(0, 0, 0), worldsize>>1, o, r, leafsize);
- }
- e.flags ^= EF_OCTA;
- if(e.type == ET_LIGHT) clearlightcache(id);
- else if(e.type == ET_PARTICLES) clearparticleemitters();
- else if(flags&MODOE_LIGHTENT) lightent(e);
- return true;
+ if(flags&MODOE_ADD ? e.flags&EF_OCTA : !(e.flags&EF_OCTA)) return false;
+
+ ivec o, r;
+ if(!getentboundingbox(e, o, r)) return false;
+
+ if(!insideworld(e.o))
+ {
+ int idx = outsideents.find(id);
+ if(flags&MODOE_ADD)
+ {
+ if(idx < 0) outsideents.add(id);
+ }
+ else if(idx >= 0) outsideents.removeunordered(idx);
+ }
+ else
+ {
+ int leafsize = octaentsize, limit = max(r.x - o.x, max(r.y - o.y, r.z - o.z));
+ while(leafsize < limit) leafsize *= 2;
+ int diff = ~(leafsize-1) & ((o.x^r.x)|(o.y^r.y)|(o.z^r.z));
+ if(diff && (limit > octaentsize/2 || diff < leafsize*2)) leafsize *= 2;
+ modifyoctaentity(flags, id, e, worldroot, ivec(0, 0, 0), worldsize>>1, o, r, leafsize);
+ }
+ e.flags ^= EF_OCTA;
+ if(e.type == ET_LIGHT) clearlightcache(id);
+ else if(e.type == ET_PARTICLES) clearparticleemitters();
+ else if(flags&MODOE_LIGHTENT) lightent(e);
+ return true;
}
static inline bool modifyoctaent(int flags, int id)
{
- vector<extentity *> &ents = entities::getents();
- return ents.inrange(id) && modifyoctaent(flags, id, *ents[id]);
+ vector<extentity *> &ents = entities::getents();
+ return ents.inrange(id) && modifyoctaent(flags, id, *ents[id]);
}
-static inline void addentity(int id) { modifyoctaent(MODOE_ADD|MODOE_UPDATEBB|MODOE_LIGHTENT, id); }
+static inline void addentity(int id) { modifyoctaent(MODOE_ADD|MODOE_UPDATEBB|MODOE_LIGHTENT, id); }
static inline void removeentity(int id) { modifyoctaent(MODOE_UPDATEBB, id); }
void freeoctaentities(cube &c)
{
- if(!c.ext) return;
- if(entities::getents().length())
- {
- while(c.ext->ents && !c.ext->ents->mapmodels.empty()) removeentity(c.ext->ents->mapmodels.pop());
- while(c.ext->ents && !c.ext->ents->other.empty()) removeentity(c.ext->ents->other.pop());
- }
- if(c.ext->ents)
- {
- delete c.ext->ents;
- c.ext->ents = NULL;
- }
+ if(!c.ext) return;
+ if(entities::getents().length())
+ {
+ while(c.ext->ents && !c.ext->ents->mapmodels.empty()) removeentity(c.ext->ents->mapmodels.pop());
+ while(c.ext->ents && !c.ext->ents->other.empty()) removeentity(c.ext->ents->other.pop());
+ }
+ if(c.ext->ents)
+ {
+ delete c.ext->ents;
+ c.ext->ents = NULL;
+ }
}
void entitiesinoctanodes()
{
- vector<extentity *> &ents = entities::getents();
- loopv(ents) modifyoctaent(MODOE_ADD, i, *ents[i]);
+ vector<extentity *> &ents = entities::getents();
+ loopv(ents) modifyoctaent(MODOE_ADD, i, *ents[i]);
}
static inline void findents(octaentities &oe, int low, int high, bool notspawned, const vec &pos, const vec &invradius, vector<int> &found)
{
- vector<extentity *> &ents = entities::getents();
- loopv(oe.other)
- {
- int id = oe.other[i];
- extentity &e = *ents[id];
- if(e.type >= low && e.type <= high && (e.spawned() || notspawned) && vec(e.o).sub(pos).mul(invradius).squaredlen() <= 1) found.add(id);
- }
+ vector<extentity *> &ents = entities::getents();
+ loopv(oe.other)
+ {
+ int id = oe.other[i];
+ extentity &e = *ents[id];
+ if(e.type >= low && e.type <= high && (e.spawned() || notspawned) && vec(e.o).sub(pos).mul(invradius).squaredlen() <= 1) found.add(id);
+ }
}
static inline void findents(cube *c, const ivec &o, int size, const ivec &bo, const ivec &br, int low, int high, bool notspawned, const vec &pos, const vec &invradius, vector<int> &found)
{
- loopoctabox(o, size, bo, br)
- {
- if(c[i].ext && c[i].ext->ents) findents(*c[i].ext->ents, low, high, notspawned, pos, invradius, found);
- if(c[i].children && size > octaentsize)
- {
- ivec co(i, o, size);
- findents(c[i].children, co, size>>1, bo, br, low, high, notspawned, pos, invradius, found);
- }
- }
+ loopoctabox(o, size, bo, br)
+ {
+ if(c[i].ext && c[i].ext->ents) findents(*c[i].ext->ents, low, high, notspawned, pos, invradius, found);
+ if(c[i].children && size > octaentsize)
+ {
+ ivec co(i, o, size);
+ findents(c[i].children, co, size>>1, bo, br, low, high, notspawned, pos, invradius, found);
+ }
+ }
}
void findents(int low, int high, bool notspawned, const vec &pos, const vec &radius, vector<int> &found)
{
- vec invradius(1/radius.x, 1/radius.y, 1/radius.z);
- ivec bo(vec(pos).sub(radius).sub(1)),
- br(vec(pos).add(radius).add(1));
- int diff = (bo.x^br.x) | (bo.y^br.y) | (bo.z^br.z) | octaentsize,
- scale = worldscale-1;
- if(diff&~((1<<scale)-1) || uint(bo.x|bo.y|bo.z|br.x|br.y|br.z) >= uint(worldsize))
- {
- findents(worldroot, ivec(0, 0, 0), 1<<scale, bo, br, low, high, notspawned, pos, invradius, found);
- return;
- }
- cube *c = &worldroot[octastep(bo.x, bo.y, bo.z, scale)];
- if(c->ext && c->ext->ents) findents(*c->ext->ents, low, high, notspawned, pos, invradius, found);
- scale--;
- while(c->children && !(diff&(1<<scale)))
- {
- c = &c->children[octastep(bo.x, bo.y, bo.z, scale)];
- if(c->ext && c->ext->ents) findents(*c->ext->ents, low, high, notspawned, pos, invradius, found);
- scale--;
- }
- if(c->children && 1<<scale >= octaentsize) findents(c->children, ivec(bo).mask(~((2<<scale)-1)), 1<<scale, bo, br, low, high, notspawned, pos, invradius, found);
+ vec invradius(1/radius.x, 1/radius.y, 1/radius.z);
+ ivec bo(vec(pos).sub(radius).sub(1)),
+ br(vec(pos).add(radius).add(1));
+ int diff = (bo.x^br.x) | (bo.y^br.y) | (bo.z^br.z) | octaentsize,
+ scale = worldscale-1;
+ if(diff&~((1<<scale)-1) || uint(bo.x|bo.y|bo.z|br.x|br.y|br.z) >= uint(worldsize))
+ {
+ findents(worldroot, ivec(0, 0, 0), 1<<scale, bo, br, low, high, notspawned, pos, invradius, found);
+ return;
+ }
+ cube *c = &worldroot[octastep(bo.x, bo.y, bo.z, scale)];
+ if(c->ext && c->ext->ents) findents(*c->ext->ents, low, high, notspawned, pos, invradius, found);
+ scale--;
+ while(c->children && !(diff&(1<<scale)))
+ {
+ c = &c->children[octastep(bo.x, bo.y, bo.z, scale)];
+ if(c->ext && c->ext->ents) findents(*c->ext->ents, low, high, notspawned, pos, invradius, found);
+ scale--;
+ }
+ if(c->children && 1<<scale >= octaentsize) findents(c->children, ivec(bo).mask(~((2<<scale)-1)), 1<<scale, bo, br, low, high, notspawned, pos, invradius, found);
}
char *entname(entity &e)
{
- static string fullentname;
- copystring(fullentname, entities::entname(e.type));
- const char *einfo = entities::entnameinfo(e);
- if(*einfo)
- {
- concatstring(fullentname, ": ");
- concatstring(fullentname, einfo);
- }
- return fullentname;
+ static string fullentname;
+ copystring(fullentname, entities::entname(e.type));
+ const char *einfo = entities::entnameinfo(e);
+ if(*einfo)
+ {
+ concatstring(fullentname, ": ");
+ concatstring(fullentname, einfo);
+ }
+ return fullentname;
}
extern selinfo sel;
@@ -279,138 +279,138 @@ VARF(entediting, 0, 0, 1, { if(!entediting) { entcancel(); efocus = enthover = -
bool noentedit()
{
- if(!editmode) { conoutf(CON_ERROR, "operation only allowed in edit mode"); return true; }
- return !entediting;
+ if(!editmode) { conoutf(CON_ERROR, "operation only allowed in edit mode"); return true; }
+ return !entediting;
}
bool pointinsel(const selinfo &sel, const vec &o)
{
- return(o.x <= sel.o.x+sel.s.x*sel.grid
- && o.x >= sel.o.x
- && o.y <= sel.o.y+sel.s.y*sel.grid
- && o.y >= sel.o.y
- && o.z <= sel.o.z+sel.s.z*sel.grid
- && o.z >= sel.o.z);
+ return(o.x <= sel.o.x+sel.s.x*sel.grid
+ && o.x >= sel.o.x
+ && o.y <= sel.o.y+sel.s.y*sel.grid
+ && o.y >= sel.o.y
+ && o.z <= sel.o.z+sel.s.z*sel.grid
+ && o.z >= sel.o.z);
}
vector<int> entgroup;
bool haveselent()
{
- return entgroup.length() > 0;
+ return entgroup.length() > 0;
}
void entcancel()
{
- entgroup.shrink(0);
+ entgroup.shrink(0);
}
void entadd(int id)
{
- undonext = true;
- entgroup.add(id);
+ undonext = true;
+ entgroup.add(id);
}
undoblock *newundoent()
{
- int numents = entgroup.length();
- if(numents <= 0) return NULL;
- undoblock *u = (undoblock *)new uchar[sizeof(undoblock) + numents*sizeof(undoent)];
- u->numents = numents;
- undoent *e = (undoent *)(u + 1);
- loopv(entgroup)
- {
- e->i = entgroup[i];
- e->e = *entities::getents()[entgroup[i]];
- e++;
- }
- return u;
+ int numents = entgroup.length();
+ if(numents <= 0) return NULL;
+ undoblock *u = (undoblock *)new uchar[sizeof(undoblock) + numents*sizeof(undoent)];
+ u->numents = numents;
+ undoent *e = (undoent *)(u + 1);
+ loopv(entgroup)
+ {
+ e->i = entgroup[i];
+ e->e = *entities::getents()[entgroup[i]];
+ e++;
+ }
+ return u;
}
void makeundoent()
{
- if(!undonext) return;
- undonext = false;
- oldhover = enthover;
- undoblock *u = newundoent();
- if(u) addundo(u);
+ if(!undonext) return;
+ undonext = false;
+ oldhover = enthover;
+ undoblock *u = newundoent();
+ if(u) addundo(u);
}
void detachentity(extentity &e)
{
- if(!e.attached) return;
- e.attached->attached = NULL;
- e.attached = NULL;
+ if(!e.attached) return;
+ e.attached->attached = NULL;
+ e.attached = NULL;
}
VAR(attachradius, 1, 100, 1000);
void attachentity(extentity &e)
{
- switch(e.type)
- {
- case ET_SPOTLIGHT:
- break;
-
- default:
- if(e.type<ET_GAMESPECIFIC || !entities::mayattach(e)) return;
- break;
- }
-
- detachentity(e);
-
- vector<extentity *> &ents = entities::getents();
- int closest = -1;
- float closedist = 1e10f;
- loopv(ents)
- {
- extentity *a = ents[i];
- if(a->attached) continue;
- switch(e.type)
- {
- case ET_SPOTLIGHT:
- if(a->type!=ET_LIGHT) continue;
- break;
-
- default:
- if(e.type<ET_GAMESPECIFIC || !entities::attachent(e, *a)) continue;
- break;
- }
- float dist = e.o.dist(a->o);
- if(dist < closedist)
- {
- closest = i;
- closedist = dist;
- }
- }
- if(closedist>attachradius) return;
- e.attached = ents[closest];
- ents[closest]->attached = &e;
+ switch(e.type)
+ {
+ case ET_SPOTLIGHT:
+ break;
+
+ default:
+ if(e.type<ET_GAMESPECIFIC || !entities::mayattach(e)) return;
+ break;
+ }
+
+ detachentity(e);
+
+ vector<extentity *> &ents = entities::getents();
+ int closest = -1;
+ float closedist = 1e10f;
+ loopv(ents)
+ {
+ extentity *a = ents[i];
+ if(a->attached) continue;
+ switch(e.type)
+ {
+ case ET_SPOTLIGHT:
+ if(a->type!=ET_LIGHT) continue;
+ break;
+
+ default:
+ if(e.type<ET_GAMESPECIFIC || !entities::attachent(e, *a)) continue;
+ break;
+ }
+ float dist = e.o.dist(a->o);
+ if(dist < closedist)
+ {
+ closest = i;
+ closedist = dist;
+ }
+ }
+ if(closedist>attachradius) return;
+ e.attached = ents[closest];
+ ents[closest]->attached = &e;
}
void attachentities()
{
- vector<extentity *> &ents = entities::getents();
- loopv(ents) attachentity(*ents[i]);
+ vector<extentity *> &ents = entities::getents();
+ loopv(ents) attachentity(*ents[i]);
}
// convenience macros implicitly define:
-// e entity, currently edited ent
-// n int, index to currently edited ent
-#define addimplicit(f) { if(entgroup.empty() && enthover>=0) { entadd(enthover); undonext = (enthover != oldhover); f; entgroup.drop(); } else f; }
+// e entity, currently edited ent
+// n int, index to currently edited ent
+#define addimplicit(f) { if(entgroup.empty() && enthover>=0) { entadd(enthover); undonext = (enthover != oldhover); f; entgroup.drop(); } else f; }
#define entfocusv(i, f, v){ int n = efocus = (i); if(n>=0) { extentity &e = *v[n]; f; } }
-#define entfocus(i, f) entfocusv(i, f, entities::getents())
+#define entfocus(i, f) entfocusv(i, f, entities::getents())
#define enteditv(i, f, v) \
{ \
- entfocusv(i, \
- { \
- int oldtype = e.type; \
- removeentity(n); \
- f; \
- if(oldtype!=e.type) detachentity(e); \
- if(e.type!=ET_EMPTY) { addentity(n); if(oldtype!=e.type) attachentity(e); } \
- entities::editent(n, true); \
- }, v); \
+ entfocusv(i, \
+ { \
+ int oldtype = e.type; \
+ removeentity(n); \
+ f; \
+ if(oldtype!=e.type) detachentity(e); \
+ if(e.type!=ET_EMPTY) { addentity(n); if(oldtype!=e.type) attachentity(e); } \
+ entities::editent(n, true); \
+ }, v); \
}
#define entedit(i, f) enteditv(i, f, entities::getents())
#define addgroup(exp) { vector<extentity *> &ents = entities::getents(); loopv(ents) entfocusv(i, if(exp) entadd(n), ents); }
@@ -418,93 +418,93 @@ void attachentities()
#define groupeditloop(f){ vector<extentity *> &ents = entities::getents(); entlooplevel++; int _ = efocus; loopv(entgroup) enteditv(entgroup[i], f, ents); efocus = _; entlooplevel--; }
#define groupeditpure(f){ if(entlooplevel>0) { entedit(efocus, f); } else groupeditloop(f); }
#define groupeditundo(f){ makeundoent(); groupeditpure(f); }
-#define groupedit(f) { addimplicit(groupeditundo(f)); }
+#define groupedit(f) { addimplicit(groupeditundo(f)); }
vec getselpos()
{
- vector<extentity *> &ents = entities::getents();
- if(entgroup.length() && ents.inrange(entgroup[0])) return ents[entgroup[0]]->o;
- if(ents.inrange(enthover)) return ents[enthover]->o;
- return vec(sel.o);
+ vector<extentity *> &ents = entities::getents();
+ if(entgroup.length() && ents.inrange(entgroup[0])) return ents[entgroup[0]]->o;
+ if(ents.inrange(enthover)) return ents[enthover]->o;
+ return vec(sel.o);
}
undoblock *copyundoents(undoblock *u)
{
- entcancel();
- undoent *e = u->ents();
- loopi(u->numents)
- entadd(e[i].i);
- undoblock *c = newundoent();
+ entcancel();
+ undoent *e = u->ents();
+ loopi(u->numents)
+ entadd(e[i].i);
+ undoblock *c = newundoent();
loopi(u->numents) if(e[i].e.type==ET_EMPTY)
entgroup.removeobj(e[i].i);
- return c;
+ return c;
}
void pasteundoent(int idx, const entity &ue)
{
- if(idx < 0 || idx >= MAXENTS) return;
- vector<extentity *> &ents = entities::getents();
- while(ents.length() < idx) ents.add(entities::newentity())->type = ET_EMPTY;
- int efocus = -1;
- entedit(idx, (entity &)e = ue);
+ if(idx < 0 || idx >= MAXENTS) return;
+ vector<extentity *> &ents = entities::getents();
+ while(ents.length() < idx) ents.add(entities::newentity())->type = ET_EMPTY;
+ int efocus = -1;
+ entedit(idx, (entity &)e = ue);
}
void pasteundoents(undoblock *u)
{
- undoent *ue = u->ents();
- loopi(u->numents)
- entedit(ue[i].i, (entity &)e = ue[i].e);
+ undoent *ue = u->ents();
+ loopi(u->numents)
+ entedit(ue[i].i, (entity &)e = ue[i].e);
}
void entflip()
{
- if(noentedit()) return;
- int d = dimension(sel.orient);
- float mid = sel.s[d]*sel.grid/2+sel.o[d];
- groupeditundo(e.o[d] -= (e.o[d]-mid)*2);
+ if(noentedit()) return;
+ int d = dimension(sel.orient);
+ float mid = sel.s[d]*sel.grid/2+sel.o[d];
+ groupeditundo(e.o[d] -= (e.o[d]-mid)*2);
}
void entrotate(int *cw)
{
- if(noentedit()) return;
- int d = dimension(sel.orient);
- int dd = (*cw<0) == dimcoord(sel.orient) ? R[d] : C[d];
- float mid = sel.s[dd]*sel.grid/2+sel.o[dd];
- vec s(sel.o.v);
- groupeditundo(
- e.o[dd] -= (e.o[dd]-mid)*2;
- e.o.sub(s);
- swap(e.o[R[d]], e.o[C[d]]);
- e.o.add(s);
- );
+ if(noentedit()) return;
+ int d = dimension(sel.orient);
+ int dd = (*cw<0) == dimcoord(sel.orient) ? R[d] : C[d];
+ float mid = sel.s[dd]*sel.grid/2+sel.o[dd];
+ vec s(sel.o.v);
+ groupeditundo(
+ e.o[dd] -= (e.o[dd]-mid)*2;
+ e.o.sub(s);
+ swap(e.o[R[d]], e.o[C[d]]);
+ e.o.add(s);
+ );
}
void entselectionbox(const entity &e, vec &eo, vec &es)
{
- model *m = NULL;
- const char *mname = entities::entmodel(e);
- if(mname && (m = loadmodel(mname)))
- {
- m->collisionbox(eo, es);
- if(es.x > es.y) es.y = es.x; else es.x = es.y; // square
- es.z = (es.z + eo.z + 1 + entselradius)/2; // enclose ent radius box and model box
- eo.x += e.o.x;
- eo.y += e.o.y;
- eo.z = e.o.z - entselradius + es.z;
- }
- else if(e.type == ET_MAPMODEL && (m = loadmapmodel(e.attr2)))
- {
- mmcollisionbox(e, m, eo, es);
- es.max(entselradius);
- eo.add(e.o);
- }
- else
- {
- es = vec(entselradius);
- eo = e.o;
- }
- eo.sub(es);
- es.mul(2);
+ model *m = NULL;
+ const char *mname = entities::entmodel(e);
+ if(mname && (m = loadmodel(mname)))
+ {
+ m->collisionbox(eo, es);
+ if(es.x > es.y) es.y = es.x; else es.x = es.y; // square
+ es.z = (es.z + eo.z + 1 + entselradius)/2; // enclose ent radius box and model box
+ eo.x += e.o.x;
+ eo.y += e.o.y;
+ eo.z = e.o.z - entselradius + es.z;
+ }
+ else if(e.type == ET_MAPMODEL && (m = loadmapmodel(e.attr2)))
+ {
+ mmcollisionbox(e, m, eo, es);
+ es.max(entselradius);
+ eo.add(e.o);
+ }
+ else
+ {
+ es = vec(entselradius);
+ eo = e.o;
+ }
+ eo.sub(es);
+ es.mul(2);
}
VAR(entselsnap, 0, 0, 1);
@@ -519,327 +519,327 @@ int entmoving = 0;
void entdrag(const vec &ray)
{
- if(noentedit() || !haveselent()) return;
+ if(noentedit() || !haveselent()) return;
- float r = 0, c = 0;
- static vec v, handle;
- vec eo, es;
- int d = dimension(entorient),
- dc= dimcoord(entorient);
+ float r = 0, c = 0;
+ static vec v, handle;
+ vec eo, es;
+ int d = dimension(entorient),
+ dc= dimcoord(entorient);
- entfocus(entgroup.last(),
- entselectionbox(e, eo, es);
+ entfocus(entgroup.last(),
+ entselectionbox(e, eo, es);
- if(!editmoveplane(e.o, ray, d, eo[d] + (dc ? es[d] : 0), handle, v, entmoving==1))
- return;
+ if(!editmoveplane(e.o, ray, d, eo[d] + (dc ? es[d] : 0), handle, v, entmoving==1))
+ return;
- ivec g(v);
- int z = g[d]&(~(sel.grid-1));
- g.add(sel.grid/2).mask(~(sel.grid-1));
- g[d] = z;
+ ivec g(v);
+ int z = g[d]&(~(sel.grid-1));
+ g.add(sel.grid/2).mask(~(sel.grid-1));
+ g[d] = z;
- r = (entselsnap ? g[R[d]] : v[R[d]]) - e.o[R[d]];
- c = (entselsnap ? g[C[d]] : v[C[d]]) - e.o[C[d]];
- );
+ r = (entselsnap ? g[R[d]] : v[R[d]]) - e.o[R[d]];
+ c = (entselsnap ? g[C[d]] : v[C[d]]) - e.o[C[d]];
+ );
- if(entmoving==1) makeundoent();
- groupeditpure(e.o[R[d]] += r; e.o[C[d]] += c);
- entmoving = 2;
+ if(entmoving==1) makeundoent();
+ groupeditpure(e.o[R[d]] += r; e.o[C[d]] += c);
+ entmoving = 2;
}
VAR(showentradius, 0, 1, 1);
void renderentring(const extentity &e, float radius, int axis)
{
- if(radius <= 0) return;
- gle::defvertex();
- gle::begin(GL_LINE_LOOP);
- loopi(15)
- {
- vec p(e.o);
- const vec2 &sc = sincos360[i*(360/15)];
- p[axis>=2 ? 1 : 0] += radius*sc.x;
- p[axis>=1 ? 2 : 1] += radius*sc.y;
- gle::attrib(p);
- }
- xtraverts += gle::end();
+ if(radius <= 0) return;
+ gle::defvertex();
+ gle::begin(GL_LINE_LOOP);
+ loopi(15)
+ {
+ vec p(e.o);
+ const vec2 &sc = sincos360[i*(360/15)];
+ p[axis>=2 ? 1 : 0] += radius*sc.x;
+ p[axis>=1 ? 2 : 1] += radius*sc.y;
+ gle::attrib(p);
+ }
+ xtraverts += gle::end();
}
void renderentsphere(const extentity &e, float radius)
{
- if(radius <= 0) return;
- loopk(3) renderentring(e, radius, k);
+ if(radius <= 0) return;
+ loopk(3) renderentring(e, radius, k);
}
void renderentattachment(const extentity &e)
{
- if(!e.attached) return;
- gle::defvertex();
- gle::begin(GL_LINES);
- gle::attrib(e.o);
- gle::attrib(e.attached->o);
- xtraverts += gle::end();
+ if(!e.attached) return;
+ gle::defvertex();
+ gle::begin(GL_LINES);
+ gle::attrib(e.o);
+ gle::attrib(e.attached->o);
+ xtraverts += gle::end();
}
void renderentarrow(const extentity &e, const vec &dir, float radius)
{
- if(radius <= 0) return;
- float arrowsize = min(radius/8, 0.5f);
- vec target = vec(dir).mul(radius).add(e.o), arrowbase = vec(dir).mul(radius - arrowsize).add(e.o), spoke;
- spoke.orthogonal(dir);
- spoke.normalize();
- spoke.mul(arrowsize);
+ if(radius <= 0) return;
+ float arrowsize = min(radius/8, 0.5f);
+ vec target = vec(dir).mul(radius).add(e.o), arrowbase = vec(dir).mul(radius - arrowsize).add(e.o), spoke;
+ spoke.orthogonal(dir);
+ spoke.normalize();
+ spoke.mul(arrowsize);
- gle::defvertex();
+ gle::defvertex();
- gle::begin(GL_LINES);
- gle::attrib(e.o);
- gle::attrib(target);
- xtraverts += gle::end();
+ gle::begin(GL_LINES);
+ gle::attrib(e.o);
+ gle::attrib(target);
+ xtraverts += gle::end();
- gle::begin(GL_TRIANGLE_FAN);
- gle::attrib(target);
- loopi(5) gle::attrib(vec(spoke).rotate(2*M_PI*i/4.0f, dir).add(arrowbase));
- xtraverts += gle::end();
+ gle::begin(GL_TRIANGLE_FAN);
+ gle::attrib(target);
+ loopi(5) gle::attrib(vec(spoke).rotate(2*M_PI*i/4.0f, dir).add(arrowbase));
+ xtraverts += gle::end();
}
void renderentcone(const extentity &e, const vec &dir, float radius, float angle)
{
- if(radius <= 0) return;
- vec spot = vec(dir).mul(radius*cosf(angle*RAD)).add(e.o), spoke;
- spoke.orthogonal(dir);
- spoke.normalize();
- spoke.mul(radius*sinf(angle*RAD));
+ if(radius <= 0) return;
+ vec spot = vec(dir).mul(radius*cosf(angle*RAD)).add(e.o), spoke;
+ spoke.orthogonal(dir);
+ spoke.normalize();
+ spoke.mul(radius*sinf(angle*RAD));
- gle::defvertex();
+ gle::defvertex();
- gle::begin(GL_LINES);
- loopi(8)
- {
- gle::attrib(e.o);
- gle::attrib(vec(spoke).rotate(2*M_PI*i/8.0f, dir).add(spot));
- }
- xtraverts += gle::end();
+ gle::begin(GL_LINES);
+ loopi(8)
+ {
+ gle::attrib(e.o);
+ gle::attrib(vec(spoke).rotate(2*M_PI*i/8.0f, dir).add(spot));
+ }
+ xtraverts += gle::end();
- gle::begin(GL_LINE_LOOP);
- loopi(8) gle::attrib(vec(spoke).rotate(2*M_PI*i/8.0f, dir).add(spot));
- xtraverts += gle::end();
+ gle::begin(GL_LINE_LOOP);
+ loopi(8) gle::attrib(vec(spoke).rotate(2*M_PI*i/8.0f, dir).add(spot));
+ xtraverts += gle::end();
}
void renderentradius(extentity &e, bool color)
{
- switch(e.type)
- {
- case ET_LIGHT:
- if(color) gle::colorf(e.attr2/255.0f, e.attr3/255.0f, e.attr4/255.0f);
- renderentsphere(e, e.attr1);
- break;
-
- case ET_SPOTLIGHT:
- if(e.attached)
- {
- if(color) gle::colorf(0, 1, 1);
- float radius = e.attached->attr1;
- if(!radius) radius = 2*e.o.dist(e.attached->o);
- vec dir = vec(e.o).sub(e.attached->o).normalize();
- float angle = clamp(int(e.attr1), 1, 89);
- renderentattachment(e);
- renderentcone(*e.attached, dir, radius, angle);
- }
- break;
-
- case ET_SOUND:
- if(color) gle::colorf(0, 1, 1);
- renderentsphere(e, e.attr2);
- break;
-
- case ET_ENVMAP:
- {
- extern int envmapradius;
- if(color) gle::colorf(0, 1, 1);
- renderentsphere(e, e.attr1 ? max(0, min(10000, int(e.attr1))) : envmapradius);
- break;
- }
-
- case ET_MAPMODEL:
- case ET_PLAYERSTART:
- {
- if(color) gle::colorf(0, 1, 1);
- entities::entradius(e, color);
- vec dir;
- vecfromyawpitch(e.attr1, 0, 1, 0, dir);
- renderentarrow(e, dir, 4);
- break;
- }
-
- default:
- if(e.type>=ET_GAMESPECIFIC)
- {
- if(color) gle::colorf(0, 1, 1);
- entities::entradius(e, color);
- }
- break;
- }
+ switch(e.type)
+ {
+ case ET_LIGHT:
+ if(color) gle::colorf(e.attr2/255.0f, e.attr3/255.0f, e.attr4/255.0f);
+ renderentsphere(e, e.attr1);
+ break;
+
+ case ET_SPOTLIGHT:
+ if(e.attached)
+ {
+ if(color) gle::colorf(0, 1, 1);
+ float radius = e.attached->attr1;
+ if(!radius) radius = 2*e.o.dist(e.attached->o);
+ vec dir = vec(e.o).sub(e.attached->o).normalize();
+ float angle = clamp(int(e.attr1), 1, 89);
+ renderentattachment(e);
+ renderentcone(*e.attached, dir, radius, angle);
+ }
+ break;
+
+ case ET_SOUND:
+ if(color) gle::colorf(0, 1, 1);
+ renderentsphere(e, e.attr2);
+ break;
+
+ case ET_ENVMAP:
+ {
+ extern int envmapradius;
+ if(color) gle::colorf(0, 1, 1);
+ renderentsphere(e, e.attr1 ? max(0, min(10000, int(e.attr1))) : envmapradius);
+ break;
+ }
+
+ case ET_MAPMODEL:
+ case ET_PLAYERSTART:
+ {
+ if(color) gle::colorf(0, 1, 1);
+ entities::entradius(e, color);
+ vec dir;
+ vecfromyawpitch(e.attr1, 0, 1, 0, dir);
+ renderentarrow(e, dir, 4);
+ break;
+ }
+
+ default:
+ if(e.type>=ET_GAMESPECIFIC)
+ {
+ if(color) gle::colorf(0, 1, 1);
+ entities::entradius(e, color);
+ }
+ break;
+ }
}
static void renderentbox(const vec &eo, vec es)
{
- es.add(eo);
+ es.add(eo);
- // bottom quad
- gle::attrib(eo.x, eo.y, eo.z); gle::attrib(es.x, eo.y, eo.z);
- gle::attrib(es.x, eo.y, eo.z); gle::attrib(es.x, es.y, eo.z);
- gle::attrib(es.x, es.y, eo.z); gle::attrib(eo.x, es.y, eo.z);
- gle::attrib(eo.x, es.y, eo.z); gle::attrib(eo.x, eo.y, eo.z);
+ // bottom quad
+ gle::attrib(eo.x, eo.y, eo.z); gle::attrib(es.x, eo.y, eo.z);
+ gle::attrib(es.x, eo.y, eo.z); gle::attrib(es.x, es.y, eo.z);
+ gle::attrib(es.x, es.y, eo.z); gle::attrib(eo.x, es.y, eo.z);
+ gle::attrib(eo.x, es.y, eo.z); gle::attrib(eo.x, eo.y, eo.z);
- // top quad
- gle::attrib(eo.x, eo.y, es.z); gle::attrib(es.x, eo.y, es.z);
- gle::attrib(es.x, eo.y, es.z); gle::attrib(es.x, es.y, es.z);
- gle::attrib(es.x, es.y, es.z); gle::attrib(eo.x, es.y, es.z);
- gle::attrib(eo.x, es.y, es.z); gle::attrib(eo.x, eo.y, es.z);
+ // top quad
+ gle::attrib(eo.x, eo.y, es.z); gle::attrib(es.x, eo.y, es.z);
+ gle::attrib(es.x, eo.y, es.z); gle::attrib(es.x, es.y, es.z);
+ gle::attrib(es.x, es.y, es.z); gle::attrib(eo.x, es.y, es.z);
+ gle::attrib(eo.x, es.y, es.z); gle::attrib(eo.x, eo.y, es.z);
- // sides
- gle::attrib(eo.x, eo.y, eo.z); gle::attrib(eo.x, eo.y, es.z);
- gle::attrib(es.x, eo.y, eo.z); gle::attrib(es.x, eo.y, es.z);
- gle::attrib(es.x, es.y, eo.z); gle::attrib(es.x, es.y, es.z);
- gle::attrib(eo.x, es.y, eo.z); gle::attrib(eo.x, es.y, es.z);
+ // sides
+ gle::attrib(eo.x, eo.y, eo.z); gle::attrib(eo.x, eo.y, es.z);
+ gle::attrib(es.x, eo.y, eo.z); gle::attrib(es.x, eo.y, es.z);
+ gle::attrib(es.x, es.y, eo.z); gle::attrib(es.x, es.y, es.z);
+ gle::attrib(eo.x, es.y, eo.z); gle::attrib(eo.x, es.y, es.z);
}
void renderentselection(const vec &o, const vec &ray, bool entmoving)
{
- if(noentedit()) return;
- vec eo, es;
-
- if(entgroup.length())
- {
- gle::colorub(0, 40, 0);
- gle::defvertex();
- gle::begin(GL_LINES, entgroup.length()*24);
- loopv(entgroup) entfocus(entgroup[i],
- entselectionbox(e, eo, es);
- renderentbox(eo, es);
- );
- xtraverts += gle::end();
- }
-
- if(enthover >= 0)
- {
- gle::colorub(0, 40, 0);
- entfocus(enthover, entselectionbox(e, eo, es)); // also ensures enthover is back in focus
- boxs3D(eo, es, 1);
- if(entmoving && entmovingshadow==1)
- {
- vec a, b;
- gle::colorub(20, 20, 20);
- (a = eo).x = eo.x - fmod(eo.x, worldsize); (b = es).x = a.x + worldsize; boxs3D(a, b, 1);
- (a = eo).y = eo.y - fmod(eo.y, worldsize); (b = es).y = a.x + worldsize; boxs3D(a, b, 1);
- (a = eo).z = eo.z - fmod(eo.z, worldsize); (b = es).z = a.x + worldsize; boxs3D(a, b, 1);
- }
- gle::colorub(150,0,0);
- boxs(entorient, eo, es);
- boxs(entorient, eo, es, clamp(0.015f*camera1->o.dist(eo)*tan(fovy*0.5f*RAD), 0.1f, 1.0f));
- }
-
- if(showentradius && (entgroup.length() || enthover >= 0))
- {
- glDepthFunc(GL_GREATER);
- gle::colorf(0.25f, 0.25f, 0.25f);
- loopv(entgroup) entfocus(entgroup[i], renderentradius(e, false));
- if(enthover>=0) entfocus(enthover, renderentradius(e, false));
- glDepthFunc(GL_LESS);
- loopv(entgroup) entfocus(entgroup[i], renderentradius(e, true));
- if(enthover>=0) entfocus(enthover, renderentradius(e, true));
- }
+ if(noentedit()) return;
+ vec eo, es;
+
+ if(entgroup.length())
+ {
+ gle::colorub(0, 40, 0);
+ gle::defvertex();
+ gle::begin(GL_LINES, entgroup.length()*24);
+ loopv(entgroup) entfocus(entgroup[i],
+ entselectionbox(e, eo, es);
+ renderentbox(eo, es);
+ );
+ xtraverts += gle::end();
+ }
+
+ if(enthover >= 0)
+ {
+ gle::colorub(0, 40, 0);
+ entfocus(enthover, entselectionbox(e, eo, es)); // also ensures enthover is back in focus
+ boxs3D(eo, es, 1);
+ if(entmoving && entmovingshadow==1)
+ {
+ vec a, b;
+ gle::colorub(20, 20, 20);
+ (a = eo).x = eo.x - fmod(eo.x, worldsize); (b = es).x = a.x + worldsize; boxs3D(a, b, 1);
+ (a = eo).y = eo.y - fmod(eo.y, worldsize); (b = es).y = a.x + worldsize; boxs3D(a, b, 1);
+ (a = eo).z = eo.z - fmod(eo.z, worldsize); (b = es).z = a.x + worldsize; boxs3D(a, b, 1);
+ }
+ gle::colorub(150,0,0);
+ boxs(entorient, eo, es);
+ boxs(entorient, eo, es, clamp(0.015f*camera1->o.dist(eo)*tan(fovy*0.5f*RAD), 0.1f, 1.0f));
+ }
+
+ if(showentradius && (entgroup.length() || enthover >= 0))
+ {
+ glDepthFunc(GL_GREATER);
+ gle::colorf(0.25f, 0.25f, 0.25f);
+ loopv(entgroup) entfocus(entgroup[i], renderentradius(e, false));
+ if(enthover>=0) entfocus(enthover, renderentradius(e, false));
+ glDepthFunc(GL_LESS);
+ loopv(entgroup) entfocus(entgroup[i], renderentradius(e, true));
+ if(enthover>=0) entfocus(enthover, renderentradius(e, true));
+ }
}
bool enttoggle(int id)
{
- undonext = true;
- int i = entgroup.find(id);
- if(i < 0)
- entadd(id);
- else
- entgroup.remove(i);
- return i < 0;
+ undonext = true;
+ int i = entgroup.find(id);
+ if(i < 0)
+ entadd(id);
+ else
+ entgroup.remove(i);
+ return i < 0;
}
bool hoveringonent(int ent, int orient)
{
- if(noentedit()) return false;
- entorient = orient;
- if((efocus = enthover = ent) >= 0)
- return true;
- efocus = entgroup.empty() ? -1 : entgroup.last();
- enthover = -1;
- return false;
+ if(noentedit()) return false;
+ entorient = orient;
+ if((efocus = enthover = ent) >= 0)
+ return true;
+ efocus = entgroup.empty() ? -1 : entgroup.last();
+ enthover = -1;
+ return false;
}
VAR(entitysurf, 0, 0, 1);
ICOMMAND(entadd, "", (),
{
- if(enthover >= 0 && !noentedit())
- {
- if(entgroup.find(enthover) < 0) entadd(enthover);
- if(entmoving > 1) entmoving = 1;
- }
+ if(enthover >= 0 && !noentedit())
+ {
+ if(entgroup.find(enthover) < 0) entadd(enthover);
+ if(entmoving > 1) entmoving = 1;
+ }
});
ICOMMAND(enttoggle, "", (),
{
- if(enthover < 0 || noentedit() || !enttoggle(enthover)) { entmoving = 0; intret(0); }
- else { if(entmoving > 1) entmoving = 1; intret(1); }
+ if(enthover < 0 || noentedit() || !enttoggle(enthover)) { entmoving = 0; intret(0); }
+ else { if(entmoving > 1) entmoving = 1; intret(1); }
});
ICOMMAND(entmoving, "b", (int *n),
{
- if(*n >= 0)
- {
- if(!*n || enthover < 0 || noentedit()) entmoving = 0;
- else
- {
- if(entgroup.find(enthover) < 0) { entadd(enthover); entmoving = 1; }
- else if(!entmoving) entmoving = 1;
- }
- }
- intret(entmoving);
+ if(*n >= 0)
+ {
+ if(!*n || enthover < 0 || noentedit()) entmoving = 0;
+ else
+ {
+ if(entgroup.find(enthover) < 0) { entadd(enthover); entmoving = 1; }
+ else if(!entmoving) entmoving = 1;
+ }
+ }
+ intret(entmoving);
});
void entpush(int *dir)
{
- if(noentedit()) return;
- int d = dimension(entorient);
- int s = dimcoord(entorient) ? -*dir : *dir;
- if(entmoving)
- {
- groupeditpure(e.o[d] += float(s*sel.grid)); // editdrag supplies the undo
- }
- else
- groupedit(e.o[d] += float(s*sel.grid));
- if(entitysurf==1)
- {
- player->o[d] += float(s*sel.grid);
- player->resetinterp();
- }
+ if(noentedit()) return;
+ int d = dimension(entorient);
+ int s = dimcoord(entorient) ? -*dir : *dir;
+ if(entmoving)
+ {
+ groupeditpure(e.o[d] += float(s*sel.grid)); // editdrag supplies the undo
+ }
+ else
+ groupedit(e.o[d] += float(s*sel.grid));
+ if(entitysurf==1)
+ {
+ player->o[d] += float(s*sel.grid);
+ player->resetinterp();
+ }
}
VAR(entautoviewdist, 0, 25, 100);
void entautoview(int *dir)
{
- if(!haveselent()) return;
- static int s = 0;
- vec v(player->o);
- v.sub(worldpos);
- v.normalize();
- v.mul(entautoviewdist);
- int t = s + *dir;
- s = abs(t) % entgroup.length();
- if(t<0 && s>0) s = entgroup.length() - s;
- entfocus(entgroup[s],
- v.add(e.o);
- player->o = v;
- player->resetinterp();
- );
+ if(!haveselent()) return;
+ static int s = 0;
+ vec v(player->o);
+ v.sub(worldpos);
+ v.normalize();
+ v.mul(entautoviewdist);
+ int t = s + *dir;
+ s = abs(t) % entgroup.length();
+ if(t<0 && s>0) s = entgroup.length() - s;
+ entfocus(entgroup[s],
+ v.add(e.o);
+ player->o = v;
+ player->resetinterp();
+ );
}
COMMAND(entautoview, "i");
@@ -849,76 +849,76 @@ COMMAND(entpush, "i");
void delent()
{
- if(noentedit()) return;
- groupedit(e.type = ET_EMPTY;);
- entcancel();
+ if(noentedit()) return;
+ groupedit(e.type = ET_EMPTY;);
+ entcancel();
}
int findtype(char *what)
{
- for(int i = 0; *entities::entname(i); i++) if(strcmp(what, entities::entname(i))==0) return i;
- conoutf(CON_ERROR, "unknown entity type \"%s\"", what);
- return ET_EMPTY;
+ for(int i = 0; *entities::entname(i); i++) if(strcmp(what, entities::entname(i))==0) return i;
+ conoutf(CON_ERROR, "unknown entity type \"%s\"", what);
+ return ET_EMPTY;
}
VAR(entdrop, 0, 2, 3);
bool dropentity(entity &e, int drop = -1)
{
- vec radius(4.0f, 4.0f, 4.0f);
- if(drop<0) drop = entdrop;
- if(e.type == ET_MAPMODEL)
- {
- model *m = loadmapmodel(e.attr2);
- if(m)
- {
- vec center;
- mmboundbox(e, m, center, radius);
- radius.x += fabs(center.x);
- radius.y += fabs(center.y);
- }
- radius.z = 0.0f;
- }
- switch(drop)
- {
- case 1:
- if(e.type != ET_LIGHT && e.type != ET_SPOTLIGHT)
- dropenttofloor(&e);
- break;
- case 2:
- case 3:
- int cx = 0, cy = 0;
- if(sel.cxs == 1 && sel.cys == 1)
- {
- cx = (sel.cx ? 1 : -1) * sel.grid / 2;
- cy = (sel.cy ? 1 : -1) * sel.grid / 2;
- }
- e.o = vec(sel.o);
- int d = dimension(sel.orient), dc = dimcoord(sel.orient);
- e.o[R[d]] += sel.grid / 2 + cx;
- e.o[C[d]] += sel.grid / 2 + cy;
- if(!dc)
- e.o[D[d]] -= radius[D[d]];
- else
- e.o[D[d]] += sel.grid + radius[D[d]];
-
- if(drop == 3)
- dropenttofloor(&e);
- break;
- }
- return true;
+ vec radius(4.0f, 4.0f, 4.0f);
+ if(drop<0) drop = entdrop;
+ if(e.type == ET_MAPMODEL)
+ {
+ model *m = loadmapmodel(e.attr2);
+ if(m)
+ {
+ vec center;
+ mmboundbox(e, m, center, radius);
+ radius.x += fabs(center.x);
+ radius.y += fabs(center.y);
+ }
+ radius.z = 0.0f;
+ }
+ switch(drop)
+ {
+ case 1:
+ if(e.type != ET_LIGHT && e.type != ET_SPOTLIGHT)
+ dropenttofloor(&e);
+ break;
+ case 2:
+ case 3:
+ int cx = 0, cy = 0;
+ if(sel.cxs == 1 && sel.cys == 1)
+ {
+ cx = (sel.cx ? 1 : -1) * sel.grid / 2;
+ cy = (sel.cy ? 1 : -1) * sel.grid / 2;
+ }
+ e.o = vec(sel.o);
+ int d = dimension(sel.orient), dc = dimcoord(sel.orient);
+ e.o[R[d]] += sel.grid / 2 + cx;
+ e.o[C[d]] += sel.grid / 2 + cy;
+ if(!dc)
+ e.o[D[d]] -= radius[D[d]];
+ else
+ e.o[D[d]] += sel.grid + radius[D[d]];
+
+ if(drop == 3)
+ dropenttofloor(&e);
+ break;
+ }
+ return true;
}
void dropent()
{
- if(noentedit()) return;
- groupedit(dropentity(e));
+ if(noentedit()) return;
+ groupedit(dropentity(e));
}
void attachent()
{
- if(noentedit()) return;
- groupedit(attachentity(e));
+ if(noentedit()) return;
+ groupedit(attachentity(e));
}
COMMAND(attachent, "");
@@ -929,63 +929,63 @@ static int keepents = 0;
extentity *newentity(bool local, const vec &o, int type, int v1, int v2, int v3, int v4, int v5, int &idx)
{
- vector<extentity *> &ents = entities::getents();
- if(local)
- {
- idx = -1;
- for(int i = keepents; i < ents.length(); i++) if(ents[i]->type == ET_EMPTY) { idx = i; break; }
- if(idx < 0 && ents.length() >= MAXENTS) { conoutf(CON_ERROR, "too many entities"); return NULL; }
- }
- else while(ents.length() < idx) ents.add(entities::newentity())->type = ET_EMPTY;
- extentity &e = *entities::newentity();
- e.o = o;
- e.attr1 = v1;
- e.attr2 = v2;
- e.attr3 = v3;
- e.attr4 = v4;
- e.attr5 = v5;
- e.type = type;
- e.reserved = 0;
- e.light.color = vec(1, 1, 1);
- e.light.dir = vec(0, 0, 1);
- if(local)
- {
- if(entcamdir) switch(type)
- {
- case ET_MAPMODEL:
- case ET_PLAYERSTART:
- e.attr5 = e.attr4;
- e.attr4 = e.attr3;
- e.attr3 = e.attr2;
- e.attr2 = e.attr1;
- e.attr1 = (int)camera1->yaw;
- break;
- }
- entities::fixentity(e);
- }
- if(ents.inrange(idx)) { entities::deleteentity(ents[idx]); ents[idx] = &e; }
- else { idx = ents.length(); ents.add(&e); }
- return &e;
+ vector<extentity *> &ents = entities::getents();
+ if(local)
+ {
+ idx = -1;
+ for(int i = keepents; i < ents.length(); i++) if(ents[i]->type == ET_EMPTY) { idx = i; break; }
+ if(idx < 0 && ents.length() >= MAXENTS) { conoutf(CON_ERROR, "too many entities"); return NULL; }
+ }
+ else while(ents.length() < idx) ents.add(entities::newentity())->type = ET_EMPTY;
+ extentity &e = *entities::newentity();
+ e.o = o;
+ e.attr1 = v1;
+ e.attr2 = v2;
+ e.attr3 = v3;
+ e.attr4 = v4;
+ e.attr5 = v5;
+ e.type = type;
+ e.reserved = 0;
+ e.light.color = vec(1, 1, 1);
+ e.light.dir = vec(0, 0, 1);
+ if(local)
+ {
+ if(entcamdir) switch(type)
+ {
+ case ET_MAPMODEL:
+ case ET_PLAYERSTART:
+ e.attr5 = e.attr4;
+ e.attr4 = e.attr3;
+ e.attr3 = e.attr2;
+ e.attr2 = e.attr1;
+ e.attr1 = (int)camera1->yaw;
+ break;
+ }
+ entities::fixentity(e);
+ }
+ if(ents.inrange(idx)) { entities::deleteentity(ents[idx]); ents[idx] = &e; }
+ else { idx = ents.length(); ents.add(&e); }
+ return &e;
}
void newentity(int type, int a1, int a2, int a3, int a4, int a5)
{
- int idx;
- extentity *t = newentity(true, player->o, type, a1, a2, a3, a4, a5, idx);
- if(!t) return;
- dropentity(*t);
- t->type = ET_EMPTY;
- enttoggle(idx);
- makeundoent();
- entedit(idx, e.type = type);
+ int idx;
+ extentity *t = newentity(true, player->o, type, a1, a2, a3, a4, a5, idx);
+ if(!t) return;
+ dropentity(*t);
+ t->type = ET_EMPTY;
+ enttoggle(idx);
+ makeundoent();
+ entedit(idx, e.type = type);
}
void newent(char *what, int *a1, int *a2, int *a3, int *a4, int *a5)
{
- if(noentedit()) return;
- int type = findtype(what);
- if(type != ET_EMPTY)
- newentity(type, *a1, *a2, *a3, *a4, *a5);
+ if(noentedit()) return;
+ int type = findtype(what);
+ if(type != ET_EMPTY)
+ newentity(type, *a1, *a2, *a3, *a4, *a5);
}
int entcopygrid;
@@ -993,33 +993,33 @@ vector<entity> entcopybuf;
void entcopy()
{
- if(noentedit()) return;
- entcopygrid = sel.grid;
- entcopybuf.shrink(0);
- loopv(entgroup)
- entfocus(entgroup[i], entcopybuf.add(e).o.sub(vec(sel.o)));
+ if(noentedit()) return;
+ entcopygrid = sel.grid;
+ entcopybuf.shrink(0);
+ loopv(entgroup)
+ entfocus(entgroup[i], entcopybuf.add(e).o.sub(vec(sel.o)));
}
void entpaste()
{
- if(noentedit()) return;
- if(entcopybuf.length()==0) return;
- entcancel();
- float m = float(sel.grid)/float(entcopygrid);
- loopv(entcopybuf)
- {
- entity &c = entcopybuf[i];
- vec o(c.o);
- o.mul(m).add(vec(sel.o));
- int idx;
- extentity *e = newentity(true, o, ET_EMPTY, c.attr1, c.attr2, c.attr3, c.attr4, c.attr5, idx);
- if(!e) continue;
- entadd(idx);
- keepents = max(keepents, idx+1);
- }
- keepents = 0;
- int j = 0;
- groupeditundo(e.type = entcopybuf[j++].type;);
+ if(noentedit()) return;
+ if(entcopybuf.length()==0) return;
+ entcancel();
+ float m = float(sel.grid)/float(entcopygrid);
+ loopv(entcopybuf)
+ {
+ entity &c = entcopybuf[i];
+ vec o(c.o);
+ o.mul(m).add(vec(sel.o));
+ int idx;
+ extentity *e = newentity(true, o, ET_EMPTY, c.attr1, c.attr2, c.attr3, c.attr4, c.attr5, idx);
+ if(!e) continue;
+ entadd(idx);
+ keepents = max(keepents, idx+1);
+ }
+ keepents = 0;
+ int j = 0;
+ groupeditundo(e.type = entcopybuf[j++].type;);
}
COMMAND(newent, "siiiii");
@@ -1030,101 +1030,101 @@ COMMAND(entpaste, "");
void entset(char *what, int *a1, int *a2, int *a3, int *a4, int *a5)
{
- if(noentedit()) return;
- int type = findtype(what);
- if(type != ET_EMPTY)
- groupedit(e.type=type;
- e.attr1=*a1;
- e.attr2=*a2;
- e.attr3=*a3;
- e.attr4=*a4;
- e.attr5=*a5);
+ if(noentedit()) return;
+ int type = findtype(what);
+ if(type != ET_EMPTY)
+ groupedit(e.type=type;
+ e.attr1=*a1;
+ e.attr2=*a2;
+ e.attr3=*a3;
+ e.attr4=*a4;
+ e.attr5=*a5);
}
void printent(extentity &e, char *buf, int len)
{
- switch(e.type)
- {
- case ET_PARTICLES:
- if(printparticles(e, buf, len)) return;
- break;
+ switch(e.type)
+ {
+ case ET_PARTICLES:
+ if(printparticles(e, buf, len)) return;
+ break;
- default:
- if(e.type >= ET_GAMESPECIFIC && entities::printent(e, buf, len)) return;
- break;
- }
- nformatstring(buf, len, "%s %d %d %d %d %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5);
+ default:
+ if(e.type >= ET_GAMESPECIFIC && entities::printent(e, buf, len)) return;
+ break;
+ }
+ nformatstring(buf, len, "%s %d %d %d %d %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5);
}
void nearestent()
{
- if(noentedit()) return;
- int closest = -1;
- float closedist = 1e16f;
- vector<extentity *> &ents = entities::getents();
- loopv(ents)
- {
- extentity &e = *ents[i];
- if(e.type == ET_EMPTY) continue;
- float dist = e.o.dist(player->o);
- if(dist < closedist)
- {
- closest = i;
- closedist = dist;
- }
- }
- if(closest >= 0 && entgroup.find(closest) < 0) entadd(closest);
+ if(noentedit()) return;
+ int closest = -1;
+ float closedist = 1e16f;
+ vector<extentity *> &ents = entities::getents();
+ loopv(ents)
+ {
+ extentity &e = *ents[i];
+ if(e.type == ET_EMPTY) continue;
+ float dist = e.o.dist(player->o);
+ if(dist < closedist)
+ {
+ closest = i;
+ closedist = dist;
+ }
+ }
+ if(closest >= 0 && entgroup.find(closest) < 0) entadd(closest);
}
ICOMMAND(enthavesel,"", (), addimplicit(intret(entgroup.length())));
ICOMMAND(entselect, "e", (uint *body), if(!noentedit()) addgroup(e.type != ET_EMPTY && entgroup.find(n)<0 && executebool(body)));
ICOMMAND(entloop, "e", (uint *body), if(!noentedit()) addimplicit(groupeditloop(((void)e, execute(body)))));
-ICOMMAND(insel, "", (), entfocus(efocus, intret(pointinsel(sel, e.o))));
-ICOMMAND(entget, "", (), entfocus(efocus, string s; printent(e, s, sizeof(s)); result(s)));
+ICOMMAND(insel, "", (), entfocus(efocus, intret(pointinsel(sel, e.o))));
+ICOMMAND(entget, "", (), entfocus(efocus, string s; printent(e, s, sizeof(s)); result(s)));
ICOMMAND(entindex, "", (), intret(efocus));
COMMAND(entset, "siiiii");
COMMAND(nearestent, "");
void enttype(char *type, int *numargs)
{
- if(*numargs >= 1)
- {
- int typeidx = findtype(type);
- if(typeidx != ET_EMPTY) groupedit(e.type = typeidx);
- }
- else entfocus(efocus,
- {
- result(entities::entname(e.type));
- })
+ if(*numargs >= 1)
+ {
+ int typeidx = findtype(type);
+ if(typeidx != ET_EMPTY) groupedit(e.type = typeidx);
+ }
+ else entfocus(efocus,
+ {
+ result(entities::entname(e.type));
+ })
}
void entattr(int *attr, int *val, int *numargs)
{
- if(*numargs >= 2)
- {
- if(*attr >= 0 && *attr <= 4)
- groupedit(
- switch(*attr)
- {
- case 0: e.attr1 = *val; break;
- case 1: e.attr2 = *val; break;
- case 2: e.attr3 = *val; break;
- case 3: e.attr4 = *val; break;
- case 4: e.attr5 = *val; break;
- }
- );
- }
- else entfocus(efocus,
- {
- switch(*attr)
- {
- case 0: intret(e.attr1); break;
- case 1: intret(e.attr2); break;
- case 2: intret(e.attr3); break;
- case 3: intret(e.attr4); break;
- case 4: intret(e.attr5); break;
- }
- });
+ if(*numargs >= 2)
+ {
+ if(*attr >= 0 && *attr <= 4)
+ groupedit(
+ switch(*attr)
+ {
+ case 0: e.attr1 = *val; break;
+ case 1: e.attr2 = *val; break;
+ case 2: e.attr3 = *val; break;
+ case 3: e.attr4 = *val; break;
+ case 4: e.attr5 = *val; break;
+ }
+ );
+ }
+ else entfocus(efocus,
+ {
+ switch(*attr)
+ {
+ case 0: intret(e.attr1); break;
+ case 1: intret(e.attr2); break;
+ case 2: intret(e.attr3); break;
+ case 3: intret(e.attr4); break;
+ case 4: intret(e.attr5); break;
+ }
+ });
}
COMMAND(enttype, "sN");
@@ -1132,21 +1132,21 @@ COMMAND(entattr, "iiN");
int findentity(int type, int index, int attr1, int attr2)
{
- const vector<extentity *> &ents = entities::getents();
- if(index > ents.length()) index = ents.length();
- else for(int i = index; i<ents.length(); i++)
- {
- extentity &e = *ents[i];
- if(e.type==type && (attr1<0 || e.attr1==attr1) && (attr2<0 || e.attr2==attr2))
- return i;
- }
- loopj(index)
- {
- extentity &e = *ents[j];
- if(e.type==type && (attr1<0 || e.attr1==attr1) && (attr2<0 || e.attr2==attr2))
- return j;
- }
- return -1;
+ const vector<extentity *> &ents = entities::getents();
+ if(index > ents.length()) index = ents.length();
+ else for(int i = index; i<ents.length(); i++)
+ {
+ extentity &e = *ents[i];
+ if(e.type==type && (attr1<0 || e.attr1==attr1) && (attr2<0 || e.attr2==attr2))
+ return i;
+ }
+ loopj(index)
+ {
+ extentity &e = *ents[j];
+ if(e.type==type && (attr1<0 || e.attr1==attr1) && (attr2<0 || e.attr2==attr2))
+ return j;
+ }
+ return -1;
}
struct spawninfo { const extentity *e; float weight; };
@@ -1155,18 +1155,18 @@ struct spawninfo { const extentity *e; float weight; };
// which serves as a measure of its desirability for a spawning player.
float gatherspawninfos(dynent *d, int tag, vector<spawninfo> &spawninfos)
{
- const vector<extentity *> &ents = entities::getents();
- float total = 0.0f;
- loopv(ents)
- {
- const extentity &e = *ents[i];
- if(e.type != ET_PLAYERSTART || e.attr2 != tag) continue;
- spawninfo &s = spawninfos.add();
- s.e = &e;
- s.weight = game::ratespawn(d, e);
- total += s.weight;
- }
- return total;
+ const vector<extentity *> &ents = entities::getents();
+ float total = 0.0f;
+ loopv(ents)
+ {
+ const extentity &e = *ents[i];
+ if(e.type != ET_PLAYERSTART || e.attr2 != tag) continue;
+ spawninfo &s = spawninfos.add();
+ s.e = &e;
+ s.weight = game::ratespawn(d, e);
+ total += s.weight;
+ }
+ return total;
}
// Randomly picks a weighted spawn from the provided vector and removes it.
@@ -1174,179 +1174,178 @@ float gatherspawninfos(dynent *d, int tag, vector<spawninfo> &spawninfos)
// If all weights are zero, the index is picked uniformly.
static const extentity *poprandomspawn(vector<spawninfo> &spawninfos, float &total)
{
- if(spawninfos.empty()) return NULL;
- int index = 0;
- if(total > 0.0f)
- {
- float x = rndscale(total);
- do x -= spawninfos[index].weight; while(x > 0 && ++index < spawninfos.length()-1);
- }
- else index = rnd(spawninfos.length());
- spawninfo s = spawninfos.removeunordered(index);
- total -= s.weight;
- return s.e;
+ if(spawninfos.empty()) return NULL;
+ int index = 0;
+ if(total > 0.0f)
+ {
+ float x = rndscale(total);
+ do x -= spawninfos[index].weight; while(x > 0 && ++index < spawninfos.length()-1);
+ }
+ else index = rnd(spawninfos.length());
+ spawninfo s = spawninfos.removeunordered(index);
+ total -= s.weight;
+ return s.e;
}
static inline bool tryspawn(dynent *d, const extentity &e)
{
- d->o = e.o;
- d->yaw = e.attr1;
- return entinmap(d, true);
+ d->o = e.o;
+ d->yaw = e.attr1;
+ return entinmap(d, true);
}
void findplayerspawn(dynent *d, int forceent, int tag)
{
- const vector<extentity *> &ents = entities::getents();
- d->pitch = 0;
- d->roll = 0;
- if(ents.inrange(forceent) && tryspawn(d, *ents[forceent])) return;
- vector<spawninfo> spawninfos;
- float total = gatherspawninfos(d, tag, spawninfos);
- while(const extentity *e = poprandomspawn(spawninfos, total)) if(tryspawn(d, *e)) return;
- d->o = vec(0.5f * worldsize).addz(1);
- d->yaw = 0;
- entinmap(d);
+ const vector<extentity *> &ents = entities::getents();
+ d->pitch = 0;
+ d->roll = 0;
+ if(ents.inrange(forceent) && tryspawn(d, *ents[forceent])) return;
+ vector<spawninfo> spawninfos;
+ float total = gatherspawninfos(d, tag, spawninfos);
+ while(const extentity *e = poprandomspawn(spawninfos, total)) if(tryspawn(d, *e)) return;
+ d->o = vec(0.5f * worldsize).addz(1);
+ d->yaw = 0;
+ entinmap(d);
}
void splitocta(cube *c, int size)
{
- if(size <= 0x1000) return;
- loopi(8)
- {
- if(!c[i].children) c[i].children = newcubes(isempty(c[i]) ? F_EMPTY : F_SOLID);
- splitocta(c[i].children, size>>1);
- }
+ if(size <= 0x1000) return;
+ loopi(8)
+ {
+ if(!c[i].children) c[i].children = newcubes(isempty(c[i]) ? F_EMPTY : F_SOLID);
+ splitocta(c[i].children, size>>1);
+ }
}
void resetmap()
{
- clearoverrides();
- clearmapsounds();
- cleanreflections();
- resetblendmap();
- resetlightmaps();
- clearslots();
- clearparticles();
- cleardecals();
- cleardamagescreen();
- clearsleep();
- cancelsel();
- pruneundos();
- clearmapcrc();
+ clearoverrides();
+ clearmapsounds();
+ cleanreflections();
+ resetblendmap();
+ resetlightmaps();
+ clearslots();
+ clearparticles();
+ cleardecals();
+ clearsleep();
+ cancelsel();
+ pruneundos();
+ clearmapcrc();
- entities::clearents();
- outsideents.setsize(0);
+ entities::clearents();
+ outsideents.setsize(0);
}
void startmap(const char *name)
{
- game::startmap(name);
+ game::startmap(name);
}
-bool emptymap(int scale, bool force, const char *mname, bool usecfg) // main empty world creation routine
+bool emptymap(int scale, bool force, const char *mname, bool usecfg) // main empty world creation routine
{
- if(!force && !editmode)
- {
- conoutf(CON_ERROR, "newmap only allowed in edit mode");
- return false;
- }
+ if(!force && !editmode)
+ {
+ conoutf(CON_ERROR, "newmap only allowed in edit mode");
+ return false;
+ }
- resetmap();
+ resetmap();
- setvar("mapscale", scale<10 ? 10 : (scale>16 ? 16 : scale), true, false);
- setvar("mapsize", 1<<worldscale, true, false);
+ setvar("mapscale", scale<10 ? 10 : (scale>16 ? 16 : scale), true, false);
+ setvar("mapsize", 1<<worldscale, true, false);
- texmru.shrink(0);
- freeocta(worldroot);
- worldroot = newcubes(F_EMPTY);
- loopi(4) solidfaces(worldroot[i]);
+ texmru.shrink(0);
+ freeocta(worldroot);
+ worldroot = newcubes(F_EMPTY);
+ loopi(4) solidfaces(worldroot[i]);
- if(worldsize > 0x1000) splitocta(worldroot, worldsize>>1);
+ if(worldsize > 0x1000) splitocta(worldroot, worldsize>>1);
- clearmainmenu();
+ clearmainmenu();
- if(usecfg)
- {
- identflags |= IDF_OVERRIDDEN;
- execfile("data/default_map.cfg", false);
- identflags &= ~IDF_OVERRIDDEN;
- }
+ if(usecfg)
+ {
+ identflags |= IDF_OVERRIDDEN;
+ execfile("data/default_map.cfg", false);
+ identflags &= ~IDF_OVERRIDDEN;
+ }
- initlights();
- allchanged(true);
+ initlights();
+ allchanged(true);
- startmap(mname);
+ startmap(mname);
- return true;
+ return true;
}
bool enlargemap(bool force)
{
- if(!force && !editmode)
- {
- conoutf(CON_ERROR, "mapenlarge only allowed in edit mode");
- return false;
- }
- if(worldsize >= 1<<16) return false;
+ if(!force && !editmode)
+ {
+ conoutf(CON_ERROR, "mapenlarge only allowed in edit mode");
+ return false;
+ }
+ if(worldsize >= 1<<16) return false;
- while(outsideents.length()) removeentity(outsideents.pop());
+ while(outsideents.length()) removeentity(outsideents.pop());
- worldscale++;
- worldsize *= 2;
- cube *c = newcubes(F_EMPTY);
- c[0].children = worldroot;
- loopi(3) solidfaces(c[i+1]);
- worldroot = c;
+ worldscale++;
+ worldsize *= 2;
+ cube *c = newcubes(F_EMPTY);
+ c[0].children = worldroot;
+ loopi(3) solidfaces(c[i+1]);
+ worldroot = c;
- if(worldsize > 0x1000) splitocta(worldroot, worldsize>>1);
+ if(worldsize > 0x1000) splitocta(worldroot, worldsize>>1);
- enlargeblendmap();
+ enlargeblendmap();
- allchanged();
+ allchanged();
- return true;
+ return true;
}
static bool isallempty(cube &c)
{
- if(!c.children) return isempty(c);
- loopi(8) if(!isallempty(c.children[i])) return false;
- return true;
+ if(!c.children) return isempty(c);
+ loopi(8) if(!isallempty(c.children[i])) return false;
+ return true;
}
void shrinkmap()
{
- extern int nompedit;
- if(noedit(true) || (nompedit && multiplayer())) return;
- if(worldsize <= 1<<10) return;
+ extern int nompedit;
+ if(noedit(true) || (nompedit && multiplayer())) return;
+ if(worldsize <= 1<<10) return;
- int octant = -1;
- loopi(8) if(!isallempty(worldroot[i]))
- {
- if(octant >= 0) return;
- octant = i;
- }
- if(octant < 0) return;
+ int octant = -1;
+ loopi(8) if(!isallempty(worldroot[i]))
+ {
+ if(octant >= 0) return;
+ octant = i;
+ }
+ if(octant < 0) return;
- while(outsideents.length()) removeentity(outsideents.pop());
+ while(outsideents.length()) removeentity(outsideents.pop());
- if(!worldroot[octant].children) subdividecube(worldroot[octant], false, false);
- cube *root = worldroot[octant].children;
- worldroot[octant].children = NULL;
- freeocta(worldroot);
- worldroot = root;
- worldscale--;
- worldsize /= 2;
+ if(!worldroot[octant].children) subdividecube(worldroot[octant], false, false);
+ cube *root = worldroot[octant].children;
+ worldroot[octant].children = NULL;
+ freeocta(worldroot);
+ worldroot = root;
+ worldscale--;
+ worldsize /= 2;
- ivec offset(octant, ivec(0, 0, 0), worldsize);
- vector<extentity *> &ents = entities::getents();
- loopv(ents) ents[i]->o.sub(vec(offset));
+ ivec offset(octant, ivec(0, 0, 0), worldsize);
+ vector<extentity *> &ents = entities::getents();
+ loopv(ents) ents[i]->o.sub(vec(offset));
- shrinkblendmap(octant);
+ shrinkblendmap(octant);
- allchanged();
+ allchanged();
- conoutf("shrunk map to size %d", worldscale);
+ conoutf("shrunk map to size %d", worldscale);
}
void newmap(int *i) { bool force = !isconnected(); if(force) game::forceedit(""); if(emptymap(*i, force, NULL)) game::newmap(max(*i, 0)); }
@@ -1357,35 +1356,35 @@ COMMAND(shrinkmap, "");
void mapname()
{
- result(game::getclientmap());
+ result(game::getclientmap());
}
COMMAND(mapname, "");
void mpeditent(int i, const vec &o, int type, int attr1, int attr2, int attr3, int attr4, int attr5, bool local)
{
- if(i < 0 || i >= MAXENTS) return;
- vector<extentity *> &ents = entities::getents();
- if(ents.length()<=i)
- {
- extentity *e = newentity(local, o, type, attr1, attr2, attr3, attr4, attr5, i);
- if(!e) return;
- addentity(i);
- attachentity(*e);
- }
- else
- {
- extentity &e = *ents[i];
- removeentity(i);
- int oldtype = e.type;
- if(oldtype!=type) detachentity(e);
- e.type = type;
- e.o = o;
- e.attr1 = attr1; e.attr2 = attr2; e.attr3 = attr3; e.attr4 = attr4; e.attr5 = attr5;
- addentity(i);
- if(oldtype!=type) attachentity(e);
- }
- entities::editent(i, local);
+ if(i < 0 || i >= MAXENTS) return;
+ vector<extentity *> &ents = entities::getents();
+ if(ents.length()<=i)
+ {
+ extentity *e = newentity(local, o, type, attr1, attr2, attr3, attr4, attr5, i);
+ if(!e) return;
+ addentity(i);
+ attachentity(*e);
+ }
+ else
+ {
+ extentity &e = *ents[i];
+ removeentity(i);
+ int oldtype = e.type;
+ if(oldtype!=type) detachentity(e);
+ e.type = type;
+ e.o = o;
+ e.attr1 = attr1; e.attr2 = attr2; e.attr3 = attr3; e.attr4 = attr4; e.attr5 = attr5;
+ addentity(i);
+ if(oldtype!=type) attachentity(e);
+ }
+ entities::editent(i, local);
}
int getworldsize() { return worldsize; }
diff --git a/src/engine/world.h b/src/engine/world.h
index 9c8c502..ef4a5e6 100644
--- a/src/engine/world.h
+++ b/src/engine/world.h
@@ -1,46 +1,46 @@
-enum // hardcoded texture numbers
+enum // hardcoded texture numbers
{
- DEFAULT_SKY = 0,
- DEFAULT_GEOM
+ DEFAULT_SKY = 0,
+ DEFAULT_GEOM
};
-#define MAPVERSION 33 // bump if map format changes, see worldio.cpp
+#define MAPVERSION 34 // bump if map format changes, see worldio.cpp
struct octaheader
{
- char magic[4]; // "OCTA"
- int version; // any >8bit quantity is little endian
- int headersize; // sizeof(header)
- int worldsize;
- int numents;
- int numpvs;
- int lightmaps;
- int blendmap;
- int numvars;
- int numvslots;
+ char magic[4]; // "OCTA"
+ int version; // any >8bit quantity is little endian
+ int headersize; // sizeof(header)
+ int worldsize;
+ int numents;
+ int numpvs;
+ int lightmaps;
+ int blendmap;
+ int numvars;
+ int numvslots;
};
-struct compatheader // map file format header
+struct compatheader // map file format header
{
- char magic[4]; // "OCTA"
- int version; // any >8bit quantity is little endian
- int headersize; // sizeof(header)
- int worldsize;
- int numents;
- int numpvs;
- int lightmaps;
- int lightprecision, lighterror, lightlod;
- uchar ambient;
- uchar watercolour[3];
- uchar blendmap;
- uchar lerpangle, lerpsubdiv, lerpsubdivsize;
- uchar bumperror;
- uchar skylight[3];
- uchar lavacolour[3];
- uchar waterfallcolour[3];
- uchar reserved[10];
- char maptitle[128];
+ char magic[4]; // "OCTA"
+ int version; // any >8bit quantity is little endian
+ int headersize; // sizeof(header)
+ int worldsize;
+ int numents;
+ int numpvs;
+ int lightmaps;
+ int lightprecision, lighterror, lightlod;
+ uchar ambient;
+ uchar watercolour[3];
+ uchar blendmap;
+ uchar lerpangle, lerpsubdiv, lerpsubdivsize;
+ uchar bumperror;
+ uchar skylight[3];
+ uchar lavacolour[3];
+ uchar waterfallcolour[3];
+ uchar reserved[10];
+ char maptitle[128];
};
#define WATER_AMPLITUDE 0.4f
@@ -48,9 +48,9 @@ struct compatheader // map file format header
enum
{
- MATSURF_NOT_VISIBLE = 0,
- MATSURF_VISIBLE,
- MATSURF_EDIT_ONLY
+ MATSURF_NOT_VISIBLE = 0,
+ MATSURF_VISIBLE,
+ MATSURF_EDIT_ONLY
};
#define TEX_SCALE 8.0f
diff --git a/src/engine/worldio.cpp b/src/engine/worldio.cpp
index d1fd52f..34823fa 100644
--- a/src/engine/worldio.cpp
+++ b/src/engine/worldio.cpp
@@ -4,169 +4,169 @@
void validmapname(char *dst, const char *src, const char *prefix = NULL, const char *alt = "untitled", size_t maxlen = 100)
{
- if(prefix) while(*prefix) *dst++ = *prefix++;
- const char *start = dst;
- if(src) loopi(maxlen)
- {
- char c = *src++;
- if(iscubealnum(c) || c == '_' || c == '-' || c == '/' || c == '\\') *dst++ = c;
- else break;
- }
- if(dst > start) *dst = '\0';
- else if(dst != alt) copystring(dst, alt, maxlen);
+ if(prefix) while(*prefix) *dst++ = *prefix++;
+ const char *start = dst;
+ if(src) loopi(maxlen)
+ {
+ char c = *src++;
+ if(iscubealnum(c) || c == '_' || c == '-' || c == '/' || c == '\\') *dst++ = c;
+ else break;
+ }
+ if(dst > start) *dst = '\0';
+ else if(dst != alt) copystring(dst, alt, maxlen);
}
void fixmapname(char *name)
{
- validmapname(name, name, NULL, "");
+ validmapname(name, name, NULL, "");
}
void getmapfilenames(const char *fname, const char *cname, char *pakname, char *mapname, char *cfgname)
{
- if(!cname) cname = fname;
- string name;
- validmapname(name, cname);
- char *slash = strpbrk(name, "/\\");
- if(slash)
- {
- copystring(pakname, name, slash-name+1);
- copystring(cfgname, slash+1, MAXSTRLEN);
- }
- else
- {
- copystring(pakname, "maps", MAXSTRLEN);
- copystring(cfgname, name, MAXSTRLEN);
- }
- validmapname(mapname, fname, strpbrk(fname, "/\\") ? NULL : "maps/");
+ if(!cname) cname = fname;
+ string name;
+ validmapname(name, cname);
+ char *slash = strpbrk(name, "/\\");
+ if(slash)
+ {
+ copystring(pakname, name, slash-name+1);
+ copystring(cfgname, slash+1, MAXSTRLEN);
+ }
+ else
+ {
+ copystring(pakname, "maps", MAXSTRLEN);
+ copystring(cfgname, name, MAXSTRLEN);
+ }
+ validmapname(mapname, fname, strpbrk(fname, "/\\") ? NULL : "maps/");
}
static void fixent(entity &e, int version)
{
- if(version <= 10 && e.type >= 7) e.type++;
- if(version <= 12 && e.type >= 8) e.type++;
- if(version <= 14 && e.type >= ET_MAPMODEL && e.type <= 16)
- {
- if(e.type == 16) e.type = ET_MAPMODEL;
- else e.type++;
- }
- if(version <= 20 && e.type >= ET_ENVMAP) e.type++;
- if(version <= 21 && e.type >= ET_PARTICLES) e.type++;
- if(version <= 22 && e.type >= ET_SOUND) e.type++;
- if(version <= 23 && e.type >= ET_SPOTLIGHT) e.type++;
- if(version <= 30 && (e.type == ET_MAPMODEL || e.type == ET_PLAYERSTART)) e.attr1 = (int(e.attr1)+180)%360;
- if(version <= 31 && e.type == ET_MAPMODEL) { int yaw = (int(e.attr1)%360 + 360)%360 + 7; e.attr1 = yaw - yaw%15; }
+ if(version <= 10 && e.type >= 7) e.type++;
+ if(version <= 12 && e.type >= 8) e.type++;
+ if(version <= 14 && e.type >= ET_MAPMODEL && e.type <= 16)
+ {
+ if(e.type == 16) e.type = ET_MAPMODEL;
+ else e.type++;
+ }
+ if(version <= 20 && e.type >= ET_ENVMAP) e.type++;
+ if(version <= 21 && e.type >= ET_PARTICLES) e.type++;
+ if(version <= 22 && e.type >= ET_SOUND) e.type++;
+ if(version <= 23 && e.type >= ET_SPOTLIGHT) e.type++;
+ if(version <= 30 && (e.type == ET_MAPMODEL || e.type == ET_PLAYERSTART)) e.attr1 = (int(e.attr1)+180)%360;
+ if(version <= 31 && e.type == ET_MAPMODEL) { int yaw = (int(e.attr1)%360 + 360)%360 + 7; e.attr1 = yaw - yaw%15; }
}
bool loadents(const char *fname, vector<entity> &ents, uint *crc)
{
- string pakname, mapname, mcfgname, ogzname;
- getmapfilenames(fname, NULL, pakname, mapname, mcfgname);
- formatstring(ogzname, "packages/%s.ogz", mapname);
- path(ogzname);
- stream *f = opengzfile(ogzname, "rb");
- if(!f) return false;
- octaheader hdr;
- if(f->read(&hdr, 7*sizeof(int)) != 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
- lilswap(&hdr.version, 6);
- if(memcmp(hdr.magic, "OCTA", 4) || hdr.worldsize <= 0|| hdr.numents < 0) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
- if(hdr.version>MAPVERSION) { conoutf(CON_ERROR, "map %s requires a newer version of Cube 2: Sauerbraten", ogzname); delete f; return false; }
- compatheader chdr;
- if(hdr.version <= 28)
- {
- if(f->read(&chdr.lightprecision, sizeof(chdr) - 7*sizeof(int)) != sizeof(chdr) - 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
- }
- else
- {
- int extra = 0;
- if(hdr.version <= 29) extra++;
- if(f->read(&hdr.blendmap, sizeof(hdr) - (7+extra)*sizeof(int)) != sizeof(hdr) - (7+extra)*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
- }
-
- if(hdr.version <= 28)
- {
- lilswap(&chdr.lightprecision, 3);
- hdr.blendmap = chdr.blendmap;
- hdr.numvars = 0;
- hdr.numvslots = 0;
- }
- else
- {
- lilswap(&hdr.blendmap, 2);
- if(hdr.version <= 29) hdr.numvslots = 0;
- else lilswap(&hdr.numvslots, 1);
- }
-
- loopi(hdr.numvars)
- {
- int type = f->getchar(), ilen = f->getlil<ushort>();
- f->seek(ilen, SEEK_CUR);
- switch(type)
- {
- case ID_VAR: f->getlil<int>(); break;
- case ID_FVAR: f->getlil<float>(); break;
- case ID_SVAR: { int slen = f->getlil<ushort>(); f->seek(slen, SEEK_CUR); break; }
- }
- }
-
- string gametype;
- copystring(gametype, "fps");
- bool samegame = true;
- int eif = 0;
- if(hdr.version>=16)
- {
- int len = f->getchar();
- f->read(gametype, len+1);
- }
- if(strcmp(gametype, game::gameident()))
- {
- samegame = false;
- conoutf(CON_WARN, "WARNING: loading map from %s game, ignoring entities except for lights/mapmodels", gametype);
- }
- if(hdr.version>=16)
- {
- eif = f->getlil<ushort>();
- int extrasize = f->getlil<ushort>();
- f->seek(extrasize, SEEK_CUR);
- }
-
- if(hdr.version<14)
- {
- f->seek(256, SEEK_CUR);
- }
- else
- {
- ushort nummru = f->getlil<ushort>();
- f->seek(nummru*sizeof(ushort), SEEK_CUR);
- }
-
- loopi(min(hdr.numents, MAXENTS))
- {
- entity &e = ents.add();
- f->read(&e, sizeof(entity));
- lilswap(&e.o.x, 3);
- lilswap(&e.attr1, 5);
- fixent(e, hdr.version);
- if(eif > 0) f->seek(eif, SEEK_CUR);
- if(samegame)
- {
- entities::readent(e, NULL, hdr.version);
- }
- else if(e.type>=ET_GAMESPECIFIC || hdr.version<=14)
- {
- ents.pop();
- continue;
- }
- }
-
- if(crc)
- {
- f->seek(0, SEEK_END);
- *crc = f->getcrc();
- }
-
- delete f;
-
- return true;
+ string pakname, mapname, mcfgname, ogzname;
+ getmapfilenames(fname, NULL, pakname, mapname, mcfgname);
+ formatstring(ogzname, "packages/%s.ogz", mapname);
+ path(ogzname);
+ stream *f = opengzfile(ogzname, "rb");
+ if(!f) return false;
+ octaheader hdr;
+ if(f->read(&hdr, 7*sizeof(int)) != 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
+ lilswap(&hdr.version, 6);
+ if(memcmp(hdr.magic, "OCTA", 4) || hdr.worldsize <= 0|| hdr.numents < 0) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
+ if(hdr.version>MAPVERSION) { conoutf(CON_ERROR, "map %s requires a newer version of Cube 2: Sauerbraten", ogzname); delete f; return false; }
+ compatheader chdr;
+ if(hdr.version <= 28)
+ {
+ if(f->read(&chdr.lightprecision, sizeof(chdr) - 7*sizeof(int)) != sizeof(chdr) - 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
+ }
+ else
+ {
+ int extra = 0;
+ if(hdr.version <= 29) extra++;
+ if(f->read(&hdr.blendmap, sizeof(hdr) - (7+extra)*sizeof(int)) != sizeof(hdr) - (7+extra)*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
+ }
+
+ if(hdr.version <= 28)
+ {
+ lilswap(&chdr.lightprecision, 3);
+ hdr.blendmap = chdr.blendmap;
+ hdr.numvars = 0;
+ hdr.numvslots = 0;
+ }
+ else
+ {
+ lilswap(&hdr.blendmap, 2);
+ if(hdr.version <= 29) hdr.numvslots = 0;
+ else lilswap(&hdr.numvslots, 1);
+ }
+
+ loopi(hdr.numvars)
+ {
+ int type = f->getchar(), ilen = f->getlil<ushort>();
+ f->seek(ilen, SEEK_CUR);
+ switch(type)
+ {
+ case ID_VAR: f->getlil<int>(); break;
+ case ID_FVAR: f->getlil<float>(); break;
+ case ID_SVAR: { int slen = f->getlil<ushort>(); f->seek(slen, SEEK_CUR); break; }
+ }
+ }
+
+ string gametype;
+ copystring(gametype, "fps");
+ bool samegame = true;
+ int eif = 0;
+ if(hdr.version>=16)
+ {
+ int len = f->getchar();
+ f->read(gametype, len+1);
+ }
+ if(strcmp(gametype, game::gameident()))
+ {
+ samegame = false;
+ conoutf(CON_WARN, "WARNING: loading map from %s game, ignoring entities except for lights/mapmodels", gametype);
+ }
+ if(hdr.version>=16)
+ {
+ eif = f->getlil<ushort>();
+ int extrasize = f->getlil<ushort>();
+ f->seek(extrasize, SEEK_CUR);
+ }
+
+ if(hdr.version<14)
+ {
+ f->seek(256, SEEK_CUR);
+ }
+ else
+ {
+ ushort nummru = f->getlil<ushort>();
+ f->seek(nummru*sizeof(ushort), SEEK_CUR);
+ }
+
+ loopi(min(hdr.numents, MAXENTS))
+ {
+ entity &e = ents.add();
+ f->read(&e, sizeof(entity));
+ lilswap(&e.o.x, 3);
+ lilswap(&e.attr1, 5);
+ fixent(e, hdr.version);
+ if(eif > 0) f->seek(eif, SEEK_CUR);
+ if(samegame)
+ {
+ entities::readent(e, NULL, hdr.version);
+ }
+ else if(e.type>=ET_GAMESPECIFIC || hdr.version<=14)
+ {
+ ents.pop();
+ continue;
+ }
+ }
+
+ if(crc)
+ {
+ f->seek(0, SEEK_END);
+ *crc = f->getcrc();
+ }
+
+ delete f;
+
+ return true;
}
#ifndef STANDALONE
@@ -176,39 +176,39 @@ VARP(savebak, 0, 2, 2);
void setmapfilenames(const char *fname, const char *cname = NULL)
{
- string pakname, mapname, mcfgname;
- getmapfilenames(fname, cname, pakname, mapname, mcfgname);
-
- formatstring(ogzname, "packages/%s.ogz", mapname);
- if(savebak==1) formatstring(bakname, "packages/%s.BAK", mapname);
- else formatstring(bakname, "packages/%s_%d.BAK", mapname, totalmillis);
- formatstring(cfgname, "packages/%s/%s.cfg", pakname, mcfgname);
- formatstring(picname, "packages/%s.png", mapname);
-
- path(ogzname);
- path(bakname);
- path(cfgname);
- path(picname);
+ string pakname, mapname, mcfgname;
+ getmapfilenames(fname, cname, pakname, mapname, mcfgname);
+
+ formatstring(ogzname, "packages/%s.ogz", mapname);
+ if(savebak==1) formatstring(bakname, "packages/%s.BAK", mapname);
+ else formatstring(bakname, "packages/%s_%d.BAK", mapname, totalmillis);
+ formatstring(cfgname, "packages/%s/%s.cfg", pakname, mcfgname);
+ formatstring(picname, "packages/%s.png", mapname);
+
+ path(ogzname);
+ path(bakname);
+ path(cfgname);
+ path(picname);
}
void mapcfgname()
{
- const char *mname = game::getclientmap();
- string pakname, mapname, mcfgname;
- getmapfilenames(mname, NULL, pakname, mapname, mcfgname);
- defformatstring(cfgname, "packages/%s/%s.cfg", pakname, mcfgname);
- path(cfgname);
- result(cfgname);
+ const char *mname = game::getclientmap();
+ string pakname, mapname, mcfgname;
+ getmapfilenames(mname, NULL, pakname, mapname, mcfgname);
+ defformatstring(cfgname, "packages/%s/%s.cfg", pakname, mcfgname);
+ path(cfgname);
+ result(cfgname);
}
COMMAND(mapcfgname, "");
void backup(char *name, char *backupname)
{
- string backupfile;
- copystring(backupfile, findfile(backupname, "wb"));
- remove(backupfile);
- rename(findfile(name, "wb"), backupfile);
+ string backupfile;
+ copystring(backupfile, findfile(backupname, "wb"));
+ remove(backupfile);
+ rename(findfile(name, "wb"), backupfile);
}
enum { OCTSAV_CHILDREN = 0, OCTSAV_EMPTY, OCTSAV_SOLID, OCTSAV_NORMAL, OCTSAV_LODCUBE };
@@ -217,773 +217,773 @@ static int savemapprogress = 0;
void savec(cube *c, const ivec &o, int size, stream *f, bool nolms)
{
- if((savemapprogress++&0xFFF)==0) renderprogress(float(savemapprogress)/allocnodes, "saving octree...");
-
- loopi(8)
- {
- ivec co(i, o, size);
- if(c[i].children)
- {
- f->putchar(OCTSAV_CHILDREN);
- savec(c[i].children, co, size>>1, f, nolms);
- }
- else
- {
- int oflags = 0, surfmask = 0, totalverts = 0;
- if(c[i].material!=MAT_AIR) oflags |= 0x40;
- if(isempty(c[i])) f->putchar(oflags | OCTSAV_EMPTY);
- else
- {
- if(!nolms)
- {
- if(c[i].merged) oflags |= 0x80;
- if(c[i].ext) loopj(6)
- {
- const surfaceinfo &surf = c[i].ext->surfaces[j];
- if(!surf.used()) continue;
- oflags |= 0x20;
- surfmask |= 1<<j;
- totalverts += surf.totalverts();
- }
- }
-
- if(isentirelysolid(c[i])) f->putchar(oflags | OCTSAV_SOLID);
- else
- {
- f->putchar(oflags | OCTSAV_NORMAL);
- f->write(c[i].edges, 12);
- }
- }
-
- loopj(6) f->putlil<ushort>(c[i].texture[j]);
-
- if(oflags&0x40) f->putlil<ushort>(c[i].material);
- if(oflags&0x80) f->putchar(c[i].merged);
- if(oflags&0x20)
- {
- f->putchar(surfmask);
- f->putchar(totalverts);
- loopj(6) if(surfmask&(1<<j))
- {
- surfaceinfo surf = c[i].ext->surfaces[j];
- vertinfo *verts = c[i].ext->verts() + surf.verts;
- int layerverts = surf.numverts&MAXFACEVERTS, numverts = surf.totalverts(),
- vertmask = 0, vertorder = 0, uvorder = 0,
- dim = dimension(j), vc = C[dim], vr = R[dim];
- if(numverts)
- {
- if(c[i].merged&(1<<j))
- {
- vertmask |= 0x04;
- if(layerverts == 4)
- {
- ivec v[4] = { verts[0].getxyz(), verts[1].getxyz(), verts[2].getxyz(), verts[3].getxyz() };
- loopk(4)
- {
- const ivec &v0 = v[k], &v1 = v[(k+1)&3], &v2 = v[(k+2)&3], &v3 = v[(k+3)&3];
- if(v1[vc] == v0[vc] && v1[vr] == v2[vr] && v3[vc] == v2[vc] && v3[vr] == v0[vr])
- {
- vertmask |= 0x01;
- vertorder = k;
- break;
- }
- }
- }
- }
- else
- {
- int vis = visibletris(c[i], j, co, size);
- if(vis&4 || faceconvexity(c[i], j) < 0) vertmask |= 0x01;
- if(layerverts < 4 && vis&2) vertmask |= 0x02;
- }
- bool matchnorm = true;
- loopk(numverts)
- {
- const vertinfo &v = verts[k];
- if(v.u || v.v) vertmask |= 0x40;
- if(v.norm) { vertmask |= 0x80; if(v.norm != verts[0].norm) matchnorm = false; }
- }
- if(matchnorm) vertmask |= 0x08;
- if(vertmask&0x40 && layerverts == 4)
- {
- loopk(4)
- {
- const vertinfo &v0 = verts[k], &v1 = verts[(k+1)&3], &v2 = verts[(k+2)&3], &v3 = verts[(k+3)&3];
- if(v1.u == v0.u && v1.v == v2.v && v3.u == v2.u && v3.v == v0.v)
- {
- if(surf.numverts&LAYER_DUP)
- {
- const vertinfo &b0 = verts[4+k], &b1 = verts[4+((k+1)&3)], &b2 = verts[4+((k+2)&3)], &b3 = verts[4+((k+3)&3)];
- if(b1.u != b0.u || b1.v != b2.v || b3.u != b2.u || b3.v != b0.v)
- continue;
- }
- uvorder = k;
- vertmask |= 0x02 | (((k+4-vertorder)&3)<<4);
- break;
- }
- }
- }
- }
- surf.verts = vertmask;
- f->write(&surf, sizeof(surfaceinfo));
- bool hasxyz = (vertmask&0x04)!=0, hasuv = (vertmask&0x40)!=0, hasnorm = (vertmask&0x80)!=0;
- if(layerverts == 4)
- {
- if(hasxyz && vertmask&0x01)
- {
- ivec v0 = verts[vertorder].getxyz(), v2 = verts[(vertorder+2)&3].getxyz();
- f->putlil<ushort>(v0[vc]); f->putlil<ushort>(v0[vr]);
- f->putlil<ushort>(v2[vc]); f->putlil<ushort>(v2[vr]);
- hasxyz = false;
- }
- if(hasuv && vertmask&0x02)
- {
- const vertinfo &v0 = verts[uvorder], &v2 = verts[(uvorder+2)&3];
- f->putlil<ushort>(v0.u); f->putlil<ushort>(v0.v);
- f->putlil<ushort>(v2.u); f->putlil<ushort>(v2.v);
- if(surf.numverts&LAYER_DUP)
- {
- const vertinfo &b0 = verts[4+uvorder], &b2 = verts[4+((uvorder+2)&3)];
- f->putlil<ushort>(b0.u); f->putlil<ushort>(b0.v);
- f->putlil<ushort>(b2.u); f->putlil<ushort>(b2.v);
- }
- hasuv = false;
- }
- }
- if(hasnorm && vertmask&0x08) { f->putlil<ushort>(verts[0].norm); hasnorm = false; }
- if(hasxyz || hasuv || hasnorm) loopk(layerverts)
- {
- const vertinfo &v = verts[(k+vertorder)%layerverts];
- if(hasxyz)
- {
- ivec xyz = v.getxyz();
- f->putlil<ushort>(xyz[vc]); f->putlil<ushort>(xyz[vr]);
- }
- if(hasuv) { f->putlil<ushort>(v.u); f->putlil<ushort>(v.v); }
- if(hasnorm) f->putlil<ushort>(v.norm);
- }
- if(surf.numverts&LAYER_DUP) loopk(layerverts)
- {
- const vertinfo &v = verts[layerverts + (k+vertorder)%layerverts];
- if(hasuv) { f->putlil<ushort>(v.u); f->putlil<ushort>(v.v); }
- }
- }
- }
- }
- }
+ if((savemapprogress++&0xFFF)==0) renderprogress(float(savemapprogress)/allocnodes, "saving octree...");
+
+ loopi(8)
+ {
+ ivec co(i, o, size);
+ if(c[i].children)
+ {
+ f->putchar(OCTSAV_CHILDREN);
+ savec(c[i].children, co, size>>1, f, nolms);
+ }
+ else
+ {
+ int oflags = 0, surfmask = 0, totalverts = 0;
+ if(c[i].material!=MAT_AIR) oflags |= 0x40;
+ if(isempty(c[i])) f->putchar(oflags | OCTSAV_EMPTY);
+ else
+ {
+ if(!nolms)
+ {
+ if(c[i].merged) oflags |= 0x80;
+ if(c[i].ext) loopj(6)
+ {
+ const surfaceinfo &surf = c[i].ext->surfaces[j];
+ if(!surf.used()) continue;
+ oflags |= 0x20;
+ surfmask |= 1<<j;
+ totalverts += surf.totalverts();
+ }
+ }
+
+ if(isentirelysolid(c[i])) f->putchar(oflags | OCTSAV_SOLID);
+ else
+ {
+ f->putchar(oflags | OCTSAV_NORMAL);
+ f->write(c[i].edges, 12);
+ }
+ }
+
+ loopj(6) f->putlil<ushort>(c[i].texture[j]);
+
+ if(oflags&0x40) f->putlil<ushort>(c[i].material);
+ if(oflags&0x80) f->putchar(c[i].merged);
+ if(oflags&0x20)
+ {
+ f->putchar(surfmask);
+ f->putchar(totalverts);
+ loopj(6) if(surfmask&(1<<j))
+ {
+ surfaceinfo surf = c[i].ext->surfaces[j];
+ vertinfo *verts = c[i].ext->verts() + surf.verts;
+ int layerverts = surf.numverts&MAXFACEVERTS, numverts = surf.totalverts(),
+ vertmask = 0, vertorder = 0, uvorder = 0,
+ dim = dimension(j), vc = C[dim], vr = R[dim];
+ if(numverts)
+ {
+ if(c[i].merged&(1<<j))
+ {
+ vertmask |= 0x04;
+ if(layerverts == 4)
+ {
+ ivec v[4] = { verts[0].getxyz(), verts[1].getxyz(), verts[2].getxyz(), verts[3].getxyz() };
+ loopk(4)
+ {
+ const ivec &v0 = v[k], &v1 = v[(k+1)&3], &v2 = v[(k+2)&3], &v3 = v[(k+3)&3];
+ if(v1[vc] == v0[vc] && v1[vr] == v2[vr] && v3[vc] == v2[vc] && v3[vr] == v0[vr])
+ {
+ vertmask |= 0x01;
+ vertorder = k;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ int vis = visibletris(c[i], j, co, size);
+ if(vis&4 || faceconvexity(c[i], j) < 0) vertmask |= 0x01;
+ if(layerverts < 4 && vis&2) vertmask |= 0x02;
+ }
+ bool matchnorm = true;
+ loopk(numverts)
+ {
+ const vertinfo &v = verts[k];
+ if(v.u || v.v) vertmask |= 0x40;
+ if(v.norm) { vertmask |= 0x80; if(v.norm != verts[0].norm) matchnorm = false; }
+ }
+ if(matchnorm) vertmask |= 0x08;
+ if(vertmask&0x40 && layerverts == 4)
+ {
+ loopk(4)
+ {
+ const vertinfo &v0 = verts[k], &v1 = verts[(k+1)&3], &v2 = verts[(k+2)&3], &v3 = verts[(k+3)&3];
+ if(v1.u == v0.u && v1.v == v2.v && v3.u == v2.u && v3.v == v0.v)
+ {
+ if(surf.numverts&LAYER_DUP)
+ {
+ const vertinfo &b0 = verts[4+k], &b1 = verts[4+((k+1)&3)], &b2 = verts[4+((k+2)&3)], &b3 = verts[4+((k+3)&3)];
+ if(b1.u != b0.u || b1.v != b2.v || b3.u != b2.u || b3.v != b0.v)
+ continue;
+ }
+ uvorder = k;
+ vertmask |= 0x02 | (((k+4-vertorder)&3)<<4);
+ break;
+ }
+ }
+ }
+ }
+ surf.verts = vertmask;
+ f->write(&surf, sizeof(surfaceinfo));
+ bool hasxyz = (vertmask&0x04)!=0, hasuv = (vertmask&0x40)!=0, hasnorm = (vertmask&0x80)!=0;
+ if(layerverts == 4)
+ {
+ if(hasxyz && vertmask&0x01)
+ {
+ ivec v0 = verts[vertorder].getxyz(), v2 = verts[(vertorder+2)&3].getxyz();
+ f->putlil<ushort>(v0[vc]); f->putlil<ushort>(v0[vr]);
+ f->putlil<ushort>(v2[vc]); f->putlil<ushort>(v2[vr]);
+ hasxyz = false;
+ }
+ if(hasuv && vertmask&0x02)
+ {
+ const vertinfo &v0 = verts[uvorder], &v2 = verts[(uvorder+2)&3];
+ f->putlil<ushort>(v0.u); f->putlil<ushort>(v0.v);
+ f->putlil<ushort>(v2.u); f->putlil<ushort>(v2.v);
+ if(surf.numverts&LAYER_DUP)
+ {
+ const vertinfo &b0 = verts[4+uvorder], &b2 = verts[4+((uvorder+2)&3)];
+ f->putlil<ushort>(b0.u); f->putlil<ushort>(b0.v);
+ f->putlil<ushort>(b2.u); f->putlil<ushort>(b2.v);
+ }
+ hasuv = false;
+ }
+ }
+ if(hasnorm && vertmask&0x08) { f->putlil<ushort>(verts[0].norm); hasnorm = false; }
+ if(hasxyz || hasuv || hasnorm) loopk(layerverts)
+ {
+ const vertinfo &v = verts[(k+vertorder)%layerverts];
+ if(hasxyz)
+ {
+ ivec xyz = v.getxyz();
+ f->putlil<ushort>(xyz[vc]); f->putlil<ushort>(xyz[vr]);
+ }
+ if(hasuv) { f->putlil<ushort>(v.u); f->putlil<ushort>(v.v); }
+ if(hasnorm) f->putlil<ushort>(v.norm);
+ }
+ if(surf.numverts&LAYER_DUP) loopk(layerverts)
+ {
+ const vertinfo &v = verts[layerverts + (k+vertorder)%layerverts];
+ if(hasuv) { f->putlil<ushort>(v.u); f->putlil<ushort>(v.v); }
+ }
+ }
+ }
+ }
+ }
}
struct surfacecompat
{
- uchar texcoords[8];
- uchar w, h;
- ushort x, y;
- uchar lmid, layer;
+ uchar texcoords[8];
+ uchar w, h;
+ ushort x, y;
+ uchar lmid, layer;
};
struct normalscompat
{
- bvec normals[4];
+ bvec normals[4];
};
struct mergecompat
{
- ushort u1, u2, v1, v2;
+ ushort u1, u2, v1, v2;
};
cube *loadchildren(stream *f, const ivec &co, int size, bool &failed);
void convertoldsurfaces(cube &c, const ivec &co, int size, surfacecompat *srcsurfs, int hassurfs, normalscompat *normals, int hasnorms, mergecompat *merges, int hasmerges)
{
- surfaceinfo dstsurfs[6];
- vertinfo verts[6*2*MAXFACEVERTS];
- int totalverts = 0, numsurfs = 6;
- memset(dstsurfs, 0, sizeof(dstsurfs));
- loopi(6) if((hassurfs|hasnorms|hasmerges)&(1<<i))
- {
- surfaceinfo &dst = dstsurfs[i];
- vertinfo *curverts = NULL;
- int numverts = 0;
- surfacecompat *src = NULL, *blend = NULL;
- if(hassurfs&(1<<i))
- {
- src = &srcsurfs[i];
- if(src->layer&2)
- {
- blend = &srcsurfs[numsurfs++];
- dst.lmid[0] = src->lmid;
- dst.lmid[1] = blend->lmid;
- dst.numverts |= LAYER_BLEND;
- if(blend->lmid >= LMID_RESERVED && (src->x != blend->x || src->y != blend->y || src->w != blend->w || src->h != blend->h || memcmp(src->texcoords, blend->texcoords, sizeof(src->texcoords))))
- dst.numverts |= LAYER_DUP;
- }
- else if(src->layer == 1) { dst.lmid[1] = src->lmid; dst.numverts |= LAYER_BOTTOM; }
- else { dst.lmid[0] = src->lmid; dst.numverts |= LAYER_TOP; }
- }
- else dst.numverts |= LAYER_TOP;
- bool uselms = hassurfs&(1<<i) && (dst.lmid[0] >= LMID_RESERVED || dst.lmid[1] >= LMID_RESERVED || dst.numverts&~LAYER_TOP),
- usemerges = hasmerges&(1<<i) && merges[i].u1 < merges[i].u2 && merges[i].v1 < merges[i].v2,
- usenorms = hasnorms&(1<<i) && normals[i].normals[0] != bvec(128, 128, 128);
- if(uselms || usemerges || usenorms)
- {
- ivec v[4], pos[4], e1, e2, e3, n, vo = ivec(co).mask(0xFFF).shl(3);
- genfaceverts(c, i, v);
- n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0]));
- if(usemerges)
- {
- const mergecompat &m = merges[i];
- int offset = -n.dot(v[0].mul(size).add(vo)),
- dim = dimension(i), vc = C[dim], vr = R[dim];
- loopk(4)
- {
- const ivec &coords = facecoords[i][k];
- int cc = coords[vc] ? m.u2 : m.u1,
- rc = coords[vr] ? m.v2 : m.v1,
- dc = n[dim] ? -(offset + n[vc]*cc + n[vr]*rc)/n[dim] : vo[dim];
- ivec &mv = pos[k];
- mv[vc] = cc;
- mv[vr] = rc;
- mv[dim] = dc;
- }
- }
- else
- {
- int convex = (e3 = v[0]).sub(v[3]).dot(n), vis = 3;
- if(!convex)
- {
- if(ivec().cross(e3, e2).iszero()) { if(!n.iszero()) vis = 1; }
- else if(n.iszero()) vis = 2;
- }
- int order = convex < 0 ? 1 : 0;
- pos[0] = v[order].mul(size).add(vo);
- pos[1] = vis&1 ? v[order+1].mul(size).add(vo) : pos[0];
- pos[2] = v[order+2].mul(size).add(vo);
- pos[3] = vis&2 ? v[(order+3)&3].mul(size).add(vo) : pos[0];
- }
- curverts = verts + totalverts;
- loopk(4)
- {
- if(k > 0 && (pos[k] == pos[0] || pos[k] == pos[k-1])) continue;
- vertinfo &dv = curverts[numverts++];
- dv.setxyz(pos[k]);
- if(uselms)
- {
- float u = src->x + (src->texcoords[k*2] / 255.0f) * (src->w - 1),
- v = src->y + (src->texcoords[k*2+1] / 255.0f) * (src->h - 1);
- dv.u = ushort(floor(clamp((u) * float(USHRT_MAX+1)/LM_PACKW + 0.5f, 0.0f, float(USHRT_MAX))));
- dv.v = ushort(floor(clamp((v) * float(USHRT_MAX+1)/LM_PACKH + 0.5f, 0.0f, float(USHRT_MAX))));
- }
- else dv.u = dv.v = 0;
- dv.norm = usenorms && normals[i].normals[k] != bvec(128, 128, 128) ? encodenormal(normals[i].normals[k].tonormal().normalize()) : 0;
- }
- dst.verts = totalverts;
- dst.numverts |= numverts;
- totalverts += numverts;
- if(dst.numverts&LAYER_DUP) loopk(4)
- {
- if(k > 0 && (pos[k] == pos[0] || pos[k] == pos[k-1])) continue;
- vertinfo &bv = verts[totalverts++];
- bv.setxyz(pos[k]);
- bv.u = ushort(floor(clamp((blend->x + (blend->texcoords[k*2] / 255.0f) * (blend->w - 1)) * float(USHRT_MAX+1)/LM_PACKW, 0.0f, float(USHRT_MAX))));
- bv.v = ushort(floor(clamp((blend->y + (blend->texcoords[k*2+1] / 255.0f) * (blend->h - 1)) * float(USHRT_MAX+1)/LM_PACKH, 0.0f, float(USHRT_MAX))));
- bv.norm = usenorms && normals[i].normals[k] != bvec(128, 128, 128) ? encodenormal(normals[i].normals[k].tonormal().normalize()) : 0;
- }
- }
- }
- setsurfaces(c, dstsurfs, verts, totalverts);
+ surfaceinfo dstsurfs[6];
+ vertinfo verts[6*2*MAXFACEVERTS];
+ int totalverts = 0, numsurfs = 6;
+ memset(dstsurfs, 0, sizeof(dstsurfs));
+ loopi(6) if((hassurfs|hasnorms|hasmerges)&(1<<i))
+ {
+ surfaceinfo &dst = dstsurfs[i];
+ vertinfo *curverts = NULL;
+ int numverts = 0;
+ surfacecompat *src = NULL, *blend = NULL;
+ if(hassurfs&(1<<i))
+ {
+ src = &srcsurfs[i];
+ if(src->layer&2)
+ {
+ blend = &srcsurfs[numsurfs++];
+ dst.lmid[0] = src->lmid;
+ dst.lmid[1] = blend->lmid;
+ dst.numverts |= LAYER_BLEND;
+ if(blend->lmid >= LMID_RESERVED && (src->x != blend->x || src->y != blend->y || src->w != blend->w || src->h != blend->h || memcmp(src->texcoords, blend->texcoords, sizeof(src->texcoords))))
+ dst.numverts |= LAYER_DUP;
+ }
+ else if(src->layer == 1) { dst.lmid[1] = src->lmid; dst.numverts |= LAYER_BOTTOM; }
+ else { dst.lmid[0] = src->lmid; dst.numverts |= LAYER_TOP; }
+ }
+ else dst.numverts |= LAYER_TOP;
+ bool uselms = hassurfs&(1<<i) && (dst.lmid[0] >= LMID_RESERVED || dst.lmid[1] >= LMID_RESERVED || dst.numverts&~LAYER_TOP),
+ usemerges = hasmerges&(1<<i) && merges[i].u1 < merges[i].u2 && merges[i].v1 < merges[i].v2,
+ usenorms = hasnorms&(1<<i) && normals[i].normals[0] != bvec(128, 128, 128);
+ if(uselms || usemerges || usenorms)
+ {
+ ivec v[4], pos[4], e1, e2, e3, n, vo = ivec(co).mask(0xFFF).shl(3);
+ genfaceverts(c, i, v);
+ n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0]));
+ if(usemerges)
+ {
+ const mergecompat &m = merges[i];
+ int offset = -n.dot(v[0].mul(size).add(vo)),
+ dim = dimension(i), vc = C[dim], vr = R[dim];
+ loopk(4)
+ {
+ const ivec &coords = facecoords[i][k];
+ int cc = coords[vc] ? m.u2 : m.u1,
+ rc = coords[vr] ? m.v2 : m.v1,
+ dc = n[dim] ? -(offset + n[vc]*cc + n[vr]*rc)/n[dim] : vo[dim];
+ ivec &mv = pos[k];
+ mv[vc] = cc;
+ mv[vr] = rc;
+ mv[dim] = dc;
+ }
+ }
+ else
+ {
+ int convex = (e3 = v[0]).sub(v[3]).dot(n), vis = 3;
+ if(!convex)
+ {
+ if(ivec().cross(e3, e2).iszero()) { if(!n.iszero()) vis = 1; }
+ else if(n.iszero()) vis = 2;
+ }
+ int order = convex < 0 ? 1 : 0;
+ pos[0] = v[order].mul(size).add(vo);
+ pos[1] = vis&1 ? v[order+1].mul(size).add(vo) : pos[0];
+ pos[2] = v[order+2].mul(size).add(vo);
+ pos[3] = vis&2 ? v[(order+3)&3].mul(size).add(vo) : pos[0];
+ }
+ curverts = verts + totalverts;
+ loopk(4)
+ {
+ if(k > 0 && (pos[k] == pos[0] || pos[k] == pos[k-1])) continue;
+ vertinfo &dv = curverts[numverts++];
+ dv.setxyz(pos[k]);
+ if(uselms)
+ {
+ float u = src->x + (src->texcoords[k*2] / 255.0f) * (src->w - 1),
+ v = src->y + (src->texcoords[k*2+1] / 255.0f) * (src->h - 1);
+ dv.u = ushort(floor(clamp((u) * float(USHRT_MAX+1)/LM_PACKW + 0.5f, 0.0f, float(USHRT_MAX))));
+ dv.v = ushort(floor(clamp((v) * float(USHRT_MAX+1)/LM_PACKH + 0.5f, 0.0f, float(USHRT_MAX))));
+ }
+ else dv.u = dv.v = 0;
+ dv.norm = usenorms && normals[i].normals[k] != bvec(128, 128, 128) ? encodenormal(normals[i].normals[k].tonormal().normalize()) : 0;
+ }
+ dst.verts = totalverts;
+ dst.numverts |= numverts;
+ totalverts += numverts;
+ if(dst.numverts&LAYER_DUP) loopk(4)
+ {
+ if(k > 0 && (pos[k] == pos[0] || pos[k] == pos[k-1])) continue;
+ vertinfo &bv = verts[totalverts++];
+ bv.setxyz(pos[k]);
+ bv.u = ushort(floor(clamp((blend->x + (blend->texcoords[k*2] / 255.0f) * (blend->w - 1)) * float(USHRT_MAX+1)/LM_PACKW, 0.0f, float(USHRT_MAX))));
+ bv.v = ushort(floor(clamp((blend->y + (blend->texcoords[k*2+1] / 255.0f) * (blend->h - 1)) * float(USHRT_MAX+1)/LM_PACKH, 0.0f, float(USHRT_MAX))));
+ bv.norm = usenorms && normals[i].normals[k] != bvec(128, 128, 128) ? encodenormal(normals[i].normals[k].tonormal().normalize()) : 0;
+ }
+ }
+ }
+ setsurfaces(c, dstsurfs, verts, totalverts);
}
static inline int convertoldmaterial(int mat)
{
- return ((mat&7)<<MATF_VOLUME_SHIFT) | (((mat>>3)&3)<<MATF_CLIP_SHIFT) | (((mat>>5)&7)<<MATF_FLAG_SHIFT);
+ return ((mat&7)<<MATF_VOLUME_SHIFT) | (((mat>>3)&3)<<MATF_CLIP_SHIFT) | (((mat>>5)&7)<<MATF_FLAG_SHIFT);
}
void loadc(stream *f, cube &c, const ivec &co, int size, bool &failed)
{
- bool haschildren = false;
- int octsav = f->getchar();
- switch(octsav&0x7)
- {
- case OCTSAV_CHILDREN:
- c.children = loadchildren(f, co, size>>1, failed);
- return;
-
- case OCTSAV_LODCUBE: haschildren = true; break;
- case OCTSAV_EMPTY: emptyfaces(c); break;
- case OCTSAV_SOLID: solidfaces(c); break;
- case OCTSAV_NORMAL: f->read(c.edges, 12); break;
- default: failed = true; return;
- }
- loopi(6) c.texture[i] = mapversion<14 ? f->getchar() : f->getlil<ushort>();
- if(mapversion < 7) f->seek(3, SEEK_CUR);
- else if(mapversion <= 31)
- {
- uchar mask = f->getchar();
- if(mask & 0x80)
- {
- int mat = f->getchar();
- if(mapversion < 27)
- {
- static const ushort matconv[] = { MAT_AIR, MAT_WATER, MAT_CLIP, MAT_GLASS|MAT_CLIP, MAT_NOCLIP, MAT_LAVA|MAT_DEATH, MAT_GAMECLIP, MAT_DEATH };
- c.material = size_t(mat) < sizeof(matconv)/sizeof(matconv[0]) ? (int) matconv[mat] : (int) MAT_AIR;
- }
- else c.material = convertoldmaterial(mat);
- }
- surfacecompat surfaces[12];
- normalscompat normals[6];
- mergecompat merges[6];
- int hassurfs = 0, hasnorms = 0, hasmerges = 0;
- if(mask & 0x3F)
- {
- int numsurfs = 6;
- loopi(numsurfs)
- {
- if(i >= 6 || mask & (1 << i))
- {
- f->read(&surfaces[i], sizeof(surfacecompat));
- lilswap(&surfaces[i].x, 2);
- if(mapversion < 10) ++surfaces[i].lmid;
- if(mapversion < 18)
- {
- if(surfaces[i].lmid >= LMID_AMBIENT1) ++surfaces[i].lmid;
- if(surfaces[i].lmid >= LMID_BRIGHT1) ++surfaces[i].lmid;
- }
- if(mapversion < 19)
- {
- if(surfaces[i].lmid >= LMID_DARK) surfaces[i].lmid += 2;
- }
- if(i < 6)
- {
- if(mask & 0x40) { hasnorms |= 1<<i; f->read(&normals[i], sizeof(normalscompat)); }
- if(surfaces[i].layer != 0 || surfaces[i].lmid != LMID_AMBIENT)
- hassurfs |= 1<<i;
- if(surfaces[i].layer&2) numsurfs++;
- }
- }
- }
- }
- if(mapversion <= 8) edgespan2vectorcube(c);
- if(mapversion <= 11)
- {
- swap(c.faces[0], c.faces[2]);
- swap(c.texture[0], c.texture[4]);
- swap(c.texture[1], c.texture[5]);
- if(hassurfs&0x33)
- {
- swap(surfaces[0], surfaces[4]);
- swap(surfaces[1], surfaces[5]);
- hassurfs = (hassurfs&~0x33) | ((hassurfs&0x30)>>4) | ((hassurfs&0x03)<<4);
- }
- }
- if(mapversion >= 20)
- {
- if(octsav&0x80)
- {
- int merged = f->getchar();
- c.merged = merged&0x3F;
- if(merged&0x80)
- {
- int mask = f->getchar();
- if(mask)
- {
- hasmerges = mask&0x3F;
- loopi(6) if(mask&(1<<i))
- {
- mergecompat *m = &merges[i];
- f->read(m, sizeof(mergecompat));
- lilswap(&m->u1, 4);
- if(mapversion <= 25)
- {
- int uorigin = m->u1 & 0xE000, vorigin = m->v1 & 0xE000;
- m->u1 = (m->u1 - uorigin) << 2;
- m->u2 = (m->u2 - uorigin) << 2;
- m->v1 = (m->v1 - vorigin) << 2;
- m->v2 = (m->v2 - vorigin) << 2;
- }
- }
- }
- }
- }
- }
- if(hassurfs || hasnorms || hasmerges)
- convertoldsurfaces(c, co, size, surfaces, hassurfs, normals, hasnorms, merges, hasmerges);
- }
- else
- {
- if(octsav&0x40)
- {
- if(mapversion <= 32)
- {
- int mat = f->getchar();
- c.material = convertoldmaterial(mat);
- }
- else c.material = f->getlil<ushort>();
- }
- if(octsav&0x80) c.merged = f->getchar();
- if(octsav&0x20)
- {
- int surfmask, totalverts;
- surfmask = f->getchar();
- totalverts = max(f->getchar(), 0);
- newcubeext(c, totalverts, false);
- memset(c.ext->surfaces, 0, sizeof(c.ext->surfaces));
- memset(c.ext->verts(), 0, totalverts*sizeof(vertinfo));
- int offset = 0;
- loopi(6) if(surfmask&(1<<i))
- {
- surfaceinfo &surf = c.ext->surfaces[i];
- f->read(&surf, sizeof(surfaceinfo));
- int vertmask = surf.verts, numverts = surf.totalverts();
- if(!numverts) { surf.verts = 0; continue; }
- surf.verts = offset;
- vertinfo *verts = c.ext->verts() + offset;
- offset += numverts;
- ivec v[4], n, vo = ivec(co).mask(0xFFF).shl(3);
- int layerverts = surf.numverts&MAXFACEVERTS, dim = dimension(i), vc = C[dim], vr = R[dim], bias = 0;
- genfaceverts(c, i, v);
- bool hasxyz = (vertmask&0x04)!=0, hasuv = (vertmask&0x40)!=0, hasnorm = (vertmask&0x80)!=0;
- if(hasxyz)
- {
- ivec e1, e2, e3;
- n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0]));
- if(n.iszero()) n.cross(e2, (e3 = v[3]).sub(v[0]));
- bias = -n.dot(ivec(v[0]).mul(size).add(vo));
- }
- else
- {
- int vis = layerverts < 4 ? (vertmask&0x02 ? 2 : 1) : 3, order = vertmask&0x01 ? 1 : 0, k = 0;
- verts[k++].setxyz(v[order].mul(size).add(vo));
- if(vis&1) verts[k++].setxyz(v[order+1].mul(size).add(vo));
- verts[k++].setxyz(v[order+2].mul(size).add(vo));
- if(vis&2) verts[k++].setxyz(v[(order+3)&3].mul(size).add(vo));
- }
- if(layerverts == 4)
- {
- if(hasxyz && vertmask&0x01)
- {
- ushort c1 = f->getlil<ushort>(), r1 = f->getlil<ushort>(), c2 = f->getlil<ushort>(), r2 = f->getlil<ushort>();
- ivec xyz;
- xyz[vc] = c1; xyz[vr] = r1; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim];
- verts[0].setxyz(xyz);
- xyz[vc] = c1; xyz[vr] = r2; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim];
- verts[1].setxyz(xyz);
- xyz[vc] = c2; xyz[vr] = r2; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim];
- verts[2].setxyz(xyz);
- xyz[vc] = c2; xyz[vr] = r1; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim];
- verts[3].setxyz(xyz);
- hasxyz = false;
- }
- if(hasuv && vertmask&0x02)
- {
- int uvorder = (vertmask&0x30)>>4;
- vertinfo &v0 = verts[uvorder], &v1 = verts[(uvorder+1)&3], &v2 = verts[(uvorder+2)&3], &v3 = verts[(uvorder+3)&3];
- v0.u = f->getlil<ushort>(); v0.v = f->getlil<ushort>();
- v2.u = f->getlil<ushort>(); v2.v = f->getlil<ushort>();
- v1.u = v0.u; v1.v = v2.v;
- v3.u = v2.u; v3.v = v0.v;
- if(surf.numverts&LAYER_DUP)
- {
- vertinfo &b0 = verts[4+uvorder], &b1 = verts[4+((uvorder+1)&3)], &b2 = verts[4+((uvorder+2)&3)], &b3 = verts[4+((uvorder+3)&3)];
- b0.u = f->getlil<ushort>(); b0.v = f->getlil<ushort>();
- b2.u = f->getlil<ushort>(); b2.v = f->getlil<ushort>();
- b1.u = b0.u; b1.v = b2.v;
- b3.u = b2.u; b3.v = b0.v;
- }
- hasuv = false;
- }
- }
- if(hasnorm && vertmask&0x08)
- {
- ushort norm = f->getlil<ushort>();
- loopk(layerverts) verts[k].norm = norm;
- hasnorm = false;
- }
- if(hasxyz || hasuv || hasnorm) loopk(layerverts)
- {
- vertinfo &v = verts[k];
- if(hasxyz)
- {
- ivec xyz;
- xyz[vc] = f->getlil<ushort>(); xyz[vr] = f->getlil<ushort>();
- xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim];
- v.setxyz(xyz);
- }
- if(hasuv) { v.u = f->getlil<ushort>(); v.v = f->getlil<ushort>(); }
- if(hasnorm) v.norm = f->getlil<ushort>();
- }
- if(surf.numverts&LAYER_DUP) loopk(layerverts)
- {
- vertinfo &v = verts[k+layerverts], &t = verts[k];
- v.setxyz(t.x, t.y, t.z);
- if(hasuv) { v.u = f->getlil<ushort>(); v.v = f->getlil<ushort>(); }
- v.norm = t.norm;
- }
- }
- }
- }
-
- c.children = (haschildren ? loadchildren(f, co, size>>1, failed) : NULL);
+ bool haschildren = false;
+ int octsav = f->getchar();
+ switch(octsav&0x7)
+ {
+ case OCTSAV_CHILDREN:
+ c.children = loadchildren(f, co, size>>1, failed);
+ return;
+
+ case OCTSAV_LODCUBE: haschildren = true; break;
+ case OCTSAV_EMPTY: emptyfaces(c); break;
+ case OCTSAV_SOLID: solidfaces(c); break;
+ case OCTSAV_NORMAL: f->read(c.edges, 12); break;
+ default: failed = true; return;
+ }
+ loopi(6) c.texture[i] = mapversion<14 ? f->getchar() : f->getlil<ushort>();
+ if(mapversion < 7) f->seek(3, SEEK_CUR);
+ else if(mapversion <= 31)
+ {
+ uchar mask = f->getchar();
+ if(mask & 0x80)
+ {
+ int mat = f->getchar();
+ if(mapversion < 27)
+ {
+ static const ushort matconv[] = { MAT_AIR, MAT_WATER, MAT_CLIP, MAT_GLASS|MAT_CLIP, MAT_NOCLIP, MAT_LAVA|MAT_DEATH, MAT_GAMECLIP, MAT_DEATH };
+ c.material = size_t(mat) < sizeof(matconv)/sizeof(matconv[0]) ? (int) matconv[mat] : (int) MAT_AIR;
+ }
+ else c.material = convertoldmaterial(mat);
+ }
+ surfacecompat surfaces[12];
+ normalscompat normals[6];
+ mergecompat merges[6];
+ int hassurfs = 0, hasnorms = 0, hasmerges = 0;
+ if(mask & 0x3F)
+ {
+ int numsurfs = 6;
+ loopi(numsurfs)
+ {
+ if(i >= 6 || mask & (1 << i))
+ {
+ f->read(&surfaces[i], sizeof(surfacecompat));
+ lilswap(&surfaces[i].x, 2);
+ if(mapversion < 10) ++surfaces[i].lmid;
+ if(mapversion < 18)
+ {
+ if(surfaces[i].lmid >= LMID_AMBIENT1) ++surfaces[i].lmid;
+ if(surfaces[i].lmid >= LMID_BRIGHT1) ++surfaces[i].lmid;
+ }
+ if(mapversion < 19)
+ {
+ if(surfaces[i].lmid >= LMID_DARK) surfaces[i].lmid += 2;
+ }
+ if(i < 6)
+ {
+ if(mask & 0x40) { hasnorms |= 1<<i; f->read(&normals[i], sizeof(normalscompat)); }
+ if(surfaces[i].layer != 0 || surfaces[i].lmid != LMID_AMBIENT)
+ hassurfs |= 1<<i;
+ if(surfaces[i].layer&2) numsurfs++;
+ }
+ }
+ }
+ }
+ if(mapversion <= 8) edgespan2vectorcube(c);
+ if(mapversion <= 11)
+ {
+ swap(c.faces[0], c.faces[2]);
+ swap(c.texture[0], c.texture[4]);
+ swap(c.texture[1], c.texture[5]);
+ if(hassurfs&0x33)
+ {
+ swap(surfaces[0], surfaces[4]);
+ swap(surfaces[1], surfaces[5]);
+ hassurfs = (hassurfs&~0x33) | ((hassurfs&0x30)>>4) | ((hassurfs&0x03)<<4);
+ }
+ }
+ if(mapversion >= 20)
+ {
+ if(octsav&0x80)
+ {
+ int merged = f->getchar();
+ c.merged = merged&0x3F;
+ if(merged&0x80)
+ {
+ int mask = f->getchar();
+ if(mask)
+ {
+ hasmerges = mask&0x3F;
+ loopi(6) if(mask&(1<<i))
+ {
+ mergecompat *m = &merges[i];
+ f->read(m, sizeof(mergecompat));
+ lilswap(&m->u1, 4);
+ if(mapversion <= 25)
+ {
+ int uorigin = m->u1 & 0xE000, vorigin = m->v1 & 0xE000;
+ m->u1 = (m->u1 - uorigin) << 2;
+ m->u2 = (m->u2 - uorigin) << 2;
+ m->v1 = (m->v1 - vorigin) << 2;
+ m->v2 = (m->v2 - vorigin) << 2;
+ }
+ }
+ }
+ }
+ }
+ }
+ if(hassurfs || hasnorms || hasmerges)
+ convertoldsurfaces(c, co, size, surfaces, hassurfs, normals, hasnorms, merges, hasmerges);
+ }
+ else
+ {
+ if(octsav&0x40)
+ {
+ if(mapversion <= 32)
+ {
+ int mat = f->getchar();
+ c.material = convertoldmaterial(mat);
+ }
+ else c.material = f->getlil<ushort>();
+ }
+ if(octsav&0x80) c.merged = f->getchar();
+ if(octsav&0x20)
+ {
+ int surfmask, totalverts;
+ surfmask = f->getchar();
+ totalverts = max(f->getchar(), 0);
+ newcubeext(c, totalverts, false);
+ memset(c.ext->surfaces, 0, sizeof(c.ext->surfaces));
+ memset(c.ext->verts(), 0, totalverts*sizeof(vertinfo));
+ int offset = 0;
+ loopi(6) if(surfmask&(1<<i))
+ {
+ surfaceinfo &surf = c.ext->surfaces[i];
+ f->read(&surf, sizeof(surfaceinfo));
+ int vertmask = surf.verts, numverts = surf.totalverts();
+ if(!numverts) { surf.verts = 0; continue; }
+ surf.verts = offset;
+ vertinfo *verts = c.ext->verts() + offset;
+ offset += numverts;
+ ivec v[4], n, vo = ivec(co).mask(0xFFF).shl(3);
+ int layerverts = surf.numverts&MAXFACEVERTS, dim = dimension(i), vc = C[dim], vr = R[dim], bias = 0;
+ genfaceverts(c, i, v);
+ bool hasxyz = (vertmask&0x04)!=0, hasuv = (vertmask&0x40)!=0, hasnorm = (vertmask&0x80)!=0;
+ if(hasxyz)
+ {
+ ivec e1, e2, e3;
+ n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0]));
+ if(n.iszero()) n.cross(e2, (e3 = v[3]).sub(v[0]));
+ bias = -n.dot(ivec(v[0]).mul(size).add(vo));
+ }
+ else
+ {
+ int vis = layerverts < 4 ? (vertmask&0x02 ? 2 : 1) : 3, order = vertmask&0x01 ? 1 : 0, k = 0;
+ verts[k++].setxyz(v[order].mul(size).add(vo));
+ if(vis&1) verts[k++].setxyz(v[order+1].mul(size).add(vo));
+ verts[k++].setxyz(v[order+2].mul(size).add(vo));
+ if(vis&2) verts[k++].setxyz(v[(order+3)&3].mul(size).add(vo));
+ }
+ if(layerverts == 4)
+ {
+ if(hasxyz && vertmask&0x01)
+ {
+ ushort c1 = f->getlil<ushort>(), r1 = f->getlil<ushort>(), c2 = f->getlil<ushort>(), r2 = f->getlil<ushort>();
+ ivec xyz;
+ xyz[vc] = c1; xyz[vr] = r1; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim];
+ verts[0].setxyz(xyz);
+ xyz[vc] = c1; xyz[vr] = r2; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim];
+ verts[1].setxyz(xyz);
+ xyz[vc] = c2; xyz[vr] = r2; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim];
+ verts[2].setxyz(xyz);
+ xyz[vc] = c2; xyz[vr] = r1; xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim];
+ verts[3].setxyz(xyz);
+ hasxyz = false;
+ }
+ if(hasuv && vertmask&0x02)
+ {
+ int uvorder = (vertmask&0x30)>>4;
+ vertinfo &v0 = verts[uvorder], &v1 = verts[(uvorder+1)&3], &v2 = verts[(uvorder+2)&3], &v3 = verts[(uvorder+3)&3];
+ v0.u = f->getlil<ushort>(); v0.v = f->getlil<ushort>();
+ v2.u = f->getlil<ushort>(); v2.v = f->getlil<ushort>();
+ v1.u = v0.u; v1.v = v2.v;
+ v3.u = v2.u; v3.v = v0.v;
+ if(surf.numverts&LAYER_DUP)
+ {
+ vertinfo &b0 = verts[4+uvorder], &b1 = verts[4+((uvorder+1)&3)], &b2 = verts[4+((uvorder+2)&3)], &b3 = verts[4+((uvorder+3)&3)];
+ b0.u = f->getlil<ushort>(); b0.v = f->getlil<ushort>();
+ b2.u = f->getlil<ushort>(); b2.v = f->getlil<ushort>();
+ b1.u = b0.u; b1.v = b2.v;
+ b3.u = b2.u; b3.v = b0.v;
+ }
+ hasuv = false;
+ }
+ }
+ if(hasnorm && vertmask&0x08)
+ {
+ ushort norm = f->getlil<ushort>();
+ loopk(layerverts) verts[k].norm = norm;
+ hasnorm = false;
+ }
+ if(hasxyz || hasuv || hasnorm) loopk(layerverts)
+ {
+ vertinfo &v = verts[k];
+ if(hasxyz)
+ {
+ ivec xyz;
+ xyz[vc] = f->getlil<ushort>(); xyz[vr] = f->getlil<ushort>();
+ xyz[dim] = n[dim] ? -(bias + n[vc]*xyz[vc] + n[vr]*xyz[vr])/n[dim] : vo[dim];
+ v.setxyz(xyz);
+ }
+ if(hasuv) { v.u = f->getlil<ushort>(); v.v = f->getlil<ushort>(); }
+ if(hasnorm) v.norm = f->getlil<ushort>();
+ }
+ if(surf.numverts&LAYER_DUP) loopk(layerverts)
+ {
+ vertinfo &v = verts[k+layerverts], &t = verts[k];
+ v.setxyz(t.x, t.y, t.z);
+ if(hasuv) { v.u = f->getlil<ushort>(); v.v = f->getlil<ushort>(); }
+ v.norm = t.norm;
+ }
+ }
+ }
+ }
+
+ c.children = (haschildren ? loadchildren(f, co, size>>1, failed) : NULL);
}
cube *loadchildren(stream *f, const ivec &co, int size, bool &failed)
{
- cube *c = newcubes();
- loopi(8)
- {
- loadc(f, c[i], ivec(i, co, size), size, failed);
- if(failed) break;
- }
- return c;
+ cube *c = newcubes();
+ loopi(8)
+ {
+ loadc(f, c[i], ivec(i, co, size), size, failed);
+ if(failed) break;
+ }
+ return c;
}
void savevslot(stream *f, VSlot &vs, int prev)
{
- f->putlil<int>(vs.changed);
- f->putlil<int>(prev);
- if(vs.changed & (1<<VSLOT_SHPARAM))
- {
- f->putlil<ushort>(vs.params.length());
- loopv(vs.params)
- {
- SlotShaderParam &p = vs.params[i];
- f->putlil<ushort>(strlen(p.name));
- f->write(p.name, strlen(p.name));
- loopk(4) f->putlil<float>(p.val[k]);
- }
- }
- if(vs.changed & (1<<VSLOT_SCALE)) f->putlil<float>(vs.scale);
- if(vs.changed & (1<<VSLOT_ROTATION)) f->putlil<int>(vs.rotation);
- if(vs.changed & (1<<VSLOT_OFFSET))
- {
- f->putlil<int>(vs.offset.x);
- f->putlil<int>(vs.offset.y);
- }
- if(vs.changed & (1<<VSLOT_SCROLL))
- {
- f->putlil<float>(vs.scroll.x);
- f->putlil<float>(vs.scroll.y);
- }
- if(vs.changed & (1<<VSLOT_LAYER)) f->putlil<int>(vs.layer);
- if(vs.changed & (1<<VSLOT_ALPHA))
- {
- f->putlil<float>(vs.alphafront);
- f->putlil<float>(vs.alphaback);
- }
- if(vs.changed & (1<<VSLOT_COLOR))
- {
- loopk(3) f->putlil<float>(vs.colorscale[k]);
- }
+ f->putlil<int>(vs.changed);
+ f->putlil<int>(prev);
+ if(vs.changed & (1<<VSLOT_SHPARAM))
+ {
+ f->putlil<ushort>(vs.params.length());
+ loopv(vs.params)
+ {
+ SlotShaderParam &p = vs.params[i];
+ f->putlil<ushort>(strlen(p.name));
+ f->write(p.name, strlen(p.name));
+ loopk(4) f->putlil<float>(p.val[k]);
+ }
+ }
+ if(vs.changed & (1<<VSLOT_SCALE)) f->putlil<float>(vs.scale);
+ if(vs.changed & (1<<VSLOT_ROTATION)) f->putlil<int>(vs.rotation);
+ if(vs.changed & (1<<VSLOT_OFFSET))
+ {
+ f->putlil<int>(vs.offset.x);
+ f->putlil<int>(vs.offset.y);
+ }
+ if(vs.changed & (1<<VSLOT_SCROLL))
+ {
+ f->putlil<float>(vs.scroll.x);
+ f->putlil<float>(vs.scroll.y);
+ }
+ if(vs.changed & (1<<VSLOT_LAYER)) f->putlil<int>(vs.layer);
+ if(vs.changed & (1<<VSLOT_ALPHA))
+ {
+ f->putlil<float>(vs.alphafront);
+ f->putlil<float>(vs.alphaback);
+ }
+ if(vs.changed & (1<<VSLOT_COLOR))
+ {
+ loopk(3) f->putlil<float>(vs.colorscale[k]);
+ }
}
void savevslots(stream *f, int numvslots)
{
- if(vslots.empty()) return;
- int *prev = new int[numvslots];
- for(int i=0;i<numvslots;++i)prev[i]=-1;
- loopi(numvslots)
- {
- VSlot *vs = vslots[i];
- if(vs->changed) continue;
- for(;;)
- {
- VSlot *cur = vs;
- do vs = vs->next; while(vs && vs->index >= numvslots);
- if(!vs) break;
- prev[vs->index] = cur->index;
- }
- }
- int lastroot = 0;
- loopi(numvslots)
- {
- VSlot &vs = *vslots[i];
- if(!vs.changed) continue;
- if(lastroot < i) f->putlil<int>(-(i - lastroot));
- savevslot(f, vs, prev[i]);
- lastroot = i+1;
- }
- if(lastroot < numvslots) f->putlil<int>(-(numvslots - lastroot));
- delete[] prev;
+ if(vslots.empty()) return;
+ int *prev = new int[numvslots];
+ for(int i=0;i<numvslots;++i)prev[i]=-1;
+ loopi(numvslots)
+ {
+ VSlot *vs = vslots[i];
+ if(vs->changed) continue;
+ for(;;)
+ {
+ VSlot *cur = vs;
+ do vs = vs->next; while(vs && vs->index >= numvslots);
+ if(!vs) break;
+ prev[vs->index] = cur->index;
+ }
+ }
+ int lastroot = 0;
+ loopi(numvslots)
+ {
+ VSlot &vs = *vslots[i];
+ if(!vs.changed) continue;
+ if(lastroot < i) f->putlil<int>(-(i - lastroot));
+ savevslot(f, vs, prev[i]);
+ lastroot = i+1;
+ }
+ if(lastroot < numvslots) f->putlil<int>(-(numvslots - lastroot));
+ delete[] prev;
}
void loadvslot(stream *f, VSlot &vs, int changed)
{
- vs.changed = changed;
- if(vs.changed & (1<<VSLOT_SHPARAM))
- {
- int numparams = f->getlil<ushort>();
- string name;
- loopi(numparams)
- {
- SlotShaderParam &p = vs.params.add();
- int nlen = f->getlil<ushort>();
- f->read(name, min(nlen, MAXSTRLEN-1));
- name[min(nlen, MAXSTRLEN-1)] = '\0';
- if(nlen >= MAXSTRLEN) f->seek(nlen - (MAXSTRLEN-1), SEEK_CUR);
- p.name = getshaderparamname(name);
- p.loc = -1;
- loopk(4) p.val[k] = f->getlil<float>();
- }
- }
- if(vs.changed & (1<<VSLOT_SCALE)) vs.scale = f->getlil<float>();
- if(vs.changed & (1<<VSLOT_ROTATION)) vs.rotation = clamp(f->getlil<int>(), 0, 7);
- if(vs.changed & (1<<VSLOT_OFFSET))
- {
- vs.offset.x = f->getlil<int>();
- vs.offset.y = f->getlil<int>();
- }
- if(vs.changed & (1<<VSLOT_SCROLL))
- {
- vs.scroll.x = f->getlil<float>();
- vs.scroll.y = f->getlil<float>();
- }
- if(vs.changed & (1<<VSLOT_LAYER)) vs.layer = f->getlil<int>();
- if(vs.changed & (1<<VSLOT_ALPHA))
- {
- vs.alphafront = f->getlil<float>();
- vs.alphaback = f->getlil<float>();
- }
- if(vs.changed & (1<<VSLOT_COLOR))
- {
- loopk(3) vs.colorscale[k] = f->getlil<float>();
- }
+ vs.changed = changed;
+ if(vs.changed & (1<<VSLOT_SHPARAM))
+ {
+ int numparams = f->getlil<ushort>();
+ string name;
+ loopi(numparams)
+ {
+ SlotShaderParam &p = vs.params.add();
+ int nlen = f->getlil<ushort>();
+ f->read(name, min(nlen, MAXSTRLEN-1));
+ name[min(nlen, MAXSTRLEN-1)] = '\0';
+ if(nlen >= MAXSTRLEN) f->seek(nlen - (MAXSTRLEN-1), SEEK_CUR);
+ p.name = getshaderparamname(name);
+ p.loc = -1;
+ loopk(4) p.val[k] = f->getlil<float>();
+ }
+ }
+ if(vs.changed & (1<<VSLOT_SCALE)) vs.scale = f->getlil<float>();
+ if(vs.changed & (1<<VSLOT_ROTATION)) vs.rotation = clamp(f->getlil<int>(), 0, 7);
+ if(vs.changed & (1<<VSLOT_OFFSET))
+ {
+ vs.offset.x = f->getlil<int>();
+ vs.offset.y = f->getlil<int>();
+ }
+ if(vs.changed & (1<<VSLOT_SCROLL))
+ {
+ vs.scroll.x = f->getlil<float>();
+ vs.scroll.y = f->getlil<float>();
+ }
+ if(vs.changed & (1<<VSLOT_LAYER)) vs.layer = f->getlil<int>();
+ if(vs.changed & (1<<VSLOT_ALPHA))
+ {
+ vs.alphafront = f->getlil<float>();
+ vs.alphaback = f->getlil<float>();
+ }
+ if(vs.changed & (1<<VSLOT_COLOR))
+ {
+ loopk(3) vs.colorscale[k] = f->getlil<float>();
+ }
}
void loadvslots(stream *f, int numvslots)
{
- int *prev = new (false) int[numvslots];
- if(!prev) return;
- for(int i=0;i<numvslots;++i)prev[i]=-1;
- while(numvslots > 0)
- {
- int changed = f->getlil<int>();
- if(changed < 0)
- {
- loopi(-changed) vslots.add(new VSlot(NULL, vslots.length()));
- numvslots += changed;
- }
- else
- {
- prev[vslots.length()] = f->getlil<int>();
- loadvslot(f, *vslots.add(new VSlot(NULL, vslots.length())), changed);
- numvslots--;
- }
- }
- loopv(vslots) if(vslots.inrange(prev[i])) vslots[prev[i]]->next = vslots[i];
- delete[] prev;
+ int *prev = new (false) int[numvslots];
+ if(!prev) return;
+ for(int i=0;i<numvslots;++i)prev[i]=-1;
+ while(numvslots > 0)
+ {
+ int changed = f->getlil<int>();
+ if(changed < 0)
+ {
+ loopi(-changed) vslots.add(new VSlot(NULL, vslots.length()));
+ numvslots += changed;
+ }
+ else
+ {
+ prev[vslots.length()] = f->getlil<int>();
+ loadvslot(f, *vslots.add(new VSlot(NULL, vslots.length())), changed);
+ numvslots--;
+ }
+ }
+ loopv(vslots) if(vslots.inrange(prev[i])) vslots[prev[i]]->next = vslots[i];
+ delete[] prev;
}
bool save_world(const char *mname, bool nolms)
{
- if(!*mname) mname = game::getclientmap();
- setmapfilenames(mname);
- if(savebak) backup(ogzname, bakname);
- stream *f = opengzfile(ogzname, "wb");
- if(!f) { conoutf(CON_WARN, "could not write map to %s", ogzname); return false; }
-
- int numvslots = vslots.length();
- if(!nolms && !multiplayer(false))
- {
- numvslots = compactvslots();
- allchanged();
- }
-
- savemapprogress = 0;
- renderprogress(0, "saving map...");
-
- octaheader hdr;
- memcpy(hdr.magic, "OCTA", 4);
- hdr.version = MAPVERSION;
- hdr.headersize = sizeof(hdr);
- hdr.worldsize = worldsize;
- hdr.numents = 0;
- const vector<extentity *> &ents = entities::getents();
- loopv(ents) if(ents[i]->type!=ET_EMPTY || nolms) hdr.numents++;
- hdr.numpvs = 0;
- hdr.lightmaps = nolms ? 0 : lightmaps.length();
- hdr.blendmap = shouldsaveblendmap();
- hdr.numvars = 0;
- hdr.numvslots = numvslots;
- enumerate(idents, ident, id,
- {
- if((id.type == ID_VAR || id.type == ID_FVAR || id.type == ID_SVAR) && id.flags&IDF_OVERRIDE && !(id.flags&IDF_READONLY) && id.flags&IDF_OVERRIDDEN) hdr.numvars++;
- });
- lilswap(&hdr.version, 9);
- f->write(&hdr, sizeof(hdr));
-
- enumerate(idents, ident, id,
- {
- if((id.type!=ID_VAR && id.type!=ID_FVAR && id.type!=ID_SVAR) || !(id.flags&IDF_OVERRIDE) || id.flags&IDF_READONLY || !(id.flags&IDF_OVERRIDDEN)) continue;
- f->putchar(id.type);
- f->putlil<ushort>(strlen(id.name));
- f->write(id.name, strlen(id.name));
- switch(id.type)
- {
- case ID_VAR:
- f->putlil<int>(*id.storage.i);
- break;
-
- case ID_FVAR:
- f->putlil<float>(*id.storage.f);
- break;
-
- case ID_SVAR:
- f->putlil<ushort>(strlen(*id.storage.s));
- f->write(*id.storage.s, strlen(*id.storage.s));
- break;
- }
- });
-
- f->putchar((int)strlen(game::gameident()));
- f->write(game::gameident(), (int)strlen(game::gameident())+1);
- f->putlil<ushort>(entities::extraentinfosize());
- vector<char> extras;
- game::writegamedata(extras);
- f->putlil<ushort>(extras.length());
- f->write(extras.getbuf(), extras.length());
-
- f->putlil<ushort>(texmru.length());
- loopv(texmru) f->putlil<ushort>(texmru[i]);
- char *ebuf = new char[entities::extraentinfosize()];
- loopv(ents)
- {
- if(ents[i]->type!=ET_EMPTY || nolms)
- {
- entity tmp = *ents[i];
- lilswap(&tmp.o.x, 3);
- lilswap(&tmp.attr1, 5);
- f->write(&tmp, sizeof(entity));
- entities::writeent(*ents[i], ebuf);
- if(entities::extraentinfosize()) f->write(ebuf, entities::extraentinfosize());
- }
- }
- delete[] ebuf;
-
- savevslots(f, numvslots);
-
- renderprogress(0, "saving octree...");
- savec(worldroot, ivec(0, 0, 0), worldsize>>1, f, nolms);
-
- if(!nolms)
- {
- if(lightmaps.length()) renderprogress(0, "saving lightmaps...");
- loopv(lightmaps)
- {
- LightMap &lm = lightmaps[i];
- f->putchar(lm.type | (lm.unlitx>=0 ? 0x80 : 0));
- if(lm.unlitx>=0)
- {
- f->putlil<ushort>(ushort(lm.unlitx));
- f->putlil<ushort>(ushort(lm.unlity));
- }
- f->write(lm.data, lm.bpp*LM_PACKW*LM_PACKH);
- renderprogress(float(i+1)/lightmaps.length(), "saving lightmaps...");
- }
- }
- if(shouldsaveblendmap()) { renderprogress(0, "saving blendmap..."); saveblendmap(f); }
-
- delete f;
- conoutf("wrote map file %s", ogzname);
- return true;
+ if(!*mname) mname = game::getclientmap();
+ setmapfilenames(mname);
+ if(savebak) backup(ogzname, bakname);
+ stream *f = opengzfile(ogzname, "wb");
+ if(!f) { conoutf(CON_WARN, "could not write map to %s", ogzname); return false; }
+
+ int numvslots = vslots.length();
+ if(!nolms && !multiplayer(false))
+ {
+ numvslots = compactvslots();
+ allchanged();
+ }
+
+ savemapprogress = 0;
+ renderprogress(0, "saving map...");
+
+ octaheader hdr;
+ memcpy(hdr.magic, "OCTA", 4);
+ hdr.version = MAPVERSION;
+ hdr.headersize = sizeof(hdr);
+ hdr.worldsize = worldsize;
+ hdr.numents = 0;
+ const vector<extentity *> &ents = entities::getents();
+ loopv(ents) if(ents[i]->type!=ET_EMPTY || nolms) hdr.numents++;
+ hdr.numpvs = 0;
+ hdr.lightmaps = nolms ? 0 : lightmaps.length();
+ hdr.blendmap = shouldsaveblendmap();
+ hdr.numvars = 0;
+ hdr.numvslots = numvslots;
+ enumerate(idents, ident, id,
+ {
+ if((id.type == ID_VAR || id.type == ID_FVAR || id.type == ID_SVAR) && id.flags&IDF_OVERRIDE && !(id.flags&IDF_READONLY) && id.flags&IDF_OVERRIDDEN) hdr.numvars++;
+ });
+ lilswap(&hdr.version, 9);
+ f->write(&hdr, sizeof(hdr));
+
+ enumerate(idents, ident, id,
+ {
+ if((id.type!=ID_VAR && id.type!=ID_FVAR && id.type!=ID_SVAR) || !(id.flags&IDF_OVERRIDE) || id.flags&IDF_READONLY || !(id.flags&IDF_OVERRIDDEN)) continue;
+ f->putchar(id.type);
+ f->putlil<ushort>(strlen(id.name));
+ f->write(id.name, strlen(id.name));
+ switch(id.type)
+ {
+ case ID_VAR:
+ f->putlil<int>(*id.storage.i);
+ break;
+
+ case ID_FVAR:
+ f->putlil<float>(*id.storage.f);
+ break;
+
+ case ID_SVAR:
+ f->putlil<ushort>(strlen(*id.storage.s));
+ f->write(*id.storage.s, strlen(*id.storage.s));
+ break;
+ }
+ });
+
+ f->putchar((int)strlen(game::gameident()));
+ f->write(game::gameident(), (int)strlen(game::gameident())+1);
+ f->putlil<ushort>(entities::extraentinfosize());
+ vector<char> extras;
+ game::writegamedata(extras);
+ f->putlil<ushort>(extras.length());
+ f->write(extras.getbuf(), extras.length());
+
+ f->putlil<ushort>(texmru.length());
+ loopv(texmru) f->putlil<ushort>(texmru[i]);
+ char *ebuf = new char[entities::extraentinfosize()];
+ loopv(ents)
+ {
+ if(ents[i]->type!=ET_EMPTY || nolms)
+ {
+ entity tmp = *ents[i];
+ lilswap(&tmp.o.x, 3);
+ lilswap(&tmp.attr1, 5);
+ f->write(&tmp, sizeof(entity));
+ entities::writeent(*ents[i], ebuf);
+ if(entities::extraentinfosize()) f->write(ebuf, entities::extraentinfosize());
+ }
+ }
+ delete[] ebuf;
+
+ savevslots(f, numvslots);
+
+ renderprogress(0, "saving octree...");
+ savec(worldroot, ivec(0, 0, 0), worldsize>>1, f, nolms);
+
+ if(!nolms)
+ {
+ if(lightmaps.length()) renderprogress(0, "saving lightmaps...");
+ loopv(lightmaps)
+ {
+ LightMap &lm = lightmaps[i];
+ f->putchar(lm.type | (lm.unlitx>=0 ? 0x80 : 0));
+ if(lm.unlitx>=0)
+ {
+ f->putlil<ushort>(ushort(lm.unlitx));
+ f->putlil<ushort>(ushort(lm.unlity));
+ }
+ f->write(lm.data, lm.bpp*LM_PACKW*LM_PACKH);
+ renderprogress(float(i+1)/lightmaps.length(), "saving lightmaps...");
+ }
+ }
+ if(shouldsaveblendmap()) { renderprogress(0, "saving blendmap..."); saveblendmap(f); }
+
+ delete f;
+ conoutf("wrote map file %s", ogzname);
+ return true;
}
static uint mapcrc = 0;
@@ -991,276 +991,271 @@ static uint mapcrc = 0;
uint getmapcrc() { return mapcrc; }
void clearmapcrc() { mapcrc = 0; }
-bool load_world(const char *mname, const char *cname) // still supports all map formats that have existed since the earliest cube betas!
+bool load_world(const char *mname, const char *cname) // still supports all map formats that have existed since the earliest cube betas!
{
- int loadingstart = SDL_GetTicks();
- setmapfilenames(mname, cname);
- stream *f = opengzfile(ogzname, "rb");
- if(!f) { conoutf(CON_ERROR, "could not read map %s", ogzname); return false; }
- octaheader hdr;
- if(f->read(&hdr, 7*sizeof(int)) != 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
- lilswap(&hdr.version, 6);
- if(memcmp(hdr.magic, "OCTA", 4) || hdr.worldsize <= 0|| hdr.numents < 0) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
- if(hdr.version>MAPVERSION) { conoutf(CON_ERROR, "map %s requires a newer version of Cube 2: Sauerbraten", ogzname); delete f; return false; }
- compatheader chdr;
- if(hdr.version <= 28)
- {
- if(f->read(&chdr.lightprecision, sizeof(chdr) - 7*sizeof(int)) != sizeof(chdr) - 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
- }
- else
- {
- int extra = 0;
- if(hdr.version <= 29) extra++;
- if(f->read(&hdr.blendmap, sizeof(hdr) - (7+extra)*sizeof(int)) != sizeof(hdr) - (7+extra)*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
- }
-
- resetmap();
-
- Texture *mapshot = textureload(picname, 3, true, false);
- renderbackground("loading...", mapshot, mname, game::getmapinfo());
-
- game::loadingmap(cname ? cname : mname);
-
- setvar("mapversion", hdr.version, true, false);
-
- if(hdr.version <= 28)
- {
- lilswap(&chdr.lightprecision, 3);
- if(chdr.lightprecision) setvar("lightprecision", chdr.lightprecision);
- if(chdr.lighterror) setvar("lighterror", chdr.lighterror);
- if(chdr.bumperror) setvar("bumperror", chdr.bumperror);
- setvar("lightlod", chdr.lightlod);
- if(chdr.ambient) setvar("ambient", chdr.ambient);
- setvar("skylight", (int(chdr.skylight[0])<<16) | (int(chdr.skylight[1])<<8) | int(chdr.skylight[2]));
- setvar("watercolour", (int(chdr.watercolour[0])<<16) | (int(chdr.watercolour[1])<<8) | int(chdr.watercolour[2]), true);
- setvar("waterfallcolour", (int(chdr.waterfallcolour[0])<<16) | (int(chdr.waterfallcolour[1])<<8) | int(chdr.waterfallcolour[2]));
- setvar("lavacolour", (int(chdr.lavacolour[0])<<16) | (int(chdr.lavacolour[1])<<8) | int(chdr.lavacolour[2]));
- setvar("fullbright", 0, true);
- if(chdr.lerpsubdivsize || chdr.lerpangle) setvar("lerpangle", chdr.lerpangle);
- if(chdr.lerpsubdivsize)
- {
- setvar("lerpsubdiv", chdr.lerpsubdiv);
- setvar("lerpsubdivsize", chdr.lerpsubdivsize);
- }
- setsvar("maptitle", chdr.maptitle);
- hdr.blendmap = chdr.blendmap;
- hdr.numvars = 0;
- hdr.numvslots = 0;
- }
- else
- {
- lilswap(&hdr.blendmap, 2);
- if(hdr.version <= 29) hdr.numvslots = 0;
- else lilswap(&hdr.numvslots, 1);
- }
-
- renderprogress(0, "clearing world...");
-
- freeocta(worldroot);
- worldroot = NULL;
-
- int worldscale = 0;
- while(1<<worldscale < hdr.worldsize) worldscale++;
- setvar("mapsize", 1<<worldscale, true, false);
- setvar("mapscale", worldscale, true, false);
-
- renderprogress(0, "loading vars...");
-
- loopi(hdr.numvars)
- {
- int type = f->getchar(), ilen = f->getlil<ushort>();
- string name;
- f->read(name, min(ilen, MAXSTRLEN-1));
- name[min(ilen, MAXSTRLEN-1)] = '\0';
- if(ilen >= MAXSTRLEN) f->seek(ilen - (MAXSTRLEN-1), SEEK_CUR);
- ident *id = getident(name);
- bool exists = id && id->type == type && id->flags&IDF_OVERRIDE;
- switch(type)
- {
- case ID_VAR:
- {
- int val = f->getlil<int>();
- if(exists && id->minval <= id->maxval) setvar(name, val);
- break;
- }
-
- case ID_FVAR:
- {
- float val = f->getlil<float>();
- if(exists && id->minvalf <= id->maxvalf) setfvar(name, val);
- break;
- }
-
- case ID_SVAR:
- {
- int slen = f->getlil<ushort>();
- string val;
- f->read(val, min(slen, MAXSTRLEN-1));
- val[min(slen, MAXSTRLEN-1)] = '\0';
- if(slen >= MAXSTRLEN) f->seek(slen - (MAXSTRLEN-1), SEEK_CUR);
- if(exists) setsvar(name, val);
- break;
- }
- }
- }
-
- string gametype;
- copystring(gametype, "fps");
- bool samegame = true;
- int eif = 0;
- if(hdr.version>=16)
- {
- int len = f->getchar();
- f->read(gametype, len+1);
- }
- if(strcmp(gametype, game::gameident())!=0)
- {
- samegame = false;
- conoutf(CON_WARN, "WARNING: loading map from %s game, ignoring entities except for lights/mapmodels", gametype);
- }
- if(hdr.version>=16)
- {
- eif = f->getlil<ushort>();
- int extrasize = f->getlil<ushort>();
- vector<char> extras;
- f->read(extras.pad(extrasize), extrasize);
- if(samegame) game::readgamedata(extras);
- }
-
- texmru.shrink(0);
- if(hdr.version<14)
- {
- uchar oldtl[256];
- f->read(oldtl, sizeof(oldtl));
- loopi(256) texmru.add(oldtl[i]);
- }
- else
- {
- ushort nummru = f->getlil<ushort>();
- loopi(nummru) texmru.add(f->getlil<ushort>());
- }
-
- renderprogress(0, "loading entities...");
-
- vector<extentity *> &ents = entities::getents();
- int einfosize = entities::extraentinfosize();
- char *ebuf = einfosize > 0 ? new char[einfosize] : NULL;
- loopi(min(hdr.numents, MAXENTS))
- {
- extentity &e = *entities::newentity();
- ents.add(&e);
- f->read(&e, sizeof(entity));
- lilswap(&e.o.x, 3);
- lilswap(&e.attr1, 5);
- fixent(e, hdr.version);
- if(samegame)
- {
- if(einfosize > 0) f->read(ebuf, einfosize);
- entities::readent(e, ebuf, mapversion);
- }
- else
- {
- if(eif > 0) f->seek(eif, SEEK_CUR);
- if(e.type>=ET_GAMESPECIFIC || hdr.version<=14)
- {
- entities::deleteentity(ents.pop());
- continue;
- }
- }
- if(!insideworld(e.o))
- {
- if(e.type != ET_LIGHT && e.type != ET_SPOTLIGHT)
- {
- conoutf(CON_WARN, "warning: ent outside of world: enttype[%s] index %d (%f, %f, %f)", entities::entname(e.type), i, e.o.x, e.o.y, e.o.z);
- }
- }
- if(hdr.version <= 14 && e.type == ET_MAPMODEL)
- {
- e.o.z += e.attr3;
- if(e.attr4) conoutf(CON_WARN, "warning: mapmodel ent (index %d) uses texture slot %d", i, e.attr4);
- e.attr3 = e.attr4 = 0;
- }
- }
- if(ebuf) delete[] ebuf;
-
- if(hdr.numents > MAXENTS)
- {
- conoutf(CON_WARN, "warning: map has %d entities", hdr.numents);
- f->seek((hdr.numents-MAXENTS)*(samegame ? sizeof(entity) + einfosize : eif), SEEK_CUR);
- }
-
- renderprogress(0, "loading slots...");
- loadvslots(f, hdr.numvslots);
-
- renderprogress(0, "loading octree...");
- bool failed = false;
- worldroot = loadchildren(f, ivec(0, 0, 0), hdr.worldsize>>1, failed);
- if(failed) conoutf(CON_ERROR, "garbage in map");
-
- renderprogress(0, "validating...");
- validatec(worldroot, hdr.worldsize>>1);
-
- if(!failed)
- {
- if(hdr.version >= 7) loopi(hdr.lightmaps)
- {
- renderprogress(i/(float)hdr.lightmaps, "loading lightmaps...");
- LightMap &lm = lightmaps.add();
- if(hdr.version >= 17)
- {
- int type = f->getchar();
- lm.type = type&0x7F;
- if(hdr.version >= 20 && type&0x80)
- {
- lm.unlitx = f->getlil<ushort>();
- lm.unlity = f->getlil<ushort>();
- }
- }
- if(lm.type&LM_ALPHA && (lm.type&LM_TYPE)!=LM_BUMPMAP1) lm.bpp = 4;
- lm.data = new uchar[lm.bpp*LM_PACKW*LM_PACKH];
- f->read(lm.data, lm.bpp * LM_PACKW * LM_PACKH);
- lm.finalize();
- }
-
- if(hdr.version >= 28 && hdr.blendmap) loadblendmap(f, hdr.blendmap);
- }
-
- mapcrc = f->getcrc();
- delete f;
-
- conoutf("read map %s (%.1f seconds)", ogzname, (SDL_GetTicks()-loadingstart)/1000.0f);
-
- clearmainmenu();
-
- identflags |= IDF_OVERRIDDEN;
- execfile("data/default_map.cfg", false);
- execfile(cfgname, false);
- identflags &= ~IDF_OVERRIDDEN;
-
- extern void fixlightmapnormals();
- if(hdr.version <= 25) fixlightmapnormals();
- extern void fixrotatedlightmaps();
- if(hdr.version <= 31) fixrotatedlightmaps();
-
- preloadusedmapmodels(true);
-
- game::preload();
- flushpreloadedmodels();
-
- preloadmapsounds();
-
- entitiesinoctanodes();
- attachentities();
- initlights();
- allchanged(true);
-
- renderbackground("loading...", mapshot, mname, game::getmapinfo());
-
- if(maptitle[0] && strcmp(maptitle, "Untitled Map by Unknown")) conoutf(CON_ECHO, "%s", maptitle);
-
- startmap(cname ? cname : mname);
-
- return true;
+ int loadingstart = SDL_GetTicks();
+ setmapfilenames(mname, cname);
+ stream *f = opengzfile(ogzname, "rb");
+ if(!f) { conoutf(CON_ERROR, "could not read map %s", ogzname); return false; }
+ octaheader hdr;
+ if(f->read(&hdr, 7*sizeof(int)) != 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
+ lilswap(&hdr.version, 6);
+ if(memcmp(hdr.magic, "OCTA", 4) || hdr.worldsize <= 0|| hdr.numents < 0) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
+ if(hdr.version>MAPVERSION) { conoutf(CON_ERROR, "map %s requires a newer version of Cube 2: Sauerbraten", ogzname); delete f; return false; }
+ compatheader chdr;
+ if(hdr.version <= 28)
+ {
+ if(f->read(&chdr.lightprecision, sizeof(chdr) - 7*sizeof(int)) != sizeof(chdr) - 7*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
+ }
+ else
+ {
+ int extra = 0;
+ if(hdr.version <= 29) extra++;
+ if(f->read(&hdr.blendmap, sizeof(hdr) - (7+extra)*sizeof(int)) != sizeof(hdr) - (7+extra)*sizeof(int)) { conoutf(CON_ERROR, "map %s has malformatted header", ogzname); delete f; return false; }
+ }
+
+ resetmap();
+
+ Texture *mapshot = textureload(picname, 3, true, false);
+ renderbackground("loading...", mapshot, mname, game::getmapinfo());
+
+ game::loadingmap(cname ? cname : mname);
+
+ setvar("mapversion", hdr.version, true, false);
+
+ if(hdr.version <= 28)
+ {
+ lilswap(&chdr.lightprecision, 3);
+ if(chdr.lightprecision) setvar("lightprecision", chdr.lightprecision);
+ if(chdr.lighterror) setvar("lighterror", chdr.lighterror);
+ if(chdr.bumperror) setvar("bumperror", chdr.bumperror);
+ setvar("lightlod", chdr.lightlod);
+ if(chdr.ambient) setvar("ambient", chdr.ambient);
+ setvar("skylight", (int(chdr.skylight[0])<<16) | (int(chdr.skylight[1])<<8) | int(chdr.skylight[2]));
+ setvar("watercolour", (int(chdr.watercolour[0])<<16) | (int(chdr.watercolour[1])<<8) | int(chdr.watercolour[2]), true);
+ setvar("waterfallcolour", (int(chdr.waterfallcolour[0])<<16) | (int(chdr.waterfallcolour[1])<<8) | int(chdr.waterfallcolour[2]));
+ setvar("lavacolour", (int(chdr.lavacolour[0])<<16) | (int(chdr.lavacolour[1])<<8) | int(chdr.lavacolour[2]));
+ setvar("fullbright", 0, true);
+ if(chdr.lerpsubdivsize || chdr.lerpangle) setvar("lerpangle", chdr.lerpangle);
+ if(chdr.lerpsubdivsize)
+ {
+ setvar("lerpsubdiv", chdr.lerpsubdiv);
+ setvar("lerpsubdivsize", chdr.lerpsubdivsize);
+ }
+ setsvar("maptitle", chdr.maptitle);
+ hdr.blendmap = chdr.blendmap;
+ hdr.numvars = 0;
+ hdr.numvslots = 0;
+ }
+ else
+ {
+ lilswap(&hdr.blendmap, 2);
+ if(hdr.version <= 29) hdr.numvslots = 0;
+ else lilswap(&hdr.numvslots, 1);
+ }
+
+ renderprogress(0, "clearing world...");
+
+ freeocta(worldroot);
+ worldroot = NULL;
+
+ int worldscale = 0;
+ while(1<<worldscale < hdr.worldsize) worldscale++;
+ setvar("mapsize", 1<<worldscale, true, false);
+ setvar("mapscale", worldscale, true, false);
+
+ renderprogress(0, "loading vars...");
+
+ loopi(hdr.numvars)
+ {
+ int type = f->getchar(), ilen = f->getlil<ushort>();
+ string name;
+ f->read(name, min(ilen, MAXSTRLEN-1));
+ name[min(ilen, MAXSTRLEN-1)] = '\0';
+ if(ilen >= MAXSTRLEN) f->seek(ilen - (MAXSTRLEN-1), SEEK_CUR);
+ ident *id = getident(name);
+ bool exists = id && id->type == type && id->flags&IDF_OVERRIDE;
+ switch(type)
+ {
+ case ID_VAR:
+ {
+ int val = f->getlil<int>();
+ if(exists && id->minval <= id->maxval) setvar(name, val);
+ break;
+ }
+
+ case ID_FVAR:
+ {
+ float val = f->getlil<float>();
+ if(exists && id->minvalf <= id->maxvalf) setfvar(name, val);
+ break;
+ }
+
+ case ID_SVAR:
+ {
+ int slen = f->getlil<ushort>();
+ string val;
+ f->read(val, min(slen, MAXSTRLEN-1));
+ val[min(slen, MAXSTRLEN-1)] = '\0';
+ if(slen >= MAXSTRLEN) f->seek(slen - (MAXSTRLEN-1), SEEK_CUR);
+ if(exists) setsvar(name, val);
+ break;
+ }
+ }
+ }
+
+ string gametype;
+ copystring(gametype, "fps");
+ bool samegame = true;
+ int eif = 0;
+ if(hdr.version>=16)
+ {
+ int len = f->getchar();
+ f->read(gametype, len+1);
+ }
+ if(strcmp(gametype, game::gameident())!=0)
+ {
+ samegame = false;
+ conoutf(CON_WARN, "WARNING: loading map from %s game, ignoring entities except for lights/mapmodels", gametype);
+ }
+ if(hdr.version>=16)
+ {
+ eif = f->getlil<ushort>();
+ int extrasize = f->getlil<ushort>();
+ vector<char> extras;
+ f->read(extras.pad(extrasize), extrasize);
+ if(samegame) game::readgamedata(extras);
+ }
+
+ texmru.shrink(0);
+ if(hdr.version<14)
+ {
+ uchar oldtl[256];
+ f->read(oldtl, sizeof(oldtl));
+ loopi(256) texmru.add(oldtl[i]);
+ }
+ else
+ {
+ ushort nummru = f->getlil<ushort>();
+ loopi(nummru) texmru.add(f->getlil<ushort>());
+ }
+
+ renderprogress(0, "loading entities...");
+
+ vector<extentity *> &ents = entities::getents();
+ int einfosize = entities::extraentinfosize();
+ char *ebuf = einfosize > 0 ? new char[einfosize] : NULL;
+ loopi(min(hdr.numents, MAXENTS))
+ {
+ extentity &e = *entities::newentity();
+ ents.add(&e);
+ f->read(&e, sizeof(entity));
+ lilswap(&e.o.x, 3);
+ lilswap(&e.attr1, 5);
+ fixent(e, hdr.version);
+ if(samegame)
+ {
+ if(einfosize > 0) f->read(ebuf, einfosize);
+ entities::readent(e, ebuf, mapversion);
+ }
+ else
+ {
+ if(eif > 0) f->seek(eif, SEEK_CUR);
+ if(e.type>=ET_GAMESPECIFIC || hdr.version<=14)
+ {
+ entities::deleteentity(ents.pop());
+ continue;
+ }
+ }
+ if(!insideworld(e.o))
+ {
+ if(e.type != ET_LIGHT && e.type != ET_SPOTLIGHT)
+ {
+ conoutf(CON_WARN, "warning: ent outside of world: enttype[%s] index %d (%f, %f, %f)", entities::entname(e.type), i, e.o.x, e.o.y, e.o.z);
+ }
+ }
+ if(hdr.version <= 14 && e.type == ET_MAPMODEL)
+ {
+ e.o.z += e.attr3;
+ if(e.attr4) conoutf(CON_WARN, "warning: mapmodel ent (index %d) uses texture slot %d", i, e.attr4);
+ e.attr3 = e.attr4 = 0;
+ }
+ }
+ if(ebuf) delete[] ebuf;
+
+ if(hdr.numents > MAXENTS)
+ {
+ conoutf(CON_WARN, "warning: map has %d entities", hdr.numents);
+ f->seek((hdr.numents-MAXENTS)*(samegame ? sizeof(entity) + einfosize : eif), SEEK_CUR);
+ }
+
+ renderprogress(0, "loading slots...");
+ loadvslots(f, hdr.numvslots);
+
+ renderprogress(0, "loading octree...");
+ bool failed = false;
+ worldroot = loadchildren(f, ivec(0, 0, 0), hdr.worldsize>>1, failed);
+ if(failed) conoutf(CON_ERROR, "garbage in map");
+
+ renderprogress(0, "validating...");
+ validatec(worldroot, hdr.worldsize>>1);
+
+ if(!failed)
+ {
+ if(hdr.version >= 7) loopi(hdr.lightmaps)
+ {
+ renderprogress(i/(float)hdr.lightmaps, "loading lightmaps...");
+ LightMap &lm = lightmaps.add();
+ if(hdr.version >= 17)
+ {
+ int type = f->getchar();
+ lm.type = type&0x7F;
+ if(hdr.version >= 20 && type&0x80)
+ {
+ lm.unlitx = f->getlil<ushort>();
+ lm.unlity = f->getlil<ushort>();
+ }
+ }
+ if(lm.type&LM_ALPHA && (lm.type&LM_TYPE)!=LM_BUMPMAP1) lm.bpp = 4;
+ lm.data = new uchar[lm.bpp*LM_PACKW*LM_PACKH];
+ f->read(lm.data, lm.bpp * LM_PACKW * LM_PACKH);
+ lm.finalize();
+ }
+
+ if(hdr.version >= 28 && hdr.blendmap) loadblendmap(f, hdr.blendmap);
+ }
+
+ mapcrc = f->getcrc();
+ delete f;
+
+ conoutf("read map %s (%.1f seconds)", ogzname, (SDL_GetTicks()-loadingstart)/1000.0f);
+
+ clearmainmenu();
+
+ identflags |= IDF_OVERRIDDEN;
+ execfile("data/default_map.cfg", false);
+ execfile(cfgname, false);
+ identflags &= ~IDF_OVERRIDDEN;
+
+ preloadusedmapmodels(true);
+
+ game::preload();
+ flushpreloadedmodels();
+
+ preloadmapsounds();
+
+ entitiesinoctanodes();
+ attachentities();
+ initlights();
+ allchanged(true);
+
+ renderbackground("loading...", mapshot, mname, game::getmapinfo());
+
+ if(maptitle[0] && strcmp(maptitle, "Untitled Map by Unknown")) conoutf(CON_ECHO, "%s", maptitle);
+
+ startmap(cname ? cname : mname);
+
+ return true;
}
void savecurrentmap() { save_world(game::getclientmap()); }