~ubuntu-branches/ubuntu/hardy/sauerbraten/hardy-backports

« back to all changes in this revision

Viewing changes to engine/octarender.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bruno "Fuddl" Kleinert
  • Date: 2007-01-09 18:19:30 UTC
  • Revision ID: james.westby@ubuntu.com-20070109181930-zy2ulzq3ukfjhrix
Tags: upstream-0.0.20061204.dfsg
ImportĀ upstreamĀ versionĀ 0.0.20061204.dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// rendercubes.cpp: sits in between worldrender.cpp and rendergl.cpp and fills the vertex array for different cube surfaces.
 
2
 
 
3
#include "pch.h"
 
4
#include "engine.h"
 
5
 
 
6
vector<vertex> verts;
 
7
 
 
8
struct cstat { int size, nleaf, nnode, nface; } cstats[32];
 
9
 
 
10
VAR(showcstats, 0, 0, 1);
 
11
 
 
12
void printcstats()
 
13
{
 
14
    if(showcstats) loopi(32)
 
15
    {
 
16
        if(!cstats[i].size) continue;
 
17
        conoutf("%d: %d faces, %d leafs, %d nodes", cstats[i].size, cstats[i].nface, cstats[i].nleaf, cstats[i].nnode);
 
18
    };
 
19
};
 
20
 
 
21
VARF(floatvtx, 0, 0, 1, allchanged());
 
22
 
 
23
void genfloatverts(fvertex *f)
 
24
{
 
25
    loopv(verts)
 
26
    {
 
27
        const vertex &v = verts[i];
 
28
        f->x = v.x;
 
29
        f->y = v.y;
 
30
        f->z = v.z;
 
31
        f->u = v.u;
 
32
        f->v = v.v;
 
33
        f->n = v.n;
 
34
        f++;
 
35
    };
 
36
};
 
37
 
 
38
struct vboinfo
 
39
{
 
40
    int uses;
 
41
};
 
42
 
 
43
static inline uint hthash(GLuint key)
 
44
{
 
45
    return key;
 
46
};
 
47
 
 
48
static inline bool htcmp(GLuint x, GLuint y)
 
49
{
 
50
    return x==y;
 
51
};
 
52
 
 
53
hashtable<GLuint, vboinfo> vbos;
 
54
 
 
55
VAR(printvbo, 0, 0, 1);
 
56
VARF(vbosize, 0, 0, 1024*1024, allchanged());
 
57
 
 
58
enum
 
59
{
 
60
    VBO_VBUF = 0,
 
61
    VBO_EBUF_L0,
 
62
    VBO_EBUF_L1,
 
63
    VBO_SKYBUF_L0,
 
64
    VBO_SKYBUF_L1,
 
65
    NUMVBO
 
66
};
 
67
 
 
68
static vector<char> vbodata[NUMVBO];
 
69
static vector<vtxarray *> vbovas[NUMVBO];
 
70
 
 
71
void destroyvbo(GLuint vbo)
 
72
{
 
73
    vboinfo &vbi = vbos[vbo];
 
74
    if(vbi.uses <= 0) return;
 
75
    vbi.uses--;
 
76
    if(!vbi.uses) glDeleteBuffers_(1, &vbo);
 
77
};
 
78
 
 
79
void genvbo(int type, void *buf, int len, vtxarray **vas, int numva)
 
80
{
 
81
    GLuint vbo;
 
82
    glGenBuffers_(1, &vbo);
 
83
    GLenum target = type==VBO_VBUF ? GL_ARRAY_BUFFER_ARB : GL_ELEMENT_ARRAY_BUFFER_ARB;
 
84
    glBindBuffer_(target, vbo);
 
85
    glBufferData_(target, len, buf, GL_STATIC_DRAW_ARB);
 
86
    glBindBuffer_(target, 0);
 
87
    
 
88
    vboinfo &vbi = vbos[vbo]; 
 
89
    vbi.uses = numva;
 
90
    
 
91
    if(printvbo) conoutf("vbo %d: type %d, size %d, %d uses", vbo, type, len, numva);
 
92
 
 
93
    loopi(numva)
 
94
    {
 
95
        vtxarray *va = vas[i];
 
96
        switch(type)
 
97
        {
 
98
            case VBO_VBUF: va->vbufGL = vbo; break;
 
99
            case VBO_EBUF_L0: va->l0.ebufGL = vbo; break;
 
100
            case VBO_EBUF_L1: va->l1.ebufGL = vbo; break;
 
101
            case VBO_SKYBUF_L0: va->l0.skybufGL = vbo; break;
 
102
            case VBO_SKYBUF_L1: va->l1.skybufGL = vbo; break;
 
103
        };
 
104
    };
 
105
};
 
106
 
 
107
void flushvbo(int type = -1)
 
108
{
 
109
    if(type < 0)
 
110
    {
 
111
        loopi(NUMVBO) flushvbo(i);
 
112
        return;
 
113
    };
 
114
 
 
115
    vector<char> &data = vbodata[type];
 
116
    if(data.empty()) return;
 
117
    vector<vtxarray *> &vas = vbovas[type];
 
118
    genvbo(type, data.getbuf(), data.length(), vas.getbuf(), vas.length());
 
119
    data.setsizenodelete(0);
 
120
    vas.setsizenodelete(0);
 
121
};
 
122
 
 
123
void *addvbo(vtxarray *va, int type, void *buf, int len)
 
124
{
 
125
    int minsize = type==VBO_VBUF ? min(vbosize, int(floatvtx ? sizeof(fvertex) : sizeof(vertex)) << 16) : vbosize;
 
126
 
 
127
    if(len >= minsize)
 
128
    {
 
129
        genvbo(type, buf, len, &va, 1);
 
130
        return 0;
 
131
    };
 
132
 
 
133
    vector<char> &data = vbodata[type];
 
134
    vector<vtxarray *> &vas = vbovas[type];
 
135
 
 
136
    if(data.length() && data.length() + len > minsize) flushvbo(type);
 
137
 
 
138
    data.reserve(len);
 
139
 
 
140
    size_t offset = data.length();
 
141
    data.reserve(len);
 
142
    memcpy(&data.getbuf()[offset], buf, len);
 
143
    data.ulen += len;
 
144
 
 
145
    vas.add(va); 
 
146
 
 
147
    if(data.length() >= minsize) flushvbo(type);
 
148
 
 
149
    return (void *)offset;
 
150
};
 
151
 
 
152
struct vechash
 
153
{
 
154
    static const int size = 1<<16;
 
155
    int table[size];
 
156
    vector<int> chain;
 
157
 
 
158
    vechash() { clear(); };
 
159
    void clear() { loopi(size) table[i] = -1; chain.setsizenodelete(0); };
 
160
 
 
161
    int access(const vvec &v, short tu, short tv, const bvec &n)
 
162
    {
 
163
        const uchar *iv = (const uchar *)&v;
 
164
        uint h = 5381;
 
165
        loopl(sizeof(v)) h = ((h<<5)+h)^iv[l];
 
166
        h = h&(size-1);
 
167
        for(int i = table[h]; i>=0; i = chain[i])
 
168
        {
 
169
            const vertex &c = verts[i];
 
170
            if(c.x==v.x && c.y==v.y && c.z==v.z && c.n==n)
 
171
            {
 
172
                 if(!tu && !tv) return i; 
 
173
                 if(c.u==tu && c.v==tv) return i;
 
174
            };
 
175
        };
 
176
        vertex &vtx = verts.add();
 
177
        ((vvec &)vtx) = v;
 
178
        vtx.u = tu;
 
179
        vtx.v = tv;
 
180
        vtx.n = n;
 
181
        chain.add(table[h]);
 
182
        return table[h] = verts.length()-1;
 
183
    };
 
184
};
 
185
 
 
186
vechash vh;
 
187
 
 
188
struct sortkey
 
189
{
 
190
     uint tex, lmid;
 
191
     sortkey() {};
 
192
     sortkey(uint tex, uint lmid)
 
193
      : tex(tex), lmid(lmid)
 
194
     {};
 
195
 
 
196
     bool operator==(const sortkey &o) const { return tex==o.tex && lmid==o.lmid; };
 
197
};
 
198
 
 
199
struct sortval
 
200
{
 
201
     int unlit;
 
202
     usvector dims[3];
 
203
 
 
204
     sortval() : unlit(0) {};
 
205
};
 
206
 
 
207
static inline bool htcmp(const sortkey &x, const sortkey &y)
 
208
{
 
209
    return x.tex == y.tex && x.lmid == y.lmid;
 
210
};
 
211
 
 
212
static inline uint hthash(const sortkey &k)
 
213
{
 
214
    return k.tex + k.lmid*9741;
 
215
};
 
216
 
 
217
struct lodcollect
 
