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; } }; struct ShaderParamBinding { 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_UNSIGNED_INT: glUniform1uiv_(loc, size, param->uval); break; case GL_UNSIGNED_INT_VEC2: glUniform2uiv_(loc, size, param->uval); break; case GL_UNSIGNED_INT_VEC3: glUniform3uiv_(loc, size, param->uval); break; case GL_UNSIGNED_INT_VEC4: glUniform4uiv_(loc, size, param->uval); 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; }; struct SlotShaderParam { enum { REUSE = 1<<0 }; const char *name; int loc, flags; float val[4]; }; struct SlotShaderParamState : LocalShaderParamState { int flags; float val[4]; SlotShaderParamState() {} SlotShaderParamState(const SlotShaderParam &p) { name = p.name; loc = -1; size = 1; format = GL_FLOAT_VEC4; flags = p.flags; memcpy(val, p.val, sizeof(val)); } }; enum { SHADER_DEFAULT = 0, SHADER_WORLD = 1<<0, SHADER_ENVMAP = 1<<1, SHADER_REFRACT = 1<<2, SHADER_OPTION = 1<<3, SHADER_DYNAMIC = 1<<4, SHADER_TRIPLANAR = 1<<5, SHADER_INVALID = 1<<8, SHADER_DEFERRED = 1<<9 }; #define MAXVARIANTROWS 32 struct Slot; 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) {} }; struct AttribLoc { const char *name; int loc; AttribLoc(const char *name = NULL, int loc = -1) : name(name), loc(loc) {} }; struct FragDataLoc { const char *name; int loc; GLenum format; int index; FragDataLoc(const char *name = NULL, int loc = -1, GLenum format = GL_FALSE, int index = 0) : name(name), loc(loc), format(format), index(index) {} }; 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 *variantshader; vector variants; ushort *variantrows; bool standard, forced, used; Shader *reusevs, *reuseps; vector uniformlocs; vector attriblocs; vector fragdatalocs; const void *owner; Shader() : name(NULL), vsstr(NULL), psstr(NULL), defer(NULL), type(SHADER_DEFAULT), program(0), vsobj(0), psobj(0), variantshader(NULL), variantrows(NULL), standard(false), forced(false), used(false), reusevs(NULL), reuseps(NULL), owner(NULL) { } ~Shader() { DELETEA(name); DELETEA(vsstr); DELETEA(psstr); DELETEA(defer); DELETEA(variantrows); } 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 !(type&(SHADER_DEFERRED|SHADER_INVALID)); } bool hasoption() const { return (type&SHADER_OPTION)!=0; } bool isdynamic() const { return (type&SHADER_DYNAMIC)!=0; } static inline bool isnull(const Shader *s) { return !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; } 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 *s = this; if(variantrows) { int start = variantrows[row], end = variantrows[row+1]; for(col = min(start + col, end-1); col >= start; --col) if(!variants[col]->invalid()) { s = variants[col]; break; } } if(lastshader!=s) s->bindprograms(); } void setvariant(int col, int row) { if(isnull() || !loaded()) return; setvariant_(col, row); lastshader->flushparams(); } void setvariant(int col, int row, Slot &slot) { if(isnull() || !loaded()) return; setvariant_(col, row); lastshader->flushparams(&slot); lastshader->setslotparams(slot); } void setvariant(int col, int row, Slot &slot, VSlot &vslot) { if(isnull() || !loaded()) return; setvariant_(col, row); lastshader->flushparams(&slot); lastshader->setslotparams(slot, vslot); } void set_() { if(lastshader!=this) bindprograms(); } void set() { if(isnull() || !loaded()) return; set_(); lastshader->flushparams(); } void set(Slot &slot) { if(isnull() || !loaded()) return; set_(); lastshader->flushparams(&slot); lastshader->setslotparams(slot); } void set(Slot &slot, VSlot &vslot) { if(isnull() || !loaded()) return; set_(); lastshader->flushparams(&slot); lastshader->setslotparams(slot, vslot); } bool compile(); void cleanup(bool full = 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; } }; 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; case GL_UNSIGNED_INT: glUniform1ui_(b->loc, uint(x)); break; case GL_UNSIGNED_INT_VEC2: glUniform2ui_(b->loc, uint(x), uint(y)); break; case GL_UNSIGNED_INT_VEC3: glUniform3ui_(b->loc, uint(x), uint(y), uint(z)); break; case GL_UNSIGNED_INT_VEC4: glUniform4ui_(b->loc, uint(x), uint(y), uint(z), uint(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; case GL_UNSIGNED_INT: glUniform1ui_(b->loc, x); break; case GL_UNSIGNED_INT_VEC2: glUniform2ui_(b->loc, x, y); break; case GL_UNSIGNED_INT_VEC3: glUniform3ui_(b->loc, x, y, z); break; case GL_UNSIGNED_INT_VEC4: glUniform4ui_(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); } void setu(uint x = 0, uint y = 0, uint z = 0, uint w = 0) { sett(x, y, z, w); } void setv(const uint *u, int n = 1) { ShaderParamBinding *b = resolve(); if(b) glUniform1uiv_(b->loc, n, u); } }; #define LOCALPARAM(name, vals) do { static LocalShaderParam param( #name ); param.set(vals); } while(0) #define LOCALPARAMF(name, ...) do { static LocalShaderParam param( #name ); param.setf(__VA_ARGS__); } while(0) #define LOCALPARAMI(name, ...) do { static LocalShaderParam param( #name ); param.seti(__VA_ARGS__); } while(0) #define LOCALPARAMV(name, vals, num) do { static LocalShaderParam param( #name ); param.setv(vals, num); } while(0) #define GLOBALPARAM(name, vals) do { static GlobalShaderParam param( #name ); param.set(vals); } while(0) #define GLOBALPARAMF(name, ...) do { static GlobalShaderParam param( #name ); param.setf(__VA_ARGS__); } while(0) #define GLOBALPARAMI(name, ...) do { static GlobalShaderParam param( #name ); param.seti(__VA_ARGS__); } while(0) #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) #define SETVARIANT(name, ...) \ 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; } }; // management of texture slots // each texture slot can have multiple texture frames, of which currently only the first is used // additional frames can be used for various shaders 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 { TEX_DIFFUSE = 0, TEX_NORMAL, TEX_GLOW, TEX_ENVMAP, TEX_SPEC, TEX_DEPTH, TEX_UNKNOWN, TEX_DETAIL = TEX_SPEC }; enum { VSLOT_SHPARAM = 0, VSLOT_SCALE, VSLOT_ROTATION, VSLOT_OFFSET, VSLOT_SCROLL, VSLOT_LAYER, VSLOT_ALPHA, VSLOT_COLOR, VSLOT_RESERVED, // used by RE VSLOT_REFRACT, VSLOT_DETAIL, 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, detail; float alphafront, alphaback; vec colorscale; vec glowcolor; float refractscale; vec refractcolor; 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.setsize(0); linked = false; scale = 1; rotation = 0; offset = ivec2(0, 0); scroll = vec2(0, 0); layer = detail = 0; alphafront = 0.5f; alphaback = 0; colorscale = vec(1, 1, 1); glowcolor = vec(1, 1, 1); refractscale = 0; refractcolor = vec(1, 1, 1); } void cleanup() { linked = false; } bool isdynamic() const; }; struct Slot { enum { OCTA, MATERIAL, DECAL }; struct Tex { int type; Texture *t; string name; int combined; Tex() : t(NULL), combined(-1) {} }; int index, smooth; vector sts; Shader *shader; vector params; VSlot *variants; bool loaded; uint texmask; char *grass; Texture *grasstex, *thumbnail; Slot(int index = -1) : index(index), variants(NULL), grass(NULL) { reset(); } virtual ~Slot() {} virtual int type() const { return OCTA; } virtual const char *name() const; virtual const char *texturedir() const { return "media/texture"; } virtual VSlot &emptyvslot(); virtual int cancombine(int type) const; virtual bool shouldpremul(int type) const { return false; } int findtextype(int type, int last = -1) const; void load(int index, Slot::Tex &t); void load(); Texture *loadthumbnail(); void reset() { smooth = -1; sts.setsize(0); shader = NULL; params.setsize(0); loaded = false; texmask = 0; DELETEA(grass); grasstex = NULL; thumbnail = NULL; } void cleanup() { loaded = false; grasstex = NULL; 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; } } inline bool VSlot::isdynamic() const { return !scroll.iszero() || slot->shader->isdynamic(); } struct MatSlot : Slot, VSlot { MatSlot(); int type() const { return MATERIAL; } const char *name() const; VSlot &emptyvslot() { return *this; } int cancombine(int type) const { return -1; } void reset() { Slot::reset(); VSlot::reset(); } void cleanup() { Slot::cleanup(); VSlot::cleanup(); } }; struct DecalSlot : Slot, VSlot { float depth, fade; DecalSlot(int index = -1) : Slot(index), VSlot(this), depth(1), fade(0.5f) {} int type() const { return DECAL; } const char *name() const; const char *texturedir() const { return "media/decal"; } VSlot &emptyvslot() { return *this; } int cancombine(int type) const; bool shouldpremul(int type) const; void reset() { Slot::reset(); VSlot::reset(); depth = 1; fade = 0.5f; } void cleanup() { Slot::cleanup(); VSlot::cleanup(); } }; struct cubemapside { GLenum target; const char *name; bool flipx, flipy, swapxy; }; extern const cubemapside cubemapsides[6]; extern Texture *notexture; extern Shader *nullshader, *hudshader, *hudtextshader, *hudnotextureshader, *nocolorshader, *foggedshader, *foggednotextureshader, *ldrshader, *ldrnotextureshader, *stdworldshader; extern int maxvsuniforms, maxfsuniforms; extern Shader *lookupshaderbyname(const char *name); extern Shader *useshaderbyname(const char *name); extern Shader *generateshader(const char *name, const char *cmd, ...); extern void resetslotshader(); extern void setslotshader(Slot &s); extern void linkslotshader(Slot &s, bool load = true); extern void linkvslotshader(VSlot &s, bool load = true); extern void linkslotshaders(); extern const char *getshaderparamname(const char *name, bool insert = true); extern bool shouldreuseparams(Slot &s, VSlot &p); extern void setupshaders(); extern void reloadshaders(); extern void cleanupshaders(); #define MAXBLURRADIUS 7 extern float blursigma; extern void setupblurkernel(int radius, float *weights, float *offsets); extern void setblurshader(int pass, int size, int radius, float *weights, float *offsets, GLenum target = GL_TEXTURE_2D); extern void savepng(const char *filename, ImageData &image, bool flip = false); extern void savetga(const char *filename, ImageData &image, bool flip = false); extern bool loaddds(const char *filename, ImageData &image, int force = 0); extern bool loadimage(const char *filename, ImageData &image); extern MatSlot &lookupmaterialslot(int slot, bool load = true); extern Slot &lookupslot(int slot, bool load = true); extern VSlot &lookupvslot(int slot, bool load = true); extern DecalSlot &lookupdecalslot(int slot, bool load = true); extern VSlot *findvslot(Slot &slot, const VSlot &src, const VSlot &delta); extern VSlot *editvslot(const VSlot &src, const VSlot &delta); extern void mergevslot(VSlot &dst, const VSlot &src, const VSlot &delta); extern void packvslot(vector &buf, const VSlot &src); extern bool unpackvslot(ucharbuf &buf, VSlot &dst, bool delta); extern Slot dummyslot; extern VSlot dummyvslot; extern DecalSlot dummydecalslot; extern vector slots; extern vector vslots;