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