1 #include "engine.h"
2 
3 enum
4 {
5     BM_BRANCH = 0,
6     BM_SOLID,
7     BM_IMAGE
8 };
9 
10 struct BlendMapBranch;
11 struct BlendMapSolid;
12 struct BlendMapImage;
13 
14 struct BlendMapNode
15 {
16     union
17     {
18         BlendMapBranch *branch;
19         BlendMapSolid *solid;
20         BlendMapImage *image;
21     };
22 
23     void cleanup(int type);
24     void splitsolid(uchar &type, uchar val);
25 };
26 
27 struct BlendMapBranch
28 {
29     uchar type[4];
30     BlendMapNode children[4];
31 
~BlendMapBranchBlendMapBranch32     ~BlendMapBranch()
33     {
34         loopi(4) children[i].cleanup(type[i]);
35     }
36 };
37 
38 struct BlendMapSolid
39 {
40     uchar val;
41 
BlendMapSolidBlendMapSolid42     BlendMapSolid(uchar val) : val(val) {}
43 };
44 
45 #define BM_SCALE 1
46 #define BM_IMAGE_SIZE 64
47 
48 struct BlendMapImage
49 {
50     uchar data[BM_IMAGE_SIZE*BM_IMAGE_SIZE];
51 };
52 
cleanup(int type)53 void BlendMapNode::cleanup(int type)
54 {
55     switch(type)
56     {
57         case BM_BRANCH: delete branch; break;
58         case BM_IMAGE: delete image; break;
59     }
60 }
61 
62 #define DEFBMSOLIDS(n) n, n+1, n+2, n+3, n+4, n+5, n+6, n+7, n+8, n+9, n+10, n+11, n+12, n+13, n+14, n+15
63 
64 static BlendMapSolid bmsolids[256] =
65 {
66     DEFBMSOLIDS(0x00), DEFBMSOLIDS(0x10), DEFBMSOLIDS(0x20), DEFBMSOLIDS(0x30),
67     DEFBMSOLIDS(0x40), DEFBMSOLIDS(0x50), DEFBMSOLIDS(0x60), DEFBMSOLIDS(0x70),
68     DEFBMSOLIDS(0x80), DEFBMSOLIDS(0x90), DEFBMSOLIDS(0xA0), DEFBMSOLIDS(0xB0),
69     DEFBMSOLIDS(0xC0), DEFBMSOLIDS(0xD0), DEFBMSOLIDS(0xE0), DEFBMSOLIDS(0xF0),
70 };
71 
splitsolid(uchar & type,uchar val)72 void BlendMapNode::splitsolid(uchar &type, uchar val)
73 {
74     cleanup(type);
75     type = BM_BRANCH;
76     branch = new BlendMapBranch;
77     loopi(4)
78     {
79         branch->type[i] = BM_SOLID;
80         branch->children[i].solid = &bmsolids[val];
81     }
82 }
83 
84 struct BlendMapRoot : BlendMapNode
85 {
86     uchar type;
87 
BlendMapRootBlendMapRoot88     BlendMapRoot() : type(BM_SOLID) { solid = &bmsolids[0xFF]; }
BlendMapRootBlendMapRoot89     BlendMapRoot(uchar type, const BlendMapNode &node) : BlendMapNode(node), type(type) {}
90 
cleanupBlendMapRoot91     void cleanup() { BlendMapNode::cleanup(type); }
92 };
93 
94 static BlendMapRoot blendmap, curbm;
95 static int curbmscale;
96 static ivec curbmo;
97 
setblendmaporigin(const ivec & o,int size)98 bool setblendmaporigin(const ivec &o, int size)
99 {
100     if(blendmap.type!=BM_BRANCH)
101     {
102         curbm = blendmap;
103         curbmscale = worldscale-BM_SCALE;
104         curbmo = ivec(0, 0, 0);
105         return curbm.solid!=&bmsolids[0xFF];
106     }
107 
108     BlendMapBranch *bm = blendmap.branch;
109     int bmscale = worldscale-BM_SCALE, bmsize = 1<<bmscale,
110         x = o.x>>BM_SCALE, y = o.y>>BM_SCALE,
111         x1 = max(x-1, 0), y1 = max(y-1, 0),
112         x2 = min(((o.x + size + (1<<BM_SCALE)-1)>>BM_SCALE) + 1, bmsize),
113         y2 = min(((o.y + size + (1<<BM_SCALE)-1)>>BM_SCALE) + 1, bmsize),
114         diff = (x1^x2)|(y1^y2);
115     if(diff < bmsize) while(!(diff&(1<<(bmscale-1))))
116     {
117         bmscale--;
118         int n = (((y1>>bmscale)&1)<<1) | ((x1>>bmscale)&1);
119         if(bm->type[n]!=BM_BRANCH)
120         {
121             curbm = BlendMapRoot(bm->type[n], bm->children[n]);
122             curbmscale = bmscale;
123             curbmo = ivec(x1&(~0U<<bmscale), y1&(~0U<<bmscale), 0);
124             return curbm.solid!=&bmsolids[0xFF];
125         }
126         bm = bm->children[n].branch;
127     }
128 
129     curbm.type = BM_BRANCH;
130     curbm.branch = bm;
131     curbmscale = bmscale;
132     curbmo = ivec(x1&(~0U<<bmscale), y1&(~0U<<bmscale), 0);
133     return true;
134 }
135 
hasblendmap()136 bool hasblendmap()
137 {
138     return curbm.solid!=&bmsolids[0xFF];
139 }
140 
lookupblendmap(int x,int y,BlendMapBranch * bm,int bmscale)141 static uchar lookupblendmap(int x, int y, BlendMapBranch *bm, int bmscale)
142 {
143     for(;;)
144     {
145         bmscale--;
146         int n = (((y>>bmscale)&1)<<1) | ((x>>bmscale)&1);
147         switch(bm->type[n])
148         {
149             case BM_SOLID: return bm->children[n].solid->val;
150             case BM_IMAGE: return bm->children[n].image->data[(y&((1<<bmscale)-1))*BM_IMAGE_SIZE + (x&((1<<bmscale)-1))];
151         }
152         bm = bm->children[n].branch;
153     }
154 }
155 
lookupblendmap(const vec & pos)156 uchar lookupblendmap(const vec &pos)
157 {
158     if(curbm.type==BM_SOLID) return curbm.solid->val;
159 
160     uchar vals[4], *val = vals;
161     float bx = pos.x/(1<<BM_SCALE) - 0.5f, by = pos.y/(1<<BM_SCALE) - 0.5f;
162     int ix = (int)floor(bx), iy = (int)floor(by),
163         rx = ix-curbmo.x, ry = iy-curbmo.y;
164     loop(vy, 2) loop(vx, 2)
165     {
166         int cx = clamp(rx+vx, 0, (1<<curbmscale)-1), cy = clamp(ry+vy, 0, (1<<curbmscale)-1);
167         if(curbm.type==BM_IMAGE)
168             *val++ = curbm.image->data[cy*BM_IMAGE_SIZE + cx];
169         else *val++ = lookupblendmap(cx, cy, curbm.branch, curbmscale);
170     }
171     float fx = bx - ix, fy = by - iy;
172     return uchar((1-fy)*((1-fx)*vals[0] + fx*vals[1]) +
173                  fy*((1-fx)*vals[2] + fx*vals[3]));
174 }
175 
fillblendmap(uchar & type,BlendMapNode & node,int size,uchar val,int x1,int y1,int x2,int y2)176 static void fillblendmap(uchar &type, BlendMapNode &node, int size, uchar val, int x1, int y1, int x2, int y2)
177 {
178     if(max(x1, y1) <= 0 && min(x2, y2) >= size)
179     {
180         node.cleanup(type);
181         type = BM_SOLID;
182         node.solid = &bmsolids[val];
183         return;
184     }
185 
186     if(type==BM_BRANCH)
187     {
188         size /= 2;
189         if(y1 < size)
190         {
191             if(x1 < size) fillblendmap(node.branch->type[0], node.branch->children[0], size, val,
192                                         x1, y1, min(x2, size), min(y2, size));
193             if(x2 > size) fillblendmap(node.branch->type[1], node.branch->children[1], size, val,
194                                         max(x1-size, 0), y1, x2-size, min(y2, size));
195         }
196         if(y2 > size)
197         {
198             if(x1 < size) fillblendmap(node.branch->type[2], node.branch->children[2], size, val,
199                                         x1, y1-size, min(x2, size), y2-size);
200             if(x2 > size) fillblendmap(node.branch->type[3], node.branch->children[3], size, val,
201                                         max(x1-size, 0), y1-size, x2-size, y2-size);
202         }
203         loopi(4) if(node.branch->type[i]!=BM_SOLID || node.branch->children[i].solid->val!=val) return;
204         node.cleanup(type);
205         type = BM_SOLID;
206         node.solid = &bmsolids[val];
207         return;
208     }
209     else if(type==BM_SOLID)
210     {
211         uchar oldval = node.solid->val;
212         if(oldval==val) return;
213 
214         if(size > BM_IMAGE_SIZE)
215         {
216             node.splitsolid(type, oldval);
217             fillblendmap(type, node, size, val, x1, y1, x2, y2);
218             return;
219         }
220 
221         type = BM_IMAGE;
222         node.image = new BlendMapImage;
223         memset(node.image->data, oldval, sizeof(node.image->data));
224     }
225 
226     uchar *dst = &node.image->data[y1*BM_IMAGE_SIZE + x1];
227     loopi(y2-y1)
228     {
229         memset(dst, val, x2-x1);
230         dst += BM_IMAGE_SIZE;
231     }
232 }
233 
fillblendmap(int x,int y,int w,int h,uchar val)234 void fillblendmap(int x, int y, int w, int h, uchar val)
235 {
236     int bmsize = hdr.worldsize>>BM_SCALE,
237         x1 = clamp(x, 0, bmsize),
238         y1 = clamp(y, 0, bmsize),
239         x2 = clamp(x+w, 0, bmsize),
240         y2 = clamp(y+h, 0, bmsize);
241     if(max(x1, y1) >= bmsize || min(x2, y2) <= 0 || x1>=x2 || y1>=y2) return;
242     fillblendmap(blendmap.type, blendmap, bmsize, val, x1, y1, x2, y2);
243 }
244 
optimizeblendmap(uchar & type,BlendMapNode & node)245 static void optimizeblendmap(uchar &type, BlendMapNode &node)
246 {
247     switch(type)
248     {
249         case BM_IMAGE:
250         {
251             uint val = node.image->data[0];
252             val |= val<<8;
253             val |= val<<16;
254             for(uint *data = (uint *)node.image->data, *end = &data[sizeof(node.image->data)/sizeof(uint)]; data < end; data++)
255                 if(*data != val) return;
256             node.cleanup(type);
257             type = BM_SOLID;
258             node.solid = &bmsolids[val&0xFF];
259             break;
260         }
261         case BM_BRANCH:
262         {
263             loopi(4) optimizeblendmap(node.branch->type[i], node.branch->children[i]);
264             if(node.branch->type[3]!=BM_SOLID) return;
265             uint val = node.branch->children[3].solid->val;
266             loopi(3) if(node.branch->type[i]!=BM_SOLID || node.branch->children[i].solid->val != val) return;
267             node.cleanup(type);
268             type = BM_SOLID;
269             node.solid = &bmsolids[val];
270             break;
271         }
272     }
273 }
274 
optimizeblendmap()275 void optimizeblendmap()
276 {
277     optimizeblendmap(blendmap.type, blendmap);
278 }
279 
280 VAR(blendpaintmode, 0, 0, 5);
281 
blitblendmap(uchar & type,BlendMapNode & node,int bmx,int bmy,int bmsize,uchar * src,int sx,int sy,int sw,int sh)282 static void blitblendmap(uchar &type, BlendMapNode &node, int bmx, int bmy, int bmsize, uchar *src, int sx, int sy, int sw, int sh)
283 {
284     if(type==BM_BRANCH)
285     {
286         bmsize /= 2;
287         if(sy < bmy + bmsize)
288         {
289             if(sx < bmx + bmsize) blitblendmap(node.branch->type[0], node.branch->children[0], bmx, bmy, bmsize, src, sx, sy, sw, sh);
290             if(sx + sw > bmx + bmsize) blitblendmap(node.branch->type[1], node.branch->children[1], bmx+bmsize, bmy, bmsize, src, sx, sy, sw, sh);
291         }
292         if(sy + sh > bmy + bmsize)
293         {
294             if(sx < bmx + bmsize) blitblendmap(node.branch->type[2], node.branch->children[2], bmx, bmy+bmsize, bmsize, src, sx, sy, sw, sh);
295             if(sx + sw > bmx + bmsize) blitblendmap(node.branch->type[3], node.branch->children[3], bmx+bmsize, bmy+bmsize, bmsize, src, sx, sy, sw, sh);
296         }
297         return;
298     }
299     if(type==BM_SOLID)
300     {
301         uchar val = node.solid->val;
302         if(bmsize > BM_IMAGE_SIZE)
303         {
304             node.splitsolid(type, val);
305             blitblendmap(type, node, bmx, bmy, bmsize, src, sx, sy, sw, sh);
306             return;
307         }
308 
309         type = BM_IMAGE;
310         node.image = new BlendMapImage;
311         memset(node.image->data, val, sizeof(node.image->data));
312     }
313 
314     int x1 = clamp(sx - bmx, 0, bmsize), y1 = clamp(sy - bmy, 0, bmsize),
315         x2 = clamp(sx+sw - bmx, 0, bmsize), y2 = clamp(sy+sh - bmy, 0, bmsize);
316     uchar *dst = &node.image->data[y1*BM_IMAGE_SIZE + x1];
317     src += max(bmy - sy, 0)*sw + max(bmx - sx, 0);
318     loopi(y2-y1)
319     {
320         switch(blendpaintmode)
321         {
322             case 1:
323                 memcpy(dst, src, x2 - x1);
324                 break;
325 
326             case 2:
327                 loopi(x2 - x1) dst[i] = min(dst[i], src[i]);
328                 break;
329 
330             case 3:
331                 loopi(x2 - x1) dst[i] = max(dst[i], src[i]);
332                 break;
333 
334             case 4:
335                 loopi(x2 - x1) dst[i] = min(dst[i], uchar(0xFF - src[i]));
336                 break;
337 
338             case 5:
339                 loopi(x2 - x1) dst[i] = max(dst[i], uchar(0xFF - src[i]));
340                 break;
341         }
342         dst += BM_IMAGE_SIZE;
343         src += sw;
344     }
345 }
346 
blitblendmap(uchar * src,int sx,int sy,int sw,int sh)347 void blitblendmap(uchar *src, int sx, int sy, int sw, int sh)
348 {
349     int bmsize = hdr.worldsize>>BM_SCALE;
350     if(max(sx, sy) >= bmsize || min(sx+sw, sy+sh) <= 0 || min(sw, sh) <= 0) return;
351     blitblendmap(blendmap.type, blendmap, 0, 0, bmsize, src, sx, sy, sw, sh);
352 }
353 
resetblendmap()354 void resetblendmap()
355 {
356     blendmap.cleanup();
357     blendmap.type = BM_SOLID;
358     blendmap.solid = &bmsolids[0xFF];
359 }
360 
enlargeblendmap()361 void enlargeblendmap()
362 {
363     if(blendmap.type == BM_SOLID) return;
364     BlendMapBranch *branch = new BlendMapBranch;
365     branch->type[0] = blendmap.type;
366     branch->children[0] = blendmap;
367     loopi(3)
368     {
369         branch->type[i+1] = BM_SOLID;
370         branch->children[i+1].solid = &bmsolids[0xFF];
371     }
372     blendmap.type = BM_BRANCH;
373     blendmap.branch = branch;
374 }
375 
376 struct BlendBrush
377 {
378     char *name;
379     int w, h;
380     uchar *data;
381     GLuint tex;
382 
BlendBrushBlendBrush383     BlendBrush(const char *name, int w, int h) :
384       name(newstring(name)), w(w), h(h), data(new uchar[w*h]), tex(0)
385     {}
386 
~BlendBrushBlendBrush387     ~BlendBrush()
388     {
389         cleanup();
390         delete[] name;
391         if(data) delete[] data;
392     }
393 
cleanupBlendBrush394     void cleanup()
395     {
396         if(tex) { glDeleteTextures(1, &tex); tex = 0; }
397     }
398 
gentexBlendBrush399     void gentex()
400     {
401         if(!tex) glGenTextures(1, &tex);
402         uchar *buf = new uchar[2*(w+2)*(h+2)];
403         memset(buf, 0, 2*(w+2));
404         uchar *dst = &buf[2*(w+2)], *src = data;
405         loopi(h)
406         {
407             *dst++ = 0;
408             *dst++ = 0;
409             loopj(w)
410             {
411                 *dst++ = 255 - *src;
412                 *dst++ = 255 - *src++;
413             }
414             *dst++ = 0;
415             *dst++ = 0;
416         }
417         memset(dst, 0, 2*(w+2));
418         createtexture(tex, w+2, h+2, buf, 3, 1, GL_LUMINANCE_ALPHA);
419         delete[] buf;
420     }
421 
reorientBlendBrush422     void reorient(bool flipx, bool flipy, bool swapxy)
423     {
424         uchar *rdata = new uchar[w*h];
425         int stridex = 1, stridey = 1;
426         if(swapxy) stridex *= h; else stridey *= w;
427         uchar *src = data, *dst = rdata;
428         if(flipx) { dst += (w-1)*stridex; stridex = -stridex; }
429         if(flipy) { dst += (h-1)*stridey; stridey = -stridey; }
430         loopi(h)
431         {
432             uchar *curdst = dst;
433             loopj(w)
434             {
435                 *curdst = *src++;
436                 curdst += stridex;
437             }
438             dst += stridey;
439         }
440         if(swapxy) swap(w, h);
441         delete[] data;
442         data = rdata;
443         if(tex) gentex();
444     }
445 };
446 
447 static vector<BlendBrush *> brushes;
448 static int curbrush = -1;
449 
cleanupblendmap()450 void cleanupblendmap()
451 {
452     loopv(brushes) brushes[i]->cleanup();
453 }
454 
clearblendbrushes()455 void clearblendbrushes()
456 {
457     while(brushes.length()) delete brushes.pop();
458     curbrush = -1;
459 }
460 
delblendbrush(const char * name)461 void delblendbrush(const char *name)
462 {
463     loopv(brushes) if(!strcmp(brushes[i]->name, name))
464     {
465         delete brushes[i];
466         brushes.remove(i--);
467     }
468     curbrush = brushes.empty() ? -1 : clamp(curbrush, 0, brushes.length()-1);
469 }
470 
addblendbrush(const char * name,const char * imgname)471 void addblendbrush(const char *name, const char *imgname)
472 {
473     delblendbrush(name);
474 
475     ImageData s;
476     if(!loadimage(imgname, s)) { conoutf("\frcould not load blend brush image %s", imgname); return; }
477     if(max(s.w, s.h) > (1<<12))
478     {
479         conoutf("\frblend brush image size exceeded %dx%d pixels: %s", 1<<12, 1<<12, imgname);
480         return;
481     }
482 
483     BlendBrush *brush = new BlendBrush(name, s.w, s.h);
484 
485     uchar *dst = brush->data, *srcrow = s.data;
486     loopi(s.h)
487     {
488         for(uchar *src = srcrow, *end = &srcrow[s.w*s.bpp]; src < end; src += s.bpp)
489             *dst++ = src[0];
490         srcrow += s.pitch;
491     }
492 
493     brushes.add(brush);
494     if(curbrush < 0) curbrush = 0;
495     else if(curbrush >= brushes.length()) curbrush = brushes.length()-1;
496 
497 }
498 
nextblendbrush(int * dir)499 void nextblendbrush(int *dir)
500 {
501     curbrush += *dir < 0 ? -1 : 1;
502     if(brushes.empty()) curbrush = -1;
503     else if(!brushes.inrange(curbrush)) curbrush = *dir < 0 ? brushes.length()-1 : 0;
504 }
505 
setblendbrush(const char * name)506 void setblendbrush(const char *name)
507 {
508     loopv(brushes) if(!strcmp(brushes[i]->name, name)) { curbrush = i; break; }
509 }
510 
getblendbrushname(int * n)511 void getblendbrushname(int *n)
512 {
513     result(brushes.inrange(*n) ? brushes[*n]->name : "");
514 }
515 
curblendbrush()516 void curblendbrush()
517 {
518     intret(curbrush);
519 }
520 
521 COMMAND(clearblendbrushes, "");
522 COMMAND(delblendbrush, "s");
523 COMMAND(addblendbrush, "ss");
524 COMMAND(nextblendbrush, "i");
525 COMMAND(setblendbrush, "s");
526 COMMAND(getblendbrushname, "i");
527 COMMAND(curblendbrush, "");
528 
canpaintblendmap(bool brush=true,bool sel=false)529 bool canpaintblendmap(bool brush = true, bool sel = false)
530 {
531     if(noedit(!sel)) return false;
532     if(!blendpaintmode)
533     {
534         conoutft(CON_MESG, "\froperation only allowed in blend paint mode");
535         return false;
536     }
537     if(brush && !brushes.inrange(curbrush))
538     {
539         conoutft(CON_MESG, "\frno blend brush selected");
540         return false;
541     }
542     return true;
543 }
544 
rotateblendbrush(int * val)545 void rotateblendbrush(int *val)
546 {
547     if(!canpaintblendmap()) return;
548 
549     int numrots = clamp(*val, 1, 5);
550     BlendBrush *brush = brushes[curbrush];
551     brush->reorient(numrots>=2 && numrots<=4, numrots<=2 || numrots==5, (numrots&5)==1);
552 }
553 
554 COMMAND(rotateblendbrush, "i");
555 
paintblendmap()556 void paintblendmap()
557 {
558     if(!canpaintblendmap()) return;
559 
560     BlendBrush *brush = brushes[curbrush];
561     int x = (int)floor(clamp(worldpos.x, 0.0f, float(hdr.worldsize))/(1<<BM_SCALE) - 0.5f*brush->w),
562         y = (int)floor(clamp(worldpos.y, 0.0f, float(hdr.worldsize))/(1<<BM_SCALE) - 0.5f*brush->h);
563     blitblendmap(brush->data, x, y, brush->w, brush->h);
564     previewblends(ivec((x-1)<<BM_SCALE, (y-1)<<BM_SCALE, 0),
565                   ivec((brush->w+2)<<BM_SCALE, (brush->h+2)<<BM_SCALE, hdr.worldsize));
566 }
567 
568 COMMAND(paintblendmap, "");
569 
clearblendmapsel()570 void clearblendmapsel()
571 {
572     if(noedit(false)) return;
573     extern selinfo sel;
574     int x1 = sel.o.x>>BM_SCALE, y1 = sel.o.y>>BM_SCALE,
575         x2 = (sel.o.x+sel.s.x*sel.grid+(1<<BM_SCALE)-1)>>BM_SCALE,
576         y2 = (sel.o.y+sel.s.y*sel.grid+(1<<BM_SCALE)-1)>>BM_SCALE;
577     fillblendmap(x1, y1, x2-x1, y2-y1, 0xFF);
578     previewblends(ivec(x1<<BM_SCALE, y1<<BM_SCALE, 0),
579                   ivec((x2-x1)<<BM_SCALE, (y2-y1)<<BM_SCALE, hdr.worldsize));
580 }
581 
582 COMMAND(clearblendmapsel, "");
583 
showblendmap()584 void showblendmap()
585 {
586     if(noedit(true)) return;
587     previewblends(ivec(0, 0, 0), ivec(hdr.worldsize, hdr.worldsize, hdr.worldsize));
588 }
589 
590 COMMAND(showblendmap, "");
591 COMMAND(optimizeblendmap, "");
592 ICOMMAND(clearblendmap, "", (),
593 {
594     if(noedit(true)) return;
595     resetblendmap();
596     showblendmap();
597 });
598 
renderblendbrush()599 void renderblendbrush()
600 {
601     if(!blendpaintmode || !brushes.inrange(curbrush)) return;
602 
603     BlendBrush *brush = brushes[curbrush];
604     int x1 = (int)floor(clamp(worldpos.x, 0.0f, float(hdr.worldsize))/(1<<BM_SCALE) - 0.5f*(brush->w+2)) << BM_SCALE,
605         y1 = (int)floor(clamp(worldpos.y, 0.0f, float(hdr.worldsize))/(1<<BM_SCALE) - 0.5f*(brush->h+2)) << BM_SCALE,
606         x2 = x1 + ((brush->w+2) << BM_SCALE),
607         y2 = y1 + ((brush->h+2) << BM_SCALE);
608 
609     if(max(x1, y1) >= hdr.worldsize || min(x2, y2) <= 0 || x1>=x2 || y1>=y2) return;
610 
611     if(!brush->tex) brush->gentex();
612     renderblendbrush(brush->tex, x1, y1, x2 - x1, y2 - y1);
613 }
614 
loadblendmap(stream * f,uchar & type,BlendMapNode & node)615 bool loadblendmap(stream *f, uchar &type, BlendMapNode &node)
616 {
617     type = f->getchar();
618     switch(type)
619     {
620         case BM_SOLID:
621         {
622             int val = f->getchar();
623             if(val<0 || val>0xFF) return false;
624             node.solid = &bmsolids[val];
625             break;
626         }
627 
628         case BM_IMAGE:
629             node.image = new BlendMapImage;
630             if(f->read(node.image->data, sizeof(node.image->data)) != sizeof(node.image->data))
631                 return false;
632             break;
633 
634         case BM_BRANCH:
635             node.branch = new BlendMapBranch;
636             loopi(4) { node.branch->type[i] = BM_SOLID; node.branch->children[i].solid = &bmsolids[0xFF]; }
637             loopi(4) if(!loadblendmap(f, node.branch->type[i], node.branch->children[i]))
638                 return false;
639             break;
640 
641         default:
642             type = BM_SOLID;
643             node.solid = &bmsolids[0xFF];
644             return false;
645     }
646     return true;
647 }
648 
loadblendmap(stream * f)649 bool loadblendmap(stream *f)
650 {
651     resetblendmap();
652     return loadblendmap(f, blendmap.type, blendmap);
653 }
654 
saveblendmap(stream * f,uchar type,BlendMapNode & node)655 void saveblendmap(stream *f, uchar type, BlendMapNode &node)
656 {
657     f->putchar(type);
658     switch(type)
659     {
660         case BM_SOLID:
661             f->putchar(node.solid->val);
662             break;
663 
664         case BM_IMAGE:
665             f->write(node.image->data, sizeof(node.image->data));
666             break;
667 
668         case BM_BRANCH:
669             loopi(4) saveblendmap(f, node.branch->type[i], node.branch->children[i]);
670             break;
671     }
672 }
673 
saveblendmap(stream * f)674 void saveblendmap(stream *f)
675 {
676     saveblendmap(f, blendmap.type, blendmap);
677 }
678 
shouldsaveblendmap()679 uchar shouldsaveblendmap()
680 {
681     return blendmap.solid!=&bmsolids[0xFF] ? 1 : 0;
682 }
683 
684