Index: data/stdshader.cfg
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/data/stdshader.cfg,v
retrieving revision 1.340
diff -r1.340 stdshader.cfg
220c220
<         MUL diffuse, diffuse, 2;
---
>         MUL diffuse.rgb, diffuse, 2;
265c265
<     MUL diffuse, diffuse, @pixelparam0;
---
>     MUL diffuse.rgb, diffuse, @pixelparam0;
338a339
>     #pragma CUBE2_variant TEX result.color.a, fragment.texcoord[1], texture[1], 2D;
377c378,379
<     LRP result.color, @pixelparam0, reflect, diffuse;
---
>     LRP result.color.rgb, @pixelparam0, reflect, diffuse;
>     MOV result.color.a, diffuse.a;
393c395,396
<     LRP result.color, @pixelparam0, reflect, diffuse;
---
>     LRP result.color.rgb, @pixelparam0, reflect, diffuse;
>     MOV result.color.a, diffuse.a;
437c440
<     MUL diffuse, diffuse, 2;
---
>     MUL diffuse.rgb, diffuse, 2;
492a496
>     stype = (if (btopt "e") [result 3] [result 1])
513a518,519
>     ] [
>         if (btopt "s") [stype = (+ $stype 8)]
515c521
<     variantshader (if (btopt "e") [result 3] [result 1]) $arg1 (if (btopt "i") [result 4] [result -1]) [
---
>     variantshader $stype $arg1 (if (btopt "i") [result 4] [result -1]) [
594c600
<         TEMP diffuse, lmc, lmlv, bump, light;
---
>         TEMP diffuse, lmc, lmlv, bump;
597a604
>             MOV result.color.a, lmc;
663,665c670,672
<             DP3_SAT light, bump, lmlv;
<             MUL light, light, lmc;
<             MAX light, light, program.env[5];
---
>             DP3_SAT lmlv, bump, lmlv;
>             MUL lmc.rgb, lmc, lmlv;
>             MAX lmc.rgb, lmc, program.env[5];
668c675
<                 #pragma CUBE2_dynlight light
---
>                 #pragma CUBE2_dynlight lmc
670c677
<                 MUL @(if (btopt "g") [result "diffuse.rgb"] [result "result.color"]), diffuse, light;
---
>                 MUL @(if (btopt "g") [result "diffuse.rgb"] [result "result.color.rgb"]), diffuse, lmc;
672,673c679,680
<                 #pragma CUBE2_shadowmap light
<                 #pragma CUBE2_dynlight light
---
>                 #pragma CUBE2_shadowmap lmc
>                 #pragma CUBE2_dynlight lmc
675c682
<                 MUL @(if (|| (btopt "g") (btopt "r")) [result "diffuse.rgb"] [result "result.color"]), diffuse, light;
---
>                 MUL @(if (|| (btopt "g") (btopt "r")) [result "diffuse.rgb"] [result "result.color.rgb"]), diffuse, lmc;
709c716
<             LRP @(if (btopt "g") [result "diffuse.rgb"] [result "result.color"]), rmod, reflect, diffuse;
---
>             LRP @(if (btopt "g") [result "diffuse.rgb"] [result "result.color.rgb"]), rmod, reflect, diffuse;
727c734
<                     MAD result.color, k, glow, diffuse;
---
>                     MAD result.color.rgb, k, glow, diffuse;
729c736,737
<                     MUL result.color, k, glow;
---
>                     MUL result.color.rgb, k, glow;
>                     #pragma CUBE2_variant TEX result.color.a, @(if $minimizetcusage [result "lmtc.wzyx"] [result "lmtc"]), texture[1], 2D;
732c740
<                 MAD result.color, glow, @(if (btopt "G") [result "pulsecol"] [result $pixelparam0]), diffuse;
---
>                 MAD result.color.rgb, glow, @(if (btopt "G") [result "pulsecol"] [result $pixelparam0]), diffuse;
1877a1886,1887
>             DP4 projtc.z, state.matrix.modelview.row[2], -opos; 
>             MAD projtc.z, projtc, program.env[5].x, program.env[5].y;
1880,1888c1890
<             MOV result.texcoord[2].xyw, projtc;
< 
<             TEMP offset;
<             MAD offset.xyz, vertex.texcoord[0], { 2.82842712474619, 2.82842712474619, 0, 0 }, { -1.4142135623731, -1.4142135623731, 1, 0 };
<             MOV result.texcoord[3].xyz, offset;
< 
<             DP4 offset.z, state.matrix.modelview.row[2], -opos;
<             MAD offset.z, offset.z, program.env[5].x, program.env[5].y;
<             MOV result.texcoord[4].xyz, offset;
---
>             MOV result.texcoord[2], projtc;
1901c1903,1904
<             DP3 offset.x, fragment.texcoord[3], fragment.texcoord[4];
---
>             MAD offset, fragment.texcoord[0], { 2.82842712474619, 2.82842712474619, 0, 0 }, { -1.4142135623731, -1.4142135623731, 0, 0 };
>             DP3 offset.x, offset, offset;
1904c1907
<                 SUB_SAT depth.x, depth.x, offset.x;
---
>                 SUB_SAT depth.x, depth.x, fragment.texcoord[2].z;
1906c1909
<                 MAD_SAT depth.x, depth.x, program.env[5].z, -offset.x;
---
>                 MAD_SAT depth.x, depth.x, program.env[5].z, -fragment.texcoord[2].z;
1907a1911
>             SUB_SAT depth.x, depth.x, offset.x;
1918a1923,1936
> shader 0 "blendbrush" [
>     @vpstart
>     DP4 result.texcoord[0].x, opos, program.env[0];
>     DP4 result.texcoord[0].y, opos, program.env[1];
>     MOV result.color, vertex.color;
>     END
> ] [
>     @fpstart
>     TEMP brush;
>     TEX brush, fragment.texcoord[0], texture[0], 2D;
>     MUL result.color, fragment.color, brush;
>     END
> ]
> 
2736c2754,2755
<         vec4 diffuse = texture2D(diffusemap, dtc) * 2.0;
---
>         vec4 diffuse = texture2D(diffusemap, dtc);
>         diffuse.rgb *= 2.0;
2757,2761c2776,2780
<         vec4 light = lmc * clamp(intensity, 0.0, 1.0);
<         #pragma CUBE2_shadowmap light
<         #pragma CUBE2_dynlight light
<         light = max(light, ambient);
<         gl_FragColor = diffuse * light;
---
>         lmc.rgb *= clamp(intensity, 0.0, 1.0);
>         #pragma CUBE2_shadowmap lmc
>         #pragma CUBE2_dynlight lmc
>         lmc.rgb = max(light.rgb, ambient.rgb);
>         gl_FragColor = diffuse * lmc;
Index: src/engine/3dgui.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/3dgui.cpp,v
retrieving revision 1.151
diff -r1.151 3dgui.cpp
242c242
<     int texture(Texture *t, float scale, int rotate, int xoff, int yoff, Texture *glowtex, const vec &glowcolor)
---
>     int texture(Texture *t, float scale, int rotate, int xoff, int yoff, Texture *glowtex, const vec &glowcolor, Texture *layertex)
247c247
<         if(t!=notexture && visible()) icon_(t, true, true, curx, cury, size, ishit(size+SHADOW, size+SHADOW), rotate, xoff, yoff, glowtex, glowcolor);
---
>         if(t!=notexture && visible()) icon_(t, true, true, curx, cury, size, ishit(size+SHADOW, size+SHADOW), rotate, xoff, yoff, glowtex, glowcolor, layertex);
429c429
<     void icon_(Texture *t, bool overlaid, bool tiled, int x, int y, int size, bool hit, int rotate = 0, int xoff = 0, int yoff = 0, Texture *glowtex = NULL, const vec &glowcolor = vec(1, 1, 1)) 
---
>     void icon_(Texture *t, bool overlaid, bool tiled, int x, int y, int size, bool hit, int rotate = 0, int xoff = 0, int yoff = 0, Texture *glowtex = NULL, const vec &glowcolor = vec(1, 1, 1), Texture *layertex = NULL) 
496a497,508
>         if(layertex)
>         {
>             glBindTexture(GL_TEXTURE_2D, layertex->id);
>             glColor3fv(color.v);
>             glBegin(GL_QUADS);
>             glTexCoord2fv(tc[0]); glVertex2f(x+xs/2, y+ys/2);
>             glTexCoord2fv(tc[1]); glVertex2f(x+xs,   y+ys/2);
>             glTexCoord2fv(tc[2]); glVertex2f(x+xs,   y+ys);
>             glTexCoord2fv(tc[3]); glVertex2f(x+xs/2, y+ys);
>             glEnd();
>         }
>             
Index: src/engine/engine.h
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/engine.h,v
retrieving revision 1.300
diff -r1.300 engine.h
236c236
< extern void invalidatemerges(cube &c);
---
> extern void invalidatemerges(cube &c, bool msg);
534a535,547
> // blendmap
> extern bool setblendmaporigin(const ivec &o, int size);
> extern bool hasblendmap();
> extern uchar lookupblendmap(const vec &pos);
> extern void resetblendmap();
> extern void enlargeblendmap();
> extern void optimizeblendmap();
> extern void renderblendbrush(GLuint tex, float x, float y, float w, float h);
> extern void renderblendbrush();
> extern bool loadblendmap(gzFile f);
> extern void saveblendmap(gzFile f);
> extern uchar shouldsaveblendmap();
> 
Index: src/engine/grass.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/grass.cpp,v
retrieving revision 1.40
diff -r1.40 grass.cpp
35c35
<         memcpy(g.color, &lm->data[3*(int(tv)*LM_PACKW + int(tu))], 3);
---
>         memcpy(g.color, &lm->data[lm->bpp*(int(tv)*LM_PACKW + int(tu))], 3);
Index: src/engine/lightmap.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/lightmap.cpp,v
retrieving revision 1.189
diff -r1.189 lightmap.cpp
10d9
< VARF(worldlod, 0, 0, 1,  hdr.mapwlod = worldlod);
29a29,38
> static surfaceinfo brightsurfaces[6] =
> {
>     {{0}, 0, 0, 0, 0, LMID_BRIGHT, LAYER_TOP},
>     {{0}, 0, 0, 0, 0, LMID_BRIGHT, LAYER_TOP},
>     {{0}, 0, 0, 0, 0, LMID_BRIGHT, LAYER_TOP},
>     {{0}, 0, 0, 0, 0, LMID_BRIGHT, LAYER_TOP},
>     {{0}, 0, 0, 0, 0, LMID_BRIGHT, LAYER_TOP},
>     {{0}, 0, 0, 0, 0, LMID_BRIGHT, LAYER_TOP},
> };
> 
35,36c44,45
< static int lmtype, lmorient, lmrotate;
< static uchar lm[3*LM_MAXW*LM_MAXH];
---
> static int lmtype, lmbpp, lmorient, lmrotate;
> static uchar lm[4*LM_MAXW*LM_MAXH];
73c82
<         loopvrev(lightmaps) if(lightmaps[i].type==LM_DIFFUSE || lightmaps[i].type==LM_BUMPMAP0)
---
>         loopvrev(lightmaps) switch(lightmaps[i].type&LM_TYPE)
75,78c84,90
<             if(progresstexticks++ % 4) break;
<             glBindTexture(GL_TEXTURE_2D, progresstex);
<             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LM_PACKW, LM_PACKH, GL_RGB, GL_UNSIGNED_BYTE, lightmaps[i].data);
<             break;
---
>             case LM_DIFFUSE:
>             case LM_BUMPMAP0:
>                 if(progresstexticks++ % 4) break;
>                 glBindTexture(GL_TEXTURE_2D, progresstex);
>                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LM_PACKW, LM_PACKH, 
>                     lightmaps[i].type&LM_ALPHA ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, lightmaps[i].data);
>                 break;
124c136
<     if(type != LM_BUMPMAP1 && !packroot.insert(tx, ty, tw, th))
---
>     if((type&LM_TYPE) != LM_BUMPMAP1 && !packroot.insert(tx, ty, tw, th))
127c139,145
<     uchar *dst = data + 3 * tx + ty * 3 * LM_PACKW;
---
>     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;
130,132c148,150
<         memcpy(dst, src, 3 * tw);
<         dst += 3 * LM_PACKW;
<         src += 3 * tw;
---
>         memcpy(dst, src, bpp * tw);
>         dst += bpp * LM_PACKW;
>         src += bpp * tw;
136d153
<     return true;
142c159
<     if(l.type != LM_DIFFUSE && l.type != LM_BUMPMAP0)
---
>     if((l.type&LM_TYPE) == LM_BUMPMAP1)
148c165
<     uchar unlit[3] = { hdr.ambient, hdr.ambient, hdr.ambient };
---
>     uchar unlit[4] = { hdr.ambient, hdr.ambient, hdr.ambient, 255 };
151c168
<         if(l.type == LM_BUMPMAP0)
---
>         if((l.type&LM_TYPE) == LM_BUMPMAP0)
161c178
< void insert_lightmap(int type, ushort &x, ushort &y, ushort &lmid)
---
> void insert_lightmap(ushort &x, ushort &y, uchar &lmid)
165c182
<         if(lightmaps[i].type == type && lightmaps[i].insert(x, y, lm, lm_w, lm_h))
---
>         if(lightmaps[i].type == lmtype && lightmaps[i].insert(x, y, lm, lm_w, lm_h))
168c185
<             if(type == LM_BUMPMAP0) ASSERT(lightmaps[i+1].insert(x, y, (uchar *)lm_ray, lm_w, lm_h));
---
>             if((lmtype&LM_TYPE) == LM_BUMPMAP0) ASSERT(lightmaps[i+1].insert(x, y, (uchar *)lm_ray, lm_w, lm_h));
175c192,195
<     l.type = type;
---
>     l.type = lmtype;
>     l.bpp = lmbpp;
>     l.data = new uchar[lmbpp*LM_PACKW*LM_PACKH];
>     memset(l.data, 0, lmbpp*LM_PACKW*LM_PACKH);
177c197
<     if(type == LM_BUMPMAP0)
---
>     if((lmtype&LM_TYPE) == LM_BUMPMAP0)
180c200,203
<         r.type = LM_BUMPMAP1;
---
>         r.type = LM_BUMPMAP1 | (lmtype&~LM_TYPE);
>         r.bpp = 3;
>         r.data = new uchar[3*LM_PACKW*LM_PACKH];
>         memset(r.data, 0, 3*LM_PACKW*LM_PACKH);
184a208,214
> void copy_lightmap(surfaceinfo &surface)
> {
>     lightmaps[surface.lmid-LMID_RESERVED].copy(surface.x, surface.y, lm, lm_w, lm_h);
>     if((lmtype&LM_TYPE)==LM_BUMPMAP0 && lightmaps.inrange(surface.lmid+1-LMID_RESERVED))
>         lightmaps[surface.lmid+1-LMID_RESERVED].copy(surface.x, surface.y, (uchar *)lm_ray, lm_w, lm_h);
> }
> 
207c237
<     const uchar *xcolor = lm, *ycolor = ylm.data + 3*(y.x + y.y*LM_PACKW);
---
>     const uchar *xcolor = lm, *ycolor = ylm.data + lmbpp*(y.x + y.y*LM_PACKW);
210,214c240,242
<         loopj(lm_w)
<         {
<             loopk(3) if(*xcolor++ != *ycolor++) return false;
<         }
<         ycolor += 3*(LM_PACKW - y.w);
---
>         if(memcmp(xcolor, ycolor, lmbpp*lm_w)) return false;
>         xcolor += lmbpp*lm_w;
>         ycolor += lmbpp*LM_PACKW;
216c244
<     if(lmtype != LM_BUMPMAP0) return true;
---
>     if((lmtype&LM_TYPE) != LM_BUMPMAP0) return true;
220,221c248,250
<         loopj(lm_w) if(*xdir++ != *ydir++) return false;
<         ydir += LM_PACKW - y.w;
---
>         if(memcmp(xdir, ydir, lm_w*sizeof(bvec))) return false;
>         xdir += lm_w;
>         ydir += LM_PACKW;
233c262
<        color += 3;
---
>        color += lmbpp;
242c271
< void pack_lightmap(int type, surfaceinfo &surface) 
---
> bool pack_lightmap(surfaceinfo &surface) 
249c278
<             insert_lightmap(type, surface.x, surface.y, surface.lmid);
---
>             insert_lightmap(surface.x, surface.y, surface.lmid);
256a286
>             return false;
259c289,290
<     else insert_lightmap(type, surface.x, surface.y, surface.lmid);
---
>     else insert_lightmap(surface.x, surface.y, surface.lmid);
>     return true;
261a293,341
> void update_lightmap(const surfaceinfo &surface)
> {
>     if(max(LM_PACKW, LM_PACKH) > hwtexsize) 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 = renderpath==R_FIXEDFUNCTION ? (lm.type&~LM_TYPE) | LM_DIFFUSE : 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, false, tex.type&LM_ALPHA ? GL_RGBA : GL_RGB);
>         if(renderpath!=R_FIXEDFUNCTION && (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, false, 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.type&LM_ALPHA ? 4 : 3)]);
>     if(renderpath!=R_FIXEDFUNCTION && (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);
> }
>  
>         
296c376
<         switch(lmtype)
---
>         switch(lmtype&LM_TYPE)
405,407c485,486
<     int stride = 3*lm_w;
< 
<     loop(y, lm_h) loop(x, lm_w) loopk(3)
---
>     int stride = lmbpp*lm_w;
>     loop(y, lm_h) loop(x, lm_w) 
409,411c488
<         int c = *src, val = 0; 
<         const int *m = n>1 ? matrix5x5 : matrix3x3;
<         for(int t = -n; t<=n; t++) for(int s = -n; s<=n; s++, m++)
---
>         loopk(3)
413c490,497
<             val += *m * (x+s>=0 && x+s<lm_w && y+t>=0 && y+t<lm_h ? src[t*stride+3*s] : c);
---
>             int c = *src, val = 0; 
>             const int *m = n>1 ? matrix5x5 : matrix3x3;
>             for(int t = -n; t<=n; t++) for(int s = -n; s<=n; s++, m++)
>             {
>                 val += *m * (x+s>=0 && x+s<lm_w && y+t>=0 && y+t<lm_h ? src[t*stride+lmbpp*s] : c);
>             }
>             *dst++ = val/(n>1 ? matrix5x5sum : matrix3x3sum);
>             src++;
415,416c499
<         *dst++ = val/(n>1 ? matrix5x5sum : matrix3x3sum);
<         src++;
---
>         if(lmtype&LM_ALPHA) *dst++ = *src++;
418c501
<     memcpy(lm, blur, 3*lm_w*lm_h);
---
>     memcpy(lm, blur, lmbpp*lm_w*lm_h);
420a504,508
> static inline void generate_alpha(float tolerance, const vec &pos, uchar &alpha)
> {
>     alpha = lookupblendmap(pos);
> }
>         
424c512,525
< bool generate_lightmap(float lpu, int y1, int y2, const vec &origin, const lerpvert *lv, int numv, const vec &ustep, const vec &vstep)
---
> enum
> {
>     NO_SURFACE = 0,
>     SURFACE_AMBIENT_BOTTOM,
>     SURFACE_AMBIENT_TOP,
>     SURFACE_LIGHTMAP_BOTTOM,
>     SURFACE_LIGHTMAP_TOP,
>     SURFACE_LIGHTMAP_BLEND 
> };
> 
> #define SURFACE_AMBIENT SURFACE_AMBIENT_BOTTOM
> #define SURFACE_LIGHTMAP SURFACE_LIGHTMAP_BOTTOM
> 
> int generate_lightmap(float lpu, int y1, int y2, const vec &origin, const lerpvert *lv, int numv, const vec &ustep, const vec &vstep)
426c527
<     static uchar mincolor[3], maxcolor[3];
---
>     static uchar mincolor[4], maxcolor[4];
447,449c548,550
<         memset(mincolor, 255, 3);
<         memset(maxcolor, 0, 3);
<         if(lmtype == LM_BUMPMAP0) memset(lm_ray, 0, sizeof(lm_ray));
---
>         memset(mincolor, 255, sizeof(mincolor));
>         memset(maxcolor, 0, sizeof(maxcolor));
>         if((lmtype&LM_TYPE) == LM_BUMPMAP0) memset(lm_ray, 0, sizeof(lm_ray));
452c553
<     static vec samples [4*(LM_MAXW+1)*(LM_MAXH+1)];
---
>     static vec samples[4*(LM_MAXW+1)*(LM_MAXH+1)];
457c558
<     uchar *skylight = &lm[3*lm_w*y1];
---
>     uchar *skylight = &lm[lmbpp*lm_w*y1];
466c567
<         for(int x = 0; x < lm_w; ++x, u.add(ustep), normal.add(nstep), skylight += 3) 
---
>         for(int x = 0; x < lm_w; ++x, u.add(ustep), normal.add(nstep), skylight += lmbpp) 
468c569
<             CHECK_PROGRESS(return false);
---
>             CHECK_PROGRESS(return NO_SURFACE);
472c573
<                 if(lmtype==LM_BUMPMAP0 || !adaptivesample || sample->x<hdr.skylight[0] || sample->y<hdr.skylight[1] || sample->z<hdr.skylight[2])
---
>                 if((lmtype&LM_TYPE)==LM_BUMPMAP0 || !adaptivesample || sample->x<hdr.skylight[0] || sample->y<hdr.skylight[1] || sample->z<hdr.skylight[2])
476a578
>             if(lmtype&LM_ALPHA) generate_alpha(tolerance, u, skylight[3]);
538c640
<                 CHECK_PROGRESS(return false);
---
>                 CHECK_PROGRESS(return NO_SURFACE);
590,591c692,697
< 
<                 if(lmtype == LM_BUMPMAP0)
---
>                 if(lmtype&LM_ALPHA)
>                 {
>                     mincolor[3] = min(mincolor[3], lumel[3]);
>                     maxcolor[3] = max(maxcolor[3], lumel[3]);
>                 }
>                 if((lmtype&LM_TYPE) == LM_BUMPMAP0)
596a703,704
>                         // 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 
598,599d705
<                         // 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
609a716
>                     ray++;
611,612c718
<                 lumel += 3;
<                 ray++;
---
>                 lumel += lmbpp;
618c724,725
<            int(maxcolor[2]) - int(mincolor[2]) <= lighterror)
---
>            int(maxcolor[2]) - int(mincolor[2]) <= lighterror &&
>            mincolor[3] >= maxcolor[3])
624,626c731,734
<                color[2] <= ambient + lighterror)
<                 return false;
<             if(lmtype != LM_BUMPMAP0 || 
---
>                color[2] <= ambient + lighterror &&
>                (maxcolor[3]==0 || mincolor[3]==255))
>                 return mincolor[3]==255 ? SURFACE_AMBIENT_TOP : SURFACE_AMBIENT_BOTTOM;
>             if((lmtype&LM_TYPE) != LM_BUMPMAP0 || 
629a738
> 
632c741,745
<                 if(lmtype == LM_BUMPMAP0) loopk(3) ((bvec *)lm_ray)[0][k] = uchar((int(maxray[k])+int(minray[k]))/2);
---
>                 if(lmtype&LM_ALPHA) lm[3] = mincolor[3];
>                 if((lmtype&LM_TYPE) == LM_BUMPMAP0) 
>                 {
>                     loopk(3) ((bvec *)lm_ray)[0][k] = uchar((int(maxray[k])+int(minray[k]))/2);
>                 }
639c752,754
<     return true;
---
>     if(mincolor[3]==255) return SURFACE_LIGHTMAP_TOP;
>     else if(maxcolor[3]==0) return SURFACE_LIGHTMAP_BOTTOM;
>     else return SURFACE_LIGHTMAP_BLEND;
641a757,784
> int preview_lightmap_alpha(float lpu, int y1, int y2, const vec &origin, const vec &ustep, const vec &vstep)
> {
>     extern int fullbrightlevel;
>     float tolerance = 0.5 / lpu;
>     uchar *dst = &lm[4*lm_w*y1];
>     vec v = origin;
>     uchar minalpha = 255, maxalpha = 0;
>     for(int y = y1; y < y2; ++y, v.add(vstep))
>     {
>         vec u(v);
>         for(int x = 0; x < lm_w; ++x, u.add(ustep), dst += 4)
>         {
>             loopk(3) dst[k] = fullbrightlevel;        
>             generate_alpha(tolerance, u, dst[3]);
>             minalpha = min(minalpha, dst[3]);
>             maxalpha = max(maxalpha, dst[3]);
>         }
>     }
>     if(y2 == lm_h)
>     {
>         if(minalpha==255) return SURFACE_AMBIENT_TOP;
>         if(maxalpha==0) return SURFACE_AMBIENT_BOTTOM;
>         if(minalpha==maxalpha) lm_w = lm_h = 1;    
>         loopi(lm_w*lm_h) ((bvec *)lm_ray)[i] = bvec(128, 128, 255);
>     }
>     return SURFACE_LIGHTMAP_BLEND;
> }        
> 
705,708c848
<         if(light.type != ET_LIGHT) continue;
< 
<         int radius = light.attr1;
<         if(radius > 0)
---
>         switch(light.type)
710,712c850,861
<             if(light.o.x + radius < cx || light.o.x - radius > cx + csize ||
<                light.o.y + radius < cy || light.o.y - radius > cy + csize)
<                 continue;
---
>             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;
778c927
< bool find_lights(int cx, int cy, int cz, int size, const vec *v, const vec *n, const vec *n2)
---
> bool find_lights(int cx, int cy, int cz, int size, const vec *v, const vec *n, const vec *n2, bool layered)
789c938,941
<             addlight(light, cx, cy, cz, size, v, n, n2);
---
>             switch(light.type)
>             {
>                 case ET_LIGHT: addlight(light, cx, cy, cz, size, v, n, n2); break;
>             }
795,796c947,950
<         if(light.type != ET_LIGHT) continue;
<         addlight(light, cx, cy, cz, size, v, n, n2);
---
>         switch(light.type)
>         {
>             case ET_LIGHT: addlight(light, cx, cy, cz, size, v, n, n2); break;
>         }
797a952
>     if(layered && setblendmaporigin(ivec(cx, cy, cz), size)) return true;
801c956
< bool setup_surface(plane planes[2], const vec *p, const vec *n, const vec *n2, uchar texcoords[8])
---
> int setup_surface(plane planes[2], const vec *p, const vec *n, const vec *n2, uchar texcoords[8], bool preview = false)
878c1033,1049
<     if(!n2)
---
>     int surftype = NO_SURFACE;
>     if(preview)
>     {
>         if(!n2) surftype = preview_lightmap_alpha(lpu, 0, lm_h, origin1, ustep, vstep);
>         else
>         {
>             origin2 = p[0];
>             origin2.add(uo);
>             vec tstep(t);
>             tstep.mul(tmax / (lm_h - split - 1));
> 
>             surftype = preview_lightmap_alpha(lpu, 0, split, origin1, ustep, vstep);
>             if(surftype<SURFACE_LIGHTMAP) return surftype;
>             surftype = preview_lightmap_alpha(lpu, split, lm_h, origin2, ustep, tstep);
>         }
>     }
>     else if(!n2)
884,885c1055
<         if(!generate_lightmap(lpu, 0, lm_h, origin1, lv, numv, ustep, vstep))
<             return false;
---
>         surftype = generate_lightmap(lpu, 0, lm_h, origin1, lv, numv, ustep, vstep);
901,903c1071,1073
<         if(!generate_lightmap(lpu, 0, split, origin1, lv1, numv1, ustep, vstep) ||
<            !generate_lightmap(lpu, split, lm_h, origin2, lv2, numv2, ustep, tstep))
<             return false;
---
>         surftype = generate_lightmap(lpu, 0, split, origin1, lv1, numv1, ustep, vstep);
>         if(surftype<SURFACE_LIGHTMAP) return surftype;
>         surftype = generate_lightmap(lpu, split, lm_h, origin2, lv2, numv2, ustep, tstep);
904a1075
>     if(surftype<SURFACE_LIGHTMAP) return surftype;
931c1102,1116
<     return true;
---
>     return surftype;
> }
> 
> void removelmalpha()
> {
>     if(!(lmtype&LM_ALPHA)) return;
>     for(uchar *dst = lm, *src = lm, *end = &src[lm_w*lm_h*4];
>         src < end;
>         dst += 3, src += 4)
>     {
>         dst[0] = src[0];
>         dst[1] = src[1];
>         dst[2] = src[2];
>     }
>     lmtype &= ~LM_ALPHA;
950a1136,1137
>     surfaceinfo surfaces[12];
>     int numsurfs = 0;
960c1147,1148
<         Slot &slot = lookuptexture(c.texture[i], false);
---
>         Slot &slot = lookuptexture(c.texture[i], false),
>              *layer = slot.layer ? &lookuptexture(slot.layer, false) : NULL;
961a1150,1151
>         int shadertype = shader->type;
>         if(layer && layer->shader) shadertype |= layer->shader->type;
980c1170
<             if(!find_lights(mo.x, mo.y, mo.z, 1<<msz, v, n, NULL))
---
>             if(!find_lights(mo.x, mo.y, mo.z, 1<<msz, v, n, NULL, layer!=NULL))
982c1172
<                 if(!(shader->type&(SHADER_NORMALSLMS | SHADER_ENVMAP))) continue;
---
>                 if(!(shadertype&(SHADER_NORMALSLMS | SHADER_ENVMAP))) continue;
1013c1203
<             if(!find_lights(cx, cy, cz, size, v, n, numplanes > 1 ? n2 : NULL))
---
>             if(!find_lights(cx, cy, cz, size, v, n, numplanes > 1 ? n2 : NULL, layer!=NULL))
1015c1205
<                 if(!(shader->type&(SHADER_NORMALSLMS | SHADER_ENVMAP))) continue;
---
>                 if(!(shadertype&(SHADER_NORMALSLMS | SHADER_ENVMAP))) continue;
1018,1021c1208
<         lmtype = LM_DIFFUSE;
<         lmorient = i;
<         lmrotate = slot.rotation;
<         if(shader->type&(SHADER_NORMALSLMS | SHADER_ENVMAP))
---
>         if(shadertype&(SHADER_NORMALSLMS | SHADER_ENVMAP))
1023d1209
<             if(shader->type&SHADER_NORMALSLMS) lmtype = LM_BUMPMAP0;
1031c1217,1218
<         if(lights1.empty() && lights2.empty() && hdr.skylight[0]<=ambient && hdr.skylight[1]<=ambient && hdr.skylight[2]<=ambient) continue;
---
>         if(lights1.empty() && lights2.empty() && (!layer || !hasblendmap()) && hdr.skylight[0]<=ambient && hdr.skylight[1]<=ambient && hdr.skylight[2]<=ambient) continue;
> 
1033,1034d1219
<         if(!setup_surface(planes, v, n, numplanes >= 2 ? n2 : NULL, texcoords))
<             continue;
1036,1042c1221,1296
<         CHECK_PROGRESS(return);
<         newsurfaces(c);
<         surfaceinfo &surface = c.ext->surfaces[i];
<         surface.w = lm_w;
<         surface.h = lm_h;
<         memcpy(surface.texcoords, texcoords, 8);
<         pack_lightmap(lmtype, surface);
---
>         lmtype = shader->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE;
>         if(layer) lmtype |= LM_ALPHA;
>         lmbpp = lmtype&LM_ALPHA ? 4 : 3;
>         lmorient = i;
>         lmrotate = slot.rotation;
>         int surftype = setup_surface(planes, v, n, numplanes >= 2 ? n2 : NULL, texcoords);
>         switch(surftype)
>         {
>             case SURFACE_LIGHTMAP_BOTTOM:
>                 if((shader->type^layer->shader->type)&SHADER_NORMALSLMS ||
>                    (shader->type&SHADER_NORMALSLMS && slot.rotation!=layer->rotation))
>                     break;
>                 // fall through
>             case SURFACE_LIGHTMAP_BLEND:
>             case SURFACE_LIGHTMAP_TOP:
>             {
>                 CHECK_PROGRESS(return);
>                 if(!numsurfs) { numsurfs = 6; memset(surfaces, 0, sizeof(surfaces)); }
>                 surfaceinfo &surface = surfaces[i];
>                 surface.w = lm_w;
>                 surface.h = lm_h;
>                 if(surftype==SURFACE_LIGHTMAP_BLEND) surface.layer = LAYER_TOP|LAYER_BLEND;
>                 else
>                 {
>                     if(surftype==SURFACE_LIGHTMAP_BOTTOM) surface.layer = LAYER_BOTTOM;
>                     if(lmtype&LM_ALPHA) removelmalpha();
>                 } 
>                 memcpy(surface.texcoords, texcoords, 8);
>                 pack_lightmap(surface);
>                 if(surftype!=SURFACE_LIGHTMAP_BLEND) continue;
>                 if((shader->type^layer->shader->type)&SHADER_NORMALSLMS ||
>                    (shader->type&SHADER_NORMALSLMS && slot.rotation!=layer->rotation)) 
>                     break;
>                 surfaces[numsurfs] = surface;
>                 surfaces[numsurfs++].layer = LAYER_BOTTOM;
>                 continue;
>             }
> 
>             case SURFACE_AMBIENT_BOTTOM:
>                 if(layer)
>                 {
>                     if(!numsurfs) { numsurfs = 6; memset(surfaces, 0, sizeof(surfaces)); }
>                     surfaces[i].layer = LAYER_BOTTOM;
>                 }
>                 continue;
> 
>             default: continue;
>         }
> 
>         lmtype = layer->shader->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE;
>         lmbpp = 3;
>         lmrotate = layer->rotation;
>         switch(setup_surface(planes, v, n, numplanes >= 2 ? n2 : NULL, texcoords))
>         {
>             case SURFACE_LIGHTMAP_TOP:
>             {
>                 CHECK_PROGRESS(return);
>                 if(!numsurfs) { numsurfs = 6; memset(surfaces, 0, sizeof(surfaces)); }
>                 surfaceinfo &surface = surfaces[surftype==SURFACE_LIGHTMAP_BLEND ? numsurfs++ : i];
>                 surface.w = lm_w;
>                 surface.h = lm_h;
>                 surface.layer = LAYER_BOTTOM;
>                 memcpy(surface.texcoords, texcoords, 8);
>                 pack_lightmap(surface);
>                 break;
>             }
> 
>             case SURFACE_AMBIENT_TOP:
>             {
>                 if(!numsurfs) { numsurfs = 6; memset(surfaces, 0, sizeof(surfaces)); }
>                 surfaceinfo &surface = surfaces[surftype==SURFACE_LIGHTMAP_BLEND ? numsurfs++ : i];
>                 memset(&surface, 0, sizeof(surface));
>                 surface.layer = LAYER_BOTTOM;
>                 break;
>             }
>         }
1043a1298
>     if(numsurfs) newsurfaces(c, surfaces, numsurfs);
1061a1317,1493
> bool previewblends(cube &c, const ivec &co, int size)
> {
>     if(isempty(c)) return false;
> 
>     bool usefaces[6];
>     int vertused = 0;
>     loopi(6) if((usefaces[i] = visibleface(c, i, co.x, co.y, co.z, size) && lookuptexture(c.texture[i], false).layer)) 
>         vertused |= fvmasks[1<<i];
>     if(!vertused) return false;
> 
>     if(!setblendmaporigin(co, size))
>     {
>         if(!c.ext || !c.ext->surfaces || c.ext->surfaces==brightsurfaces) return false;
>         bool blends = false;
>         loopi(6) if(c.ext->surfaces[i].layer&LAYER_BLEND || c.ext->surfaces[i].layer==LAYER_BOTTOM)
>         {
>             surfaceinfo &surface = c.ext->surfaces[i];
>             memset(&surface, 0, sizeof(surfaceinfo));
>             surface.lmid = LMID_BRIGHT;
>             surface.layer = LAYER_TOP;
>             blends = true;
>         }
>         return blends;
>     }
> 
>     vec verts[8];
>     loopi(8) if(vertused&(1<<i)) 
>     {
>         vvec vv;
>         calcvert(c, co.x, co.y, co.z, size, vv, i);
>         verts[i] = vv.tovec(co);
>     }
> 
>     surfaceinfo surfaces[12], *srcsurfaces = c.ext && c.ext->surfaces && c.ext->surfaces!=brightsurfaces ? c.ext->surfaces : NULL;
>     int numsurfs = srcsurfaces ? 6 : 0, numsrcsurfs = srcsurfaces ? 6 : 0;
>     if(srcsurfaces) memcpy(surfaces, srcsurfaces, 6*sizeof(surfaceinfo));
>     else 
>     {
>         memset(surfaces, 0, 6*sizeof(surfaceinfo));
>         loopi(6) surfaces[i].lmid = LMID_BRIGHT;
>     }
>     loopi(6)
>     {
>         if(surfaces[i].layer&LAYER_BLEND) 
>         {
>             if(!usefaces[i]) 
>             {
>                 surfaces[numsurfs++] = srcsurfaces[numsrcsurfs++];
>                 continue;
>             }
>             numsrcsurfs++;
>         }
>         else if(!usefaces[i]) continue;
> 
>         plane planes[2];
>         int numplanes = genclipplane(c, i, verts, planes);
>         if(!numplanes) continue;
> 
>         Slot &slot = lookuptexture(c.texture[i], false),
>              &layer = lookuptexture(slot.layer, false);
>         Shader *shader = slot.shader ? slot.shader : defaultshader;
>         int shadertype = shader->type;
>         if(layer.shader) shadertype |= layer.shader->type;
>             
>         vec v[4];
>         loopk(4) v[k] = verts[faceverts(c, i, k)];
>         static const vec n[4] = { vec(0, 0, 1), vec(0, 0, 1), vec(0, 0, 1), vec(0, 0, 1) };
>         uchar texcoords[8];
> 
>         lmtype = shadertype&SHADER_NORMALSLMS ? LM_BUMPMAP0|LM_ALPHA : LM_DIFFUSE|LM_ALPHA;
>         lmbpp = 4;
>         lmorient = i;
>         lmrotate = slot.rotation;
>         int surftype = setup_surface(planes, v, n, numplanes >= 2 ? n : NULL, texcoords, true);
>         switch(surftype)
>         {
>             case SURFACE_AMBIENT_TOP:
>                 if(srcsurfaces) 
>                 {
>                     memset(&surfaces[i], 0, sizeof(surfaceinfo));
>                     surfaces[i].lmid = LMID_BRIGHT;
>                 }
>                 continue;
> 
>             case SURFACE_AMBIENT_BOTTOM:
>                 if(!numsurfs) numsurfs = 6;
>                 if(srcsurfaces) 
>                 {
>                     memset(&surfaces[i], 0, sizeof(surfaceinfo));
>                     surfaces[i].lmid = LMID_BRIGHT;
>                 }
>                 surfaces[i].layer = LAYER_BOTTOM;
>                 continue;
> 
>             case SURFACE_LIGHTMAP_BLEND:
>             {
>                 if(!numsurfs) numsurfs = 6;
>                 surfaceinfo &surface = surfaces[i];
>                 if(surface.w==lm_w && surface.h==lm_h && 
>                    surface.layer==(LAYER_TOP|LAYER_BLEND) && 
>                    !memcmp(surface.texcoords, texcoords, 8) &&
>                    lightmaps.inrange(surface.lmid-LMID_RESERVED) &&
>                    lightmaps[surface.lmid-LMID_RESERVED].type==lmtype)           
>                 {
>                     copy_lightmap(surface);
>                     update_lightmap(surface);
>                     surfaces[numsurfs] = surface;
>                     surfaces[numsurfs++].layer = LAYER_BOTTOM;
>                     continue;
>                 }
>                 surface.w = lm_w;
>                 surface.h = lm_h;
>                 surface.layer = LAYER_TOP|LAYER_BLEND;
>                 memcpy(surface.texcoords, texcoords, 8);
>                 if(pack_lightmap(surface)) update_lightmap(surface);
>                 surfaces[numsurfs] = surface;
>                 surfaces[numsurfs++].layer = LAYER_BOTTOM;
>                 continue;
>             }
>         }
>     }
>     if(numsurfs>numsrcsurfs) 
>     {
>         freesurfaces(c);
>         newsurfaces(c, surfaces, numsurfs);
>         return true;
>     }
>     else if(numsurfs!=numsrcsurfs || memcmp(srcsurfaces, surfaces, numsurfs*sizeof(surfaceinfo))) 
>     {
>         if(!numsurfs) brightencube(c);
>         else memcpy(srcsurfaces, surfaces, numsurfs*sizeof(surfaceinfo));
>         return true;
>     }
>     else return false;
> }
> 
> static bool previewblends(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.x, co.y, co.z, size);
>         cubeext *ext = c[i].ext;
>         if(ext && ext->va && ext->va->hasmerges)
>         {
>             destroyva(ext->va);
>             ext->va = NULL;
>             invalidatemerges(c[i], true);
>             changed = true;
>         }
>         if(c[i].children ? previewblends(c[i].children, o, size/2, bo, bs) : previewblends(c[i], o, size))  
>         {
>             changed = true;
>             if(ext && ext->va)
>             {
>                 int hasmerges = ext->va->hasmerges;
>                 destroyva(ext->va);
>                 ext->va = NULL;
>                 if(hasmerges) invalidatemerges(c[i], true);
>             }
>         }
>     }
>     return changed;
> }
> 
> void previewblends(const ivec &bo, const ivec &bs)
> {
>     if(previewblends(worldroot, ivec(0, 0, 0), hdr.worldsize/2, bo, bs))
>     {
>         inbetweenframes = false;
>         octarender();
>         inbetweenframes = true;
>         invalidatepostfx();
>         updatevabbs();
>     }
> }
>                             
1110a1543
>     optimizeblendmap();
1281c1714,1715
<             c += 3;
---
>             if(lmc.bpp==4) dstrow[3] = c[3];
>             c += lmc.bpp;
1283c1717
<             dstrow += 3;
---
>             dstrow += lmc.bpp;
1294,1295c1728,1729
<         memcpy(dst, c, 3*LM_PACKW);
<         c += 3*LM_PACKW;
---
>         memcpy(dst, c, lm.bpp*LM_PACKW);
>         c += lm.bpp*LM_PACKW;
1358c1792
< void genlightmaptexs()
---
> void genlightmaptexs(int flagmask, int flagval)
1362c1796
<     int remaining[3] = { 0, 0, 0 }; 
---
>     int remaining[3] = { 0, 0, 0 }, total = 0; 
1366,1367c1800,1803
<         if(lm.tex >= 0) continue;
<         remaining[lm.type]++; 
---
>         if(lm.tex >= 0 || (lm.type&flagmask)!=flagval) continue;
>         int type = lm.type&LM_TYPE;
>         remaining[type]++; 
>         total++;
1380c1816
<     while(remaining[LM_DIFFUSE] || remaining[LM_BUMPMAP0] || remaining[LM_BUMPMAP1])
---
>     while(total)
1387,1389c1823,1825
<             if(lm.tex >= 0) continue;
<             if(renderpath != R_FIXEDFUNCTION) type = lm.type;
<             else if(lm.type != LM_DIFFUSE && lm.type != LM_BUMPMAP0) continue;
---
>             if(lm.tex >= 0 || (lm.type&flagmask) != flagval) continue;
>             if(renderpath != R_FIXEDFUNCTION) type = lm.type&LM_TYPE;
>             else if((lm.type&LM_TYPE) == LM_BUMPMAP1) continue;
1396a1833
>         int oldval = remaining[type];
1402a1840
>         total -= oldval - remaining[type];
1404c1842
<         tex.type = type;
---
>         tex.type = firstlm->type;
1407,1408c1845,1847
<         uchar *data = used || (renderpath == R_FIXEDFUNCTION && firstlm->type == LM_BUMPMAP0 && convertlms) ? 
<             new uchar[3*tex.w*tex.h] : 
---
>         int bpp = firstlm->bpp;
>         uchar *data = used || (renderpath == R_FIXEDFUNCTION && (firstlm->type&LM_TYPE) == LM_BUMPMAP0 && convertlms) ? 
>             new uchar[bpp*tex.w*tex.h] : 
1414,1417c1853,1856
<             if(lm.tex >= 0 ||
<                (renderpath == R_FIXEDFUNCTION ? 
<                     lm.type != LM_DIFFUSE && lm.type != LM_BUMPMAP0 : 
<                     lm.type != type))
---
>             if(lm.tex >= 0 || (lm.type&flagmask) != flagval || 
>                (renderpath==R_FIXEDFUNCTION ? 
>                 (lm.type&LM_TYPE) == LM_BUMPMAP1 : 
>                 (lm.type&LM_TYPE) != type))
1431,1433c1870,1872
<                 if(renderpath == R_FIXEDFUNCTION && lm.type == LM_BUMPMAP0 && convertlms)
<                     convertlightmap(lm, lightmaps[i+1], &data[3*(offsety*tex.w + offsetx)], 3*tex.w);
<                 else copylightmap(lm, &data[3*(offsety*tex.w + offsetx)], 3*tex.w);
---
>                 if(renderpath == R_FIXEDFUNCTION && (lm.type&LM_TYPE) == LM_BUMPMAP0 && convertlms)
>                     convertlightmap(lm, lightmaps[i+1], &data[bpp*(offsety*tex.w + offsetx)], bpp*tex.w);
>                 else copylightmap(lm, &data[bpp*(offsety*tex.w + offsetx)], bpp*tex.w);
1442c1881
<         createtexture(tex.id, tex.w, tex.h, data ? data : firstlm->data, 3, false);
---
>         createtexture(tex.id, tex.w, tex.h, data ? data : firstlm->data, 3, false, bpp==4 ? GL_RGBA : GL_RGB);
1461c1900,1901
<     genlightmaptexs();
---
>     genlightmaptexs(LM_ALPHA, 0);
>     genlightmaptexs(LM_ALPHA, LM_ALPHA);
1495c1935,1936
<     genlightmaptexs();
---
>     genlightmaptexs(LM_ALPHA, 0);
>     genlightmaptexs(LM_ALPHA, LM_ALPHA);
1608,1617d2048
< static surfaceinfo brightsurfaces[6] =
< {
<     {{0}, 0, 0, 0, 0, LMID_BRIGHT},
<     {{0}, 0, 0, 0, 0, LMID_BRIGHT},
<     {{0}, 0, 0, 0, 0, LMID_BRIGHT},
<     {{0}, 0, 0, 0, 0, LMID_BRIGHT},
<     {{0}, 0, 0, 0, 0, LMID_BRIGHT},
<     {{0}, 0, 0, 0, 0, LMID_BRIGHT},
< };
< 
1628c2059
< void newsurfaces(cube &c)
---
> void newsurfaces(cube &c, const surfaceinfo *surfs, int numsurfs)
1633,1634c2064,2065
<         c.ext->surfaces = new surfaceinfo[6];
<         memset(c.ext->surfaces, 0, 6*sizeof(surfaceinfo));
---
>         c.ext->surfaces = new surfaceinfo[numsurfs];
>         memcpy(c.ext->surfaces, surfs, numsurfs*sizeof(surfaceinfo));
1669d2099
< 
Index: src/engine/lightmap.h
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/lightmap.h,v
retrieving revision 1.24
diff -r1.24 lightmap.h
31c31,40
< enum { LM_DIFFUSE = 0, LM_BUMPMAP0, LM_BUMPMAP1 };
---
> enum 
> { 
>     LM_DIFFUSE = 0, 
>     LM_BUMPMAP0, 
>     LM_BUMPMAP1, 
>     LM_TYPE = 0x0F,
> 
>     LM_ALPHA = 1<<4,  
>     LM_FLAGS = 0xF0 
> };
35c44
<     int type, tex, offsetx, offsety;
---
>     int type, bpp, tex, offsetx, offsety;
39c48
<     uchar data[3 * LM_PACKW * LM_PACKH];
---
>     uchar *data;
42,43c51,57
<      : type(LM_DIFFUSE), tex(-1), offsetx(-1), offsety(-1),
<        lightmaps(0), lumels(0), unlitx(-1), unlity(-1)
---
>      : type(LM_DIFFUSE), bpp(3), tex(-1), offsetx(-1), offsety(-1),
>        lightmaps(0), lumels(0), unlitx(-1), unlity(-1),
>        data(NULL)
>     {
>     }
> 
>     ~LightMap()
45c59
<         memset(data, 0, sizeof(data));
---
>         if(data) delete[] data;
53a68
>     void copy(ushort tx, ushort ty, uchar *src, ushort tw, ushort th);
78c93
< extern void newsurfaces(cube &c);
---
> extern void newsurfaces(cube &c, const surfaceinfo *surfs, int numsurfs);
80a96
> extern void previewblends(const ivec &bo, const ivec &bs);
Index: src/engine/main.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/main.cpp,v
retrieving revision 1.242
diff -r1.242 main.cpp
491a492
>     extern void cleanupblendmap();
502a504
>     cleanupblendmap();
Index: src/engine/octa.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/octa.cpp,v
retrieving revision 1.80
diff -r1.80 octa.cpp
1380c1380
< void invalidatemerges(cube &c)
---
> static void invalidatemerges(cube &c)
1400a1401,1412
> static int invalidatedmerges = 0;
> 
> void invalidatemerges(cube &c, bool msg)
> {
>     if(msg && invalidatedmerges!=totalmillis)
>     {
>         show_out_of_renderloop_progress(0, "invalidating merged surfaces...");
>         invalidatedmerges = totalmillis;
>     }
>     invalidatemerges(c);
> }
>  
Index: src/engine/octa.h
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/octa.h,v
retrieving revision 1.86
diff -r1.86 octa.h
2a3,10
> enum
> {
>     LAYER_TOP = 0,
>     LAYER_BOTTOM,
> 
>     LAYER_BLEND = 1<<1
> };
> 
5c13,15
<     ushort texture, lmid, envmap;
---
>     ushort texture;
>     uchar lmid, layer;
>     ushort envmap;
40c50,51
<     ushort x, y, lmid;
---
>     ushort x, y;
>     uchar lmid, layer;
135c146
<     int verts, tris, texs, texmask, sky, explicitsky, skyfaces, skyclip, matsurfs, distance;
---
>     int verts, tris, texs, blends, texmask, sky, explicitsky, skyfaces, skyclip, matsurfs, distance;
Index: src/engine/octaedit.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/octaedit.cpp,v
retrieving revision 1.255
diff -r1.255 octaedit.cpp
469c469
< static bool invalidatedmerges = false, haschanged = false;
---
> static bool haschanged = false;
483,491c483
<                 if(hasmerges) 
<                 {
<                     if(!invalidatedmerges)
<                     {
<                         show_out_of_renderloop_progress(0, "invalidating merged surfaces...");
<                         invalidatedmerges = true;
<                     }
<                     invalidatemerges(c[i]);
<                 }
---
>                 if(hasmerges) invalidatemerges(c[i], true); 
514d505
<     invalidatedmerges = false;
1655c1646
<                         Texture *tex = notexture, *glowtex = NULL;
---
>                         Texture *tex = notexture, *glowtex = NULL, *layertex = NULL;
1661a1653,1657
>                             if(slot.layer)
>                             {
>                                 Slot &layer = lookuptexture(slot.layer);
>                                 if(!layer.sts.empty()) layertex = layer.sts[0].t;
>                             }
1665c1661
<                         if(g.texture(tex, 1.0, slot.rotation, slot.xoffset, slot.yoffset, glowtex, slot.glowcolor)&G3D_UP && (slot.loaded || tex!=notexture)) 
---
>                         if(g.texture(tex, 1.0, slot.rotation, slot.xoffset, slot.yoffset, glowtex, slot.glowcolor, layertex)&G3D_UP && (slot.loaded || tex!=notexture)) 
1728c1724
<                 Texture *tex = slot.sts[0].t, *glowtex = NULL;
---
>                 Texture *tex = slot.sts[0].t, *glowtex = NULL, *layertex = NULL;
1732a1729,1733
>                 if(slot.layer)
>                 {
>                     Slot &layer = lookuptexture(slot.layer);
>                     layertex = layer.sts[0].t;
>                 }
1759a1761,1770
>                     if(j==1 && layertex)
>                     {
>                         glBindTexture(GL_TEXTURE_2D, layertex->id);
>                         glBegin(GL_QUADS);
>                         glTexCoord2fv(tc[0]); glVertex2f(x+r/2, y+r/2);
>                         glTexCoord2fv(tc[1]); glVertex2f(x+r,   y+r/2);
>                         glTexCoord2fv(tc[2]); glVertex2f(x+r,   y+r);
>                         glTexCoord2fv(tc[3]); glVertex2f(x+r/2, y+r);
>                         glEnd();
>                     }
Index: src/engine/octarender.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/octarender.cpp,v
retrieving revision 1.346
diff -r1.346 octarender.cpp
236c236,237
<      ushort tex, lmid, envmap;
---
>      ushort tex, lmid, layer, envmap;
> 
238,239c239,240
<      sortkey(ushort tex, ushort lmid, ushort envmap = EMID_NONE)
<       : tex(tex), lmid(lmid), envmap(envmap)
---
>      sortkey(ushort tex, uchar lmid, uchar layer = LAYER_TOP, ushort envmap = EMID_NONE)
>       : tex(tex), lmid(lmid), layer(layer), envmap(envmap)
242c243
<      bool operator==(const sortkey &o) const { return tex==o.tex && lmid==o.lmid && envmap==o.envmap; }
---
>      bool operator==(const sortkey &o) const { return tex==o.tex && lmid==o.lmid && layer==o.layer && envmap==o.envmap; }
290,291c291,293
<         uint lastlmid[2] = { LMID_AMBIENT, LMID_AMBIENT }, firstlmid[2] = { LMID_AMBIENT, LMID_AMBIENT };
<         int firstlit[2] = { -1, -1 };
---
>         uint lastlmid[4] = { LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT }, 
>              firstlmid[4] = { LMID_AMBIENT, LMID_AMBIENT, LMID_AMBIENT };
>         int firstlit[4] = { -1, -1, -1, -1 };
298c300,301
<                 int type = lmtex.type;
---
>                 int type = lmtex.type&LM_TYPE;
>                 if(k.layer&LAYER_BLEND) type += 2;
310a314
>                 if(k.layer&LAYER_BLEND) type += 2;
318c322
<         if(firstlmid[0]!=LMID_AMBIENT || firstlmid[1]!=LMID_AMBIENT) loopi(max(firstlit[0], firstlit[1]))
---
>         loopj(2)
320,327c324,336
<             sortkey &k = texs[i];
<             if(k.lmid!=LMID_AMBIENT) continue;
<             Shader *s = lookuptexture(k.tex, false).shader;
<             if(!s) continue;
<             int type = s->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE;
<             if(firstlmid[type]==LMID_AMBIENT) continue;
<             indices[k].unlit = firstlmid[type];
<         } 
---
>             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(k.lmid!=LMID_AMBIENT && (j ? k.layer&LAYER_BLEND : !(k.layer&LAYER_BLEND))) continue;
>                 Shader *s = lookuptexture(k.tex, false).shader;
>                 if(!s) continue;
>                 int type = offset + (s->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE);
>                 if(firstlmid[type]==LMID_AMBIENT) continue;
>                 indices[k].unlit = firstlmid[type];
>             }
>         }  
352c361
<             sortval *dst = indices.access(sortkey(k.tex, t.unlit, k.envmap));
---
>             sortval *dst = indices.access(sortkey(k.tex, t.unlit, k.layer, k.envmap));
365c374
<                     sortkey ukey(k.tex, LMID_AMBIENT, k.envmap);
---
>                     sortkey ukey(k.tex, LMID_AMBIENT, k.layer, k.envmap);
391a401,402
>         if(x->layer < y->layer) return -1;
>         if(x->layer > y->layer) return 1;
463a475
>         va->blends = 0;
470a483
>             while(va->texs && texs[va->texs-1].layer&LAYER_BLEND) { va->texs--; va->blends++; }
477a491
>                 e.layer = k.layer;
504c518
<         loopi(va->texs)
---
>         loopi(va->texs+va->blends)
526,528d539
< VARF(lodsize, 0, 32, 128, hdr.mapwlod = lodsize);
< VAR(loddistance, 0, 2000, 100000);
< 
589c600
< void addcubeverts(int orient, int size, vvec *vv, ushort texture, surfaceinfo *surface, surfacenormals *normals, int tj = -1, ushort envmap = EMID_NONE)
---
> void addtris(const sortkey &key, int orient, vvec *vv, surfacenormals *normals, texcoords tc[4], int index[4],  int shadowmask, int tj)
591,630d601
<     int index[4];
<     int shadowmask = texture==DEFAULT_SKY ? 0 : calcshadowmask(vv);
<     LightMap *lm = NULL;
<     LightMapTexture *lmtex = NULL;
<     if(!nolights && surface && lightmaps.inrange(surface->lmid-LMID_RESERVED))
<     {
<         lm = &lightmaps[surface->lmid-LMID_RESERVED];
<         if(lm->type==LM_DIFFUSE || 
<             (lm->type==LM_BUMPMAP0 && 
<                 lightmaps.inrange(surface->lmid+1-LMID_RESERVED) && 
<                 lightmaps[surface->lmid+1-LMID_RESERVED].type==LM_BUMPMAP1))
<             lmtex = &lightmaptexs[lm->tex];
<         else lm = NULL;
<     }
<     texcoords tc[4];
<     loopk(4)
<     {
<         if(lmtex)
<         {
<             tc[k].u = short(ceilf((lm->offsetx + surface->x + (surface->texcoords[k*2] / 255.0f) * (surface->w - 1) + 0.5f) * SHRT_MAX/lmtex->w));
<             tc[k].v = short(ceilf((lm->offsety + surface->y + (surface->texcoords[k*2 + 1] / 255.0f) * (surface->h - 1) + 0.5f) * SHRT_MAX/lmtex->h));
<         }
<         else tc[k].u = tc[k].v = 0;
<         index[k] = vc.addvert(vv[k], tc[k].u, tc[k].v, renderpath!=R_FIXEDFUNCTION && normals ? normals->normals[k] : bvec(128, 128, 128));
<         if(index[k] < 0) return;
<     }
< 
<     if(texture == DEFAULT_SKY)
<     {
<         loopk(4) vc.skyclip = min(vc.skyclip, int(vv[k].z>>VVEC_FRAC));
<         vc.skyfaces |= 0x3F&~(1<<orient);
<     }
< 
<     int lmid = LMID_AMBIENT;
<     if(surface)
<     {
<         if(surface->lmid < LMID_RESERVED) lmid = surface->lmid;
<         else if(lm) lmid = lm->tex;
<     }
< 
632d602
<     sortkey key(texture, lmid, envmap);
635c605
<         usvector &idxs = texture==DEFAULT_SKY ? vc.explicitskyindices : vc.indices[key].dims[2*dim + ((shadowmask>>i)&1)]; 
---
>         usvector &idxs = key.tex==DEFAULT_SKY ? vc.explicitskyindices : vc.indices[key].dims[2*dim + ((shadowmask>>i)&1)];
686c656
<                 if(texture==DEFAULT_SKY) explicitsky++; else vc.curtris++;
---
>                 if(key.tex==DEFAULT_SKY) explicitsky++; else vc.curtris++;
696c666
<         if(texture==DEFAULT_SKY) explicitsky++; else vc.curtris++;
---
>         if(key.tex==DEFAULT_SKY) explicitsky++; else vc.curtris++;
699a670,715
> void addcubeverts(int orient, int size, vvec *vv, ushort texture, surfaceinfo *surface, surfacenormals *normals, int tj = -1, ushort envmap = EMID_NONE)
> {
>     int index[4];
>     int shadowmask = texture==DEFAULT_SKY ? 0 : calcshadowmask(vv);
>     LightMap *lm = NULL;
>     LightMapTexture *lmtex = NULL;
>     if(!nolights && surface && lightmaps.inrange(surface->lmid-LMID_RESERVED))
>     {
>         lm = &lightmaps[surface->lmid-LMID_RESERVED];
>         if((lm->type&LM_TYPE)==LM_DIFFUSE ||
>             ((lm->type&LM_TYPE)==LM_BUMPMAP0 &&
>                 lightmaps.inrange(surface->lmid+1-LMID_RESERVED) &&
>                 (lightmaps[surface->lmid+1-LMID_RESERVED].type&LM_TYPE)==LM_BUMPMAP1))
>             lmtex = &lightmaptexs[lm->tex];
>         else lm = NULL;
>     }
>     texcoords tc[4];
>     loopk(4)
>     {
>         if(lmtex)
>         {
>             tc[k].u = short(ceilf((lm->offsetx + surface->x + (surface->texcoords[k*2] / 255.0f) * (surface->w - 1) + 0.5f) * SHRT_MAX/lmtex->w));
>             tc[k].v = short(ceilf((lm->offsety + surface->y + (surface->texcoords[k*2 + 1] / 255.0f) * (surface->h - 1) + 0.5f) * SHRT_MAX/lmtex->h));
>         }
>         else tc[k].u = tc[k].v = 0;
>         index[k] = vc.addvert(vv[k], tc[k].u, tc[k].v, renderpath!=R_FIXEDFUNCTION && normals ? normals->normals[k] : bvec(128, 128, 128));
>         if(index[k] < 0) return;
>     }
> 
>     if(texture == DEFAULT_SKY)
>     {
>         loopk(4) vc.skyclip = min(vc.skyclip, int(vv[k].z>>VVEC_FRAC));
>         vc.skyfaces |= 0x3F&~(1<<orient);
>     }
> 
>     int lmid = LMID_AMBIENT;
>     if(surface)
>     {
>         if(surface->lmid < LMID_RESERVED) lmid = surface->lmid;
>         else if(lm) lmid = lm->tex;
>     }
> 
>     sortkey key(texture, lmid, surface ? surface->layer&LAYER_BLEND : LAYER_TOP, envmap);
>     addtris(key, orient, vv, normals, tc, index, shadowmask, tj);
> }
> 
852c868
<     int tj = c.ext ? c.ext->tjoints : -1;
---
>     int tj = c.ext ? c.ext->tjoints : -1, numblends = 0;
865a882,883
>         if(e.surfaces && e.surfaces[i].layer&LAYER_BLEND) numblends++; 
> 
870,871c888,890
<         ushort envmap = EMID_NONE;
<         Slot &slot = lookuptexture(c.texture[i], false);
---
>         ushort envmap = EMID_NONE, envmap2 = EMID_NONE;
>         Slot &slot = lookuptexture(c.texture[i], false),
>              *layer = slot.layer ? &lookuptexture(slot.layer, false) : NULL;
876a896,900
>         if(layer && layer->shader && layer->shader->type&SHADER_ENVMAP)
>         {
>             loopv(layer->sts) if(layer->sts[i].type==TEX_ENVMAP) { envmap2 = EMID_CUSTOM; break; }
>             if(envmap2==EMID_NONE) envmap2 = closestenvmap(i, x, y, z, size); 
>         }
878c902,913
<         addcubeverts(i, size, vv, c.texture[i], e.surfaces ? &e.surfaces[i] : NULL, e.normals ? &e.normals[i] : NULL, tj >= 0 && tjoints[tj].edge/4 == i ? tj : -1, envmap);
---
>         int hastj = tj >= 0 && tjoints[tj].edge/4 == i ? tj : -1;
>         if(e.surfaces && e.surfaces[i].layer&LAYER_BLEND)
>         {
>             addcubeverts(i, size, vv, c.texture[i], &e.surfaces[i], e.normals ? &e.normals[i] : NULL, hastj, envmap);
>             addcubeverts(i, size, vv, slot.layer, &e.surfaces[5+numblends], e.normals ? &e.normals[i] : NULL, hastj, envmap2);
>         }
>         else
>         {
>             ushort tex = c.texture[i];
>             if(e.surfaces && e.surfaces[i].layer==LAYER_BOTTOM) { tex = slot.layer; envmap = envmap2; }
>             addcubeverts(i, size, vv, tex, e.surfaces ? &e.surfaces[i] : NULL, e.normals ? &e.normals[i] : NULL, hastj, envmap);
>         }
1146,1147c1181,1182
<     int index = 0, tj = c.ext->tjoints;
<     loopi(6) if(c.ext->mergeorigin & (1<<i))
---
>     int index = 0, tj = c.ext->tjoints, numblends = 0;
>     loopi(6) 
1148a1184,1185
>         if(c.ext->surfaces && c.ext->surfaces[i].layer&LAYER_BLEND) numblends++;
>         if(!(c.ext->mergeorigin & (1<<i))) continue;
1155,1160d1191
<         Slot &slot = lookuptexture(mf.tex, false);
<         if(slot.shader && slot.shader->type&SHADER_ENVMAP)
<         {
<             loopv(slot.sts) if(slot.sts[i].type==TEX_ENVMAP) { mf.envmap = EMID_CUSTOM; break; }
<             if(mf.envmap==EMID_NONE) mf.envmap = closestenvmap(i, co.x, co.y, co.z, size);
<         }
1170a1202,1232
>             Slot &slot = lookuptexture(mf.tex, false),
>                  *layer = slot.layer ? &lookuptexture(slot.layer, false) : NULL;
>             ushort envmap2 = EMID_NONE;
>             if(slot.shader && slot.shader->type&SHADER_ENVMAP)
>             {
>                 loopv(slot.sts) if(slot.sts[i].type==TEX_ENVMAP) { mf.envmap = EMID_CUSTOM; break; }
>                 if(mf.envmap==EMID_NONE) mf.envmap = closestenvmap(i, co.x, co.y, co.z, size);
>             }
>             if(layer && layer->shader && layer->shader->type&SHADER_ENVMAP)
>             {
>                 loopv(layer->sts) if(layer->sts[i].type==TEX_ENVMAP) { envmap2 = EMID_CUSTOM; break; }
>                 if(envmap2==EMID_NONE) envmap2 = closestenvmap(i, co.x, co.y, co.z, size);
>             }
> 
>             if(c.ext->surfaces)
>             {
>                 if(c.ext->surfaces[i].layer&LAYER_BLEND)
>                 {
>                     mergedface mf2 = mf;
>                     mf2.tex = slot.layer;
>                     mf2.envmap = envmap2;
>                     mf2.surface = &c.ext->surfaces[5+numblends];
>                     vamerges[level].add(mf2);
>                 }
>                 else if(c.ext->surfaces[i].layer==LAYER_BOTTOM)
>                 {
>                     mf.tex = slot.layer;
>                     mf.envmap = envmap2;
>                 }
>             } 
> 
Index: src/engine/rendergl.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/rendergl.cpp,v
retrieving revision 1.467
diff -r1.467 rendergl.cpp
1516a1517,1518
> 
>         renderblendbrush();
Index: src/engine/renderva.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/renderva.cpp,v
retrieving revision 1.182
diff -r1.182 renderva.cpp
557a558,581
> extern int ati_texgen_bug;
> 
> static void setuptexgen(int dims = 2)
> {
>     glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
>     glEnable(GL_TEXTURE_GEN_S);
>     if(dims>=2)
>     {
>         glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
>         glEnable(GL_TEXTURE_GEN_T);
>         if(ati_texgen_bug) glEnable(GL_TEXTURE_GEN_R);     // should not be needed, but apparently makes some ATI drivers happy
>     }
> }
> 
> static void disabletexgen(int dims = 2)
> {
>     glDisable(GL_TEXTURE_GEN_S);
>     if(dims>=2)
>     {
>         glDisable(GL_TEXTURE_GEN_T);
>         if(ati_texgen_bug) glDisable(GL_TEXTURE_GEN_R);
>     }
> }
> 
618a643,726
> VAR(blendbrushcolor, 0, 0x0000C0, 0xFFFFFF);
> 
> void renderblendbrush(GLuint tex, float x, float y, float w, float h)
> {
>     static Shader *blendbrushshader = NULL;
>     if(!blendbrushshader) blendbrushshader = lookupshaderbyname("blendbrush");
>     blendbrushshader->set();
> 
>     glEnableClientState(GL_VERTEX_ARRAY);
> 
>     glPushMatrix();
> 
>     glEnable(GL_BLEND);
>     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
> 
>     glEnable(GL_TEXTURE_2D); 
>     glBindTexture(GL_TEXTURE_2D, tex);
>     glColor4ub((blendbrushcolor>>16)&0xFF, (blendbrushcolor>>8)&0xFF, blendbrushcolor&0xFF, 0x40);
> 
>     enablepolygonoffset(GL_POLYGON_OFFSET_FILL);
> 
>     GLfloat s[4] = { 0, 0, 0, 0 }, t[4] = { 0, 0, 0, 0 };
>     if(renderpath==R_FIXEDFUNCTION) setuptexgen();
> 
>     resetorigin();
>     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)
>         {
>             if(setorigin(va))
>             {
>                 s[0] = 1.0f / (w*(1<<VVEC_FRAC));
>                 s[3] = (vaorigin.x - x) / w;
>                 t[1] = 1.0f / (h*(1<<VVEC_FRAC));
>                 t[3] = (vaorigin.y - y) / h;
>                 if(renderpath==R_FIXEDFUNCTION)
>                 {
>                     glTexGenfv(GL_S, GL_OBJECT_PLANE, s);
>                     glTexGenfv(GL_T, GL_OBJECT_PLANE, t);
>                 }
>                 else
>                 {
>                     setlocalparamfv("texgenS", SHPARAM_VERTEX, 0, s);
>                     setlocalparamfv("texgenT", SHPARAM_VERTEX, 1, t);
>                 }
>             }
>         
>             if(hasVBO)
>             {
>                 glBindBuffer_(GL_ARRAY_BUFFER_ARB, va->vbuf);
>                 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, va->ebuf);
>             }
>             glVertexPointer(3, floatvtx ? GL_FLOAT : GL_SHORT, VTXSIZE, &va->vdata[0].x);
>         }
> 
>         drawvatris(va, 3*va->tris, va->edata);
>         xtravertsva += va->verts;
> 
>         prev = va;
>     }
> 
>     if(renderpath==R_FIXEDFUNCTION) disabletexgen();
> 
>     disablepolygonoffset(GL_POLYGON_OFFSET_FILL);
> 
>     glDisable(GL_TEXTURE_2D);
>     glDisable(GL_BLEND);
> 
>     glPopMatrix();
> 
>     if(hasVBO)
>     {
>         glBindBuffer_(GL_ARRAY_BUFFER_ARB, 0);
>         glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
>     }
>     glDisableClientState(GL_VERTEX_ARRAY);
> 
>     notextureshader->set();
> }
>  
779c887
<     bool colormask, depthmask, mtglow, skippedglow;
---
>     bool colormask, depthmask, blending, mtglow, skippedglow;
795c903
<     renderstate() : colormask(true), depthmask(true), mtglow(false), skippedglow(false), vbuf(0), fogplane(-1), diffusetmu(0), lightmaptmu(1), glowtmu(-1), fogtmu(-1), causticstmu(-1), glowcolor(1, 1, 1), slot(NULL), texgendim(-1), mttexgen(false), visibledynlights(0), dynlightmask(0)
---
>     renderstate() : colormask(true), depthmask(true), blending(false), mtglow(false), skippedglow(false), vbuf(0), fogplane(-1), diffusetmu(0), lightmaptmu(1), glowtmu(-1), fogtmu(-1), causticstmu(-1), glowcolor(1, 1, 1), slot(NULL), texgendim(-1), mttexgen(false), visibledynlights(0), dynlightmask(0)
827c935,936
<     RENDERPASS_DYNLIGHT
---
>     RENDERPASS_DYNLIGHT,
>     RENDERPASS_LIGHTMAP_BLEND
965c1074
<         if(fading || fogging)
---
>         if((fading && !cur.blending) || fogging)
1038c1147,1148
<     int lmid = brightengeom ? LMID_BRIGHT : b.es.lmid; 
---
>     extern int fullbright;
>     int lmid = brightengeom && (b.es.lmid < LMID_RESERVED || (fullbright && editmode)) ? LMID_BRIGHT : b.es.lmid; 
1221c1331,1332
<         s->setvariant(cur.visibledynlights, 4, &slot, noglareshader);
---
>         if(s->hasoption(4)) s->setvariant(cur.visibledynlights, 4, &slot, noglareshader);
>         else s->setvariant(cur.blending ? 1 : 0, 4, &slot, noglareshader);
1223c1334
<     else if(fading)
---
>     else if(fading && !cur.blending)
1570a1682,1692
>         case RENDERPASS_LIGHTMAP_BLEND:
>         {
>             if(doquery) startvaquery(va, { if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP); });
>             ushort *edata = va->edata;
>             loopi(va->texs) edata += va->eslist[i].length[5];
>             mergetexs(va, &va->eslist[va->texs], va->blends, edata);
>             if(doquery) endvaquery(va, { if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP); });
>             else if(!batchgeom && geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
>             break;
>         }
> 
1610,1633d1731
< extern int ati_texgen_bug;
< 
< static void setuptexgen(int dims = 2)
< {
<     glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
<     glEnable(GL_TEXTURE_GEN_S);
<     if(dims>=2) 
<     {
<         glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
<         glEnable(GL_TEXTURE_GEN_T);
<         if(ati_texgen_bug) glEnable(GL_TEXTURE_GEN_R);     // should not be needed, but apparently makes some ATI drivers happy
<     }
< }
< 
< static void disabletexgen(int dims = 2)
< {
<     glDisable(GL_TEXTURE_GEN_S);
<     if(dims>=2)
<     {
<         glDisable(GL_TEXTURE_GEN_T);
<         if(ati_texgen_bug) glDisable(GL_TEXTURE_GEN_R);
<     }
< }
< 
1826c1924
<         setuptmu(cur.lightmaptmu, "P * T x 2");
---
>         setuptmu(cur.lightmaptmu, "P * T x 2", "= Ta");
1961a2060
>     int blends = 0;
2041a2141
>         if(!doOQ) blends += va->blends;
2091a2192
>             blends += va->blends;
2109a2211,2212
> 
>             blends += va->blends;
2116a2220,2261
>     if(blends && (renderpath!=R_FIXEDFUNCTION || !nolights))
>     {
>         if(shadowmap && !envmapping && !glaring && renderpath!=R_FIXEDFUNCTION)
>         {
>             glPopMatrix();
>             glPushMatrix();
>             pushshadowmap();
>             resetorigin();
>         }
>         if(foggedvas.empty()) 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.vbuf = 0;
>         cur.blending = true;
>         for(vtxarray **prevva = &FIRSTVA, *va = FIRSTVA; va; prevva = &NEXTVA, va = NEXTVA)
>         {
>             if(!va->blends || va->occluded >= OCCLUDE_GEOM) continue;
>             if(refracting)
>             {
>                 if(refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) continue;
>                 if(isvisiblecube(va->o, va->size) >= VFC_NOT_VISIBLE) continue;
>                 if((!hasOQ || !oqfrags) && va->distance > reflectdist) break;
>             }
>             else if(reflecting)
>             {
>                 if(va->geommax.z <= reflectz || (va->rquery && checkquery(va->rquery))) continue;
>             }
>             if(fogpass ? va->geommax.z <= reflectz-waterfog : 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(foggedvas.empty()) glDepthFunc(GL_LESS);
>     }
> 
Index: src/engine/shader.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/shader.cpp,v
retrieving revision 1.131
diff -r1.131 shader.cpp
729a730,754
> static void gengenericvariant(Shader &s, const char *sname, const char *vs, const char *ps, int row)
> {
>     bool vschanged = false, pschanged = false;
>     vector<char> vsv, psv;
>     vsv.put(vs, strlen(vs)+1);
>     psv.put(ps, strlen(ps)+1);
> 
>     int len = strlen("#pragma CUBE2_variant");
>     for(char *vspragma = vsv.getbuf();; vschanged = true)
>     {
>         vspragma = strstr(vspragma, "#pragma CUBE2_variant");
>         if(!vspragma) break;
>         memset(vspragma, ' ', len);
>     }
>     for(char *pspragma = psv.getbuf();; pschanged = true)
>     {
>         pspragma = strstr(pspragma, "#pragma CUBE2_variant");
>         if(!pspragma) break;
>         memset(pspragma, ' ', len);
>     }
>     s_sprintfd(varname)("<variant:%d,%d>%s", s.variants[row].length(), row, sname);
>     s_sprintfd(reuse)("%d", row);
>     newshader(s.type, varname, vschanged ? vsv.getbuf() : reuse, pschanged ? psv.getbuf() : reuse, &s, row);
> }
> 
1209a1235
>         if(strstr(vs, "#pragma CUBE2_variant")) gengenericvariant(*s, varname, vs, ps, *row);
Index: src/engine/texture.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/texture.cpp,v
retrieving revision 1.138
diff -r1.138 texture.cpp
728a729,737
> void texlayer(int *layer)
> {
>     if(slots.empty()) return;
>     Slot &s = slots.last();
>     s.layer = *layer < 0 ? max(slots.length()-1+*layer, 0) : *layer;
>     
> }
> COMMAND(texlayer, "i");
> 
925a935,936
>     Slot *layer = slot.layer ? &lookuptexture(slot.layer, false) : NULL;
>     if(layer) addname(name, *layer, layer->sts[0], true, "<layer>");
931c942,944
<         SDL_Surface *s = texturedata(NULL, &slot.sts[0], false), *g = glow >= 0 ? texturedata(NULL, &slot.sts[glow], false) : NULL;
---
>         SDL_Surface *s = texturedata(NULL, &slot.sts[0], false), 
>                     *g = glow >= 0 ? texturedata(NULL, &slot.sts[glow], false) : NULL,
>                     *l = layer ? texturedata(NULL, &layer->sts[0], false) : NULL;
941a955,965
>             if(l)
>             {
>                 if(l->w != s->w/2 || l->h != s->h/2) l = scalesurface(l, s->w/2, s->h/2);
>                 uchar *src = (uchar *)l->pixels;
>                 loop(y, l->h) loop(x, l->w)
>                 { 
>                     uchar *dst = &((uchar *)s->pixels)[s->format->BytesPerPixel*((y + s->h/2)*s->w + x + s->w/2)];
>                     loopk(3) dst[k] = src[k];
>                     src += l->format->BytesPerPixel;
>                 }
>             }
947a972
>         if(l) SDL_FreeSurface(l);
Index: src/engine/texture.h
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/texture.h,v
retrieving revision 1.53
diff -r1.53 texture.h
97c97,98
<     SHADER_GLSLANG    = 1<<2
---
>     SHADER_GLSLANG    = 1<<2,
>     SHADER_OPTION     = 1<<3
146a148,153
>     bool hasoption(int row)
>     {
>         if(detailshader->variants[row].empty()) return false;
>         return (detailshader->variants[row][0]->type&SHADER_OPTION)!=0;
>     }
> 
233a241
>     int layer;
250a259
>         layer = 0;
Index: src/engine/world.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/world.cpp,v
retrieving revision 1.168
diff -r1.168 world.cpp
962a963
>     resetblendmap();
1003a1005
>     hdr.blendmap = 0;
1013a1016,1017
>     enlargeblendmap();
> 
Index: src/engine/world.h
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/world.h,v
retrieving revision 1.66
diff -r1.66 world.h
11c11
< #define MAPVERSION 27           // bump if map format changes, see worldio.cpp
---
> #define MAPVERSION 28           // bump if map format changes, see worldio.cpp
25c25
<     uchar mapwlod;
---
>     uchar blendmap;
Index: src/engine/worldio.cpp
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/engine/worldio.cpp,v
retrieving revision 1.129
diff -r1.129 worldio.cpp
141c141,150
<                 loopj(6) if(c[i].ext->surfaces[j].lmid >= LMID_RESERVED) mask |= 1 << j;
---
>                 int numsurfs = 6;
>                 loopj(6) 
>                 {
>                     surfaceinfo &surface = c[i].ext->surfaces[j];
>                     if(surface.lmid >= LMID_RESERVED || surface.layer!=LAYER_TOP) 
>                     {
>                         mask |= 1 << j;
>                         if(surface.layer&LAYER_BLEND) numsurfs++;
>                     }
>                 }
144c153
<                 loopj(6) if(mask & (1 << j))
---
>                 loopj(numsurfs) if(j >= 6 || mask & (1 << j))
147c156
<                     endianswap(&tmp.x, sizeof(ushort), 3);
---
>                     endianswap(&tmp.x, sizeof(ushort), 2);
149c158
<                     if(c[i].ext->normals) gzwrite(f, &c[i].ext->normals[j], sizeof(surfacenormals));
---
>                     if(j < 6 && c[i].ext->normals) gzwrite(f, &c[i].ext->normals[j], sizeof(surfacenormals));
210c219,220
<             newsurfaces(c);
---
>             static surfaceinfo surfaces[12];
>             memset(surfaces, 0, 6*sizeof(surfaceinfo));
212c222,223
<             loopi(6)
---
>             int numsurfs = 6;
>             loopi(numsurfs)
214c225
<                 if(mask & (1 << i))
---
>                 if(i >= 6 || mask & (1 << i))
216,218c227,229
<                     gzread(f, &c.ext->surfaces[i], sizeof(surfaceinfo));
<                     endianswap(&c.ext->surfaces[i].x, sizeof(ushort), 3);
<                     if(hdr.version < 10) ++c.ext->surfaces[i].lmid;
---
>                     gzread(f, &surfaces[i], sizeof(surfaceinfo));
>                     endianswap(&surfaces[i].x, sizeof(ushort), 2);
>                     if(hdr.version < 10) ++surfaces[i].lmid;
221,222c232,233
<                         if(c.ext->surfaces[i].lmid >= LMID_AMBIENT1) ++c.ext->surfaces[i].lmid;
<                         if(c.ext->surfaces[i].lmid >= LMID_BRIGHT1) ++c.ext->surfaces[i].lmid;
---
>                         if(surfaces[i].lmid >= LMID_AMBIENT1) ++surfaces[i].lmid;
>                         if(surfaces[i].lmid >= LMID_BRIGHT1) ++surfaces[i].lmid;
226c237,245
<                         if(c.ext->surfaces[i].lmid >= LMID_DARK) c.ext->surfaces[i].lmid += 2;
---
>                         if(surfaces[i].lmid >= LMID_DARK) surfaces[i].lmid += 2;
>                     }
>                     if(i < 6)
>                     {
>                         if(mask & 0x40) gzread(f, &c.ext->normals[i], sizeof(surfacenormals));
>                         if(surfaces[i].layer != LAYER_TOP) lit |= 1 << i;
>                         else if(surfaces[i].lmid == LMID_BRIGHT) bright |= 1 << i;
>                         else if(surfaces[i].lmid != LMID_AMBIENT) lit |= 1 << i;
>                         if(surfaces[i].layer&LAYER_BLEND) numsurfs++;
228d246
<                     if(mask & 0x40) gzread(f, &c.ext->normals[i], sizeof(surfacenormals));
230,237c248
<                 else c.ext->surfaces[i].lmid = LMID_AMBIENT;
<                 if(c.ext->surfaces[i].lmid == LMID_BRIGHT) bright |= 1 << i;
<                 else if(c.ext->surfaces[i].lmid != LMID_AMBIENT) lit |= 1 << i;
<             }
<             if(!lit) 
<             {
<                 freesurfaces(c);
<                 if(bright) brightencube(c);
---
>                 else surfaces[i].lmid = LMID_AMBIENT;
238a250,251
>             if(lit) newsurfaces(c, surfaces, numsurfs);
>             else if(bright) brightencube(c);
295a309
>     hdr.blendmap = nolms ? 0 : shouldsaveblendmap();
339c353
<             gzwrite(f, lm.data, sizeof(lm.data));
---
>             gzwrite(f, lm.data, lm.bpp*LM_PACKW*LM_PACKH);
341a356
>         if(shouldsaveblendmap()) saveblendmap(f);
402d416
<     setvar("lodsize", hdr.mapwlod);
537c551
<             lm.type = type&0xF;
---
>             lm.type = type&0x7F;
544c558,560
<         gzread(f, lm.data, 3 * LM_PACKW * LM_PACKH);
---
>         if(lm.type&LM_ALPHA) lm.bpp = 4;
>         lm.data = new uchar[lm.bpp*LM_PACKW*LM_PACKH];
>         gzread(f, lm.data, lm.bpp * LM_PACKW * LM_PACKH);
548a565
>     if(hdr.version >= 28 && hdr.blendmap) loadblendmap(f);
Index: src/shared/iengine.h
===================================================================
RCS file: /cvsroot/sauerbraten/sauerbraten/src/shared/iengine.h,v
retrieving revision 1.264
diff -r1.264 iengine.h
316c316
<     virtual int texture(Texture *t, float scale, int rotate = 0, int xoff = 0, int yoff = 0, Texture *glowtex = NULL, const vec &glowcolor = vec(1, 1, 1)) = 0;
---
>     virtual int texture(Texture *t, float scale, int rotate = 0, int xoff = 0, int yoff = 0, Texture *glowtex = NULL, const vec &glowcolor = vec(1, 1, 1), Texture *layertex = NULL) = 0;
