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