convert line ends
[canaan.git] / prj / cam / src / csg / csg.c
blobdb2678da08a94bb72870d035ee4590808ec219e8
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
6 // $Header: r:/t2repos/thief2/src/csg/csg.c,v 1.95 2000/02/19 12:26:35 toml Exp $
7 //
8 // csg.c
9 //
10 /////
12 #include <stdlib.h> // rand
13 #include <string.h> // memcpy
14 #include <stdio.h> // tree dumping
15 #include <math.h> // fabs, fmod
17 #include <allocapi.h>
19 #include <mprintf.h>
21 #include <lg.h>
23 #include <csg.h>
24 #include <csgbrush.h>
25 #include <r3d.h>
26 #include <wr.h>
27 #include <wrbsp.h>
28 #include <wrdbrend.h>
29 #include <portal.h>
30 #include <media.h>
31 #include <mediaop.h>
32 #include <wrbsp.h>
33 #include <wrlimit.h>
35 #include <hep.h>
36 #include <bspdata.h>
37 #include <csgcheck.h>
38 #include <bsppinfo.h>
39 #include <csgmerge.h>
40 #include <csgalloc.h>
41 #include <csgutil.h>
42 #include <csgbbox.h>
44 #include <prof.h>
45 #include <texmem.h>
46 #include <status.h>
47 #include <memall.h>
48 #include <dbmem.h> // must be last header!
51 float REAL_EPSILON = 0.0001;
53 // storage for the world rep? What's this doing here?
54 PortalCell *wr_cell[MAX_REGIONS];
55 int wr_num_cells;
57 // for each cell, we have a list of which
58 // csg brush&face it came from. The length
59 // of array #x is wr_cell[x]->num_render_polys
60 // this is edit-time only
61 int *wr_brfaces[MAX_REGIONS];
63 bool merge_polys, debug_merge;
65 extern PortalPolyhedron *SplitPortalPolyhedronByPlane(PortalPolyhedron *s, BspPlane *clip, bool merge, bool set_plane);
66 extern void BspFreeTree(void *tree);
67 extern void FreeWR(void);
69 extern void *active; // not really right type, but hey it's modular
71 ////
73 // geometry operations
76 int VertexCode(BspVertex *v, BspPlane *p)
78 Real d;
79 d = v->x*p->a + v->y*p->b + v->z*p->c + p->d;
80 if ( d > REAL_EPSILON) return IN_FRONT;
81 if (-d > REAL_EPSILON) return BEHIND;
82 return COPLANAR;
85 extern void *BspMakeTree(void);
86 extern void BspCompareNode(BspNode *n, FILE *f);
88 #define MAX_PLANES 16384
89 BspPlane all_planes[MAX_PLANES] = { { 1,0,0 }, {0,1,0}, {0,0,1} }; // 96K
90 int plane_brushid[MAX_PLANES]; // first brush with this id
92 bool coplanar=TRUE, split_polys;
93 bool optimize_bsp;
95 // This function is called with a pointer to a bspnode and
96 // a polyhedron which represents the space for this node.
97 // If we're a leaf, we emit it. If we're an internal node,
98 // we split the polyhedron and go. Splitting involves extra
99 // work due to shared polygons, so we use the weird split
100 // functions above.
102 static void PortalizeSubTree(BspNode *b, PortalPolyhedron *s, bool merge)
104 bool virtual_leaf;
106 // if we're a "supposed" leaf (we treat some subtrees as a leaf)
107 if (b->medium != NO_MEDIUM && !(coplanar || split_polys)) {
108 // stuff the "inside leaf" pointer with the polyhedron,
109 // and when we're done with the tree we'll traverse over
110 // all of the leaves
111 b->ph = (void *) s;
112 s->leaf = b;
113 } else {
114 PortalPolyhedron *out;
116 // if split_polys is true, we're splitting polys excessively,
117 // so even if this was a virtual leaf we want to recurse and
118 // split...
119 if (b->medium != NO_MEDIUM && !merge) {
120 merge = TRUE;
121 virtual_leaf = TRUE;
122 } else
123 virtual_leaf = FALSE;
125 if (!IS_LEAF(b)) {
126 // Split this polyhedron by the clipping plane. The function
127 // leaves the inside polyhedron in s, and returns the outside.
129 out = SplitPortalPolyhedronByPlane(s, &b->split_plane, merge, TRUE);
131 #ifdef DBG_ON
132 if (!merge) {
133 #if 0
134 if (s->poly || !optimize_bsp)
135 CheckPolyhedron(s, " (inner clipped polyhedron)");
136 if (out->poly || !optimize_bsp)
137 CheckPolyhedron(out, " (outer clipped polyhedron)");
138 #else
139 CheckPolyhedron(s, " (inner clipped polyhedron)");
140 CheckPolyhedron(out, " (outer clipped polyhedron)");
141 #endif
142 } else {
143 CheckPolyhedronQuick(s, " (inner clipped polyhedron)");
144 CheckPolyhedronQuick(out, " (outer clipped polyhedron)");
146 #endif
148 if (s->poly)
149 PortalizeSubTree(b->inside, s, merge);
150 if (out->poly)
151 PortalizeSubTree(b->outside, out, merge);
153 if (merge) {
154 PortalMergeCells(s, out);
158 if (virtual_leaf) {
159 #ifdef DBG_ON
160 CheckPolyhedron(s, " (remerged polyhedron)");
161 #endif
162 b->ph = (void *) s;
163 s->leaf = b;
164 } else
165 b->ph = 0;
169 // split a polyhedron just to try to simplify the db
171 #define MAX_DUMMY_NODES 4096
172 BspNode *dummy_node[MAX_DUMMY_NODES];
173 int num_dummy_nodes, cur_dummy_nodes;
175 static void clear_dummy_nodes(void)
177 while (num_dummy_nodes) {
178 --num_dummy_nodes;
179 dummy_node[num_dummy_nodes]->user_data = 0;
180 BspFreeLeaf(dummy_node[num_dummy_nodes]);
182 cur_dummy_nodes = 0;
185 BspNode *BspAllocateDummyNode(void)
187 return dummy_node[num_dummy_nodes++] = BspAllocateLeaf();
190 //////////////////////////////////////
192 // Extra data so we can determine which brush face a polygon came from
194 // the same, but this time we're using it to recompute
195 // the texture on a brush that's been updated
197 int csg_num_brushes;
199 typedef struct {
200 int cell;
201 uchar surface;
202 uchar brush_face;
203 short vertex;
204 } SurfaceRef;
206 SurfaceRef *ref_locs[MAX_FACE_BRUSHES];
207 int ref_count[MAX_FACE_BRUSHES];
209 void ClearRegisteredTextures(void)
211 int i;
212 for (i=0; i < csg_num_brushes; ++i) {
213 if (ref_locs[i])
214 Free(ref_locs[i]);
215 ref_locs[i] = 0;
216 ref_count[i] = 0;
220 // we need to register more info now so we can update
221 // the alignment and scale of the textures
223 // RegisterFace(poly_brface[i], cell, surface, vc+k);
225 void RegisterFace(ulong brface, int cell, int surface, int vertex)
227 int br = brface >> 8;
228 int face = brface & 255;
229 int c;
231 if (br >= MAX_FACE_BRUSHES)
232 Error(1, "RegisterFace: MAX_FACE_BRUSHES exceeded.\n");
234 if (ref_count[br]) {
235 c = ref_count[br]++;
236 ref_locs[br] = Realloc(ref_locs[br], sizeof(SurfaceRef) * ref_count[br]);
237 } else {
238 ref_locs[br] = Malloc(sizeof(SurfaceRef));
239 ref_count[br] = 1;
240 c = 0;
242 ref_locs[br][c].cell = cell;
243 ref_locs[br][c].surface = surface;
244 ref_locs[br][c].brush_face = face;
245 ref_locs[br][c].vertex = vertex;
248 void compute_poly_texture(PortalPolygonRenderInfo *render, Vertex *base,
249 PortalPlane *plane, int brush, int face, PortalCell *, int s, int vc);
251 static BOOL is_unlit_texture_id(int t_id)
253 return (t_id==BACKHACK_IDX || t_id==WATERIN_IDX || t_id==WATEROUT_IDX);
256 void update_surface(int cell, int surface, int brush, int face, int vertex, BOOL texture_only)
258 PortalCell *p = WR_CELL(cell);
260 // WARNING: this is somewhat wrong
261 // in particular, using is_unlit_texture_id to know it is water due to
262 // texture_id is a lie, it is really water for other reasons, but Sean
263 // wasnt sure what the right thing to test was
264 // we had to do something since CB_FACE_TEXTURE is constant throughout the
265 // call, since it looks at the real brush database, so the turn off/turn on
266 // isnt right. ideally we would figure out it was a media transition with a
267 // cooler thing and we would be ok, but im not sure how to do that
268 // so for now, i always clear it, then use sky/water texture id to tell if
269 // i should redoesntlight it, which works in all known current cases, but it
270 // somewhat dodgy
271 p->poly_list[surface].flags &= ~RENDER_DOESNT_LIGHT;
272 p->render_list[surface].texture_id = CB_FACE_TEXTURE(brush, face);
273 if (is_unlit_texture_id(p->render_list[surface].texture_id) || CB_FACE_IS_SELF_LUMINOUS(brush, face))
274 p->poly_list[surface].flags |= RENDER_DOESNT_LIGHT;
276 if (texture_only)
277 clear_surface_cache();
278 else
279 compute_poly_texture(
280 &p->render_list[surface],
281 &p->vpool[p->vertex_list[vertex]],
282 &p->plane_list[p->poly_list[surface].planeid],
283 brush, face,
284 p, surface, vertex );
287 void ReassignTexture(int br, BOOL texture_only)
289 int j;
290 SurfaceRef *loc = ref_locs[br];
291 for (j=0; j < ref_count[br]; ++j, ++loc)
292 update_surface(loc->cell,loc->surface, br, loc->brush_face, loc->vertex, texture_only);
295 extern void emit_portal_polyhedron(PortalPolyhedron *ph);
296 extern int num_cells;
297 extern int num_points;
298 extern int num_poly;
299 extern int num_portal;
300 extern int subdivide;
302 extern int find_brface_from_poly(PortalPolygon *poly);
304 void compute_brface(PortalPolyhedron *ph)
306 PortalPolygon *poly, *first;
307 first = poly = ph->poly;
308 do {
309 int n = (poly->ph[0] == ph);
310 if (poly->ph[n] && IS_MARKED(poly->ph[n]->leaf)) {
311 poly->brface = -1;
312 } else
313 poly->brface = find_brface_from_poly(poly);
315 if (poly->ph[0] == ph)
316 poly = poly->ph_next[0];
317 else
318 poly = poly->ph_next[1];
319 } while (poly != first);
322 bool resplit_cell;
323 void assign_pinfo(PortalPolyhedron *ph)
325 PortalPolygon *poly, *first;
327 // visit all polygons and compute their info
329 first = poly = ph->poly;
330 do {
331 pinfo *x;
332 int n = (poly->ph[0] == ph);
333 if (!poly->misc) {
334 x = Malloc(sizeof(*x));
335 poly->misc = x;
336 if (poly->ph[n] && poly->ph[n]->leaf->cell_id > 0) {
337 x->portal = poly->ph[!n]->leaf->cell_id;
338 x->portal_2 = poly->ph[n]->leaf->cell_id;
339 x->brface = -1;
340 } else {
341 x->portal = 0;
342 x->brface = poly->brface;
346 if (poly->ph[0] == ph)
347 poly = poly->ph_next[0];
348 else
349 poly = poly->ph_next[1];
350 } while (poly != first);
353 void free_pinfo(PortalPolyhedron *ph)
355 PortalPolygon *poly, *first;
356 // free the pinfo
357 first = poly = ph->poly;
358 do {
359 if (poly->misc) {
360 Free(poly->misc);
361 poly->misc = NULL;
364 if (poly->ph[0] == ph)
365 poly = poly->ph_next[0];
366 else
367 poly = poly->ph_next[1];
368 } while (poly != first);
371 // used by level optimizer
372 #if 1
373 // ick, we have to write it out the old way
374 void write_cell(FILE *f, PortalCell *p)
376 uchar buffer[256];
377 int i, vl;
379 vl = 0;
380 for (i=0; i < p->num_polys; ++i)
381 vl += p->poly_list[i].num_vertices;
383 fwrite(&vl, 1, sizeof(vl), f);
384 fwrite(p, 1, sizeof(*p), f);
385 fwrite(p->vpool, sizeof(Vertex), p->num_vertices, f);
386 fwrite(p->poly_list, sizeof(PortalPolygonCore), p->num_polys, f);
387 for (i=0; i < p->num_render_polys; ++i) {
388 // write the pointers to the u,v vectors
389 fwrite(buffer, 8, 1, f);
390 // write out the render list sans u,v vectors
391 fwrite(&p->render_list[i].u_base, sizeof(PortalPolygonRenderInfo)-sizeof(Vector)*2, 1, f);
394 for (i=0; i < p->num_planes; ++i) {
395 fwrite(buffer, 4, 1, f); // write out faux pointer
396 fwrite(&p->plane_list[i].plane_constant, sizeof(PortalPlane)-sizeof(Vector), 1, f);
398 fwrite(p->vertex_list, 1, vl, f);
400 for (i=0; i < p->num_render_polys; ++i) {
401 fwrite(&p->render_list[i].tex_u, sizeof(Vector), 1, f);
402 fwrite(&p->render_list[i].tex_v, sizeof(Vector), 1, f);
405 for (i=0; i < p->num_planes; ++i) {
406 fwrite(&p->plane_list[i].normal, sizeof(Vector), 1, f);
408 for (i=0; i < p->num_render_polys; ++i) {
409 fwrite(&p->light_list[i], sizeof(PortalLightMap), 1, f);
410 fwrite(buffer, 4, 1, f); // decal is gone
411 // actually, this doesn't line up exactly right, but the anim
412 // light bitmask isn't used by the optimizer either
414 for (i=0; i < p->num_render_polys; ++i) {
415 fwrite(p->light_list[i].data, 1, // the data is bogus, but the same size
416 p->light_list[i].w*p->light_list[i].h, f);
419 #else
420 void write_cell(FILE *f, PortalCell *p)
422 int i, vl;
424 vl = 0;
425 for (i=0; i < p->num_polys; ++i)
426 vl += p->poly_list[i].num_vertices;
428 fwrite(&vl, 1, sizeof(vl), f);
429 fwrite(p, 1, sizeof(*p), f);
430 fwrite(p->vpool, sizeof(Vertex), p->num_vertices, f);
431 fwrite(p->poly_list, sizeof(PortalPolygonCore), p->num_polys, f);
432 fwrite(p->render_list, sizeof(PortalPolygonRenderInfo), p->num_render_polys, f);
433 fwrite(p->plane_list, sizeof(PortalPlane), p->num_planes, f);
434 fwrite(p->vertex_list, 1, vl, f);
436 #endif
438 #ifdef DBG_ON
439 static void CheckPolyhedronFinal(PortalPolyhedron *ph)
441 CheckPolyhedron(ph, " (after splitting)");
443 #endif
445 // Mark all the nodes which are open
446 static void MarkNode(BspNode *b)
448 if (b->ph) {
449 if (b->medium == NO_MEDIUM) Error(1, "Bad medium in MarkNode.\n");
450 if (b->medium == MEDIA_SOLID)
451 UNMARK(b);
452 else {
453 #if 0
454 if (((PortalPolyhedron *) b->ph)->poly)
455 MARK(b);
456 else
457 UNMARK(b);
458 #else
459 MARK(b);
460 #endif
462 } else {
463 if (!IS_LEAF(b)) {
464 MarkNode(b->inside);
465 MarkNode(b->outside);
467 UNMARK(b);
471 static void portal_id_point_list(PortalPolyhedron *ph)
473 ph->leaf->cell_id = ++num_cells; // store cell id + 1
476 void (*traverse_func)(PortalPolyhedron *);
478 static void PortalTraverseVirtualLeavesRaw(BspNode *b)
480 if (b->ph) {
481 if (IS_MARKED(b))
482 traverse_func((PortalPolyhedron *) b->ph);
483 } else {
484 PortalTraverseVirtualLeavesRaw(b->inside);
485 PortalTraverseVirtualLeavesRaw(b->outside);
489 static void PortalTraverseVirtualLeaves(BspNode *b, void (*tfunc)(PortalPolyhedron *))
491 int i;
492 traverse_func = tfunc;
493 PortalTraverseVirtualLeavesRaw(b);
494 for (i=0; i < cur_dummy_nodes; ++i)
495 tfunc((PortalPolyhedron *) dummy_node[i]->ph);
498 int stat_edge_merge, stat_poly_merge, stat_max_vertices;
499 extern PortalPolyhedron *PortalMakeCube(Real size);
501 static void port_status(char *s, int pass)
503 char buf[128];
504 if (pass == 1)
505 sprintf(buf, "Portalize: %s", s);
506 else if (pass > 1)
507 sprintf(buf, "Port pass %d: %s", pass, s);
508 else if (pass == -1)
509 sprintf(buf, "Port edge-merge pass: %s", s);
510 else if (pass < -1)
511 sprintf(buf, "Port edge-merge pass %d: %s", -pass, s);
512 Status(buf);
515 int bsp_num_planes=3; // 3 axis aligned start planes
517 static void PortalizeTree(void *tree, Real world_size)
519 int pass;
520 PortalPolyhedron *s = PortalMakeCube(world_size);
522 // proceed depth-first through the tree, refining
523 // the polyhedron for each subtree into the polyhedra
524 // for its children. Then store them on the children
525 // and continue the traversal. Already visited locations
526 // continue to be refined via subdivision of existing polygons
527 // and edges to remove t-joints and t-faces
529 Status("Portalize");
531 #if 0
533 int i,j;
534 for (i=0; i < bsp_num_planes; ++i) {
535 BspPlane x;
536 x.a = (float) all_planes[i].a;
537 x.b = (float) all_planes[i].b;
538 x.c = (float) all_planes[i].c;
539 x.d = (float) all_planes[i].d;
540 j = find_plane(&x);
541 if (i != j) {
542 mprintf("Plane %i doesn't map to itself:\n");
543 mprintf("double: %lg %lg %lg %lg\n", all_planes[i].a,all_planes[i].b,
544 all_planes[i].c,all_planes[i].c);
545 mprintf(" float: %lg %lg %lg %lg\n", x.a,x.b,x.c,x.d);
546 if (j >= 0)
547 mprintf(" match: %lg %lg %lg %lg\n", all_planes[j].a,
548 all_planes[j].b,all_planes[j].c,all_planes[j].c);
552 #endif
554 //clear_dummy_nodes();
556 PortalizeSubTree((BspNode *) tree, s, FALSE);
558 // Now we've fully traversed the tree and stored the
559 // polyhedra in the leaves. Now we traverse the tree
560 // and allocate IDs to the leaves.
561 // and emit the final refined polyhedra. We could have
562 // put them in a linked list or something, but there isn't
563 // much point
565 Status("Port: analysis");
567 MarkNode((BspNode *) tree);
568 #ifdef DBG_ON
569 PortalTraverseVirtualLeaves((BspNode *) tree, CheckPolyhedronFinal);
570 #endif
572 PortalTraverseVirtualLeaves((BspNode *) tree, compute_brface);
573 stat_edge_merge = stat_poly_merge = 0;
575 pass = 1;
577 do {
578 num_cells = num_points = num_poly = num_portal = 0;
579 subdivide = 0;
581 cur_dummy_nodes = num_dummy_nodes;
583 FreeWR();
584 resplit_cell = FALSE;
586 port_status("number cells", pass);
587 PortalTraverseVirtualLeaves((BspNode *) tree, portal_id_point_list);
588 port_status("compute brushfaces", pass);
589 PortalTraverseVirtualLeaves((BspNode *) tree, assign_pinfo);
590 if (coplanar || merge_polys) {
591 port_status("merge polys", pass);
592 PortalTraverseVirtualLeaves((BspNode *) tree, emit_merge_cell_polys);
593 if (post_edge_merge) {
594 port_status("merge edges", pass);
595 PortalTraverseVirtualLeaves((BspNode *) tree, emit_merge_cell_edges);
598 ClearRegisteredTextures();
599 stat_max_vertices = 0;
600 port_status("build worldrep", pass);
601 PortalTraverseVirtualLeaves((BspNode *) tree, emit_portal_polyhedron);
603 // Set global world-rep variable here so deallocation can know how many
604 // cells it needs to deallocate
605 wr_num_cells = num_cells;
607 if (!resplit_cell) {
608 if (post_edge_merge)
609 break;
610 post_edge_merge = TRUE;
611 pass = -1;
612 } else
613 if (pass > 0) ++pass; else --pass;
614 } while (1);
616 post_edge_merge = FALSE;
618 // Copy CSG BSP tree into WorldRep BSP Tree
619 Status("Port: copying to worldrep BSP");
620 wrBspTreeDeallocate();
621 wrBspTreeCopy((BspNode *)tree, WRBSP_INVALID);
623 // Set up links between cells and their leaves
624 Status("Port: linking cells to leaves");
625 wrBspTreeRefCells(0);
627 mprintf("%d cells, %d portals, %d polygons; %d splits from large polys\n",
628 num_cells, num_portal, num_poly, subdivide);
629 mprintf("%d polys merged; %d colinear vertices deleted; %d vertices max\n",
630 stat_poly_merge, stat_edge_merge, stat_max_vertices);
631 mprintf("%d unique planes\n", bsp_num_planes);
633 portalize_mem_reset();
636 extern Hep csg_hep, ab_hep;
638 extern void InsertPortalPolyhedronInNode(BspNode *b, PortalPolyhedron *z);
640 bool merge_nodes=TRUE;
642 // Attach non-zero user_data values to nodes
643 // which shouldn't really be split
644 static int leaf_count;
645 static void RecodeNode(BspNode *b)
647 if (!IS_LEAF(b)) {
648 RecodeNode(b->inside);
649 RecodeNode(b->outside);
650 if (merge_nodes && b->inside->medium == b->outside->medium)
651 b->medium = b->inside->medium;
652 else {
653 b->medium = NO_MEDIUM;
654 if (b->inside->medium != NO_MEDIUM)
655 ++leaf_count;
656 if (b->outside->medium != NO_MEDIUM)
657 ++leaf_count;
662 extern void init_csgmedia(void);
664 void csg_memory_init(void)
666 MakeHep(&Nodes, sizeof(BspNode));
667 init_csgmedia();
668 portalize_mem_init();
671 void csg_memory_reset(void)
673 clear_dummy_nodes();
674 ResetHep(&Nodes);
675 ResetHep(&ab_hep);
676 ResetHep(&csg_hep);
677 portalize_mem_reset();
680 extern int num_brush_faces[MAX_FACE_BRUSHES];
681 extern BspPlane brush_faces[MAX_FACE_BRUSHES][MAX_FACES];
683 void save_csg_internal_database(CSGReadWriteFunc func)
685 int i;
687 (*func)(&wr_num_cells, sizeof(int), 1);
688 for (i = 0; i < wr_num_cells; i++)
689 (*func)(wr_brfaces[i], sizeof(int), wr_cell[i]->num_render_polys);
691 #if 0
692 (*func)(num_brush_faces, sizeof(int), MAX_FACE_BRUSHES);
693 //for (i = 0; i < MAX_FACE_BRUSHES; i++)
694 // (*func)(brush_faces[i], sizeof(BspPlane), num_brush_faces[i]);
695 (*func)(brush_faces, sizeof(BspPlane), MAX_FACE_BRUSHES * MAX_FACES);
696 #endif
698 (*func)(&csg_num_brushes, sizeof(int), 1);
699 (*func)(num_brush_faces, sizeof(int), csg_num_brushes);
700 // Slower, but saves up to a half-meg on big maps
701 for (i=0; i<csg_num_brushes; i++)
702 (*func)(brush_faces[i], sizeof(BspPlane), num_brush_faces[i]);
704 (*func)(ref_count, sizeof(int), csg_num_brushes);
705 for (i = 0; i < csg_num_brushes; i++)
706 if (ref_count[i] > 0)
707 (*func)(ref_locs[i], sizeof(SurfaceRef), ref_count[i]);
710 void free_csg_internal_database()
712 ClearRegisteredTextures();
713 FreeWR();
714 csg_num_brushes = 0;
715 csg_memory_reset();
718 void load_csg_internal_database(CSGReadWriteFunc func)
720 int i;
722 (*func)(&wr_num_cells, sizeof(int), 1);
723 for (i = 0; i < wr_num_cells; i++)
725 wr_brfaces[i] = Malloc(sizeof(int) * wr_cell[i]->num_render_polys);
726 (*func)(wr_brfaces[i], sizeof(int), wr_cell[i]->num_render_polys);
729 #if 0
730 (*func)(num_brush_faces, sizeof(int), MAX_FACE_BRUSHES);
731 //for (i = 0; i < MAX_FACE_BRUSHES; i++)
732 // (*func)(brush_faces[i], sizeof(BspPlane), num_brush_faces[i]);
733 (*func)(brush_faces, sizeof(BspPlane), MAX_FACE_BRUSHES * MAX_FACES);
734 #endif
736 (*func)(&csg_num_brushes, sizeof(int), 1);
737 (*func)(num_brush_faces, sizeof(int), csg_num_brushes);
739 for (i=0; i<csg_num_brushes; i++)
740 (*func)(brush_faces[i], sizeof(BspPlane), num_brush_faces[i]);
742 (*func)(ref_count, sizeof(int), csg_num_brushes);
743 for (i = 0; i < csg_num_brushes; i++)
744 if (ref_count[i] > 0)
746 ref_locs[i] = Malloc(sizeof(SurfaceRef) * ref_count[i]);
747 (*func)(ref_locs[i], sizeof(SurfaceRef), ref_count[i]);
751 // WARNING: i trash int *map!!!!
752 void remap_csg_database(int* map)
754 int sourceIndex, i, k, highest = 0;
756 if (wr_num_cells == 0)
757 return;
759 for (i = 0; i < wr_num_cells; i++)
760 for (k = 0; k < wr_cell[i]->num_render_polys; k++)
761 if (wr_brfaces[i][k] >= 0)
762 wr_brfaces[i][k] = (map[wr_brfaces[i][k] >> 8] << 8) // brush
763 | (wr_brfaces[i][k]&255); // face
765 for (sourceIndex = 0; sourceIndex < MAX_FACE_BRUSHES; )
766 if ((map[sourceIndex]==-1)||(map[sourceIndex]==sourceIndex))
768 sourceIndex++;
769 if (sourceIndex + 1 > highest && map[sourceIndex] == sourceIndex)
770 highest = sourceIndex + 1;
772 else
773 { // SWAP EVERYTHING... ICKY-POO (tm)
774 SurfaceRef *tmp_sref;
775 int destIndex = map[sourceIndex];
776 int tmp_int;
778 tmp_int=num_brush_faces[destIndex];
779 num_brush_faces[destIndex] = num_brush_faces[sourceIndex];
780 num_brush_faces[sourceIndex] = tmp_int;
782 for (k = 0; k < MAX_FACES; k++)
784 BspPlane tmp = brush_faces[destIndex][k];
785 brush_faces[destIndex][k] = brush_faces[sourceIndex][k];
786 brush_faces[sourceIndex][k] = tmp;
789 tmp_sref=ref_locs[destIndex];
790 ref_locs[destIndex] = ref_locs[sourceIndex];
791 ref_locs[sourceIndex] = tmp_sref;
793 tmp_int=ref_count[destIndex];
794 ref_count[destIndex] = ref_count[sourceIndex];
795 ref_count[sourceIndex] = tmp_int;
797 if (destIndex + 1 > highest)
798 highest = destIndex + 1;
800 tmp_int=map[sourceIndex];
801 map[sourceIndex]=map[destIndex];
802 map[destIndex]=tmp_int; // woo-woo
803 } // now we have completely swapped them
804 // so we can iterate and all should be ok
805 csg_num_brushes = highest;
808 BOOL eq_planes(BspPlane *p, BspPlane *q)
810 if (p->a*q->a + p->b*q->b + p->c*q->c >= VEC_DOT_ONE &&
811 fabs(p->d - q->d) < PLANE_CONST_EPSILON)
812 return TRUE;
813 if (p->a*q->a + p->b*q->b + p->c*q->c <= -VEC_DOT_ONE &&
814 fabs(p->d + q->d) < PLANE_CONST_EPSILON)
815 return TRUE;
816 return FALSE;
819 static bool eq_normals(BspPlane *p, BspPlane *q)
821 if (p->a*q->a + p->b*q->b + p->c*q->c >= VEC_DOT_ONE)
822 return TRUE;
823 if (p->a*q->a + p->b*q->b + p->c*q->c <= -VEC_DOT_ONE)
824 return TRUE;
825 return FALSE;
828 int find_plane(BspPlane *p)
830 int i;
831 for (i=0; i < bsp_num_planes; ++i)
832 if (eq_planes(&all_planes[i], p))
833 return i;
834 return -1;
837 int plane_brush(BspPlane *p)
839 int i;
840 for (i=0; i < bsp_num_planes; ++i)
841 if (eq_planes(&all_planes[i], p))
842 return plane_brushid[i];
843 return -1;
846 #if 0
847 int find_plane_2(BspPlane *p)
849 int i, best;
850 double best_err;
852 i = find_plane(p);
853 if (i >= 0) return i;
855 best_err = 0.01;
856 best = -1;
858 for (i=0; i < bsp_num_planes; ++i) {
859 double err;
860 err = fabs(all_planes[i].a - p->a)
861 + fabs(all_planes[i].b - p->b);
862 + fabs(all_planes[i].c - p->c);
863 + fabs(all_planes[i].d - p->d);
864 if (err < best_err) {
865 best_err = err;
866 best = i;
870 if (best >= 0)
871 mprintf("Warning: converting optimized plane to nearest match.\n");
872 return best;
874 #endif
876 #define PEQ(x,y) (fabs((x)-(y)) < REAL_EPSILON)
878 void show_match_plane(BspPlane *p)
880 int i;
881 for (i=0; i < bsp_num_planes; ++i) {
882 if (PEQ(p->a, all_planes[i].a) &&
883 PEQ(p->b, all_planes[i].b) &&
884 PEQ(p->c, all_planes[i].c) &&
885 PEQ(p->d, all_planes[i].d)) {
886 mprintf("%lg %lg %lg %lg\n", all_planes[i].a,
887 all_planes[i].b, all_planes[i].c, all_planes[i].d);
892 extern int cur_brush;
893 void find_matched_plane(BspPlane *p)
895 int i = find_plane(p);
896 if (i == -1) {
897 i = bsp_num_planes++;
898 if (i == MAX_PLANES)
899 Error(1, "Too many unique planes; increase MAX_PLANES.\n");
900 all_planes[i] = *p;
901 plane_brushid[i] = cur_brush;
902 //mprintf("New plane: %g %g %g %g\n",p->a,p->b,p->c,p->d);
903 } else {
904 // if the constant d is within epsilon of an integer, we should
905 // probably snap it...
906 BspPlane *q = &all_planes[i], old = *p;
907 if (p->a*q->a + p->b*q->b + p->c*q->c > 0)
908 *p = *q;
909 else {
910 p->a = -q->a;
911 p->b = -q->b;
912 p->c = -q->c;
913 p->d = -q->d;
915 #if 0
916 mprintf("Coerced plane %g %g %g %g to %g %g %g %g\n",
917 old.a,old.b,old.c,old.d,p->a,p->b,p->c,p->d);
918 #endif
922 int find_normal(BspPlane *p)
924 int i;
925 for (i=0; i < bsp_num_planes; ++i)
926 if (eq_normals(&all_planes[i], p))
927 return i;
928 return -1;
931 int normal_brush(BspPlane *p)
933 int i;
934 for (i=0; i < bsp_num_planes; ++i)
935 if (eq_normals(&all_planes[i], p))
936 return plane_brushid[i];
937 return -1;
940 extern void find_matched_normal(BspPlane *p)
942 int i = find_normal(p);
943 if (i >= 0) {
944 BspPlane *q = &all_planes[i], old = *p;
945 if (p->a*q->a + p->b*q->b + p->c*q->c > 0) {
946 p->a = q->a;
947 p->b = q->b;
948 p->c = q->c;
949 } else {
950 p->a = -q->a;
951 p->b = -q->b;
952 p->c = -q->c;
954 #if 0
955 mprintf("Coerced normal %g %g %g to %g %g %g\n",
956 old.a,old.b,old.c,p->a,p->b,p->c);
957 #endif
961 void *tree;
963 int brush_count = 0;
965 extern void free_csg_data(void *head);
966 void init_csg_mem(void)
968 static int init=1;
969 if (init) {
970 init = 0;
971 csg_memory_init();
972 CSGfree = free_csg_data;
973 } else {
974 if (tree)
975 BspFreeTree(tree);
976 tree = NULL;
977 csg_memory_reset();
981 extern void *BspReadTree(FILE *f);
982 int csg_clip_count;
984 void init_csg_internal_database(void)
986 init_csg_mem();
988 if (optimize_bsp) {
989 FILE *f = fopen("bsp.out", "r");
990 if (!f) {
991 mprintf("No bsp.out file found.\n");
992 tree = BspMakeTree();
993 } else {
994 tree = BspReadTree(f);
995 if (!tree)
996 tree = BspMakeTree();
997 fclose(f);
999 } else
1000 tree = BspMakeTree();
1002 brush_count = 0;
1003 csg_clip_count = 0;
1006 extern int base_medium;
1008 extern void recursive_recompute_node(BspNode *b);
1009 void default_csg_medium(int medium)
1011 if (medium != base_medium) {
1012 base_medium = medium;
1013 if (tree) {
1014 active = 0;
1015 recursive_recompute_node(tree);
1016 if (active)
1017 Error(1, "Active list non-empty after resetting csg medium.\n");
1022 void cid_register_start(void)
1024 init_csg_mem();
1025 bsp_num_planes = 3;
1028 extern void clear_surface_cache(void);
1029 void free_portal_database(void)
1031 clear_surface_cache();
1032 reset_dynamic_lights();
1033 FreeWR();
1034 wrBspTreeDeallocate();
1037 extern void recompute_node(BspNode *b);
1038 void portalize_csg_internal_database(void)
1040 sAllocLimits alloc_limits;
1041 ulong old_alloc_cap;
1043 AllocGetLimits(&alloc_limits);
1044 old_alloc_cap = alloc_limits.allocCap;
1046 free_portal_database();
1048 AllocSetAllocCap(0x4000000); // 64 meg
1050 mprintf("Inserted %d brushes.\n", brush_count);
1052 // now that we know for sure if we have any clipping
1053 // brushes, recompute our medium
1055 recompute_node((BspNode *) tree);
1057 // merge leaves of the same type together
1058 // by setting userdata fields
1060 leaf_count = 0;
1061 RecodeNode((BspNode *) tree);
1063 mprintf("RAW cell count: %d\n", leaf_count);
1065 if (optimize_bsp) {
1066 FILE *f = fopen("bsp.out", "r");
1067 if (f) {
1068 BspCompareNode((BspNode *) tree, f);
1069 fclose(f);
1073 // now portalize it
1074 PortalizeTree(tree, 1000.0);
1076 // And free it because it is no longer needed
1077 BspFreeTree(tree);
1078 tree = NULL;
1080 AllocGetLimits(&alloc_limits);
1081 AllocSetAllocCap((alloc_limits.totalAlloc > old_alloc_cap) ?
1082 alloc_limits.totalAlloc :
1083 old_alloc_cap);
1084 _heapmin();