218
{
 
219
    hashtable<sortkey, sortval> indices;
 
220
    vector<sortkey> texs;
 
221
    vector<materialsurface> matsurfs;
 
222
    usvector skyindices, explicitskyindices;
 
223
    int curtris;
 
224
    uint offsetindices;
 
225
 
 
226
    int size() { return texs.length()*sizeof(elementset) + (hasVBO ? 0 : (3*curtris+skyindices.length()+explicitskyindices.length())*sizeof(ushort)) + matsurfs.length()*sizeof(materialsurface); };
 
227
 
 
228
    void clearidx() { indices.clear(); };
 
229
    void clear()
 
230
    {
 
231
        curtris = 0;
 
232
        offsetindices = 0;
 
233
        skyindices.setsizenodelete(0);
 
234
        explicitskyindices.setsizenodelete(0);
 
235
        matsurfs.setsizenodelete(0);
 
236
    };
 
237
 
 
238
    void remapunlit(vector<sortkey> &unlit)
 
239
    {
 
240
        uint lastlmid = LMID_AMBIENT, firstlmid = LMID_AMBIENT;
 
241
        int firstlit = -1;
 
242
        loopv(texs)
 
243
        {
 
244
            sortkey &k = texs[i];
 
245
            if(k.lmid>=LMID_RESERVED) 
 
246
            {
 
247
                lastlmid = lightmaps[k.lmid-LMID_RESERVED].unlitx>=0 ? k.lmid : LMID_AMBIENT;
 
248
                if(firstlit<0)
 
249
                {
 
250
                    firstlit = i;
 
251
                    firstlmid = lastlmid;
 
252
                };
 
253
            }
 
254
            else if(k.lmid==LMID_AMBIENT && lastlmid!=LMID_AMBIENT)
 
255
            {
 
256
                sortval &t = indices[k];
 
257
                if(t.unlit<=0) t.unlit = lastlmid;
 
258
            };
 
259
        };
 
260
        if(firstlmid!=LMID_AMBIENT) loopi(firstlit)
 
261
        {
 
262
            sortkey &k = texs[i];
 
263
            if(k.lmid!=LMID_AMBIENT) continue;
 
264
            indices[k].unlit = firstlmid;
 
265
        }; 
 
266
        loopv(unlit)
 
267
        {
 
268
            sortkey &k = unlit[i];
 
269
            sortval &t = indices[k];
 
270
            if(t.unlit<=0) continue; 
 
271
            LightMap &lm = lightmaps[t.unlit-LMID_RESERVED];
 
272
            short u = short((lm.unlitx + 0.5f) * SHRT_MAX/LM_PACKW), 
 
273
                  v = short((lm.unlity + 0.5f) * SHRT_MAX/LM_PACKH);
 
274
            loopl(3) loopvj(t.dims[l])
 
275
            {
 
276
                vertex &vtx = verts[t.dims[l][j]];
 
277
                if(!vtx.u && !vtx.v)
 
278
                {
 
279
                    vtx.u = u;
 
280
                    vtx.v = v;
 
281
                }
 
282
                else if(vtx.u != u || vtx.v != v) 
 
283
                {
 
284
                    // necessary to copy these in case vechash reallocates verts before copying vtx
 
285
                    vvec vv = vtx;
 
286
                    bvec n = vtx.n;
 
287
                    t.dims[l][j] = vh.access(vv, u, v, n);
 
288
                };
 
289
            };
 
290
            sortval *dst = indices.access(sortkey(k.tex, t.unlit));
 
291
            if(dst) loopl(3) loopvj(t.dims[l]) dst->dims[l].add(t.dims[l][j]);
 
292
        };
 
293
    };
 
294
                    
 
295
    void optimize()
 
296
    {
 
297
        vector<sortkey> unlit;
 
298
 
 
299
        texs.setsizenodelete(0);
 
300
        enumeratekt(indices, sortkey, k, sortval, t,
 
301
            loopl(3) if(t.dims[l].length() && t.unlit<=0)
 
302
            {
 
303
                if(k.lmid>=LMID_RESERVED && lightmaps[k.lmid-LMID_RESERVED].unlitx>=0)
 
304
                {
 
305
                    sortkey ukey(k.tex, LMID_AMBIENT);
 
306
                    sortval *uval = indices.access(ukey);
 
307
                    if(uval && uval->unlit<=0)
 
308
                    {
 
309
                        if(uval->unlit<0) texs.removeobj(ukey);
 
310
                        else unlit.add(ukey);
 
311
                        uval->unlit = k.lmid;
 
312
                    };
 
313
                }
 
314
                else if(k.lmid==LMID_AMBIENT)
 
315
                {
 
316
                    unlit.add(k);
 
317
                    t.unlit = -1;
 
318
                };
 
319
                texs.add(k);
 
320
                break;
 
321
            };
 
322
        );
 
323
        texs.sort(texsort);
 
324
 
 
325
        remapunlit(unlit);
 
326
 
 
327
        matsurfs.setsize(optimizematsurfs(matsurfs.getbuf(), matsurfs.length()));
 
328
    };
 
329
 
 
330
    static int texsort(const sortkey *x, const sortkey *y)
 
331
    {
 
332
        if(x->tex == y->tex) return 0;
 
333
        Slot &xs = lookuptexture(x->tex, false), &ys = lookuptexture(y->tex, false);
 
334
        if(xs.shader < ys.shader) return -1;
 
335
        if(xs.shader > ys.shader) return 1;
 
336
        if(xs.params.length() < ys.params.length()) return -1;
 
337
        if(xs.params.length() > ys.params.length()) return 1;
 
338
        if(x->tex < y->tex) return -1;
 
339
        else return 1;
 
340
    };
 
341
 
 
342
    char *setup(vtxarray *va, lodlevel &lod, char *buf)
 
343
    {
 
344
        lod.eslist = (elementset *)buf;
 
345
 
 
346
        lod.sky = skyindices.length();
 
347
        lod.explicitsky = explicitskyindices.length();
 
348
 
 
349
        if(!hasVBO)
 
350
        {
 
351
            lod.ebufGL = lod.skybufGL = 0;
 
352
            lod.ebuf = (ushort *)(lod.eslist + texs.length());
 
353
            lod.skybuf = lod.ebuf + 3*curtris;
 
354
            lod.matbuf = (materialsurface *)(lod.skybuf+lod.sky+lod.explicitsky);
 
355
        };
 
356
 
 
357
        ushort *skybuf = NULL;
 
358
        if(lod.sky+lod.explicitsky)
 
359
        {
 
360
            skybuf = hasVBO ? new ushort[lod.sky+lod.explicitsky] : lod.skybuf;
 
361
            memcpy(skybuf, skyindices.getbuf(), lod.sky*sizeof(ushort));
 
362
            memcpy(skybuf+lod.sky, explicitskyindices.getbuf(), lod.explicitsky*sizeof(ushort));
 
363
        };
 
364
 
 
365
        if(hasVBO)
 
366
        {
 
367
            lod.ebuf = lod.skybuf = 0;
 
368
            lod.matbuf = (materialsurface *)(lod.eslist + texs.length());
 
369
 
 
370
            if(skybuf)
 
371
            {
 
372
#if 1
 
373
                if(offsetindices) loopi(lod.sky+lod.explicitsky) skybuf[i] += offsetindices;
 
374
                lod.skybuf = (ushort *)addvbo(va, &lod==&va->l0 ? VBO_SKYBUF_L0 : VBO_SKYBUF_L1, skybuf, (lod.sky+lod.explicitsky)*sizeof(ushort));
 
375
#else
 
376
                glGenBuffers_(1, &lod.skybufGL);
 
377
                glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, lod.skybufGL);
 
378
                glBufferData_(GL_ELEMENT_ARRAY_BUFFER_ARB, (lod.sky+lod.explicitsky)*sizeof(ushort), skybuf, GL_STATIC_DRAW_ARB);
 
379
#endif
 
380
                delete[] skybuf;
 
381
            }
 
382
            else lod.skybufGL = 0;
 
383
        };
 
384
 
 
385
        lod.matsurfs = matsurfs.length();
 
386
        if(lod.matsurfs) memcpy(lod.matbuf, matsurfs.getbuf(), matsurfs.length()*sizeof(materialsurface));
 
387
 
 
388
        if(texs.length())
 
389
        {
 
390
            ushort *ebuf = hasVBO ? new ushort[3*curtris] : lod.ebuf, *curbuf = ebuf;
 
391
            loopv(texs)
 
392
            {
 
393
                const sortkey &k = texs[i];
 
394
                const sortval &t = indices[k];
 
395
                lod.eslist[i].texture = k.tex;
 
396
                lod.eslist[i].lmid = t.unlit>0 ? t.unlit : k.lmid;
 
397
                loopl(3) if((lod.eslist[i].length[l] = t.dims[l].length()))
 
398
                {
 
399
                    memcpy(curbuf, t.dims[l].getbuf(), t.dims[l].length() * sizeof(ushort));
 
400
                    curbuf += t.dims[l].length();
 
401
                };
 
402
            };
 
403
            if(hasVBO)
 
404
            {
 
405
#if 1
 
406
                if(offsetindices) loopi(3*curtris) ebuf[i] += offsetindices;
 
407
                lod.ebuf = (ushort *)addvbo(va, &lod==&va->l0 ? VBO_EBUF_L0 : VBO_EBUF_L1, ebuf, 3*curtris*sizeof(ushort));
 
408
#else
 
409
                glGenBuffers_(1, &lod.ebufGL);
 
410
                glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, lod.ebufGL);
 
411
                glBufferData_(GL_ELEMENT_ARRAY_BUFFER_ARB, 3*curtris*sizeof(ushort), ebuf, GL_STATIC_DRAW_ARB);
 
412
#endif
 
413
                delete[] ebuf;
 
414
            };
 
415
        }
 
416
        else if(hasVBO) lod.ebufGL = 0;
 
417
        lod.texs = texs.length();
 
418
        lod.tris = curtris;
 
419
        return (char *)(lod.matbuf+lod.matsurfs);
 
420
    };
 
421
} l0, l1;
 
422
 
 
423
int explicitsky = 0, skyarea = 0;
 
424
 
 
425
VARF(lodsize, 0, 32, 128, hdr.mapwlod = lodsize);
 
426
VAR(loddistance, 0, 2000, 100000);
 
427
 
 
428
int addtriindexes(usvector &v, int index[4])
 
429
{
 
430
    int tris = 0;
 
431
    if(index[0]!=index[1] && index[0]!=index[2] && index[1]!=index[2])
 
432
    {
 
433
        tris++;
 
434
        v.add(index[0]);
 
435
        v.add(index[1]);
 
436
        v.add(index[2]);
 
437
    };
 
438
    if(index[0]!=index[2] && index[0]!=index[3] && index[2]!=index[3])
 
439
    {
 
440
        tris++;
 
441
        v.add(index[0]);
 
442
        v.add(index[2]);
 
443
        v.add(index[3]);
 
444
    };
 
445
    return tris;
 
446
};
 
447
 
 
448
void addcubeverts(int orient, int size, bool lodcube, vvec *vv, ushort texture, surfaceinfo *surface, surfacenormals *normals)
 
449
{
 
450
    int index[4];
 
451
    loopk(4)
 
452
    {
 
453
        short u, v;
 
454
        if(surface && surface->lmid >= LMID_RESERVED)
 
455
        {
 
456
            u = short((surface->x + (surface->texcoords[k*2] / 255.0f) * (surface->w - 1) + 0.5f) * SHRT_MAX/LM_PACKW);
 
457
            v = short((surface->y + (surface->texcoords[k*2 + 1] / 255.0f) * (surface->h - 1) + 0.5f) * SHRT_MAX/LM_PACKH);
 
458
        }
 
459
        else u = v = 0;
 
460
        index[k] = vh.access(vv[k], u, v, normals ? normals->normals[k] : bvec(128, 128, 128));
 
461
    };
 
462
 
 
463
    extern vector<GLuint> lmtexids;
 
464
    sortkey key(texture, surface && lmtexids.inrange(surface->lmid) ? surface->lmid : LMID_AMBIENT);
 
465
    if(!lodcube)
 
466
    {
 
467
        int tris = addtriindexes(texture == DEFAULT_SKY ? l0.explicitskyindices : l0.indices[key].dims[dimension(orient)], index);
 
468
        if(texture == DEFAULT_SKY) explicitsky += tris;
 
469
        else l0.curtris += tris;
 
470
    };
 
471
    if(lodsize && size>=lodsize)
 
472
    {
 
473
        int tris = addtriindexes(texture == DEFAULT_SKY ? l1.explicitskyindices : l1.indices[key].dims[dimension(orient)], index);
 
474
        if(texture != DEFAULT_SKY) l1.curtris += tris;
 
475
    };
 
476
};
 
477
 
 
478
void gencubeverts(cube &c, int x, int y, int z, int size, int csi, bool lodcube)
 
479
{
 
480
    freeclipplanes(c);                          // physics planes based on rendering
 
481
 
 
482
    loopi(6) if(visibleface(c, i, x, y, z, size, MAT_AIR, lodcube))
 
483
    {
 
484
        cubeext &e = ext(c);
 
485
 
 
486
        // this is necessary for physics to work, even if the face is merged
 
487
        if(touchingface(c, i)) e.visible |= 1<<i;
 
488
 
 
489
        if(!lodcube && e.merged&(1<<i)) continue;
 
490
 
 
491
        cstats[csi].nface++;
 
492
 
 
493
        vvec vv[4];
 
494
        loopk(4) calcvert(c, x, y, z, size, vv[k], faceverts(c, i, k));
 
495
        addcubeverts(i, size, lodcube, vv, c.texture[i], e.surfaces ? &e.surfaces[i] : NULL, e.normals ? &e.normals[i] : NULL);
 
496
    };
 
497
};
 
498
 
 
499
bool skyoccluded(cube &c, int orient)
 
500
{
 
501
    if(isempty(c)) return false;
 
502
//    if(c.texture[orient] == DEFAULT_SKY) return true;
 
503
    if(touchingface(c, orient) && faceedges(c, orient) == F_SOLID) return true;
 
504
    return false;
 
505
};
 
506
 
 
507
int hasskyfaces(cube &c, int x, int y, int z, int size, int faces[6])
 
508
{
 
509
    int numfaces = 0;
 
510
    if(x == 0 && !skyoccluded(c, O_LEFT)) faces[numfaces++] = O_LEFT;
 
511
    if(x + size == hdr.worldsize && !skyoccluded(c, O_RIGHT)) faces[numfaces++] = O_RIGHT;
 
512
    if(y == 0 && !skyoccluded(c, O_BACK)) faces[numfaces++] = O_BACK;
 
513
    if(y + size == hdr.worldsize && !skyoccluded(c, O_FRONT)) faces[numfaces++] = O_FRONT;
 
514
    if(z == 0 && !skyoccluded(c, O_BOTTOM)) faces[numfaces++] = O_BOTTOM;
 
515
    if(z + size == hdr.worldsize && !skyoccluded(c, O_TOP)) faces[numfaces++] = O_TOP;
 
516
    return numfaces;
 
517
};
 
518
 
 
519
vector<cubeface> skyfaces[6][2];
 
520
 
 
521
void minskyface(cube &cu, int orient, const ivec &co, int size, mergeinfo &orig)
 
522
{   
 
523
    mergeinfo mincf;
 
524
    mincf.u1 = orig.u2;
 
525
    mincf.u2 = orig.u1;
 
526
    mincf.v1 = orig.v2;
 
527
    mincf.v2 = orig.v1;
 
528
    mincubeface(cu, orient, co, size, orig, mincf);
 
529
    orig.u1 = max(mincf.u1, orig.u1);
 
530
    orig.u2 = min(mincf.u2, orig.u2);
 
531
    orig.v1 = max(mincf.v1, orig.v1);
 
532
    orig.v2 = min(mincf.v2, orig.v2);
 
533
};  
 
534
 
 
535
void genskyfaces(cube &c, const ivec &o, int size, bool lodcube)
 
536
{
 
537
    if(isentirelysolid(c)) return;
 
538
 
 
539
    int faces[6],
 
540
        numfaces = hasskyfaces(c, o.x, o.y, o.z, size, faces);
 
541
    if(!numfaces) return;
 
542
 
 
543
    loopi(numfaces)
 
544
    {
 
545
        int orient = faces[i], dim = dimension(orient);
 
546
        cubeface m;
 
547
        m.c = NULL;
 
548
        m.u1 = (o[C[dim]]&VVEC_INT_MASK)<<VVEC_FRAC; 
 
549
        m.u2 = ((o[C[dim]]&VVEC_INT_MASK)+size)<<VVEC_FRAC;
 
550
        m.v1 = (o[R[dim]]&VVEC_INT_MASK)<<VVEC_FRAC;
 
551
        m.v2 = ((o[R[dim]]&VVEC_INT_MASK)+size)<<VVEC_FRAC;
 
552
        minskyface(c, orient, o, size, m);
 
553
        if(m.u1 >= m.u2 || m.v1 >= m.v2) continue;
 
554
        if(!lodcube) 
 
555
        {
 
556
            skyarea += (int(m.u2-m.u1)*int(m.v2-m.v1) + (1<<(2*VVEC_FRAC))-1)>>(2*VVEC_FRAC);
 
557
            skyfaces[orient][0].add(m);
 
558
        };
 
559
        if(lodsize && size>=lodsize) skyfaces[orient][1].add(m);
 
560
    };
 
561
};
 
