1 #include "engine.h"
2 
3 VAR(importcuberemip, 0, 1024, 2048);
4 
5 struct cubeloader
6 {
7     enum
8     {
9         DEFAULT_LIQUID = 1,
10         DEFAULT_WALL,
11         DEFAULT_FLOOR,
12         DEFAULT_CEIL
13     };
14 
15     enum                              // block types, order matters!
16     {
17         C_SOLID = 0,                  // entirely solid cube [only specifies wtex]
18         C_CORNER,                     // half full corner of a wall
19         C_FHF,                        // floor heightfield using neighbour vdelta values
20         C_CHF,                        // idem ceiling
21         C_SPACE,                      // entirely empty cube
22         C_SEMISOLID,                  // generated by mipmapping
23         C_MAXTYPE
24     };
25 
26     struct c_sqr
27     {
28         uchar type;                 // one of the above
29         char floor, ceil;           // height, in cubes
30         uchar wtex, ftex, ctex;     // wall/floor/ceil texture ids
31         uchar vdelta;               // vertex delta, used for heightfield cubes
32         uchar utex;                 // upper wall tex id
33     };
34 
35     struct c_persistent_entity        // map entity
36     {
37         short x, y, z;              // cube aligned position
38         short attr1;
39         uchar type;                 // type is one of the above
40         uchar attr2, attr3, attr4;
41     };
42 
43     struct c_header                   // map file format header
44     {
45         char head[4];               // "CUBE"
46         int version;                // any >8bit quantity is little endian
47         int headersize;             // sizeof(header)
48         int sfactor;                // in bits
49         int numents;
50         char maptitle[128];
51         uchar texlists[3][256];
52         int waterlevel;
53         int reserved[15];
54     };
55 
56     c_sqr *world;
57     int ssize;
58     int x0, x1, y0, y1, z0, z1;
59     c_sqr *o[4];
60     int lastremip;
61     int progress;
62 
create_entcubeloader63     void create_ent(c_persistent_entity &ce)
64     {
65         if(ce.type>=7) ce.type++;  // grenade ammo
66         if(ce.type>=8) ce.type++;  // pistol ammo
67         if(ce.type==16) ce.type = ET_MAPMODEL;
68         else if(ce.type>=ET_MAPMODEL && ce.type<16) ce.type++;
69         if(ce.type>=ET_ENVMAP) ce.type++;
70         if(ce.type>=ET_PARTICLES) ce.type++;
71         if(ce.type>=ET_SOUND) ce.type++;
72         if(ce.type>=ET_SPOTLIGHT) ce.type++;
73         extentity &e = *entities::newentity();
74         entities::getents().add(&e);
75         e.type = ce.type;
76         e.o = vec(ce.x*4+worldsize/4, ce.y*4+worldsize/4, ce.z*4+worldsize/2);
77         e.light.color = vec(1, 1, 1);
78         e.light.dir = vec(0, 0, 1);
79         e.attr1 = ce.attr1;
80         e.attr2 = ce.attr2;
81         switch(e.type)
82         {
83             case ET_MAPMODEL:
84                 e.o.z += ce.attr3*4;
85                 e.attr3 = e.attr4 = 0;
86                 break;
87             case ET_LIGHT:
88                 e.attr1 *= 4;
89                 if(!ce.attr3 && !ce.attr4) { e.attr3 = e.attr4 = e.attr2; break; }
90                 // fall through
91             default:
92                 e.attr3 = ce.attr3;
93                 e.attr4 = ce.attr4;
94                 break;
95         }
96         switch(e.type)
97         {
98             case ET_PLAYERSTART:
99             case ET_MAPMODEL:
100             case ET_GAMESPECIFIC+12: // teleport
101             case ET_GAMESPECIFIC+13: // monster
102                 e.attr1 = (int(e.attr1)+180)%360;
103                 break;
104         }
105         e.attr5 = 0;
106     }
107 
getcubecubeloader108     cube &getcube(int x, int y, int z)
109     {
110         return lookupcube(ivec(x*4+worldsize/4, y*4+worldsize/4, z*4+worldsize/2), 4);
111     }
112 
neighbourscubeloader113     int neighbours(c_sqr &t)
114     {
115         o[0] = &t;
116         o[1] = &t+1;
117         o[2] = &t+ssize;
118         o[3] = &t+ssize+1;
119         int best = 0xFFFF;
120         loopi(4) if(o[i]->vdelta<best) best = o[i]->vdelta;
121         return best;
122     }
123 
preprocess_cubescubeloader124     void preprocess_cubes()     // pull up heighfields to where they don't cross cube boundaries
125     {
126         for(;;)
127         {
128             bool changed = false;
129             loop(x, ssize)
130             {
131                 loop(y, ssize)
132                 {
133                     c_sqr &t = world[x+y*ssize];
134                     if(t.type==C_FHF || t.type==C_CHF)
135                     {
136                         int bottom = (neighbours(t)&(~3))+4;
137                         loopj(4) if(o[j]->vdelta>bottom) { o[j]->vdelta = bottom; changed = true; }
138                     }
139                 }
140             }
141             if(!changed) break;
142         }
143     }
144 
getfloorceilcubeloader145     int getfloorceil(c_sqr &s, int &floor, int &ceil)
146     {
147         floor = s.floor;
148         ceil = s.ceil;
149         int cap = 0;
150         switch(s.type)
151         {
152             case C_SOLID: floor = ceil; break;
153             case C_FHF: floor -= (cap = neighbours(s)&(~3))/4; break;
154             case C_CHF: ceil  += (cap = neighbours(s)&(~3))/4; break;
155         }
156         return cap;
157     }
158 
boundingboxcubeloader159     void boundingbox()
160     {
161         x0 = y0 = ssize;
162         x1 = y1 = 0;
163         z0 = 128;
164         z1 = -128;
165         loop(x, ssize) loop(y, ssize)
166         {
167             c_sqr &t = world[x+y*ssize];
168             if(t.type!=C_SOLID)
169             {
170                 if(x<x0) x0 = x;
171                 if(y<y0) y0 = y;
172                 if(x>x1) x1 = x;
173                 if(y>y1) y1 = y;
174                 int floor, ceil;
175                 getfloorceil(t, floor, ceil);
176                 if(floor<z0) z0 = floor;
177                 if(ceil>z1) z1 = ceil;
178             }
179         }
180     }
181 
hfcubeloader182     void hf(int x, int y, int z, int side, int dir, int cap)
183     {
184         cube &c = getcube(x, y, z);
185         loopi(2) loopj(2) edgeset(cubeedge(c, 2, i, j), side, dir*(o[(j<<1)+i]->vdelta-cap)*2+side*8);
186     }
187 
cornersolidcubeloader188     bool cornersolid(int z, c_sqr *s) { return s->type==C_SOLID || z<s->floor || z>=s->ceil; }
189 
createcornercubeloader190     void createcorner(cube &c, int lstart, int lend, int rstart, int rend)
191     {
192         int ledge = edgemake(lstart, lend);
193         int redge = edgemake(rstart, rend);
194         cubeedge(c, 1, 0, 0) = ledge;
195         cubeedge(c, 1, 1, 0) = ledge;
196         cubeedge(c, 1, 0, 1) = redge;
197         cubeedge(c, 1, 1, 1) = redge;
198     }
199 
create_cubescubeloader200     void create_cubes()
201     {
202         preprocess_cubes();
203         boundingbox();
204         lastremip = allocnodes;
205         progress = 0;
206         for(int x = x0-1; x<=x1+1; x++) for(int y = y0-1; y<=y1+1; y++)
207         {
208             c_sqr &s = world[x+y*ssize];
209             int floor, ceil, cap = getfloorceil(s, floor, ceil);
210             for(int z = z0-1; z<=z1+1; z++)
211             {
212                 cube &c = getcube(x, y, z);
213                 c.texture[O_LEFT] = c.texture[O_RIGHT] = c.texture[O_BACK] = c.texture[O_FRONT] = s.type!=C_SOLID && z<ceil ? s.wtex : s.utex;
214                 c.texture[O_BOTTOM] = s.ctex;
215                 c.texture[O_TOP] = s.ftex;
216                 if(z>=floor && z<ceil)
217                 {
218                     setfaces(c, F_EMPTY);
219                 }
220                 else if(s.type==C_CORNER)
221                 {
222                     c_sqr *ts, *bs, *ls, *rs;
223                     bool tc = cornersolid(z, ts = &s-ssize);
224                     bool bc = cornersolid(z, bs = &s+ssize);
225                     bool lc = cornersolid(z, ls = &s-1);
226                     bool rc = cornersolid(z, rs = &s+1);
227                     if     (tc && lc && !bc && !rc) createcorner(c, 0, 8, 0, 0);    // TOP LEFT
228                     else if(tc && !lc && !bc && rc) createcorner(c, 0, 0, 0, 8);    // TOP RIGHT
229                     else if(!tc && lc && bc && !rc) createcorner(c, 0, 8, 8, 8);    // BOT LEFT
230                     else if(!tc && !lc && bc && rc) createcorner(c, 8, 8, 0, 8);    // BOT RIGHT
231                     else        // fix texture on ground of a corner
232                     {
233                         if      (ts->floor-1==z && bs->floor-1!=z) { c.texture[O_TOP] = ts->ftex; }
234                         else if (ts->floor-1!=z && bs->floor-1==z) { c.texture[O_TOP] = bs->ftex; }
235                         if      (ts->ceil==z && bs->ceil!=z)       { c.texture[O_BOTTOM] = ts->ctex; }
236                         else if (ts->ceil!=z && bs->ceil==z)       { c.texture[O_BOTTOM] = bs->ctex; }
237                     }
238                 }
239             }
240             switch(s.type)
241             {
242                 case C_FHF: hf(x, y, floor-1, 1, -1, cap); break;
243                 case C_CHF: hf(x, y, ceil, 0, 1, cap); break;
244             }
245             if(importcuberemip && (allocnodes - lastremip) * 8 > importcuberemip * 1024)
246             {
247                 mpremip(true);
248                 lastremip = allocnodes;
249             }
250             if((progress++&0x7F)==0)
251             {
252                 float bar = float((y1-y0+2)*(x-x0+1) + y-y0+1) / float((y1-y0+2)*(x1-x0+2));
253                 defformatstring(text, "creating cubes... %d%%", int(bar*100));
254                 renderprogress(bar, text);
255             }
256         }
257     }
258 
load_cube_worldcubeloader259     void load_cube_world(char *mname)
260     {
261         int loadingstart = SDL_GetTicks();
262         string pakname, cgzname;
263         formatstring(pakname, "cube/%s", mname);
264         formatstring(cgzname, "packages/%s.cgz", pakname);
265         stream *f = opengzfile(path(cgzname), "rb");
266         if(!f) { conoutf(CON_ERROR, "could not read cube map %s", cgzname); return; }
267         c_header hdr;
268         f->read(&hdr, sizeof(c_header)-sizeof(int)*16);
269         lilswap(&hdr.version, 4);
270         bool mod = false;
271         if(strncmp(hdr.head, "CUBE", 4))
272         {
273             if(!strncmp(hdr.head, "ACMP", 4)) mod = true;
274             else
275             {
276                 conoutf(CON_ERROR, "map %s has malformatted header", cgzname);
277                 delete f;
278                 return;
279             }
280         }
281         else if(hdr.version>5) mod = true;
282         if(hdr.version>5 && !mod) { conoutf(CON_ERROR, "map %s requires a newer version of the Cube 1 importer", cgzname); delete f; return; }
283         if(!haslocalclients()) game::forceedit("");
284         emptymap(12, true, NULL);
285         freeocta(worldroot);
286         worldroot = newcubes(F_SOLID);
287         defformatstring(cs, "importing %s", cgzname);
288         renderbackground(cs);
289         if(hdr.version>=4)
290         {
291             f->read(&hdr.waterlevel, sizeof(int)*16);
292             lilswap(&hdr.waterlevel, 16);
293         }
294         else
295         {
296             hdr.waterlevel = -100000;
297         }
298         if(mod) f->seek(hdr.numents*sizeof(c_persistent_entity), SEEK_CUR);
299         else loopi(hdr.numents)
300         {
301             c_persistent_entity e;
302             f->read(&e, sizeof(c_persistent_entity));
303             lilswap(&e.x, 4);
304             if(i < MAXENTS) create_ent(e);
305         }
306         ssize = 1<<hdr.sfactor;
307         world = new c_sqr[ssize*ssize];
308         c_sqr *t = NULL;
309         loopk(ssize*ssize)
310         {
311             c_sqr *s = &world[k];
312             int type = f->getchar();
313             switch(type)
314             {
315                 case 255:
316                 {
317                     int n = f->getchar();
318                     for(int i = 0; i<n; i++, k++) memcpy(&world[k], t, sizeof(c_sqr));
319                     k--;
320                     break;
321                 }
322                 case 254: // only in MAPVERSION<=2
323                 {
324                     memcpy(s, t, sizeof(c_sqr));
325                     f->getchar();
326                     f->getchar();
327                     break;
328                 }
329                 case C_SOLID:
330                 {
331                     s->type = C_SOLID;
332                     s->wtex = f->getchar();
333                     s->vdelta = f->getchar();
334                     if(hdr.version<=2) { f->getchar(); f->getchar(); }
335                     s->ftex = DEFAULT_FLOOR;
336                     s->ctex = DEFAULT_CEIL;
337                     s->utex = s->wtex;
338                     s->floor = 0;
339                     s->ceil = 16;
340                     break;
341                 }
342                 default:
343                 {
344                     if(type<0 || type>=C_MAXTYPE)
345                     {
346                         defformatstring(t, "%d @ %d", type, k);
347                         fatal("while reading map: type out of range: %s", t);
348                     }
349                     s->type = type;
350                     s->floor = f->getchar();
351                     s->ceil = f->getchar();
352                     if(s->floor>=s->ceil) s->floor = s->ceil-1;  // for pre 12_13
353                     s->wtex = f->getchar();
354                     s->ftex = f->getchar();
355                     s->ctex = f->getchar();
356                     if(hdr.version<=2) { f->getchar(); f->getchar(); }
357                     s->vdelta = f->getchar();
358                     s->utex = (hdr.version>=2) ? f->getchar() : s->wtex;
359                     if(hdr.version>=5) f->getchar();
360                     s->type = type;
361                 }
362             }
363             t = s;
364         }
365         delete f;
366 
367         string cfgname;
368         formatstring(cfgname, "packages/cube/%s.cfg", mname);
369         identflags |= IDF_OVERRIDDEN;
370         execfile("packages/cube/package.cfg");
371         execfile(path(cfgname));
372         identflags &= ~IDF_OVERRIDDEN;
373         create_cubes();
374         mpremip(true);
375         clearlights();
376         allchanged();
377         vector<extentity *> &ents = entities::getents();
378         loopv(ents) if(ents[i]->type!=ET_LIGHT) dropenttofloor(ents[i]);
379         entitiesinoctanodes();
380         conoutf("read cube map %s (%.1f seconds)", cgzname, (SDL_GetTicks()-loadingstart)/1000.0f);
381         startmap(NULL);
382     }
383 };
384 
importcube(char * name)385 void importcube(char *name)
386 {
387     if(multiplayer()) return;
388     cubeloader().load_cube_world(name);
389 }
390 
391 COMMAND(importcube, "s");
392