#ifndef ENGINE_TEXTURE_HH #define ENGINE_TEXTURE_HH #include #include #include "shader.hh" struct ImageData { int w, h, bpp, levels, align, pitch; GLenum compressed; uchar *data; void *owner; void (*freefunc)(void *); ImageData() : data(nullptr), owner(nullptr), freefunc(nullptr) {} ImageData(int nw, int nh, int nbpp, int nlevels = 1, int nalign = 0, GLenum ncompressed = GL_FALSE) { setdata(nullptr, nw, nh, nbpp, nlevels, nalign, ncompressed); } ImageData(int nw, int nh, int nbpp, uchar *data) : owner(nullptr), freefunc(nullptr) { 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 = nullptr; } } 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 = nullptr; owner = nullptr; freefunc = nullptr; } 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(nullptr) {} }; enum { TEX_DIFFUSE = 0, TEX_NORMAL, TEX_GLOW, TEX_ENVMAP, TEX_SPEC, TEX_DEPTH, TEX_ALPHA, 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 = nullptr, int index = -1) : slot(slot), next(nullptr), 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(nullptr), 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(nullptr), grass(nullptr) { 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 = nullptr; params.setsize(0); loaded = false; texmask = 0; DELETEA(grass); grasstex = nullptr; thumbnail = nullptr; } void cleanup() { loaded = false; grasstex = nullptr; thumbnail = nullptr; loopv(sts) { Tex &t = sts[i]; t.t = nullptr; 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 texrotation { bool flipx, flipy, swapxy; }; struct cubemapside { GLenum target; const char *name; bool flipx, flipy, swapxy; }; extern const texrotation texrotations[8]; extern const cubemapside cubemapsides[6]; extern int hwtexsize, hwcubetexsize, hwmaxaniso, maxtexsize, hwtexunits, hwvtexunits; #define MAXBLURRADIUS 7 extern float blursigma; void setupblurkernel(int radius, float *weights, float *offsets); void setblurshader(int pass, int size, int radius, float *weights, float *offsets, GLenum target = GL_TEXTURE_2D); void savepng(const char *filename, ImageData &image, bool flip = false); void savetga(const char *filename, ImageData &image, bool flip = false); bool loadimage(const char *filename, ImageData &image); extern Slot dummyslot; extern VSlot dummyvslot; extern DecalSlot dummydecalslot; extern vector slots; extern vector vslots; MatSlot &lookupmaterialslot(int slot, bool load = true); Slot &lookupslot(int slot, bool load = true); VSlot &lookupvslot(int slot, bool load = true); DecalSlot &lookupdecalslot(int slot, bool load = true); VSlot *findvslot(Slot &slot, const VSlot &src, const VSlot &delta); VSlot *editvslot(const VSlot &src, const VSlot &delta); void mergevslot(VSlot &dst, const VSlot &src, const VSlot &delta); void packvslot(vector &buf, const VSlot &src); void packvslot(vector &buf, int index); void packvslot(vector &buf, const VSlot *vs); bool unpackvslot(ucharbuf &buf, VSlot &dst, bool delta); struct cube; void compactvslots(cube *c, int n = 8); void compactvslot(int &index); void compactvslot(VSlot &vs); int compactvslots(bool cull = false); void clearslots(); void linkslotshaders(); extern Texture *notexture; int texalign(const void *data, int w, int bpp); bool floatformat(GLenum format); uchar *loadalphamask(Texture *t); void setuptexcompress(); void createtexture(int tnum, int w, int h, const void *pixels, int clamp, int filter, GLenum component = GL_RGB, GLenum target = GL_TEXTURE_2D, int pw = 0, int ph = 0, int pitch = 0, bool resize = true, GLenum format = GL_FALSE, bool swizzle = false); void create3dtexture(int tnum, int w, int h, int d, const void *pixels, int clamp, int filter, GLenum component = GL_RGB, GLenum target = GL_TEXTURE_3D, bool swizzle = false); Texture *textureload(const char *name, int clamp = 0, bool mipit = true, bool msg = true); bool settexture(const char *name, int clamp = 0); bool reloadtexture(Texture &tex); bool reloadtexture(const char *name); void reloadtextures(); void cleanuptextures(); Texture *cubemapload(const char *name, bool mipit = true, bool msg = true, bool transient = false); void initenvmaps(); void genenvmaps(); ushort closestenvmap(const vec &o); ushort closestenvmap(int orient, const ivec &o, int size); GLuint lookupenvmap(ushort emid); GLuint lookupenvmap(Slot &slot); #endif