562
 
 
563
void addskyverts(const ivec &o, int size)
 
564
{
 
565
    loopi(6)
 
566
    {
 
567
        int dim = dimension(i), c = C[dim], r = R[dim];
 
568
        loopl(2)
 
569
        {
 
570
            vector<cubeface> &sf = skyfaces[i][l]; 
 
571
            if(sf.empty()) continue;
 
572
            sf.setsizenodelete(mergefaces(i, sf.getbuf(), sf.length()));
 
573
            loopvj(sf)
 
574
            {
 
575
                mergeinfo &m = sf[j];
 
576
                int index[4];
 
577
                loopk(4)
 
578
                {
 
579
                    const ivec &coords = cubecoords[fv[i][3-k]];
 
580
                    vvec vv;
 
581
                    vv[dim] = (o[dim]&VVEC_INT_MASK)<<VVEC_FRAC;
 
582
                    if(coords[dim]) vv[dim] += size<<VVEC_FRAC;
 
583
                    vv[c] = coords[c] ? m.u2 : m.u1;
 
584
                    vv[r] = coords[r] ? m.v2 : m.v1;
 
585
                    index[k] = vh.access(vv, 0, 0, bvec(128, 128, 128));
 
586
                };
 
587
                addtriindexes((!l ? l0 : l1).skyindices, index);
 
588
            };
 
589
            sf.setsizenodelete(0);
 
590
        };
 
591
    };
 
592
};
 
593
                    
 
594
////////// Vertex Arrays //////////////
 
595
 
 
596
int allocva = 0;
 
597
int wtris = 0, wverts = 0, vtris = 0, vverts = 0, glde = 0;
 
598
vector<vtxarray *> valist, varoot;
 
599
 
 
600
vtxarray *newva(int x, int y, int z, int size)
 
601
{
 
602
    l0.optimize();
 
603
    l1.optimize();
 
604
    int allocsize = sizeof(vtxarray) + l0.size() + l1.size();
 
605
    int bufsize = verts.length()*(floatvtx ? sizeof(fvertex) : sizeof(vertex));
 
606
    if(!hasVBO) allocsize += bufsize; // length of vertex buffer
 
607
    vtxarray *va = (vtxarray *)new uchar[allocsize];
 
608
    if(hasVBO && verts.length())
 
609
    {
 
610
        void *vbuf;
 
611
        if(floatvtx)
 
612
        {
 
613
            fvertex *f = new fvertex[verts.length()];
 
614
            genfloatverts(f);
 
615
            vbuf = (vertex *)addvbo(va, VBO_VBUF, f, bufsize); 
 
616
            delete[] f;
 
617
        }
 
618
        else vbuf = (vertex *)addvbo(va, VBO_VBUF, verts.getbuf(), bufsize);
 
619
        int offset = int(size_t(vbuf)) / (floatvtx ? sizeof(fvertex) : sizeof(vertex)); 
 
620
        l0.offsetindices = offset;
 
621
        l1.offsetindices = offset;
 
622
        va->vbuf = 0; // Offset in VBO
 
623
    };
 
624
    char *buf = l1.setup(va, va->l1, l0.setup(va, va->l0, (char *)(va+1)));
 
625
    if(!hasVBO)
 
626
    {
 
627
        va->vbufGL = 0;
 
628
        va->vbuf = (vertex *)buf;
 
629
        if(floatvtx) genfloatverts((fvertex *)buf);
 
630
        else memcpy(va->vbuf, verts.getbuf(), bufsize);
 
631
    };
 
632
 
 
633
    va->parent = NULL;
 
634
    va->children = new vector<vtxarray *>;
 
635
    va->allocsize = allocsize;
 
636
    va->x = x; va->y = y; va->z = z; va->size = size;
 
637
    va->explicitsky = explicitsky;
 
638
    va->skyarea = skyarea;
 
639
    va->curvfc = VFC_NOT_VISIBLE;
 
640
    va->occluded = OCCLUDE_NOTHING;
 
641
    va->query = NULL;
 
642
    va->mapmodels = NULL;
 
643
    va->hasmerges = 0;
 
644
    wverts += va->verts = verts.length();
 
645
    wtris  += va->l0.tris;
 
646
    allocva++;
 
647
    valist.add(va);
 
648
    return va;
 
649
};
 
650
 
 
651
void destroyva(vtxarray *va, bool reparent)
 
652
{
 
653
    if(va->vbufGL) destroyvbo(va->vbufGL);
 
654
    if(va->l0.ebufGL) destroyvbo(va->l0.ebufGL);
 
655
    if(va->l0.skybufGL) destroyvbo(va->l0.skybufGL);
 
656
    if(va->l1.ebufGL) destroyvbo(va->l1.ebufGL);
 
657
    if(va->l1.skybufGL) destroyvbo(va->l1.skybufGL);
 
658
    wverts -= va->verts;
 
659
    wtris -= va->l0.tris;
 
660
    allocva--;
 
661
    valist.removeobj(va);
 
662
    if(!va->parent) varoot.removeobj(va);
 
663
    if(reparent)
 
664
    {
 
665
        if(va->parent) va->parent->children->removeobj(va);
 
666
        loopv(*va->children)
 
667
        {
 
668
            vtxarray *child = (*va->children)[i];
 
669
            child->parent = va->parent;
 
670
            if(child->parent) child->parent->children->add(va);
 
671
        };
 
672
    };
 
673
    if(va->mapmodels) delete va->mapmodels;
 
674
    if(va->children) delete va->children;
 
675
    delete[] (uchar *)va;
 
676
};
 
677
 
 
678
void vaclearc(cube *c)
 
679
{
 
680
    loopi(8)
 
681
    {
 
682
        if(c[i].ext)
 
683
        {
 
684
            if(c[i].ext->va) destroyva(c[i].ext->va, false);
 
685
            c[i].ext->va = NULL;
 
686
        };
 
687
        if(c[i].children) vaclearc(c[i].children);
 
688
    };
 
689
};
 
690
 
 
691
static ivec bbmin, bbmax;
 
692
static vector<octaentities *> vamms;
 
693
 
 
694
struct mergedface
 
695
{   
 
696
    mergedface *next;
 
697
    uchar orient;
 
698
    ushort tex;
 
699
    vvec v[4];
 
700
    surfaceinfo *surface;
 
701
    surfacenormals *normals;
 
702
};  
 
703
 
 
704
struct mflist
 
705
{
 
706
    mergedface *first, *last;
 
707
    int count;
 
708
};
 
709
 
 
710
static int vahasmerges = 0, vamergemax = 0;
 
711
static mflist vamerges[VVEC_INT];
 
712
 
 
713
void genmergedfaces(cube &c, const ivec &co, int size, int minlevel = 0)
 
714
{
 
715
    if(!c.ext || !c.ext->merges) return;
 
716
    int index = 0;
 
717
    loopi(6) if(c.ext->mergeorigin & (1<<i))
 
718
    {
 
719
        mergeinfo &m = c.ext->merges[index++];
 
720
        if(m.u1>=m.u2 || m.v1>=m.v2) continue;
 
721
        mergedface mf;
 
722
        mf.orient = i;
 
723
        mf.tex = c.texture[i];
 
724
        mf.surface = c.ext->surfaces ? &c.ext->surfaces[i] : NULL;
 
725
        mf.normals = c.ext->normals ? &c.ext->normals[i] : NULL;
 
726
        genmergedverts(c, i, co, size, m, mf.v);
 
727
        int level = calcmergedsize(i, co, size, m, mf.v);
 
728
        if(level > minlevel)
 
729
        {
 
730
            mergedface &nf = *new mergedface;
 
731
            nf = mf;
 
732
            mflist &mfl = vamerges[level];
 
733
            nf.next = mfl.first;
 
734
            mfl.first = &nf;
 
735
            if(!mfl.last) mfl.last = &nf;
 
736
            mfl.count++;
 
737
            vamergemax = max(vamergemax, level);
 
738
            vahasmerges |= MERGE_ORIGIN;
 
739
        };
 
740
    };
 
741
};
 
742
 
 
743
void findmergedfaces(cube &c, const ivec &co, int size, int csi, int minlevel)
 
744
{
 
745
    if(c.ext && c.ext->va && !(c.ext->va->hasmerges&MERGE_ORIGIN)) return;
 
746
    if(c.children)
 
747
    {
 
748
        loopi(8)
 
749
        {
 
750
            ivec o(i, co.x, co.y, co.z, size/2); 
 
751
            findmergedfaces(c.children[i], o, size/2, csi-1, minlevel);
 
752
        };
 
753
    }
 
754
    else if(c.ext && c.ext->merges) genmergedfaces(c, co, size, minlevel);
 
755
};
 
756
 
 
757
void addmergedverts(int level)
 
758
{
 
759
    mflist &mfl = vamerges[level];
 
760
    if(!mfl.count) return;
 
761
    while(mfl.count)
 
762
    {
 
763
        mergedface &mf = *mfl.first;
 
764
        mfl.first = mf.next;
 
765
        if(mfl.last == &mf) mfl.last = NULL;
 
766
        addcubeverts(mf.orient, 1<<level, false, mf.v, mf.tex, mf.surface, mf.normals);
 
767
        delete &mf;
 
768
        mfl.count--;
 
769
        cstats[level].nface++;
 
770
        vahasmerges |= MERGE_USE;
 
771
    };
 
772
};
 
773
 
 
774
void rendercube(cube &c, int cx, int cy, int cz, int size, int csi)  // creates vertices and indices ready to be put into a va
 
775
{
 
776
    //if(size<=16) return;
 
777
    if(c.ext && c.ext->va) return;                            // don't re-render
 
778
    cstats[csi].size = size;
 
779
    bool lodcube = false;
 
780
 
 
781
    if(c.children)
 
782
    {
 
783
        cstats[csi].nnode++;
 
784
 
 
785
        loopi(8)
 
786
        {
 
787
            ivec o(i, cx, cy, cz, size/2);
 
788
            rendercube(c.children[i], o.x, o.y, o.z, size/2, csi-1);
 
789
        };
 
790
 
 
791
        if(csi < VVEC_INT && vamerges[csi].count) addmergedverts(csi);
 
792
 
 
793
        if(size!=lodsize)
 
794
        {
 
795
            if(c.ext)
 
796
            {
 
797
                if(c.ext->ents && c.ext->ents->mapmodels.length()) vamms.add(c.ext->ents);
 
798
            };
 
799
            return;
 
800
        };
 
801
        lodcube = true;
 
802
    };
 
803
    if(!c.children || lodcube) genskyfaces(c, ivec(cx, cy, cz), size, lodcube);
 
804
 
 
805
    if(!isempty(c))
 
806
    {
 
807
        gencubeverts(c, cx, cy, cz, size, csi, lodcube);
 
808
 
 
809
        if(cx<bbmin.x) bbmin.x = cx;
 
810
        if(cy<bbmin.y) bbmin.y = cy;
 
811
        if(cz<bbmin.z) bbmin.z = cz;
 
812
        if(cx+size>bbmax.x) bbmax.x = cx+size;
 
813
        if(cy+size>bbmax.y) bbmax.y = cy+size;
 
814
        if(cz+size>bbmax.z) bbmax.z = cz+size;
 
815
    };
 
816
 
 
817
    if(lodcube) return;
 
818
 
 
819
    if(c.ext)
 
820
    {
 
821
        if(c.ext->ents && c.ext->ents->mapmodels.length()) vamms.add(c.ext->ents);
 
822
        if(c.ext->material != MAT_AIR) genmatsurfs(c, cx, cy, cz, size, l0.matsurfs);
 
823
        if(c.ext->merges) genmergedfaces(c, ivec(cx, cy, cz), size);
 
824
        if(c.ext->merged & ~c.ext->mergeorigin) vahasmerges |= MERGE_PART;
 
825
    };
 
826
 
 
827
    if(csi < VVEC_INT && vamerges[csi].count) addmergedverts(csi);
 
828
 
 
829
    cstats[csi].nleaf++;
 
830
};
 
831
 
 
832
void setva(cube &c, int cx, int cy, int cz, int size, int csi)
 
833
{
 
834
    ASSERT(size <= VVEC_INT_MASK+1);
 
835
 
 
836
    if(verts.length())                                 // since reseting is a bit slow
 
837
    {
 
838
        verts.setsizenodelete(0);
 
839
        explicitsky = skyarea = 0;
 
840
        vh.clear();
 
841
        l0.clear();
 
842
        l1.clear();
 
843
    };
 
844
 
 
845
    vamms.setsizenodelete(0);
 
846
 
 
847
    bbmin = ivec(cx+size, cy+size, cz+size);
 
848
    bbmax = ivec(cx, cy, cz);
 
849
 
 
850
    rendercube(c, cx, cy, cz, size, csi);
 
851
 
 
852
    addskyverts(ivec(cx, cy, cz), size);
 
853
 
 
854
    if(verts.length())
 
855
    {
 
856
        vtxarray *va = newva(cx, cy, cz, size);
 
857
        ext(c).va = va;
 
858
        va->min = bbmin;
 
859
        va->max = bbmax;
 
860
        if(vamms.length()) va->mapmodels = new vector<octaentities *>(vamms);
 
861
        va->hasmerges = vahasmerges;
 
862
    };
 
863
 
 
864
    l0.clearidx();
 
865
    l1.clearidx();
 
866
};
 
