From d309df4ce4d8ad0ed995a8e1c4267412a7782021 Mon Sep 17 00:00:00 2001 From: xolatile Date: Mon, 4 Aug 2025 22:53:42 +0200 Subject: Bunch of small changes... --- src/engine/3dgui.cpp | 2534 ++++++++++---------- src/engine/animmodel.h | 3165 ++++++++++++------------- src/engine/bih.cpp | 572 ++--- src/engine/bih.h | 146 +- src/engine/blend.cpp | 1168 +++++----- src/engine/client.cpp | 354 +-- src/engine/command.cpp | 5030 ++++++++++++++++++++-------------------- src/engine/console.cpp | 1110 +++++---- src/engine/decal.cpp | 1168 +++++----- src/engine/depthfx.h | 252 +- src/engine/dynlight.cpp | 338 +-- src/engine/engine.h | 79 +- src/engine/explosion.h | 474 ++-- src/engine/glare.cpp | 50 +- src/engine/iqm.h | 708 +++--- src/engine/lightmap.cpp | 4485 +++++++++++++++++------------------ src/engine/lightmap.h | 146 +- src/engine/lightning.h | 178 +- src/engine/main.cpp | 2094 ++++++++--------- src/engine/master.cpp | 1040 ++++----- src/engine/material.cpp | 1491 ++++++------ src/engine/md3.h | 302 +-- src/engine/md5.h | 792 +++---- src/engine/menus.cpp | 992 ++++---- src/engine/model.h | 152 +- src/engine/mpr.h | 1138 ++++----- src/engine/normal.cpp | 588 ++--- src/engine/octa.cpp | 2878 +++++++++++------------ src/engine/octa.h | 364 +-- src/engine/octaedit.cpp | 4221 +++++++++++++++++---------------- src/engine/octarender.cpp | 2833 +++++++++++----------- src/engine/physics.cpp | 3244 ++++++++++++-------------- src/engine/ragdoll.h | 916 ++++---- src/engine/rendergl.cpp | 2997 ++++++++++++------------ src/engine/rendermodel.cpp | 1434 ++++++------ src/engine/renderparticles.cpp | 2402 ++++++++++--------- src/engine/rendersky.cpp | 1230 +++++----- src/engine/rendertarget.h | 766 +++--- src/engine/rendertext.cpp | 550 ++--- src/engine/renderva.cpp | 2856 +++++++++++------------ src/engine/server.cpp | 1128 ++++----- src/engine/serverbrowser.cpp | 1160 ++++----- src/engine/shader.cpp | 2373 ++++++++++--------- src/engine/shadowmap.cpp | 400 ++-- src/engine/skelmodel.h | 3664 ++++++++++++++--------------- src/engine/sound.cpp | 1278 +++++----- src/engine/textedit.h | 1421 ++++++------ src/engine/texture.cpp | 4963 +++++++++++++++++++-------------------- src/engine/texture.h | 1227 +++++----- src/engine/vertmodel.h | 956 ++++---- src/engine/water.cpp | 1670 ++++++------- src/engine/world.cpp | 1895 ++++++++------- src/engine/world.h | 72 +- src/engine/worldio.cpp | 2307 +++++++++--------- 54 files changed, 40465 insertions(+), 41286 deletions(-) (limited to 'src/engine') 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 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=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=y && hity=x && hity>=y && hitx=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<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.xo.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 && txgui(*this, layoutpass); - } + struct list + { + int parent, w, h, springs, curspring, column; + }; + + int firstlist, nextlist; + int columns[MAXCOLUMNS]; + + static vector 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=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=y && hity=x && hity>=y && hitx=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<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.xo.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 && txgui(*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::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 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, 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 void smoothnorms(V *verts, int numverts, T *tris, int numtris, float limit, bool areaweight) - { - hashtable share; - int *next = new int[numverts]; - for(int i=0;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 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 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 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 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 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 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 &skins, vector &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=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 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 links; - vector skins; - vector *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, 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 &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 &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<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(diffaniminterp[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[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 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) - { - 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 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 ¢er, 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 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, 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 void smoothnorms(V *verts, int numverts, T *tris, int numtris, float limit, bool areaweight) + { + hashtable share; + int *next = new int[numverts]; + for(int i=0;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 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 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 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 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 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 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 &skins, vector &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=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 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 links; + vector skins; + vector *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, 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 &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 &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<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(diffaniminterp[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[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 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) + { + 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 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 ¢er, 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::shaderparamskey::keys; @@ -1431,35 +1432,35 @@ int animmodel::shaderparamskey::firstversion = 0, animmodel::shaderparamskey::la template 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 MDL *modelloader::loading = NULL; @@ -1467,154 +1468,154 @@ template string modelloader::dir = {'\0'}; // template 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 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 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 &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 &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 &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<>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, bmsize), - y2 = min(((o.y + size + (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<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<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<>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, bmsize), + y2 = min(((o.y + size + (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<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<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<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<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<origin.x, ry = iy-cache->origin.y; - loop(vy, 2) loop(vx, 2) - { - int cx = clamp(rx+vx, 0, (1<scale)-1), cy = clamp(ry+vy, 0, (1<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<origin.x, ry = iy-cache->origin.y; + loop(vy, 2) loop(vx, 2) + { + int cx = clamp(rx+vx, 0, (1<scale)-1), cy = clamp(ry+vy, 0, (1<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 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<w), - y = (int)floor(clamp(worldpos.y, 0.0f, float(worldsize))/(1<h); - blitblendmap(brush->data, x, y, brush->w, brush->h, blendpaintmode); - previewblends(ivec((x-1)<w+1)<h+1)<w), + y = (int)floor(clamp(worldpos.y, 0.0f, float(worldsize))/(1<h); + blitblendmap(brush->data, x, y, brush->w, brush->h, blendpaintmode); + previewblends(ivec((x-1)<w+1)<h+1)<>BM_SCALE, y1 = sel.o.y>>BM_SCALE, - x2 = (sel.o.x+sel.s.x*sel.grid+(1<>BM_SCALE, - y2 = (sel.o.y+sel.s.y*sel.grid+(1<>BM_SCALE; - fillblendmap(x1, y1, x2-x1, y2-y1, 0xFF); - previewblends(ivec(x1<>BM_SCALE, y1 = sel.o.y>>BM_SCALE, + x2 = (sel.o.x+sel.s.x*sel.grid+(1<>BM_SCALE, + y2 = (sel.o.y+sel.s.y*sel.grid+(1<>BM_SCALE; + fillblendmap(x1, y1, x2-x1, y2-y1, 0xFF); + previewblends(ivec(x1<>BM_SCALE, y1 = sel.o.y>>BM_SCALE, - x2 = (sel.o.x+sel.s.x*sel.grid+(1<>BM_SCALE, - y2 = (sel.o.y+sel.s.y*sel.grid+(1<>BM_SCALE; - invertblendmap(x1, y1, x2-x1, y2-y1); - previewblends(ivec(x1<>BM_SCALE, y1 = sel.o.y>>BM_SCALE, + x2 = (sel.o.x+sel.s.x*sel.grid+(1<>BM_SCALE, + y2 = (sel.o.y+sel.s.y*sel.grid+(1<>BM_SCALE; + invertblendmap(x1, y1, x2-x1, y2-y1); + previewblends(ivec(x1<>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<= 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<= 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<w) << BM_SCALE, - y1 = (int)floor(clamp(worldpos.y, 0.0f, float(worldsize))/(1<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<w) << BM_SCALE, + y1 = (int)floor(clamp(worldpos.y, 0.0f, float(worldsize))/(1<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 *identinits = NULL; static inline ident *addident(const ident &id) { - if(!initedidents) - { - if(!identinits) identinits = new vector; - 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; + 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<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<index))) - { - pusharg(*id, nullval, aliasstack->argstack[id->index]); - aliasstack->usedargs |= 1<index; - } - return id; + ident *id = newident(name, flags); + if(id->index < MAXARGS && !(aliasstack->usedargs&(1<index))) + { + pusharg(*id, nullval, aliasstack->argstack[id->index]); + aliasstack->usedargs |= 1<index; + } + return id; } ident *readident(const char *name) { - ident *id = idents.access(name); - if(id && id->index < MAXARGS && !(aliasstack->usedargs&(1<index))) - return NULL; - return id; + ident *id = idents.access(name); + if(id && id->index < MAXARGS && !(aliasstack->usedargs&(1<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<argstack[id.index]); - aliasstack->usedargs |= 1<usedargs&(1<argstack[id.index]); + aliasstack->usedargs |= 1<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<index)) ? i->getstr() : ""; + ident *i = idents.access(name); + return i && i->type==ID_ALIAS && (i->index >= MAXARGS || aliasstack->usedargs&(1<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< 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< 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 &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 &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 &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 &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 &code) { - code.add(CODE_VALI|RET_NULL); + code.add(CODE_VALI|RET_NULL); } static inline void compileblock(vector &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 &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 &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 &code, const char *word = NULL) { - return compileint(code, word ? parseint(word) : 0); + return compileint(code, word ? parseint(word) : 0); } static inline void compilefloat(vector &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 &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 &code, const char *&p, int wordtype); @@ -1016,641 +1016,641 @@ static void compilestatements(vector &code, const char *&p, int rettype, i static inline void compileval(vector &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 &code, const char *&p, int wordtype, char *&word, int &wordlen); static void compilelookup(vector &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)<index<<8)); goto done; - case ID_FVAR: code.add(CODE_FVAR|((ltype >= VAL_ANY ? VAL_FLOAT : ltype)<index<<8)); goto done; - case ID_SVAR: code.add(CODE_SVAR|((ltype >= VAL_ANY ? VAL_STR : ltype)<index<<8)); goto done; - case ID_ALIAS: code.add((id->index < MAXARGS ? CODE_LOOKUPARG : CODE_LOOKUP)|((ltype >= VAL_ANY ? VAL_STR : ltype)<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)<index<<8)); goto done; + case ID_FVAR: code.add(CODE_FVAR|((ltype >= VAL_ANY ? VAL_FLOAT : ltype)<index<<8)); goto done; + case ID_SVAR: code.add(CODE_SVAR|((ltype >= VAL_ANY ? VAL_STR : ltype)<index<<8)); goto done; + case ID_ALIAS: code.add((id->index < MAXARGS ? CODE_LOOKUPARG : CODE_LOOKUP)|((ltype >= VAL_ANY ? VAL_STR : ltype)<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<index<<8)); - code.add(CODE_EXIT|(ltype < VAL_ANY ? ltype<index<<8)); + code.add(CODE_EXIT|(ltype < VAL_ANY ? ltype< &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 &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 &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< 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, 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, 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 &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<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<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 &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< 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 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 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 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 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 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<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 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<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 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<index))) - { - pusharg(*id, nullval, aliasstack->argstack[id->index]); - aliasstack->usedargs |= 1<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<index))) - { - pusharg(*id, nullval, aliasstack->argstack[id->index]); - aliasstack->usedargs |= 1<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<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<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 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<index))) + { + pusharg(*id, nullval, aliasstack->argstack[id->index]); + aliasstack->usedargs |= 1<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<index))) + { + pusharg(*id, nullval, aliasstack->argstack[id->index]); + aliasstack->usedargs |= 1<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<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<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 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<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<>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<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<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 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<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<>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<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<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 code; - code.reserve(64); - compilemain(code, p, VAL_ANY); - runcode(code.getbuf()+1, result); - if(int(code[0]) >= 0x100) code.disown(); + vector 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<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<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 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 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 strbuf[3]; - static int stridx = 0; - stridx = (stridx + 1)%3; - vector &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 strbuf[3]; + static int stridx = 0; + stridx = (stridx + 1)%3; + vector &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 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 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 ids; - enumerate(idents, ident, id, if(id.flags&IDF_OVERRIDDEN) ids.add(&id)); - ids.sortname(); - loopv(ids) printvar(ids[i]); + vector 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 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 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 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 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 *"estart = listquotestart, const char *"eend = 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 &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 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 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 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 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 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 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 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 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 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 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 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 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 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 = { ¯os[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 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 = { ¯os[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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 ¢er, 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 ¢er, 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<>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< 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<>1, cu[i].escaped); - else - { - int vismask = cu[i].merged; - if(vismask) loopj(6) if(vismask&(1<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<>1, cu[i].escaped); - else - { - int vismask = cu[i].merged; - if(vismask) loopj(6) if(vismask&(1<= 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<>1, cu[i].escaped); + else + { + int vismask = cu[i].merged; + if(vismask) loopj(6) if(vismask&(1<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<>1, cu[i].escaped); + else + { + int vismask = cu[i].merged; + if(vismask) loopj(6) if(vismask&(1<packages/particles/scorch.png", DF_ROTATE, 500), - decalrenderer("packages/particles/blood.png", DF_RND4|DF_ROTATE|DF_INVMOD), - decalrenderer("packages/particles/bullet.png", DF_OVERBRIGHT) + decalrenderer("packages/particles/scorch.png", DF_ROTATE, 500), + decalrenderer("packages/particles/blood.png", DF_RND4|DF_ROTATE|DF_INVMOD), + decalrenderer("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 ¢er, 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(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 ¢er, 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(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 ¢er, 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< 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 dynlights; @@ -54,174 +54,174 @@ vector 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(lastmillis0) dynlights.remove(0, faded); + int faded = -1; + loopv(dynlights) if(lastmillis0) 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)<= 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)<= 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 entgroup; // rendertext struct font { - struct charinfo - { - short x, y, w, h, offsetx, offsety, advance, tex; - }; - - char *name; - vector texs; - vector 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 texs; + vector 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 mapmodels; - if(mapmodels.inrange(n)) - { - model *m = mapmodels[n].m; - return m ? m : loadmodel(NULL, n); - } - return NULL; + extern vector 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< (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< (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(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 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 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 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 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 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 &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<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<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<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<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->xyz 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 ¢er = *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->xyz 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 ¢er = *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 ¢er = *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 ¢er = *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<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<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 lights; + int x, y; + vector 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 &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< &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< &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 &ents = entities::getents(); - static volatile bool usinglightcache = false; - if(size <= 1< &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 &ents = entities::getents(); + static volatile bool usinglightcache = false; + if(size <= 1< &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(surftypew) 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(surftypew) 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<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<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<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<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 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<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<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<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 &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 &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 &ents = entities::getents(); - loopv(ents) lightent(*ents[i]); + const vector &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 &ents = entities::getents(); - const vector &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 &ents = entities::getents(); + const vector &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 &ents = entities::getents(); - const vector &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 &ents = entities::getents(); + const vector &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 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 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 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 static inline bool pumpevents(queue &events) { - while(events.empty()) - { - SDL_PumpEvents(); - databuf 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 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; i0, 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 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 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 &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 &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 gameservers; struct messagebuf { - vector &owner; - vector buf; - int refs; + vector &owner; + vector buf; + int refs; - messagebuf(vector &owner) : owner(owner), refs(0) {} + messagebuf(vector &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 gameserverlists, gbanlists; bool updateserverlist = true; struct client { - ENetAddress address; - ENetSocket socket; - char input[INPUT_LIMIT]; - messagebuf *message; - vector output; - int inputpos, outputpos; - enet_uint32 connecttime, lastinput; - int servport; - enet_uint32 lastauth; - vector 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 output; + int inputpos, outputpos; + enet_uint32 connecttime, lastinput; + int servport; + enet_uint32 lastauth; + vector authreqs; + bool shouldpurge; + bool registeredserver; + + client() : message(NULL), inputpos(0), outputpos(0), servport(-1), lastauth(0), shouldpurge(false), registeredserver(false) {} }; vector 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 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 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 &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 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.ym.o.x && n.o.xmaterial && 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< 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.ym.o.x && n.o.xmaterial && 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< 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 &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 &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 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 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(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] && iload(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] && iload(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 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(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 &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=0 && index=0 && index 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()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 hierarchy; - vector 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 &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=0 && index=0 && index 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()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 hierarchy; + vector 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 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 guis; static vector guistack; static vector 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 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 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 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 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 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 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 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 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 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 ¢er, 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 ¢er, 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 ¢er, vec &radius) - { - if(bbradius.x < 0) - { - calcbb(bbcenter, bbradius); - bbradius.add(bbextend); - } - center = bbcenter; - radius = bbradius; - } + void boundbox(vec ¢er, vec &radius) + { + if(bbradius.x < 0) + { + calcbb(bbcenter, bbradius); + bbradius.add(bbextend); + } + center = bbcenter; + radius = bbradius; + } - float collisionbox(vec ¢er, 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 ¢er, 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 ¢er) - { - vec radius; - boundbox(center, radius); - return radius.magnitude(); - } + float boundsphere(vec ¢er) + { + 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 ¢er, 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 ¢er, 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 ¢er, 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 - 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 - 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 ¢er, 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 ¢er, 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 ¢er, 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 + 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 + 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 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<= 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<= 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)) 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<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<= 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< 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 ax16)) 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 ax16)) 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<>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<>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 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¬ouch)==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¬ouch; - - 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¬ouch; - } - 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¬ouch; - } - 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< 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¬ouch; + + 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¬ouch; + } + 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¬ouch; + } + 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<=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< 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< 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< &links, vector &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<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<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<surfaces[orient] = brightsurface; - } + if(c.merged&(1<surfaces[orient] = brightsurface; + } } void addmerges(int orient, const ivec &co, const ivec &n, int offset, vector &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 &polys) { - if(polys.length() <= 1) { addmerges(orient, co, n, offset, polys); return; } - hashset links(polys.length() <= 32 ? 128 : 1024); - vector 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 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 links(polys.length() <= 32 ? 128 : 1024); + vector 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 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 polys; + vector polys; }; static hashtable 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<= 1<>1); + else if(!isempty(c[i])) loopj(6) if((vis = visibletris(c[i], j, co, size))) + { + cfkey k; + poly p; + if(size < 1<= 1<= x2 && - mo.y <= y1 && mo.y + (1<= y2 && - mo.z <= z1 && mo.z + (1<= 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<= x2 && + mo.y <= y1 && mo.y + (1<= y2 && + mo.z <= z1 && mo.z + (1<= 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 mapmodels; - vector 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 mapmodels; + vector 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 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 mapmodels; - int hasmerges, mergelevel; - uint dynlightmask; - bool shadowed; + vtxarray *parent; + vector 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 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)<>(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<>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<=worldsize) gridsize = worldsize/2; - cancelsel(); + if(dragging) return; + gridsize = 1<=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.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.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<c(); + loopxyz(*b, 1<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 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 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 &buf, vector &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 &buf) { - vector used; - cube *c = b.c(); - loopi(b.size()) packvslots(c[i], buf, used); - memset(buf.pad(sizeof(vslothdr)), 0, sizeof(vslothdr)); + vector used; + cube *c = b.c(); + loopi(b.size()) packvslots(c[i], buf, used); + memset(buf.pad(sizeof(vslothdr)), 0, sizeof(vslothdr)); } template 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 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 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 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 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 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 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 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 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 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 verts; - vector chain; - vector 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 verts; + vector chain; + vector 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 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 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(xbmy) select(x, y-1, z); - if(y, <, 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(xmy) ripple(x, y-1, mapz[x][y], true); - if(y 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(xbmy) select(x, y-1, z); + if(y, <, 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(xmy) ripple(x, y-1, mapz[x][y], true); + if(ymx && y>my)); // do diagonals because adjacents - DIAGONAL_RIPPLE(-1, +1, (x>mx && ymy)); - } + 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 && ymy)); + } #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 && oene) || (dir>0 && dc==0 && oe0) == 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 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<changed && nompedit && multiplayer()) return; - } - editingvslot(ds.layer); - mpeditvslot(usevdelta, ds, allfaces, sel, true); + if(noedit()) return; + VSlot ds; + ds.changed = 1<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< 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 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(tiset(); - } - } - 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(tiset(); + } + } + 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<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<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 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 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 &data = vbodata[type]; - if(data.empty()) return; - vector &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 &data = vbodata[type]; + if(data.empty()) return; + vector &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 &data = vbodata[type]; - vector &vas = vbovas[type]; + vector &data = vbodata[type]; + vector &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 verts; - vector 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 verts; + vector 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 tris[2]; + int unlit; + vector 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 indices; - vector texs; - vector matsurfs; - vector mapmodels; - vector skyindices, explicitskyindices; - vector 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 &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 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 indices; + vector texs; + vector matsurfs; + vector mapmodels; + vector skyindices, explicitskyindices; + vector 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 &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 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<type&SHADER_ENVMAP) va->texmask |= 1<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<type&SHADER_ENVMAP) va->texmask |= 1<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 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<0) + { + mask |= 1< &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 &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<= 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<= 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 cubeedges; @@ -837,273 +837,273 @@ hashtable 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< 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< 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<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<slot->shader->type&SHADER_ENVMAP ? (int) (layer->slot->texmask&(1<= 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<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<slot->shader->type&SHADER_ENVMAP ? (int) (layer->slot->texmask&(1<= 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 &sf = vc.skyfaces[i]; - if(sf.empty()) continue; - vc.skymask |= 0x3F&~(1<>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 &sf = vc.skyfaces[i]; + if(sf.empty()) continue; + vc.skymask |= 0x3F&~(1<>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 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 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<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<slot->shader->type&SHADER_ENVMAP ? (int) (layer->slot->texmask&(1<= 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<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<slot->shader->type&SHADER_ENVMAP ? (int) (layer->slot->texmask&(1<= 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 &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< &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<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<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<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<surfaces[i].numverts&MAXFACEVERTS) numvis++; - } - else - { - numvis++; - if(c.texture[i] != DEFAULT_SKY && !(c.ext && c.ext->surfaces[i].numverts&MAXFACEVERTS)) checkmask |= 1<surfaces[i].numverts&MAXFACEVERTS) numvis++; + } + else + { + numvis++; + if(c.texture[i] != DEFAULT_SKY && !(c.ext && c.ext->surfaces[i].numverts&MAXFACEVERTS)) checkmask |= 1<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<explicitsky; - skyarea += va->skyarea; - } - - visibleva = NULL; + int csi = 0; + while(1<explicitsky; + skyarea += va->skyarea; + } + + visibleva = NULL; } void precachetextures() { - vector 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 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 &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(f0 && 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 &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(f0 && 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 &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(f0) - { - hitentdist = dist = f; - hitent = outsideents[i]; - hitorient = orient; - } - } - return dist; + vec eo, es; + int orient; + float dist = radius, f = 0.0f; + const vector &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(f0) + { + 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 &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 &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 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<= 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<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<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<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<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<= 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<= 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<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<= 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<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<= 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 ¢er, 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 ¢er, 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 dynents; + int x, y; + uint frame; + vector 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 &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<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<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 &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 &dynents = checkdynentcache(x, y); + loopv(dynents) + { + physent *d = dynents[i]; + if(o.dist(d->o)-d->radius < radius) return true; + } + } + return false; } template 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(d, dir, o); - else return plcollide(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 &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(d, dir, o); + else return plcollide(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 &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 ¢er, 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 static inline bool mmcollide(physent *d, const vec &dir, const extentity &e, const vec ¢er, 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 &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(d, dir, e, center, radius, yaw)) return true; - } - else if(mmcollide(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 &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(d, dir, e, center, radius, yaw)) return true; + } + else if(mmcollide(d, dir, e, center, radius, yaw)) return true; + break; + default: continue; + } + } + return false; } template 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< 0) return false; \ - if(dist <= bestdist) continue; \ - if(!dir.iszero()) \ - { \ - if(dotval >= -cutoff*dir.magnitude()) continue; \ - if(d->typeo.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< 0) return false; \ + if(dist <= bestdist) continue; \ + if(!dir.iszero()) \ + { \ + if(dotval >= -cutoff*dir.magnitude()) continue; \ + if(d->typeo.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 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 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->typezmargin-(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->typezmargin-(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 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 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->typezmargin-(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->typezmargin-(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(d, dir, cutoff, c, co, size); - else return cubecollideplanes(d, dir, cutoff, c, co, size); - case COLLIDE_ELLIPSE: - if(isentirelysolid(c) || solid) return fuzzycollidesolid(d, dir, cutoff, c, co, size); - else return fuzzycollideplanes(d, dir, cutoff, c, co, size); - case COLLIDE_ELLIPSE_PRECISE: - if(isentirelysolid(c) || solid) return cubecollidesolid(d, dir, cutoff, c, co, size); - else return cubecollideplanes(d, dir, cutoff, c, co, size); - default: return false; - } + switch(d->collidetype) + { + case COLLIDE_OBB: + if(isentirelysolid(c) || solid) return cubecollidesolid(d, dir, cutoff, c, co, size); + else return cubecollideplanes(d, dir, cutoff, c, co, size); + case COLLIDE_ELLIPSE: + if(isentirelysolid(c) || solid) return fuzzycollidesolid(d, dir, cutoff, c, co, size); + else return fuzzycollideplanes(d, dir, cutoff, c, co, size); + case COLLIDE_ELLIPSE_PRECISE: + if(isentirelysolid(c) || solid) return cubecollidesolid(d, dir, cutoff, c, co, size); + else return cubecollideplanes(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->typeents) 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= 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<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<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= 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<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<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->typeo.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 -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(d, dir, o, margin); - else return platformcollide(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 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 &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 passengers, colliders; - passengers.setsize(0); - colliders.setsize(0); - static vector 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 verts; - vector tris; - vector distlimits; - vector rotlimits; - vector rotfrictions; - vector joints; - vector 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 verts; + vector tris; + vector distlimits; + vector rotlimits; + vector rotfrictions; + vector joints; + vector 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 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 ¢er, 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 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 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 &ents = entities::getents(); - vector 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 &ents = entities::getents(); + vector 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 ¢er, 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 batched; + model *m; + int flags; + vector batched; }; static vector batches; static vector 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->batchbatch]->m==m) b = batches[m->batch]; - else - { - if(numbatchesbatched.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->batchbatch]->m==m) b = batches[m->batch]; + else + { + if(numbatchesbatched.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 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 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 ¢er, 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 ¢er, 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) 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->occludedoccluded++; - 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) 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->occludedoccluded++; + 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 &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 anims; - findanims(name, anims); - vector 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 anims; + findanims(name, anims); + vector 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)<timeinair>100) anim |= (ANIM_JUMP|ANIM_END)<move || d->strafe)) - { - if(d->move>0) anim |= (ANIM_FORWARD|ANIM_LOOP)<strafe) - { - if(d->move<0) anim |= ((d->strafe>0 ? ANIM_RIGHT : ANIM_LEFT)|ANIM_REVERSE|ANIM_LOOP)<strafe>0 ? ANIM_LEFT : ANIM_RIGHT)|ANIM_LOOP)<move<0) anim |= (ANIM_BACKWARD|ANIM_LOOP)<>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)<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)<timeinair>100) anim |= (ANIM_JUMP|ANIM_END)<move || d->strafe)) + { + if(d->move>0) anim |= (ANIM_FORWARD|ANIM_LOOP)<strafe) + { + if(d->move<0) anim |= ((d->strafe>0 ? ANIM_RIGHT : ANIM_LEFT)|ANIM_REVERSE|ANIM_LOOP)<strafe>0 ? ANIM_LEFT : ANIM_RIGHT)|ANIM_LOOP)<move<0) anim |= (ANIM_BACKWARD|ANIM_LOOP)<>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)<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 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 &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 &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 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(const vec &o, int &blend) template 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(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(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(o, e, size, ts, grav, vs); + vec e = d; + if(grav) e.z -= float(ts)/grav; + e.div(-75.0f).add(o); + genpos(o, e, size, ts, grav, vs); } template static inline void genrotpos(const vec &o, const vec &d, float size, int grav, int ts, partvert *vs, int rot) { - genpos(o, d, size, grav, ts, vs); + genpos(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(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 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(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(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 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(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(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(o, d, p->size, ts, p->gravity, vs, (p->flags>>2)&0x1F); - else genpos(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(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(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(o, d, p->size, ts, p->gravity, vs, (p->flags>>2)&0x1F); + else genpos(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 quadrenderer; typedef varenderer taperenderer; @@ -847,92 +844,92 @@ typedef varenderer 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("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("packages/particles/smoke.png", PT_PART|PT_FLIP|PT_LERP), // smoke - new quadrenderer("packages/particles/steam.png", PT_PART|PT_FLIP), // steam - new quadrenderer("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("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("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("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("packages/particles/smoke.png", PT_PART|PT_FLIP|PT_LERP), // smoke + new quadrenderer("packages/particles/steam.png", PT_PART|PT_FLIP), // steam + new quadrenderer("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("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("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 - - 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 - - regularsplash(PART_STEAM, 0x897661, 50, 1, 200, offsetvec(e.o, e.attr2, rnd(10)), 2.4f, -20); - break; - case 2: //water fountain - - { - 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 - - newparticle(e.o, vec(0, 0, 1), 1, PART_EXPLOSION, colorfromattr(e.attr3), 4.0f)->val = 1+e.attr2; - break; - case 4: //tape - - 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 - - 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=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 - 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 - 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 - - 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 - + regularsplash(PART_STEAM, 0x897661, 50, 1, 200, offsetvec(e.o, e.attr2, rnd(10)), 2.4f, -20); + break; + case 2: //water fountain - + { + 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 - + newparticle(e.o, vec(0, 0, 1), 1, PART_EXPLOSION, colorfromattr(e.attr3), 4.0f)->val = 1+e.attr2; + break; + case 4: //tape - + 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 - + 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=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 + 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 + 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 &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 &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<= 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<= 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<= 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<= 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(reflectzo.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(reflectzo.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< 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<>= 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< 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< 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<>= 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< 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 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 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 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 &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 &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 &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 &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 &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 &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<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<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<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<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<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 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 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, "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, "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 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 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 &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 &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 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 gameargs; @@ -845,13 +845,13 @@ vector 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 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 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 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 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 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 &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 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 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<= 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 ¶m = 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<= 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 ¶m = 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 ¶m = s.params[i]; - if(name == param.name) return param.val; - } - loopv(s.shader->defaultparams) - { - SlotShaderParamState ¶m = s.shader->defaultparams[i]; - if(name == param.name) return param.val; - } - return noval; + loopv(s.params) + { + SlotShaderParam ¶m = s.params[i]; + if(name == param.name) return param.val; + } + loopv(s.shader->defaultparams) + { + SlotShaderParamState ¶m = 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 ¶m = s.params[i]; - if(name == param.name) return param.val; - } - return findslotparam(*s.slot, name, noval); + loopv(s.params) + { + SlotShaderParam ¶m = 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<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, "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, "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, "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, "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, "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, "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 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, "%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 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, "%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 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, "%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 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, "%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 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%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 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%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 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, "%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 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, "%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 &vsbuf, vector &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 &vsbuf, vector &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 vsbuf, psbuf, vsbak, psbak; + vector 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, "%s", s->numvariants(*row), *row, name); - //defformatstring(info, "shader %s", varname); - //renderprogress(loadprogress, info); - vector 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, "%s", s->numvariants(*row), *row, name); + //defformatstring(info, "shader %s", varname); + //renderprogress(loadprogress, info); + vector 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 ¶ms, Shader *sh, bool load) { - if(sh) loopv(params) - { - int loc = -1; - SlotShaderParam ¶m = 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 ¶m = 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<texmask&(1<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 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 ¶m = 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 ¶m = 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 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 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<= 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<= 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<= 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<= 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 ¶ms) { - 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<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 &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 &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.typeyaw*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< 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 - int genvbo(vector &idxs, int offset, vector &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 - int genvbo(vector &idxs, int offset, vector &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 &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 - static inline void fillvert(T &vv, int j, vert &v) - { - vv.tc = v.tc; - } - - template - 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 users; - boneinfo *bones; - int numbones, numinterpbones, numgpubones, numframes; - dualquat *framebones; - vector skelanims; - vector tags; - vector antipodes; - ragdollskel *ragdoll; - vector pitchdeps; - vector pitchtargets; - vector pitchcorrects; - - bool usegpuskel; - vector skelcache; - hashtable 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 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 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 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 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 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 + int genvbo(vector &idxs, int offset, vector &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 + int genvbo(vector &idxs, int offset, vector &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 &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 + static inline void fillvert(T &vv, int j, vert &v) + { + vv.tc = v.tc; + } + + template + 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 users; + boneinfo *bones; + int numbones, numinterpbones, numgpubones, numframes; + dualquat *framebones; + vector skelanims; + vector tags; + vector antipodes; + ragdollskel *ragdoll; + vector pitchdeps; + vector pitchtargets; + vector pitchcorrects; + + bool usegpuskel; + vector skelcache; + hashtable 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 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 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 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 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 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 struct skelloader : modelloader { - static vector adjustments; + static vector adjustments; - skelloader(const char *name) : modelloader(name) {} + skelloader(const char *name) : modelloader(name) {} - void flushpart() - { - adjustments.setsize(0); - } + void flushpart() + { + adjustments.setsize(0); + } }; template vector skelloader::adjustments; template struct skelcommands : modelcommands { - typedef modelcommands 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 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 bonestrs; - explodelist(maskstr, bonestrs); - vector 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 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 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 bonestrs; + explodelist(maskstr, bonestrs); + vector 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 &v) const - { - return p >= v.getbuf() + slots && p < v.getbuf() + slots+numslots && slots+numslots < v.length(); - } + bool hasslot(const soundslot *p, const vector &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 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 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 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 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 samples; static void cleanupsamples() { - enumerate(samples, soundsample, s, s.cleanup()); + enumerate(samples, soundsample, s, s.cleanup()); } static struct soundtype { - vector slots; - vector 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 slots; + vector 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 &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 &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 &ents = entities::getents(); - loopv(ents) - { - extentity &e = *ents[i]; - if(e.type==ET_SOUND) mapsounds.preloadsound(e.attr1); - } + const vector &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 &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 &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 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 ¤tline() - { - 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 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 ¤t = 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 ¤t = currentline(); - if(ch == '\n') - { - if(maxy == -1 || cy < maxy-1) - { - editline newline(¤t.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 ¤t = 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 ¤t = 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 ¤t = 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 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 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 ¤tline() + { + 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 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 ¤t = 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 ¤t = currentline(); + if(ch == '\n') + { + if(maxy == -1 || cy < maxy-1) + { + editline newline(¤t.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 ¤t = 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 ¤t = 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 ¤t = 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 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 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 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 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 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<> tshift; - } - src += hfrac*stride; - } + uint wfrac = sw/dw, hfrac = sh/dh, wshift = 0, hshift = 0; + while(dw<> tshift; + } + src += hfrac*stride; + } } template 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<>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<>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 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)<>= 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)<>= 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)<>= 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)<>= 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)<>= 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)<>= 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)<>= 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)<>= 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 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 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<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<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 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<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<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< &buf, const VSlot &src) { - if(src.changed & (1<changed ? src.layer : 0); - } - if(src.changed & (1<changed ? src.layer : 0); + } + if(src.changed & (1< &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 &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<= 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<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<changed = src.changed | delta.changed; + propagatevslot(*dst, src, ((1<=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<=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<=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<=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<scroll = vec2(*scrollS, *scrollT).div(1000.0f); - propagatevslot(s.variants, 1<scroll = vec2(*scrollS, *scrollT).div(1000.0f); + propagatevslot(s.variants, 1<offset = ivec2(*xoffset, *yoffset).max(0); - propagatevslot(s.variants, 1<offset = ivec2(*xoffset, *yoffset).max(0); + propagatevslot(s.variants, 1<rotation = clamp(*rot, 0, 7); - propagatevslot(s.variants, 1<rotation = clamp(*rot, 0, 7); + propagatevslot(s.variants, 1<scale = *scale <= 0 ? 1 : *scale; - propagatevslot(s.variants, 1<scale = *scale <= 0 ? 1 : *scale; + propagatevslot(s.variants, 1<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<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<alphafront = clamp(*front, 0.0f, 1.0f); - s.variants->alphaback = clamp(*back, 0.0f, 1.0f); - propagatevslot(s.variants, 1<alphafront = clamp(*front, 0.0f, 1.0f); + s.variants->alphaback = clamp(*back, 0.0f, 1.0f); + propagatevslot(s.variants, 1<colorscale = vec(clamp(*r, 0.0f, 1.0f), clamp(*g, 0.0f, 1.0f), clamp(*b, 0.0f, 1.0f)); - propagatevslot(s.variants, 1<colorscale = vec(clamp(*r, 0.0f, 1.0f), clamp(*g, 0.0f, 1.0f), clamp(*b, 0.0f, 1.0f)); + propagatevslot(s.variants, 1< &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 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< 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<= 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 name; - if(vslot.colorscale == vec(1, 1, 1)) addname(name, slot, slot.sts[0], false, ""); - else - { - defformatstring(prefix, "", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z); - addname(name, slot, slot.sts[0], false, prefix); - } - int glow = -1; - if(slot.texmask&(1<= 0) - { - defformatstring(prefix, "", 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, ""); - else - { - defformatstring(prefix, "", 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 name; + if(vslot.colorscale == vec(1, 1, 1)) addname(name, slot, slot.sts[0], false, ""); + else + { + defformatstring(prefix, "", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z); + addname(name, slot, slot.sts[0], false, prefix); + } + int glow = -1; + if(slot.texmask&(1<= 0) + { + defformatstring(prefix, "", 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, ""); + else + { + defformatstring(prefix, "", 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<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<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 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< 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< 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 &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 &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(len); - f->write(type, 4); - f->write(data, len); + f->putbig(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(crc); + uint crc = crc32(0, Z_NULL, 0); + crc = crc32(crc, (const Bytef *)type, 4); + if(data) crc = crc32(crc, data, len); + f->putbig(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(image.w), bigswap(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(len); - f->seek(0, SEEK_END); - f->putbig(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(image.w), bigswap(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(len); + f->seek(0, SEEK_END); + f->putbig(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 defaultparams; - vector globalparams; - vector localparams; - vector localparamremap; - Shader *detailshader, *variantshader, *altshader, *fastshader[MAXSHADERDETAIL]; - vector variants; - ushort *variantrows; - bool standard, forced, used; - Shader *reusevs, *reuseps; - vector uniformlocs; - vector 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 defaultparams; + vector globalparams; + vector localparams; + vector localparamremap; + Shader *detailshader, *variantshader, *altshader, *fastshader[MAXSHADERDETAIL]; + vector variants; + ushort *variantrows; + bool standard, forced, used; + Shader *reusevs, *reuseps; + vector uniformlocs; + vector 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 - 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 - 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 + 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 + 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 - 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(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 + 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(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 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 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 sts; - Shader *shader; - vector 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 sts; + Shader *shader; + vector 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 - int genvbo(vector &idxs, int offset, vector &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 &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 - static inline void fillvert(T &vv, int j, tcvert &tc, vert &v) - { - vv.tc = tc.tc; - } - - template - 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 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 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 + int genvbo(vector &idxs, int offset, vector &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 &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 + static inline void fillvert(T &vv, int j, tcvert &tc, vert &v) + { + vv.tc = tc.tc; + } + + template + 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 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 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 struct vertloader : modelloader { - vertloader(const char *name) : modelloader(name) {} + vertloader(const char *name) : modelloader(name) {} }; template struct vertcommands : modelcommands { - 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 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 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 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<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<= 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 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 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<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<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<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<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<= 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<= 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<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<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 ¢er, 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 ¢er, 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 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 &ents = entities::getents(); - return ents.inrange(id) && modifyoctaent(flags, id, *ents[id]); + vector &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 &ents = entities::getents(); - loopv(ents) modifyoctaent(MODOE_ADD, i, *ents[i]); + vector &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 &found) { - vector &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 &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 &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 &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<= uint(worldsize)) - { - findents(worldroot, ivec(0, 0, 0), 1<ext && c->ext->ents) findents(*c->ext->ents, low, high, notspawned, pos, invradius, found); - scale--; - while(c->children && !(diff&(1<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<= octaentsize) findents(c->children, ivec(bo).mask(~((2<= uint(worldsize)) + { + findents(worldroot, ivec(0, 0, 0), 1<ext && c->ext->ents) findents(*c->ext->ents, low, high, notspawned, pos, invradius, found); + scale--; + while(c->children && !(diff&(1<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<= octaentsize) findents(c->children, ivec(bo).mask(~((2<= 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 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 &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.typeo); - 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 &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.typeo); + if(dist < closedist) + { + closest = i; + closedist = dist; + } + } + if(closedist>attachradius) return; + e.attached = ents[closest]; + ents[closest]->attached = &e; } void attachentities() { - vector &ents = entities::getents(); - loopv(ents) attachentity(*ents[i]); + vector &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 &ents = entities::getents(); loopv(ents) entfocusv(i, if(exp) entadd(n), ents); } @@ -418,93 +418,93 @@ void attachentities() #define groupeditloop(f){ vector &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 &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 &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 &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 &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 &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 &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 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 &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 &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 &ents = entities::getents(); - if(index > ents.length()) index = ents.length(); - else for(int i = index; i &ents = entities::getents(); + if(index > ents.length()) index = ents.length(); + else for(int i = index; i &spawninfos) { - const vector &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 &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 &spawninfos) // If all weights are zero, the index is picked uniformly. static const extentity *poprandomspawn(vector &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 &ents = entities::getents(); - d->pitch = 0; - d->roll = 0; - if(ents.inrange(forceent) && tryspawn(d, *ents[forceent])) return; - vector 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 &ents = entities::getents(); + d->pitch = 0; + d->roll = 0; + if(ents.inrange(forceent) && tryspawn(d, *ents[forceent])) return; + vector 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<16 ? 16 : scale), true, false); + setvar("mapsize", 1< 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 &ents = entities::getents(); - loopv(ents) ents[i]->o.sub(vec(offset)); + ivec offset(octant, ivec(0, 0, 0), worldsize); + vector &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 &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 &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 &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(); - f->seek(ilen, SEEK_CUR); - switch(type) - { - case ID_VAR: f->getlil(); break; - case ID_FVAR: f->getlil(); break; - case ID_SVAR: { int slen = f->getlil(); 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(); - int extrasize = f->getlil(); - f->seek(extrasize, SEEK_CUR); - } - - if(hdr.version<14) - { - f->seek(256, SEEK_CUR); - } - else - { - ushort nummru = f->getlil(); - 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(); + f->seek(ilen, SEEK_CUR); + switch(type) + { + case ID_VAR: f->getlil(); break; + case ID_FVAR: f->getlil(); break; + case ID_SVAR: { int slen = f->getlil(); 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(); + int extrasize = f->getlil(); + f->seek(extrasize, SEEK_CUR); + } + + if(hdr.version<14) + { + f->seek(256, SEEK_CUR); + } + else + { + ushort nummru = f->getlil(); + 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<putchar(oflags | OCTSAV_SOLID); - else - { - f->putchar(oflags | OCTSAV_NORMAL); - f->write(c[i].edges, 12); - } - } - - loopj(6) f->putlil(c[i].texture[j]); - - if(oflags&0x40) f->putlil(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<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<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(v0[vc]); f->putlil(v0[vr]); - f->putlil(v2[vc]); f->putlil(v2[vr]); - hasxyz = false; - } - if(hasuv && vertmask&0x02) - { - const vertinfo &v0 = verts[uvorder], &v2 = verts[(uvorder+2)&3]; - f->putlil(v0.u); f->putlil(v0.v); - f->putlil(v2.u); f->putlil(v2.v); - if(surf.numverts&LAYER_DUP) - { - const vertinfo &b0 = verts[4+uvorder], &b2 = verts[4+((uvorder+2)&3)]; - f->putlil(b0.u); f->putlil(b0.v); - f->putlil(b2.u); f->putlil(b2.v); - } - hasuv = false; - } - } - if(hasnorm && vertmask&0x08) { f->putlil(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(xyz[vc]); f->putlil(xyz[vr]); - } - if(hasuv) { f->putlil(v.u); f->putlil(v.v); } - if(hasnorm) f->putlil(v.norm); - } - if(surf.numverts&LAYER_DUP) loopk(layerverts) - { - const vertinfo &v = verts[layerverts + (k+vertorder)%layerverts]; - if(hasuv) { f->putlil(v.u); f->putlil(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<putchar(oflags | OCTSAV_SOLID); + else + { + f->putchar(oflags | OCTSAV_NORMAL); + f->write(c[i].edges, 12); + } + } + + loopj(6) f->putlil(c[i].texture[j]); + + if(oflags&0x40) f->putlil(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<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<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(v0[vc]); f->putlil(v0[vr]); + f->putlil(v2[vc]); f->putlil(v2[vr]); + hasxyz = false; + } + if(hasuv && vertmask&0x02) + { + const vertinfo &v0 = verts[uvorder], &v2 = verts[(uvorder+2)&3]; + f->putlil(v0.u); f->putlil(v0.v); + f->putlil(v2.u); f->putlil(v2.v); + if(surf.numverts&LAYER_DUP) + { + const vertinfo &b0 = verts[4+uvorder], &b2 = verts[4+((uvorder+2)&3)]; + f->putlil(b0.u); f->putlil(b0.v); + f->putlil(b2.u); f->putlil(b2.v); + } + hasuv = false; + } + } + if(hasnorm && vertmask&0x08) { f->putlil(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(xyz[vc]); f->putlil(xyz[vr]); + } + if(hasuv) { f->putlil(v.u); f->putlil(v.v); } + if(hasnorm) f->putlil(v.norm); + } + if(surf.numverts&LAYER_DUP) loopk(layerverts) + { + const vertinfo &v = verts[layerverts + (k+vertorder)%layerverts]; + if(hasuv) { f->putlil(v.u); f->putlil(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<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<= LMID_RESERVED || dst.lmid[1] >= LMID_RESERVED || dst.numverts&~LAYER_TOP), - usemerges = hasmerges&(1< 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<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<= LMID_RESERVED || dst.lmid[1] >= LMID_RESERVED || dst.numverts&~LAYER_TOP), + usemerges = hasmerges&(1< 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)<>3)&3)<>5)&7)<>3)&3)<>5)&7)<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(); - 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<read(&normals[i], sizeof(normalscompat)); } - if(surfaces[i].layer != 0 || surfaces[i].lmid != LMID_AMBIENT) - hassurfs |= 1<>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<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(); - } - 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<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(), r1 = f->getlil(), c2 = f->getlil(), r2 = f->getlil(); - 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(); v0.v = f->getlil(); - v2.u = f->getlil(); v2.v = f->getlil(); - 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(); b0.v = f->getlil(); - b2.u = f->getlil(); b2.v = f->getlil(); - 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(); - 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(); xyz[vr] = f->getlil(); - 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(); v.v = f->getlil(); } - if(hasnorm) v.norm = f->getlil(); - } - 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(); v.v = f->getlil(); } - 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(); + 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<read(&normals[i], sizeof(normalscompat)); } + if(surfaces[i].layer != 0 || surfaces[i].lmid != LMID_AMBIENT) + hassurfs |= 1<>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<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(); + } + 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<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(), r1 = f->getlil(), c2 = f->getlil(), r2 = f->getlil(); + 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(); v0.v = f->getlil(); + v2.u = f->getlil(); v2.v = f->getlil(); + 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(); b0.v = f->getlil(); + b2.u = f->getlil(); b2.v = f->getlil(); + 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(); + 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(); xyz[vr] = f->getlil(); + 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(); v.v = f->getlil(); } + if(hasnorm) v.norm = f->getlil(); + } + 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(); v.v = f->getlil(); } + 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(vs.changed); - f->putlil(prev); - if(vs.changed & (1<putlil(vs.params.length()); - loopv(vs.params) - { - SlotShaderParam &p = vs.params[i]; - f->putlil(strlen(p.name)); - f->write(p.name, strlen(p.name)); - loopk(4) f->putlil(p.val[k]); - } - } - if(vs.changed & (1<putlil(vs.scale); - if(vs.changed & (1<putlil(vs.rotation); - if(vs.changed & (1<putlil(vs.offset.x); - f->putlil(vs.offset.y); - } - if(vs.changed & (1<putlil(vs.scroll.x); - f->putlil(vs.scroll.y); - } - if(vs.changed & (1<putlil(vs.layer); - if(vs.changed & (1<putlil(vs.alphafront); - f->putlil(vs.alphaback); - } - if(vs.changed & (1<putlil(vs.colorscale[k]); - } + f->putlil(vs.changed); + f->putlil(prev); + if(vs.changed & (1<putlil(vs.params.length()); + loopv(vs.params) + { + SlotShaderParam &p = vs.params[i]; + f->putlil(strlen(p.name)); + f->write(p.name, strlen(p.name)); + loopk(4) f->putlil(p.val[k]); + } + } + if(vs.changed & (1<putlil(vs.scale); + if(vs.changed & (1<putlil(vs.rotation); + if(vs.changed & (1<putlil(vs.offset.x); + f->putlil(vs.offset.y); + } + if(vs.changed & (1<putlil(vs.scroll.x); + f->putlil(vs.scroll.y); + } + if(vs.changed & (1<putlil(vs.layer); + if(vs.changed & (1<putlil(vs.alphafront); + f->putlil(vs.alphaback); + } + if(vs.changed & (1<putlil(vs.colorscale[k]); + } } void savevslots(stream *f, int numvslots) { - if(vslots.empty()) return; - int *prev = new int[numvslots]; - for(int i=0;ichanged) 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(-(i - lastroot)); - savevslot(f, vs, prev[i]); - lastroot = i+1; - } - if(lastroot < numvslots) f->putlil(-(numvslots - lastroot)); - delete[] prev; + if(vslots.empty()) return; + int *prev = new int[numvslots]; + for(int i=0;ichanged) 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(-(i - lastroot)); + savevslot(f, vs, prev[i]); + lastroot = i+1; + } + if(lastroot < numvslots) f->putlil(-(numvslots - lastroot)); + delete[] prev; } void loadvslot(stream *f, VSlot &vs, int changed) { - vs.changed = changed; - if(vs.changed & (1<getlil(); - string name; - loopi(numparams) - { - SlotShaderParam &p = vs.params.add(); - int nlen = f->getlil(); - 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(); - } - } - if(vs.changed & (1<getlil(); - if(vs.changed & (1<getlil(), 0, 7); - if(vs.changed & (1<getlil(); - vs.offset.y = f->getlil(); - } - if(vs.changed & (1<getlil(); - vs.scroll.y = f->getlil(); - } - if(vs.changed & (1<getlil(); - if(vs.changed & (1<getlil(); - vs.alphaback = f->getlil(); - } - if(vs.changed & (1<getlil(); - } + vs.changed = changed; + if(vs.changed & (1<getlil(); + string name; + loopi(numparams) + { + SlotShaderParam &p = vs.params.add(); + int nlen = f->getlil(); + 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(); + } + } + if(vs.changed & (1<getlil(); + if(vs.changed & (1<getlil(), 0, 7); + if(vs.changed & (1<getlil(); + vs.offset.y = f->getlil(); + } + if(vs.changed & (1<getlil(); + vs.scroll.y = f->getlil(); + } + if(vs.changed & (1<getlil(); + if(vs.changed & (1<getlil(); + vs.alphaback = f->getlil(); + } + if(vs.changed & (1<getlil(); + } } void loadvslots(stream *f, int numvslots) { - int *prev = new (false) int[numvslots]; - if(!prev) return; - for(int i=0;i 0) - { - int changed = f->getlil(); - if(changed < 0) - { - loopi(-changed) vslots.add(new VSlot(NULL, vslots.length())); - numvslots += changed; - } - else - { - prev[vslots.length()] = f->getlil(); - 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 0) + { + int changed = f->getlil(); + if(changed < 0) + { + loopi(-changed) vslots.add(new VSlot(NULL, vslots.length())); + numvslots += changed; + } + else + { + prev[vslots.length()] = f->getlil(); + 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 &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(strlen(id.name)); - f->write(id.name, strlen(id.name)); - switch(id.type) - { - case ID_VAR: - f->putlil(*id.storage.i); - break; - - case ID_FVAR: - f->putlil(*id.storage.f); - break; - - case ID_SVAR: - f->putlil(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(entities::extraentinfosize()); - vector extras; - game::writegamedata(extras); - f->putlil(extras.length()); - f->write(extras.getbuf(), extras.length()); - - f->putlil(texmru.length()); - loopv(texmru) f->putlil(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(lm.unlitx)); - f->putlil(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 &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(strlen(id.name)); + f->write(id.name, strlen(id.name)); + switch(id.type) + { + case ID_VAR: + f->putlil(*id.storage.i); + break; + + case ID_FVAR: + f->putlil(*id.storage.f); + break; + + case ID_SVAR: + f->putlil(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(entities::extraentinfosize()); + vector extras; + game::writegamedata(extras); + f->putlil(extras.length()); + f->write(extras.getbuf(), extras.length()); + + f->putlil(texmru.length()); + loopv(texmru) f->putlil(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(lm.unlitx)); + f->putlil(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<getchar(), ilen = f->getlil(); - 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(); - if(exists && id->minval <= id->maxval) setvar(name, val); - break; - } - - case ID_FVAR: - { - float val = f->getlil(); - if(exists && id->minvalf <= id->maxvalf) setfvar(name, val); - break; - } - - case ID_SVAR: - { - int slen = f->getlil(); - 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(); - int extrasize = f->getlil(); - vector 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(); - loopi(nummru) texmru.add(f->getlil()); - } - - renderprogress(0, "loading entities..."); - - vector &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(); - lm.unlity = f->getlil(); - } - } - 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<getchar(), ilen = f->getlil(); + 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(); + if(exists && id->minval <= id->maxval) setvar(name, val); + break; + } + + case ID_FVAR: + { + float val = f->getlil(); + if(exists && id->minvalf <= id->maxvalf) setfvar(name, val); + break; + } + + case ID_SVAR: + { + int slen = f->getlil(); + 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(); + int extrasize = f->getlil(); + vector 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(); + loopi(nummru) texmru.add(f->getlil()); + } + + renderprogress(0, "loading entities..."); + + vector &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(); + lm.unlity = f->getlil(); + } + } + 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()); } -- cgit v1.2.3