867
 
 
868
VARF(vacubemax, 64, 2048, 256*256, allchanged());
 
869
VARF(vacubesize, 128, 128, VVEC_INT_MASK+1, allchanged());
 
870
VARF(vacubemin, 0, 128, 256*256, allchanged());
 
871
 
 
872
int recalcprogress = 0;
 
873
#define progress(s)     if((recalcprogress++&0x7FF)==0) show_out_of_renderloop_progress(recalcprogress/(float)allocnodes, s);
 
874
 
 
875
int updateva(cube *c, int cx, int cy, int cz, int size, int csi)
 
876
{
 
877
    progress("recalculating geometry...");
 
878
    static int faces[6];
 
879
    int ccount = 0, cmergemax = vamergemax, chasmerges = vahasmerges;
 
880
    loopi(8)                                    // counting number of semi-solid/solid children cubes
 
881
    {
 
882
        int count = 0, childpos = varoot.length();
 
883
        ivec o(i, cx, cy, cz, size);
 
884
        vamergemax = 0;
 
885
        vahasmerges = 0;
 
886
        if(c[i].ext && c[i].ext->va) 
 
887
        {
 
888
            //count += vacubemax+1;       // since must already have more then max cubes
 
889
            varoot.add(c[i].ext->va);
 
890
            if(c[i].ext->va->hasmerges&MERGE_ORIGIN) findmergedfaces(c[i], o, size, csi, csi);
 
891
        }
 
892
        else if(c[i].children) count += updateva(c[i].children, o.x, o.y, o.z, size/2, csi-1);
 
893
        else if(!isempty(c[i]) || hasskyfaces(c[i], o.x, o.y, o.z, size, faces)) count++;
 
894
        int tcount = count + (csi < VVEC_INT ? vamerges[csi].count : 0);
 
895
        if(tcount > vacubemax || (tcount >= vacubemin && size == vacubesize) || (tcount && size == min(VVEC_INT_MASK+1, hdr.worldsize/2))) 
 
896
        {
 
897
            setva(c[i], o.x, o.y, o.z, size, csi);
 
898
            if(c[i].ext && c[i].ext->va)
 
899
            {
 
900
                while(varoot.length() > childpos)
 
901
                {
 
902
                    vtxarray *child = varoot.pop();
 
903
                    c[i].ext->va->children->add(child);
 
904
                    child->parent = c[i].ext->va;
 
905
                };
 
906
                varoot.add(c[i].ext->va);
 
907
                if(vamergemax > size)
 
908
                {
 
909
                    cmergemax = max(cmergemax, vamergemax);
 
910
                    vahasmerges |= vahasmerges&~MERGE_USE;
 
911
                };
 
912
                continue;
 
913
            };
 
914
        };
 
915
        if(csi < VVEC_INT-1 && vamerges[csi].count)
 
916
        {
 
917
            mflist &mfl = vamerges[csi], &nfl = vamerges[csi+1];
 
918
            mfl.last->next = nfl.first;
 
919
            nfl.first = mfl.first; 
 
920
            if(!nfl.last) nfl.last = mfl.last;
 
921
            mfl.first = mfl.last = 0;
 
922
            nfl.count += mfl.count;
 
923
            mfl.count = 0;
 
924
        };
 
925
        cmergemax = max(cmergemax, vamergemax);
 
926
        chasmerges |= vahasmerges;
 
927
        ccount += count;
 
928
    };
 
929
 
 
930
    vamergemax = cmergemax;
 
931
    vahasmerges = chasmerges;
 
932
 
 
933
    return ccount;
 
934
};
 
935
 
 
936
void genlod(cube &c, int size)
 
937
{
 
938
    if(!c.children || (c.ext && c.ext->va)) return;
 
939
    progress("generating LOD...");
 
940
 
 
941
    loopi(8) genlod(c.children[i], size/2);
 
942
 
 
943
    if(size>lodsize) return;
 
944
 
 
945
    if(c.ext) c.ext->material = MAT_AIR;
 
946
 
 
947
    loopi(8) if(!isempty(c.children[i]))
 
948
    {
 
949
        forcemip(c);
 
950
        return;
 
951
    };
 
952
 
 
953
    emptyfaces(c);
 
954
};
 
955
 
 
956
void octarender()                               // creates va s for all leaf cubes that don't already have them
 
957
{
 
958
    recalcprogress = 0;
 
959
    if(lodsize) loopi(8) genlod(worldroot[i], hdr.worldsize/2);
 
960
 
 
961
    int csi = 0;
 
962
    while(1<<csi < hdr.worldsize) csi++;
 
963
 
 
964
    recalcprogress = 0;
 
965
    varoot.setsizenodelete(0);
 
966
    updateva(worldroot, 0, 0, 0, hdr.worldsize/2, csi-1);
 
967
    flushvbo();
 
968
 
 
969
    explicitsky = 0;
 
970
    skyarea = 0;
 
971
    loopv(valist)
 
972
    {
 
973
        vtxarray *va = valist[i];
 
974
        explicitsky += va->explicitsky;
 
975
        skyarea += va->skyarea;
 
976
    };
 
977
};
 
978
 
 
979
void precachetextures(lodlevel &lod) { loopi(lod.texs) lookuptexture(lod.eslist[i].texture); };
 
980
void precacheall() { loopv(valist) { precachetextures(valist[i]->l0); precachetextures(valist[i]->l1); } ; };
 
981
 
 
982
void allchanged(bool load)
 
983
{
 
984
    show_out_of_renderloop_progress(0, "clearing VBOs...");
 
985
    vaclearc(worldroot);
 
986
    memset(cstats, 0, sizeof(cstat)*32);
 
987
    resetqueries();
 
988
    octarender();
 
989
    if(load) precacheall();
 
990
    setupmaterials(load);
 
991
    printcstats();
 
992
};
 
993
 
 
994
void recalc()
 
995
{
 
996
    allchanged(true);
 
997
};
 
998
 
 
999
COMMAND(recalc, "");
 
1000
 
 
1001
///////// view frustrum culling ///////////////////////
 
1002
 
 
1003
plane vfcP[5];  // perpindictular vectors to view frustrum bounding planes
 
1004
float vfcDfog;  // far plane culling distance (fog limit).
 
1005
int vfcw, vfch, vfcfov;
 
1006
 
 
1007
vtxarray *visibleva;
 
1008
 
 
1009
int isvisiblesphere(float rad, const vec &cv)
 
1010
{
 
1011
    int v = VFC_FULL_VISIBLE;
 
1012
    float dist;
 
1013
 
 
1014
    loopi(5)
 
1015
    {
 
1016
        dist = vfcP[i].dist(cv);
 
1017
        if(dist < -rad) return VFC_NOT_VISIBLE;
 
1018
        if(dist < rad) v = VFC_PART_VISIBLE;
 
1019
    };
 
1020
 
 
1021
    dist = vfcP[0].dist(cv) - vfcDfog;
 
1022
    if(dist > rad) return VFC_FOGGED;  //VFC_NOT_VISIBLE;    // culling when fog is closer than size of world results in HOM
 
1023
    if(dist > -rad) v = VFC_PART_VISIBLE;
 
1024
 
 
1025
    return v;
 
1026
};
 
1027
 
 
1028
int isvisiblecube(const vec &o, int size)
 
1029
{
 
1030
    vec center(o);
 
1031
    center.add(size/2.0f);
 
1032
    return isvisiblesphere(size*SQRT3/2.0f, center);
 
1033
};
 
1034
 
 
1035
 
 
1036
float vadist(vtxarray *va, const vec &p)
 
1037
{
 
1038
    if(va->min.x>va->max.x)
 
1039
    {
 
1040
        ivec o(va->x, va->y, va->z);
 
1041
        return p.dist_to_bb(o, ivec(o).add(va->size)); // box contains only sky/water
 
1042
    };
 
1043
    return p.dist_to_bb(va->min, va->max);
 
1044
};
 
1045
 
 
1046
#define VASORTSIZE 64
 
1047
 
 
1048
static vtxarray *vasort[VASORTSIZE];
 
1049
 
 
1050
void addvisibleva(vtxarray *va)
 
1051
{
 
1052
    float dist = vadist(va, camera1->o);
 
1053
    va->distance = int(dist); /*cv.dist(camera1->o) - va->size*SQRT3/2*/
 
1054
    va->curlod   = lodsize==0 || va->distance<loddistance ? 0 : 1;
 
1055
 
 
1056
    int hash = min(int(dist*VASORTSIZE/hdr.worldsize), VASORTSIZE-1);
 
1057
    vtxarray **prev = &vasort[hash], *cur = vasort[hash];
 
1058
 
 
1059
    while(cur && va->distance > cur->distance)
 
1060
    {
 
1061
        prev = &cur->next;
 
1062
        cur = cur->next;
 
1063
    };
 
1064
 
 
1065
    va->next = *prev;
 
1066
    *prev = va;
 
1067
};
 
1068
 
 
1069
void sortvisiblevas()
 
1070
{
 
1071
    visibleva = NULL; 
 
1072
    vtxarray **last = &visibleva;
 
1073
    loopi(VASORTSIZE) if(vasort[i])
 
1074
    {
 
1075
        vtxarray *va = vasort[i];
 
1076
        *last = va;
 
1077
        while(va->next) va = va->next;
 
1078
        last = &va->next;
 
1079
    };
 
1080
};
 
1081
 
 
1082
void findvisiblevas(vector<vtxarray *> &vas, bool resetocclude = false)
 
1083
{
 
1084
    loopv(vas)
 
1085
    {
 
1086
        vtxarray &v = *vas[i];
 
1087
        int prevvfc = resetocclude ? VFC_NOT_VISIBLE : v.curvfc;
 
1088
        v.curvfc = isvisiblecube(vec(v.x, v.y, v.z), v.size);
 
1089
        if(v.curvfc!=VFC_NOT_VISIBLE) 
 
1090
        {
 
1091
            addvisibleva(&v);
 
1092
            if(v.children->length()) findvisiblevas(*v.children, prevvfc==VFC_NOT_VISIBLE);
 
1093
            if(prevvfc==VFC_NOT_VISIBLE)
 
1094
            {
 
1095
                v.occluded = OCCLUDE_NOTHING;
 
1096
                v.query = NULL;
 
1097
            };
 
1098
        };
 
1099
    };
 
1100
};
 
1101
 
 
1102
void setvfcP(float pyaw, float ppitch, const vec &camera)
 
1103
{
 
1104
    float vpxo = 90.0 - vfcfov / 2.0;
 
1105
    float vpyo = 90.0 - (vfcfov * float(vfch) / float(vfcw)) / 2;
 
1106
    float yaw = pyaw * RAD;
 
1107
    float yawp = (pyaw + vpxo) * RAD;
 
1108
    float yawm = (pyaw - vpxo) * RAD;
 
1109
    float pitch = ppitch * RAD;
 
1110
    float pitchp = (ppitch + vpyo) * RAD;
 
1111
    float pitchm = (ppitch - vpyo) * RAD;
 
1112
    vfcP[0].toplane(vec(yaw,  pitch), camera);  // back/far plane
 
1113
    vfcP[1].toplane(vec(yawp, pitch), camera);  // left plane
 
1114
    vfcP[2].toplane(vec(yawm, pitch), camera);  // right plane
 
1115
    vfcP[3].toplane(vec(yaw,  pitchp), camera); // top plane
 
1116
    vfcP[4].toplane(vec(yaw,  pitchm), camera); // bottom plane
 
1117
    vfcDfog = getvar("fog");
 
1118
};
 
1119
 
 
1120
plane oldvfcP[5];
 
1121
 
 
1122
void reflectvfcP(float z)
 
1123
{
 
1124
    memcpy(oldvfcP, vfcP, sizeof(vfcP));
 
1125
 
 
1126
    vec o(camera1->o);
 
1127
    o.z = z-(camera1->o.z-z);
 
1128
    setvfcP(camera1->yaw, -camera1->pitch, o);
 
1129
};
 
1130
 
 
1131
void restorevfcP()
 
1132
{
 
1133
    memcpy(vfcP, oldvfcP, sizeof(vfcP));
 
1134
};
 
1135
 
 
1136
void visiblecubes(cube *c, int size, int cx, int cy, int cz, int w, int h, int fov)
 
1137
{
 
1138
    memset(vasort, 0, sizeof(vasort));
 
1139
 
 
1140
    vfcw = w;
 
1141
    vfch = h;
 
1142
    vfcfov = fov;
 
1143
 
 
1144
    // Calculate view frustrum: Only changes if resize, but...
 
1145
    setvfcP(camera1->yaw, camera1->pitch, camera1->o);
 
1146
 
 
1147
    findvisiblevas(varoot);
 
1148
    sortvisiblevas();
 
1149
};
 
1150
 
 
1151
bool insideva(const vtxarray *va, const vec &v)
 
1152
{
 
1153
    return va->x<=v.x && va->y<=v.y && va->z<=v.z && va->x+va->size>v.x && va->y+va->size>v.y && va->z+va->size>v.z;
 
1154
};
 
1155
 
 
1156
static ivec vaorigin;
 
1157
 
 
1158
void resetorigin()
 
1159
{
 
1160
    vaorigin = ivec(-1, -1, -1);
 
1161
};
 
1162
 
 
1163
void setorigin(vtxarray *va)
 
1164
{
 
1165
    ivec o(va->x, va->y, va->z);
 
1166
    o.mask(~VVEC_INT_MASK);
 
1167
    if(o != vaorigin)
 
1168
    {
 
1169
        vaorigin = o;
 
1170
        glPopMatrix();
 
1171
        glPushMatrix();
 
1172
        glTranslatef(o.x, o.y, o.z);
 
1173
        static const float scale = 1.0f/(1<<VVEC_FRAC);
 
1174
        glScalef(scale, scale, scale);
 
1175
    };
 
1176
};
 
1177
 
 
1178
void setupTMU()
 
1179
{
 
1180
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
 
1181
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT,  GL_MODULATE);
 
1182
    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT,  GL_PREVIOUS_EXT);
 
1183
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
 
1184
    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT,  GL_TEXTURE);
 
1185
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
 
1186
};
 
1187
 
 
1188
#define MAXQUERY 2048
 
1189
 
 
1190
struct queryframe
 
1191
{
 
1192
    int cur, max;
 
1193
    occludequery queries[MAXQUERY];
 
1194
};
 
1195
 
 
1196
static queryframe queryframes[2] = {{0, 0}, {0, 0}};
 
1197
static uint flipquery = 0;
 
1198
 
 
1199
int getnumqueries()
 
1200
{
 
1201
    return queryframes[flipquery].cur;
 
1202
};
 
1203
 
 
1204
void flipqueries()
 
1205
{
 
1206
    flipquery = (flipquery + 1) % 2;
 
1207
    queryframe &qf = queryframes[flipquery];
 
1208
    loopi(qf.cur) qf.queries[i].owner = NULL;
 
1209
    qf.cur = 0;
 
1210
};
 
1211
 
 
1212
occludequery *newquery(void *owner)
 
1213
{
 
1214
    queryframe &qf = queryframes[flipquery];
 
1215
    if(qf.cur >= qf.max)
 
1216
    {
 
1217
        if(qf.max >= MAXQUERY) return NULL;
 
1218
        glGenQueries_(1, &qf.queries[qf.max++].id);
 
1219
    };
 
1220
    occludequery *query = &qf.queries[qf.cur++];
 
1221
    query->owner = owner;
 
1222
    query->fragments = -1;
 
1223
    return query;
 
1224
};
 
1225
 
 
1226
void resetqueries()
 
1227
{
 
1228
    loopi(2) loopj(queryframes[i].max) queryframes[i].queries[j].owner = NULL;
 
1229
};
 
1230
 
 
1231
VAR(oqfrags, 0, 8, 64);
 
1232
VAR(oqreflect, 0, 4, 64);
 
1233
 
 
1234
extern float reflecting, refracting;
 
1235
 
 
1236
bool checkquery(occludequery *query, bool nowait)
 
1237
{
 
1238
    GLuint fragments;
 
1239
    if(query->fragments >= 0) fragments = query->fragments;
 
1240
    else
 
1241
    {
 
1242
        if(nowait)
 
1243
        {
 
1244
            GLint avail;
 
1245
            glGetQueryObjectiv_(query->id, GL_QUERY_RESULT_AVAILABLE, &avail);
 
1246
            if(!avail) return false;
 
1247
        };
 
1248
        glGetQueryObjectuiv_(query->id, GL_QUERY_RESULT_ARB, &fragments);
 
1249
        query->fragments = fragments;
 
1250
    };
 
1251
    return fragments < (uint)(reflecting ? oqreflect : oqfrags);
 
1252
};
 
1253
 
 
1254
void drawbb(const ivec &bo, const ivec &br, const vec &camera = camera1->o)
 
1255
{
 
1256
    glBegin(GL_QUADS);
 
1257
 
 
1258
    loopi(6)
 
1259
    {
 
1260
        int dim = dimension(i), coord = dimcoord(i);
 
1261
 
 
1262
        if(coord)
 
1263
        {
 
1264
            if(camera[dim] < bo[dim] + br[dim]) continue;
 
1265
        }
 
1266
        else if(camera[dim] > bo[dim]) continue;
 
1267
 
 
1268
        loopj(4)
 
1269
        {
 
1270
            const ivec &cc = cubecoords[fv[i][j]];
 
1271
            glVertex3i(cc.x ? bo.x+br.x : bo.x,
 
1272
                       cc.y ? bo.y+br.y : bo.y,
 
1273
                       cc.z ? bo.z+br.z : bo.z);
 
1274
        };
 
1275
 
 
1276
        xtraverts += 4;
 
1277
    };
 
1278
 
 
1279
    glEnd();
 
1280
};
 
1281
 
 
1282
extern int octaentsize;
 
1283
 
 
1284
static octaentities *visiblemms, **lastvisiblemms;
 
1285
 
 
1286
void findvisiblemms(const vector<extentity *> &ents)
 
1287
{
 
1288
    for(vtxarray *va = visibleva; va; va = va->next)
 
1289
    {
 
1290
        if(!va->mapmodels || va->curvfc >= VFC_FOGGED || va->occluded >= OCCLUDE_BB) continue;
 
1291
        loopv(*va->mapmodels)
 
1292
        {
 
1293
            octaentities *oe = (*va->mapmodels)[i];
 
1294
            if(isvisiblecube(oe->o.tovec(), oe->size) >= VFC_FOGGED) continue;
 
1295
 
 
1296
            bool occluded = oe->query && oe->query->owner == oe && checkquery(oe->query);
 
1297
            if(occluded)
 
1298
            {
 
1299
                oe->distance = -1;
 
1300
 
 
1301
                oe->next = NULL;
 
1302
                *lastvisiblemms = oe;
 
1303
                lastvisiblemms = &oe->next;
 
1304
            }
 
1305
            else
 
1306
            {
 
1307
                int visible = 0;
 
1308
                loopv(oe->mapmodels)
 
1309
                {
 
1310
                    extentity &e = *ents[oe->mapmodels[i]];
 
1311
                    if(e.visible || (e.attr3 && e.triggerstate == TRIGGER_DISAPPEARED)) continue;
 
1312
                    e.visible = true;
 
1313
                    ++visible;
 
1314
                };
 
1315
                if(!visible) continue;
 
1316
 
 
1317
                oe->distance = int(camera1->o.dist_to_bb(oe->o, oe->size));
 
1318
 
 
1319
                octaentities **prev = &visiblemms, *cur = visiblemms;
 
1320
                while(cur && cur->distance >= 0 && oe->distance > cur->distance)
 
1321
                {
 
1322
                    prev = &cur->next;
 
1323
                    cur = cur->next;
 
1324
                };
 
1325
 
 
1326
                if(*prev == NULL) lastvisiblemms = &oe->next;
 
1327
                oe->next = *prev;
 
1328
                *prev = oe;
 
1329
            };
 
1330
        };
 
1331
    };
 
1332
};
 
1333
 
 
1334
VAR(oqmm, 0, 4, 8);
 
1335
 
 
1336
extern bool getentboundingbox(extentity &e, ivec &o, ivec &r);
 
1337
 
 
1338
void rendermapmodel(extentity &e)
 
1339
{
 
1340
    int anim = ANIM_MAPMODEL|ANIM_LOOP, basetime = 0;
 
1341
    if(e.attr3) switch(e.triggerstate)
 
1342
    {
 
1343
        case TRIGGER_RESET: anim = ANIM_TRIGGER|ANIM_START; break;
 
1344
        case TRIGGERING: anim = ANIM_TRIGGER; basetime = e.lasttrigger; break;
 
1345
        case TRIGGERED: anim = ANIM_TRIGGER|ANIM_END; break;
 
1346
        case TRIGGER_RESETTING: anim = ANIM_TRIGGER|ANIM_REVERSE; basetime = e.lasttrigger; break;
 
1347
    };
 
1348
    mapmodelinfo &mmi = getmminfo(e.attr2);
 
1349
    if(&mmi) rendermodel(e.color, e.dir, mmi.name, anim, 0, mmi.tex, e.o.x, e.o.y, e.o.z, (float)((e.attr1+7)-(e.attr1+7)%15), 0, 10.0f, basetime, NULL, MDL_CULL_VFC | MDL_CULL_DIST);
 
1350
};
 
1351
 
 
1352
extern int reflectdist;
 
1353
 
 
1354
static vector<octaentities *> renderedmms;
 
1355
 
 
1356
vtxarray *reflectedva;
 
1357
 
 
1358
void renderreflectedmapmodels(float z, bool refract)
 
1359
{
 
1360
    bool reflected = !refract && camera1->o.z >= z;
 
1361
    vector<octaentities *> reflectedmms;
 
1362
    vector<octaentities *> &mms = reflected ? reflectedmms : renderedmms;
 
1363
    const vector<extentity *> &ents = et->getents();
 
1364
 
 
1365
    if(reflected)
 
1366
    {
 
1367
        reflectvfcP(z);
 
1368
        for(vtxarray *va = reflectedva; va; va = va->rnext)
 
1369
        {
 
1370
            if(!va->mapmodels || va->distance > reflectdist) continue;
 
1371
            loopv(*va->mapmodels) reflectedmms.add((*va->mapmodels)[i]);
 
1372
        };
 
1373
    };
 
1374
    loopv(mms)
 
1375
    {
 
1376
        octaentities *oe = mms[i];
 
1377
        if(refract ? oe->o.z >= z : oe->o.z+oe->size <= z) continue;
 
1378
        if(reflected && isvisiblecube(oe->o.tovec(), oe->size) >= VFC_FOGGED) continue;
 
1379
        loopv(oe->mapmodels)
 
1380
        {
 
1381
           extentity &e = *ents[oe->mapmodels[i]];
 
1382
           if(e.visible || (e.attr3 && e.triggerstate == TRIGGER_DISAPPEARED)) continue;
 
1383
           e.visible = true;
 
1384
        };
 
1385
    };
 
1386
    loopv(mms)
 
1387
    {
 
1388
        octaentities *oe = mms[i];
 
1389
        loopv(oe->mapmodels)
 
1390
        {
 
1391
           extentity &e = *ents[oe->mapmodels[i]];
 
1392
           if(!e.visible) continue;
 
1393
           rendermapmodel(e);
 
1394
           e.visible = false;
 
1395
        };
 
1396
    };
 
1397
    if(reflected) restorevfcP();
 
1398
};
 
1399
 
 
1400
void rendermapmodels()
 
1401
{
 
1402
    const vector<extentity *> &ents = et->getents();
 
1403
 
 
1404
    visiblemms = NULL;
 
1405
    lastvisiblemms = &visiblemms;
 
1406
    findvisiblemms(ents);
 
1407
 
 
1408
    static int skipoq = 0;
 
1409
 
 
1410
    renderedmms.setsizenodelete(0);
 
1411
 
 
1412
    for(octaentities *oe = visiblemms; oe; oe = oe->next)
 
1413
    {
 
1414
        bool occluded = oe->distance < 0;
 
1415
        if(!occluded)
 
1416
        {
 
1417
            bool hasmodels = false;
 
1418
            loopv(oe->mapmodels)
 
1419
            {
 
1420
                const extentity &e = *ents[oe->mapmodels[i]];
 
1421
                if(!e.visible || (e.attr3 && e.triggerstate == TRIGGER_DISAPPEARED)) continue;
 
1422
                hasmodels = true;
 
1423
                break;
 
1424
            };
 
1425
            if(!hasmodels) continue;
 
1426
        };
 
1427
 
 
1428
        if(!hasOQ || !oqfrags || !oqmm || !oe->distance) oe->query = NULL;
 
1429
        else if(!occluded && (++skipoq % oqmm)) oe->query = NULL;
 
1430
        else oe->query = newquery(oe);
 
1431
 
 
1432
        if(oe->query)
 
1433
        {
 
1434
            if(occluded)
 
1435
            {
 
1436
                glDepthMask(GL_FALSE);
 
1437
                glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
 
1438
            };
 
1439
            startquery(oe->query);
 
1440
        };
 
1441
        if(!occluded || oe->query)
 
1442
        {
 
1443
            ivec bbmin(oe->o), bbmax(oe->o);
 
1444
            bbmin.add(oe->size);
 
1445
            bool rendered = false;
 
1446
            loopv(oe->mapmodels)
 
1447
            {
 
1448
                extentity &e = *ents[oe->mapmodels[i]];
 
1449
                if(e.attr3 && e.triggerstate == TRIGGER_DISAPPEARED) continue;
 
1450
                if(occluded)
 
1451
                {
 
1452
                    ivec bo, br;
 
1453
                    if(getentboundingbox(e, bo, br))
 
1454
                    {
 
1455
                        loopj(3)
 
1456
                        {
 
1457
                            bbmin[j] = min(bbmin[j], bo[j]);
 
1458
                            bbmax[j] = max(bbmax[j], bo[j]+br[j]);
 
1459
                        };
 
1460
                    };
 
1461
                }
 
1462
                else if(e.visible)
 
1463
                {
 
1464
                    if(!rendered) { renderedmms.add(oe); rendered = true; };
 
1465
                    rendermapmodel(e);
 
1466
                    e.visible = false;
 
1467
                };
 
1468
            };
 
1469
            if(occluded)
 
1470
            {
 
1471
                loopj(3)
 
1472
                {
 
1473
                    bbmin[j] = max(bbmin[j], oe->o[j]);
 
1474
                    bbmax[j] = min(bbmax[j], oe->o[j]+oe->size);
 
1475
                };
 
1476
                drawbb(bbmin, bbmax.sub(bbmin));
 
1477
            };
 
1478
        };
 
1479
        if(oe->query)
 
1480
        {
 
1481
            endquery(oe->query);
 
1482
            if(occluded)
 
1483
            {
 
1484
                glDepthMask(GL_TRUE);
 
1485
                glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
 
1486
            };
 
1487
        };
 
1488
    };
 
1489
};
 
1490
 
 
1491
bool bboccluded(const ivec &bo, const ivec &br, cube *c, const ivec &o, int size)
 
1492
{
 
1493
    loopoctabox(o, size, bo, br)
 
1494
    {
 
1495
        ivec co(i, o.x, o.y, o.z, size);
 
1496
        if(c[i].ext && c[i].ext->va)
 
1497
        {
 
1498
            vtxarray *va = c[i].ext->va;
 
1499
            if(va->curvfc >= VFC_FOGGED || va->occluded >= OCCLUDE_BB) continue;
 
1500
        };
 
1501
        if(c[i].children && bboccluded(bo, br, c[i].children, co, size>>1)) continue;
 
1502
        return false;
 
1503
    };
 
1504
    return true;
 
1505
};
 
1506
 
 
1507
VAR(outline, 0, 0, 0xFFFFFF);
 
1508
 
 
1509
void renderoutline()
 
1510
{
 
1511
    if(!editmode || !outline) return;
 
1512
 
 
1513
    notextureshader->set();
 
1514
 
 
1515
    glDisable(GL_TEXTURE_2D);
 
1516
    glEnableClientState(GL_VERTEX_ARRAY);
 
1517
 
 
1518
    glPushMatrix();
 
1519
 
 
1520
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 
1521
    glColor3ub((outline>>16)&0xFF, (outline>>8)&0xFF, outline&0xFF);
 
1522
 
 
1523
    resetorigin();    
 
1524
    GLuint vbufGL = 0, ebufGL = 0;
 
1525
    for(vtxarray *va = visibleva; va; va = va->next)
 
1526
    {
 
1527
        lodlevel &lod = va->curlod ? va->l1 : va->l0;
 
1528
        if(!lod.texs || va->occluded >= OCCLUDE_GEOM) continue;
 
1529
 
 
1530
        setorigin(va);
 
1531
 
 
1532
        bool vbufchanged = true;
 
1533
        if(hasVBO)
 
1534
        {
 
1535
            if(vbufGL == va->vbufGL) vbufchanged = false;
 
1536
            else
 
1537
            {
 
1538
                glBindBuffer_(GL_ARRAY_BUFFER_ARB, va->vbufGL);
 
1539
                vbufGL = va->vbufGL;
 
1540
            };
 
1541
            if(ebufGL != lod.ebufGL)
 
1542
            {
 
1543
                glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, lod.ebufGL);
 
1544
                ebufGL = lod.ebufGL;
 
1545
            };
 
1546
        };
 
1547
        if(vbufchanged) glVertexPointer(3, floatvtx ? GL_FLOAT : GL_SHORT, floatvtx ? sizeof(fvertex) : sizeof(vertex), &(va->vbuf[0].x));
 
1548
 
 
1549
        glDrawElements(GL_TRIANGLES, 3*lod.tris, GL_UNSIGNED_SHORT, lod.ebuf);
 
1550
        glde++;
 
1551
        xtravertsva += va->verts;
 
1552
    };
 
1553
 
 
1554
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 
1555
    
 
1556
    glPopMatrix();
 
1557
 
 
1558
    if(hasVBO)
 
1559
    {
 
1560
        glBindBuffer_(GL_ARRAY_BUFFER_ARB, 0);
 
1561
        glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
 
1562
    };
 
1563
    glDisableClientState(GL_VERTEX_ARRAY);
 
1564
    glEnable(GL_TEXTURE_2D);
 
1565
 
 
1566
    defaultshader->set();
 
1567
};
 
1568
 
 
1569
float orientation_tangent [3][4] = { {  0,1, 0,0 }, { 1,0, 0,0 }, { 1,0,0,0 }};
 
1570
float orientation_binormal[3][4] = { {  0,0,-1,0 }, { 0,0,-1,0 }, { 0,1,0,0 }};
 
1571
 
 
1572
struct renderstate
 
1573
{
 
1574
    bool colormask, depthmask, texture;
 
1575
    GLuint vbufGL, ebufGL;
 
1576
    float fogplane;
 
1577
    Shader *shader;
 
1578
    const ShaderParam *vertparams[MAXSHADERPARAMS], *pixparams[MAXSHADERPARAMS];
 
1579
 
 
1580
    renderstate() : colormask(true), depthmask(true), texture(true), vbufGL(0), ebufGL(0), fogplane(-1), shader(NULL)
 
1581
    {
 
1582
        memset(vertparams, 0, sizeof(vertparams));
 
1583
        memset(pixparams, 0, sizeof(pixparams));
 
1584
    };
 
1585
};
 
1586
 
 
1587
#define setvertparam(param) \
 
1588
    { \
 
1589
        if(!cur.vertparams[param.index] || memcmp(cur.vertparams[param.index]->val, param.val, sizeof(param.val))) \
 
1590
            glProgramEnvParameter4fv_(GL_VERTEX_PROGRAM_ARB, 10+param.index, param.val); \
 
1591
        cur.vertparams[param.index] = &param; \
 
1592
    }
 
1593
 
 
1594
#define setpixparam(param) \
 
1595
    { \
 
1596
        if(!cur.pixparams[param.index] || memcmp(cur.pixparams[param.index]->val, param.val, sizeof(param.val))) \
 
1597
            glProgramEnvParameter4fv_(GL_FRAGMENT_PROGRAM_ARB, 10+param.index, param.val); \
 
1598
        cur.pixparams[param.index] = &param; \
 
1599
    }
 
1600
 
 
1601
void renderquery(renderstate &cur, occludequery *query, vtxarray *va)
 
1602
{
 
1603
    setorigin(va);
 
1604
    if(cur.shader!=nocolorshader) (cur.shader = nocolorshader)->set();
 
1605
    if(cur.colormask) { cur.colormask = false; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); };
 
1606
    if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); };
 
1607
 
 
1608
    startquery(query);
 
1609
 
 
1610
    ivec origin(va->x, va->y, va->z);
 
1611
    origin.mask(~VVEC_INT_MASK);
 
1612
 
 
1613
    vec camera(camera1->o);
 
1614
    if(reflecting && !refracting) camera.z = reflecting;
 
1615
    
 
1616
    ivec bbmin, bbmax;
 
1617
    if(va->children || va->mapmodels || va->l0.matsurfs || va->l0.sky || va->l0.explicitsky)
 
1618
    {
 
1619
        bbmin = ivec(va->x, va->y, va->z);
 
1620
        bbmax = ivec(va->size, va->size, va->size);
 
1621
    }
 
1622
    else
 
1623
    {
 
1624
        bbmin = va->min;
 
1625
        bbmax = va->max;
 
1626
        bbmax.sub(bbmin);
 
1627
    };
 
1628
 
 
1629
    drawbb(bbmin.sub(origin).mul(1<<VVEC_FRAC),
 
1630
           bbmax.mul(1<<VVEC_FRAC),
 
1631
           vec(camera).sub(origin.tovec()).mul(1<<VVEC_FRAC));
 
1632
 
 
1633
    endquery(query);
 
1634
};
 
1635
 
 
1636
void renderva(renderstate &cur, vtxarray *va, lodlevel &lod, bool zfill = false)
 
1637
{
 
1638
    setorigin(va);
 
1639
    bool vbufchanged = true;
 
1640
    if(hasVBO)
 
1641
    {
 
1642
        if(cur.vbufGL == va->vbufGL) vbufchanged = false;
 
1643
        else
 
1644
        {
 
1645
            glBindBuffer_(GL_ARRAY_BUFFER_ARB, va->vbufGL);
 
1646
            cur.vbufGL = va->vbufGL;
 
1647
        };
 
1648
        if(cur.ebufGL != lod.ebufGL)
 
1649
        {
 
1650
            glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, lod.ebufGL);
 
1651
            cur.ebufGL = lod.ebufGL;
 
1652
        };
 
1653
    };
 
1654
    if(vbufchanged) glVertexPointer(3, floatvtx ? GL_FLOAT : GL_SHORT, floatvtx ? sizeof(fvertex) : sizeof(vertex), &(va->vbuf[0].x));
 
1655
    if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); };
 
1656
 
 
1657
    if(zfill)
 
1658
    {
 
1659
        if(cur.shader != nocolorshader) (cur.shader = nocolorshader)->set();
 
1660
        if(cur.colormask) { cur.colormask = false; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); };
 
1661
        glDrawElements(GL_TRIANGLES, 3*lod.tris, GL_UNSIGNED_SHORT, lod.ebuf);
 
1662
        glde++;
 
1663
        xtravertsva += va->verts;
 
1664
        return;
 
1665
    };
 
1666
 
 
1667
    if(refracting)
 
1668
    {
 
1669
        float fogplane = refracting - (va->z & ~VVEC_INT_MASK);
 
1670
        if(cur.fogplane!=fogplane)
 
1671
        {
 
1672
            cur.fogplane = fogplane;
 
1673
            setfogplane(0.5f, fogplane);
 
1674
        };
 
1675
    };
 
1676
    if(!cur.colormask) { cur.colormask = true; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); };
 
1677
 
 
1678
    extern int waterfog;
 
1679
    if(refracting ? va->z+va->size<=refracting-waterfog : va->curvfc==VFC_FOGGED)
 
1680
    {
 
1681
        static Shader *fogshader = NULL;
 
1682
        if(!fogshader) fogshader = lookupshaderbyname("fogworld");
 
1683
        if(fogshader!=cur.shader) (cur.shader = fogshader)->set();
 
1684
        if(cur.texture)
 
1685
        {
 
1686
            cur.texture = false;
 
1687
            glDisable(GL_TEXTURE_2D);
 
1688
            glActiveTexture_(GL_TEXTURE1_ARB);
 
1689
            glDisable(GL_TEXTURE_2D);
 
1690
            glActiveTexture_(GL_TEXTURE0_ARB);
 
1691
        };
 
1692
        glDrawElements(GL_TRIANGLES, 3*lod.tris, GL_UNSIGNED_SHORT, lod.ebuf);
 
1693
        glde++;
 
1694
        vtris += lod.tris;
 
1695
        vverts += va->verts;
 
1696
        return;
 
1697
    };
 
1698
 
 
1699
    if(!cur.texture)
 
1700
    {
 
1701
        cur.texture = true;
 
1702
        glEnable(GL_TEXTURE_2D);
 
1703
        glActiveTexture_(GL_TEXTURE1_ARB);
 
1704
        glEnable(GL_TEXTURE_2D);
 
1705
        glActiveTexture_(GL_TEXTURE0_ARB);
 
1706
    };
 
1707
 
 
1708
    if(renderpath!=R_FIXEDFUNCTION) 
 
1709
    { 
 
1710
        if(vbufchanged) glColorPointer(3, GL_UNSIGNED_BYTE, floatvtx ? sizeof(fvertex) : sizeof(vertex), floatvtx ? &(((fvertex *)va->vbuf)[0].n) : &(va->vbuf[0].n));
 
1711
        glProgramEnvParameter4fv_(GL_VERTEX_PROGRAM_ARB, 4, vec4(camera1->o, 1).sub(ivec(va->x, va->y, va->z).mask(~VVEC_INT_MASK).tovec()).mul(2).v);
 
1712
    };
 
1713
 
 
1714
    if(vbufchanged)
 
1715
    {
 
1716
        glClientActiveTexture_(GL_TEXTURE1_ARB);
 
1717
        glTexCoordPointer(2, GL_SHORT, floatvtx ? sizeof(fvertex) : sizeof(vertex), floatvtx ? &(((fvertex *)va->vbuf)[0].u) : &(va->vbuf[0].u));
 
1718
        glClientActiveTexture_(GL_TEXTURE0_ARB);
 
1719
    };
 
1720
 
 
1721
    ushort *ebuf = lod.ebuf;
 
1722
    int lastlm = -1, lastxs = -1, lastys = -1, lastl = -1;
 
1723
    Slot *lastslot = NULL;
 
1724
    loopi(lod.texs)
 
1725
    {
 
1726
        Slot &slot = lookuptexture(lod.eslist[i].texture);
 
1727
        Texture *tex = slot.sts[0].t;
 
1728
        Shader *s = slot.shader;
 
1729
 
 
1730
        extern vector<GLuint> lmtexids;
 
1731
        int lmid = lod.eslist[i].lmid, curlm = lmtexids[lmid];
 
1732
        if(curlm!=lastlm || !lastslot || s->type!=lastslot->shader->type)
 
1733
        {
 
1734
            if(curlm!=lastlm)
 
1735
            {
 
1736
                glActiveTexture_(GL_TEXTURE1_ARB);
 
1737
                glBindTexture(GL_TEXTURE_2D, curlm);
 
1738
                lastlm = curlm;
 
1739
            };
 
1740
            if(renderpath!=R_FIXEDFUNCTION && s->type==SHADER_NORMALSLMS && (lmid<LMID_RESERVED || lightmaps[lmid-LMID_RESERVED].type==LM_BUMPMAP0))
 
1741
            {
 
1742
                glActiveTexture_(GL_TEXTURE2_ARB);
 
1743
                glBindTexture(GL_TEXTURE_2D, lmtexids[lmid+1]);
 
1744
            };
 
1745
 
 
1746
            glActiveTexture_(GL_TEXTURE0_ARB);
 
1747
        };
 
1748
 
 
1749
        if(&slot!=lastslot)
 
1750
        {
 
1751
            glBindTexture(GL_TEXTURE_2D, tex->gl);
 
1752
            if(s!=cur.shader) (cur.shader = s)->set();
 
1753
 
 
1754
            if(renderpath!=R_FIXEDFUNCTION)
 
1755
            {
 
1756
                int tmu = s->type==SHADER_NORMALSLMS ? 3 : 2;
 
1757
                loopvj(slot.sts)
 
1758
                {
 
1759
                    Slot::Tex &t = slot.sts[j];
 
1760
                    if(t.type==TEX_DIFFUSE || t.combined>=0) continue;
 
1761
                    glActiveTexture_(GL_TEXTURE0_ARB+tmu++);
 
1762
                    glBindTexture(GL_TEXTURE_2D, t.t->gl);
 
1763
                };
 
1764
                uint vertparams = 0, pixparams = 0;
 
1765
                loopvj(slot.params)
 
1766
                {
 
1767
                    const ShaderParam &param = slot.params[j];
 
1768
                    if(param.type == SHPARAM_VERTEX)
 
1769
                    {
 
1770
                        setvertparam(param);
 
1771
                        vertparams |= 1<<param.index;
 
1772
                    }
 
1773
                    else
 
1774
                    {
 
1775
                        setpixparam(param);
 
1776
                        pixparams |= 1<<param.index;
 
1777
                    };
 
1778
                };
 
1779
                loopvj(s->defaultparams)
 
1780
                {
 
1781
                    const ShaderParam &param = s->defaultparams[j];
 
1782
                    if(param.type == SHPARAM_VERTEX)
 
1783
                    {
 
1784
                        if(!(vertparams & (1<<param.index))) setvertparam(param);
 
1785
                    }
 
1786
                    else if(!(pixparams & (1<<param.index))) setpixparam(param);
 
1787
                };
 
1788
                glActiveTexture_(GL_TEXTURE0_ARB);
 
1789
            };
 
1790
            lastslot = &slot;
 
1791
        };
 
1792
 
 
1793
        loopl(3) if (lod.eslist[i].length[l])
 
1794
        {
 
1795
            if(lastl!=l || lastxs!=tex->xs || lastys!=tex->ys)
 
1796
            {
 
1797
                static int si[] = { 1, 0, 0 };
 
1798
                static int ti[] = { 2, 2, 1 };
 
1799
 
 
1800
                GLfloat s[] = { 0.0f, 0.0f, 0.0f, 0.0f };
 
1801
                s[si[l]] = 8.0f/(tex->xs<<VVEC_FRAC);
 
1802
                GLfloat t[] = { 0.0f, 0.0f, 0.0f, 0.0f };
 
1803
                t[ti[l]] = (l <= 1 ? -8.0f : 8.0f)/(tex->ys<<VVEC_FRAC);
 
1804
 
 
1805
                if(renderpath==R_FIXEDFUNCTION)
 
1806
                {
 
1807
                    glTexGenfv(GL_S, GL_OBJECT_PLANE, s);
 
1808
                    glTexGenfv(GL_T, GL_OBJECT_PLANE, t);
 
1809
                    // KLUGE: workaround for buggy nvidia drivers
 
1810
                    // object planes are somehow invalid unless texgen is toggled
 
1811
                    extern int nvidia_texgen_bug;
 
1812
                    if(nvidia_texgen_bug)
 
1813
                    {
 
1814
                        glDisable(GL_TEXTURE_GEN_S);
 
1815
                        glDisable(GL_TEXTURE_GEN_T);
 
1816
                        glEnable(GL_TEXTURE_GEN_S);
 
1817
                        glEnable(GL_TEXTURE_GEN_T);
 
1818
                    };
 
1819
                }
 
1820
                else
 
1821
                {
 
1822
                    glProgramEnvParameter4fv_(GL_VERTEX_PROGRAM_ARB, 0, s);     // have to pass in env, otherwise same problem as fixed function
 
1823
                    glProgramEnvParameter4fv_(GL_VERTEX_PROGRAM_ARB, 1, t);
 
1824
                };
 
1825
 
 
1826
                lastxs = tex->xs;
 
1827
                lastys = tex->ys;
 
1828
                lastl = l;
 
1829
            };
 
1830
 
 
1831
            if(s->type>=SHADER_NORMALSLMS && renderpath!=R_FIXEDFUNCTION)
 
1832
            {
 
1833
                glProgramEnvParameter4fv_(GL_VERTEX_PROGRAM_ARB, 2, orientation_tangent[l]);
 
1834
                glProgramEnvParameter4fv_(GL_VERTEX_PROGRAM_ARB, 3, orientation_binormal[l]);
 
1835
            };
 
1836
 
 
1837
            glDrawElements(GL_TRIANGLES, lod.eslist[i].length[l], GL_UNSIGNED_SHORT, ebuf);
 
1838
            ebuf += lod.eslist[i].length[l];  // Advance to next array.
 
1839
            glde++;
 
1840
        };
 
1841
    };
 
1842
 
 
1843
    vtris += lod.tris;
 
1844
    vverts += va->verts;
 
1845
};
 
1846
 
 
1847
VAR(oqdist, 0, 256, 1024);
 
1848
VAR(zpass, 0, 1, 1);
 
1849
 
 
1850
extern int ati_texgen_bug;
 
1851
 
 
1852
void setupTMUs()
 
1853
{
 
1854
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
 
1855
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
 
1856
    glEnable(GL_TEXTURE_GEN_S);
 
1857
    glEnable(GL_TEXTURE_GEN_T);
 
1858
    if(ati_texgen_bug) glEnable(GL_TEXTURE_GEN_R);     // should not be needed, but apparently makes some ATI drivers happy
 
1859
 
 
1860
    if(renderpath!=R_FIXEDFUNCTION) glEnableClientState(GL_COLOR_ARRAY);
 
1861
 
 
1862
    setupTMU();
 
1863
 
 
1864
    glActiveTexture_(GL_TEXTURE1_ARB);
 
1865
    glClientActiveTexture_(GL_TEXTURE1_ARB);
 
1866
 
 
1867
    glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 2.0f);
 
1868
 
 
1869
    glEnable(GL_TEXTURE_2D);
 
1870
    setupTMU();
 
1871
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
1872
 
 
1873
    glMatrixMode(GL_TEXTURE);
 
1874
    glLoadIdentity();
 
1875
    glScalef(1.0f/SHRT_MAX, 1.0f/SHRT_MAX, 1.0f);
 
1876
    glMatrixMode(GL_MODELVIEW);
 
1877
 
 
1878
    glActiveTexture_(GL_TEXTURE0_ARB);
 
1879
    glClientActiveTexture_(GL_TEXTURE0_ARB);
 
1880
 
 
1881
    if(renderpath!=R_FIXEDFUNCTION)
 
1882
    {
 
1883
        loopi(8-2) { glActiveTexture_(GL_TEXTURE2_ARB+i); glEnable(GL_TEXTURE_2D); };
 
1884
        glActiveTexture_(GL_TEXTURE0_ARB);
 
1885
        glProgramEnvParameter4f_(GL_FRAGMENT_PROGRAM_ARB, 5, hdr.ambient/255.0f, hdr.ambient/255.0f, hdr.ambient/255.0f, 0);
 
1886
    };
 
1887
 
 
1888
    glColor4f(1, 1, 1, 1);
 
1889
};
 
1890
 
 
1891
void cleanupTMUs()
 
1892
{
 
1893
    if(hasVBO) 
 
1894
    {
 
1895
        glBindBuffer_(GL_ARRAY_BUFFER_ARB, 0);
 
1896
        glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
 
1897
    };
 
1898
    glDisableClientState(GL_VERTEX_ARRAY);
 
1899
    if(renderpath!=R_FIXEDFUNCTION)
 
1900
    {
 
1901
        glDisableClientState(GL_COLOR_ARRAY);
 
1902
        loopi(8-2) { glActiveTexture_(GL_TEXTURE2_ARB+i); glDisable(GL_TEXTURE_2D); };
 
1903
    };
 
1904
 
 
1905
    glActiveTexture_(GL_TEXTURE1_ARB);
 
1906
    glClientActiveTexture_(GL_TEXTURE1_ARB);
 
1907
 
 
1908
    glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1.0f);
 
1909
 
 
1910
    glDisable(GL_TEXTURE_2D);
 
1911
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 
1912
 
 
1913
    glActiveTexture_(GL_TEXTURE0_ARB);
 
1914
    glClientActiveTexture_(GL_TEXTURE0_ARB);
 
1915
    glEnable(GL_TEXTURE_2D);
 
1916
 
 
1917
    glDisable(GL_TEXTURE_GEN_S);
 
1918
    glDisable(GL_TEXTURE_GEN_T);
 
1919
    if(ati_texgen_bug) glDisable(GL_TEXTURE_GEN_R);
 
1920
};
 
1921
 
 
1922
#ifdef SHOWVA
 
1923
VAR(showva, 0, 0, 1);
 
1924
#endif
 
1925
 
 
1926
void rendergeom()
 
1927
{
 
1928
    glEnableClientState(GL_VERTEX_ARRAY);
 
1929
 
 
1930
    if(!zpass) setupTMUs();
 
1931
 
 
1932
    flipqueries();
 
1933
 
 
1934
    vtris = vverts = 0;
 
1935
 
 
1936
    glPushMatrix();
 
1937
 
 
1938
    resetorigin();
 
1939
 
 
1940
    renderstate cur;
 
1941
    for(vtxarray *va = visibleva; va; va = va->next)
 
1942
    {
 
1943
        lodlevel &lod = va->curlod ? va->l1 : va->l0;
 
1944
        if(!lod.texs) continue;
 
1945
        if(hasOQ && oqfrags && (zpass || va->distance > oqdist) && !insideva(va, camera1->o))
 
1946
        {
 
1947
            if(!zpass && va->query && va->query->owner == va) 
 
1948
                va->occluded = checkquery(va->query) ? min(va->occluded+1, OCCLUDE_BB) : OCCLUDE_NOTHING;
 
1949
            if(zpass && va->parent && 
 
1950
               (va->parent->occluded == OCCLUDE_PARENT || 
 
1951
                (va->parent->occluded >= OCCLUDE_BB && 
 
1952
                 va->parent->query && va->parent->query->owner == va->parent && va->parent->query->fragments < 0)))
 
1953
            {
 
1954
                va->query = NULL;
 
1955
                if(va->occluded >= OCCLUDE_GEOM)
 
1956
                {
 
1957
                    va->occluded = OCCLUDE_PARENT;
 
1958
                    continue;
 
1959
                };
 
1960
            }
 
1961
            else if(va->occluded >= OCCLUDE_GEOM)
 
1962
            {
 
1963
                va->query = newquery(va);
 
1964
                if(va->query) renderquery(cur, va->query, va);
 
1965
                continue;
 
1966
            }
 
1967
            else va->query = newquery(va);
 
1968
        }
 
1969
        else
 
1970
        {
 
1971
            va->query = NULL;
 
1972
            va->occluded = OCCLUDE_NOTHING;
 
1973
        };
 
1974
 
 
1975
 
 
1976
        if(va->query) startquery(va->query);
 
1977
 
 
1978
        renderva(cur, va, lod, zpass!=0);
 
1979
 
 
1980
        if(va->query) endquery(va->query);
 
1981
    };
 
1982
 
 
1983
    if(!cur.colormask) { cur.colormask = true; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); };
 
1984
    if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); };
 
1985
 
 
1986
    if(zpass) 
 
1987
    {
 
1988
        setupTMUs();
 
1989
        glDepthFunc(GL_LEQUAL);
 
1990
#ifdef SHOWVA
 
1991
        int showvas = 0;
 
1992
#endif
 
1993
        cur.vbufGL = 0;
 
1994
        for(vtxarray *va = visibleva; va; va = va->next)
 
1995
        {
 
1996
            lodlevel &lod = va->curlod ? va->l1 : va->l0;
 
1997
            if(!lod.texs) continue;
 
1998
            if(va->parent && va->parent->occluded >= OCCLUDE_BB && (!va->parent->query || va->parent->query->fragments >= 0)) 
 
1999
            {
 
2000
                va->query = NULL;
 
2001
                va->occluded = OCCLUDE_BB;
 
2002
                continue;
 
2003
            }
 
2004
            else if(va->query)
 
2005
            {
 
2006
                va->occluded = checkquery(va->query) ? min(va->occluded+1, OCCLUDE_BB) : OCCLUDE_NOTHING;
 
2007
                if(va->occluded >= OCCLUDE_GEOM) continue;
 
2008
            }
 
2009
            else if(va->occluded == OCCLUDE_PARENT) va->occluded = OCCLUDE_NOTHING;
 
2010
 
 
2011
#ifdef SHOWVA
 
2012
            if(showva && editmode && renderpath==R_FIXEDFUNCTION)
 
2013
            {
 
2014
                if(insideva(va, worldpos)) 
 
2015
                {
 
2016
                    glColor3f(1, showvas/3.0f, 1-showvas/3.0f);
 
2017
                    showvas++;
 
2018
                }
 
2019
                else glColor3f(1, 1, 1);
 
2020
            };
 
2021
#endif
 
2022
            renderva(cur, va, lod);
 
2023
        };
 
2024
        glDepthFunc(GL_LESS);
 
2025
    };
 
2026
 
 
2027
    glPopMatrix();
 
2028
    cleanupTMUs();
 
2029
};
 
2030
 
 
2031
void findreflectedvas(vector<vtxarray *> &vas, float z, bool refract, bool vfc = true)
 
2032
{
 
2033
    bool doOQ = hasOQ && oqfrags && oqreflect;
 
2034
    loopv(vas)
 
2035
    {
 
2036
        vtxarray *va = vas[i];
 
2037
        if(!vfc) va->curvfc = VFC_NOT_VISIBLE;
 
2038
        if(va->curvfc == VFC_FOGGED || va->z+va->size <= z || isvisiblecube(vec(va->x, va->y, va->z), va->size) >= VFC_FOGGED) continue;
 
2039
        bool render = true;
 
2040
        if(va->curvfc == VFC_FULL_VISIBLE)
 
2041
        {
 
2042
            if(va->occluded >= OCCLUDE_BB) continue;
 
2043
            if(va->occluded >= OCCLUDE_GEOM) render = false;
 
2044
        };
 
2045
        if(render)
 
2046
        {
 
2047
            if(va->curvfc == VFC_NOT_VISIBLE) va->distance = (int)vadist(va, camera1->o);
 
2048
            if(!doOQ && va->distance > reflectdist) continue;
 
2049
            va->rquery = NULL;
 
2050
            vtxarray **vprev = &reflectedva, *vcur = reflectedva;
 
2051
            while(vcur && va->distance > vcur->distance)
 
2052
            {
 
2053
                vprev = &vcur->rnext;
 
2054
                vcur = vcur->rnext;
 
2055
            };
 
2056
            va->rnext = *vprev;
 
2057
            *vprev = va;
 
2058
        };
 
2059
        if(va->children->length()) findreflectedvas(*va->children, z, refract, va->curvfc != VFC_NOT_VISIBLE);
 
2060
    };
 
2061
};
 
2062
 
 
2063
void renderreflectedgeom(float z, bool refract)
 
2064
{
 
2065
    glEnableClientState(GL_VERTEX_ARRAY);
 
2066
    setupTMUs();
 
2067
    glPushMatrix();
 
2068
 
 
2069
    resetorigin();
 
2070
 
 
2071
    renderstate cur;
 
2072
    if(!refract && camera1->o.z >= z)
 
2073
    {
 
2074
        reflectvfcP(z);
 
2075
        reflectedva = NULL;
 
2076
        findreflectedvas(varoot, z, refract);
 
2077
        bool doOQ = hasOQ && oqfrags && oqreflect;
 
2078
        for(vtxarray *va = reflectedva; va; va = va->rnext)
 
2079
        {
 
2080
            lodlevel &lod = va->curlod ? va->l1 : va->l0;
 
2081
            if(!lod.texs || va->max.z <= z) continue;
 
2082
            if(doOQ)
 
2083
            {
 
2084
                va->rquery = newquery(&va->rquery);
 
2085
                if(!va->rquery) continue;
 
2086
                if(va->occluded >= OCCLUDE_BB || va->curvfc == VFC_NOT_VISIBLE)
 
2087
                {
 
2088
                    renderquery(cur, va->rquery, va);
 
2089
                    continue;
 
2090
                };
 
2091
            };
 
2092
            if(va->rquery) startquery(va->rquery);
 
2093
            renderva(cur, va, lod, doOQ);
 
2094
            if(va->rquery) endquery(va->rquery);
 
2095
        };            
 
2096
        if(doOQ)
 
2097
        {
 
2098
            glDepthFunc(GL_LEQUAL);
 
2099
            if(!cur.colormask) { cur.colormask = true; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); };
 
2100
            if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); };
 
2101
            cur.vbufGL = 0;
 
2102
            for(vtxarray **prevva = &reflectedva, *va = reflectedva; va; prevva = &va->rnext, va = va->rnext)
 
2103
            {
 
2104
                lodlevel &lod = va->curlod ? va->l1 : va->l0;
 
2105
                if(!lod.texs || va->max.z <= z) continue;
 
2106
                if(va->rquery && checkquery(va->rquery)) 
 
2107
                {
 
2108
                    if(va->occluded >= OCCLUDE_BB || va->curvfc == VFC_NOT_VISIBLE) *prevva = va->rnext;
 
2109
                    continue;
 
2110
                };
 
2111
                renderva(cur, va, lod);
 
2112
            };
 
2113
            glDepthFunc(GL_LESS);
 
2114
        };
 
2115
        restorevfcP();
 
2116
    }
 
2117
    else
 
2118
    {
 
2119
        for(vtxarray *va = visibleva; va; va = va->next)
 
2120
        {
 
2121
            lodlevel &lod = va->curlod ? va->l1 : va->l0;
 
2122
            if(!lod.texs) continue;
 
2123
            if(va->curvfc == VFC_FOGGED || (refract && camera1->o.z >= z ? va->min.z > z : va->max.z <= z) || va->occluded >= OCCLUDE_GEOM) continue;
 
2124
            if((!hasOQ || !oqfrags) && va->distance > reflectdist) break;
 
2125
            renderva(cur, va, lod);
 
2126
        };
 
2127
    };
 
2128
 
 
2129
    glPopMatrix();
 
2130
    cleanupTMUs();
 
2131
};
 
2132
 
 
2133
static GLuint skyvbufGL, skyebufGL;
 
2134
 
 
2135
void renderskyva(vtxarray *va, lodlevel &lod, bool explicitonly = false)
 
2136
{
 
2137
    setorigin(va);
 
2138
 
 
2139
    bool vbufchanged = true;
 
2140
    if(hasVBO)
 
2141
    {
 
2142
        if(skyvbufGL == va->vbufGL) vbufchanged = false;
 
2143
        else
 
2144
        {
 
2145
            glBindBuffer_(GL_ARRAY_BUFFER_ARB, va->vbufGL);
 
2146
            glVertexPointer(3, floatvtx ? GL_FLOAT : GL_SHORT, floatvtx ? sizeof(fvertex) : sizeof(vertex), &(va->vbuf[0].x));
 
2147
            skyvbufGL = va->vbufGL;
 
2148
        };
 
2149
        if(skyebufGL != lod.skybufGL)
 
2150
        {
 
2151
            glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, lod.skybufGL);
 
2152
            skyebufGL = lod.skybufGL;
 
2153
        };
 
2154
    };
 
2155
    if(vbufchanged) glVertexPointer(3, floatvtx ? GL_FLOAT : GL_SHORT, floatvtx ? sizeof(fvertex) : sizeof(vertex), &(va->vbuf[0].x));
 
2156
 
 
2157
    glDrawElements(GL_TRIANGLES, explicitonly  ? lod.explicitsky : lod.sky+lod.explicitsky, GL_UNSIGNED_SHORT, explicitonly ? lod.skybuf+lod.sky : lod.skybuf);
 
2158
    glde++;
 
2159
 
 
2160
    if(!explicitonly) xtraverts += lod.sky/3;
 
2161
    xtraverts += lod.explicitsky/3;
 
2162
};
 
2163
 
 
2164
void renderreflectedskyvas(vector<vtxarray *> &vas, float z, bool vfc = true)
 
2165
{
 
2166
    loopv(vas)
 
2167
    {
 
2168
        vtxarray *va = vas[i];
 
2169
        lodlevel &lod = va->curlod ? va->l1 : va->l0;
 
2170
        if((vfc && va->curvfc == VFC_FULL_VISIBLE) && va->occluded >= OCCLUDE_BB) continue;
 
2171
        if(va->z+va->size <= z || isvisiblecube(vec(va->x, va->y, va->z), va->size) == VFC_NOT_VISIBLE) continue;
 
2172
        if(lod.sky+lod.explicitsky) renderskyva(va, lod);
 
2173
        if(va->children->length()) renderreflectedskyvas(*va->children, z, vfc && va->curvfc != VFC_NOT_VISIBLE);
 
2174
    };
 
2175
};
 
2176
 
 
2177
void rendersky(bool explicitonly, float zreflect)
 
2178
{
 
2179
    glEnableClientState(GL_VERTEX_ARRAY);
 
2180
 
 
2181
    glPushMatrix();
 
2182
 
 
2183
    resetorigin();
 
2184
 
 
2185
    skyvbufGL = skyebufGL = 0;
 
2186
 
 
2187
    if(zreflect)
 
2188
    {
 
2189
        reflectvfcP(zreflect);
 
2190
        renderreflectedskyvas(varoot, zreflect);
 
2191
        restorevfcP();
 
2192
    }
 
2193
    else for(vtxarray *va = visibleva; va; va = va->next)
 
2194
    {
 
2195
        lodlevel &lod = va->curlod ? va->l1 : va->l0;
 
2196
        if(va->occluded >= OCCLUDE_BB || !(explicitonly ? lod.explicitsky : lod.sky+lod.explicitsky)) continue;
 
2197
 
 
2198
        renderskyva(va, lod, explicitonly);
 
2199
    };
 
2200
 
 
2201
    glPopMatrix();
 
2202
 
 
2203
    if(hasVBO)
 
2204
    {
 
2205
        glBindBuffer_(GL_ARRAY_BUFFER_ARB, 0);
 
2206
        glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
 
2207
    };
 
2208
    glDisableClientState(GL_VERTEX_ARRAY);
 
2209
};
 
2210
 
 
2211
void writeobj(char *name)
 
2212
{
 
2213
    bool oldVBO = hasVBO;
 
2214
    hasVBO = false;
 
2215
    allchanged();
 
2216
    s_sprintfd(fname)("%s.obj", name);
 
2217
    FILE *f = fopen(fname, "w");
 
2218
    if(!f) return;
 
2219
    fprintf(f, "# obj file of sauerbraten level\n");
 
2220
    loopv(valist)
 
2221
    {
 
2222
        vtxarray &v = *valist[i];
 
2223
        vertex *verts = v.vbuf;
 
2224
        if(verts)
 
2225
        {
 
2226
            loopj(v.verts) fprintf(f, "v %d %d %d\n", verts[j].x, verts[j].y, verts[j].z);
 
2227
            lodlevel &lod = v.curlod ? v.l1 : v.l0;
 
2228
            ushort *ebuf = lod.ebuf;
 
2229
            loopi(lod.texs) loopl(3) loopj(lod.eslist[i].length[l]/3)
 
2230
            {
 
2231
                fprintf(f, "f");
 
2232
                for(int k = 3; k>=0; k--) fprintf(f, " %d", ebuf[k]-v.verts);
 
2233
                ebuf += 3;
 
2234
                fprintf(f, "\n");
 
2235
            };
 
2236
        };
 
2237
    };
 
2238
    fclose(f);
 
2239
    hasVBO = oldVBO;
 
2240
    allchanged();
 
2241
};
 
2242
 
 
2243
COMMAND(writeobj, "s");