2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
6 // $Header: r:/t2repos/thief2/src/csg/csg.c,v 1.95 2000/02/19 12:26:35 toml Exp $
12 #include <stdlib.h> // rand
13 #include <string.h> // memcpy
14 #include <stdio.h> // tree dumping
15 #include <math.h> // fabs, fmod
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
];
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
73 // geometry operations
76 int VertexCode(BspVertex
*v
, BspPlane
*p
)
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
;
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
;
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
102 static void PortalizeSubTree(BspNode
*b
, PortalPolyhedron
*s
, bool merge
)
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
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
119 if (b
->medium
!= NO_MEDIUM
&& !merge
) {
123 virtual_leaf
= FALSE
;
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
);
134 if (s
->poly
|| !optimize_bsp
)
135 CheckPolyhedron(s
, " (inner clipped polyhedron)");
136 if (out
->poly
|| !optimize_bsp
)
137 CheckPolyhedron(out
, " (outer clipped polyhedron)");
139 CheckPolyhedron(s
, " (inner clipped polyhedron)");
140 CheckPolyhedron(out
, " (outer clipped polyhedron)");
143 CheckPolyhedronQuick(s
, " (inner clipped polyhedron)");
144 CheckPolyhedronQuick(out
, " (outer clipped polyhedron)");
149 PortalizeSubTree(b
->inside
, s
, merge
);
151 PortalizeSubTree(b
->outside
, out
, merge
);
154 PortalMergeCells(s
, out
);
160 CheckPolyhedron(s
, " (remerged polyhedron)");
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
) {
179 dummy_node
[num_dummy_nodes
]->user_data
= 0;
180 BspFreeLeaf(dummy_node
[num_dummy_nodes
]);
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
206 SurfaceRef
*ref_locs
[MAX_FACE_BRUSHES
];
207 int ref_count
[MAX_FACE_BRUSHES
];
209 void ClearRegisteredTextures(void)
212 for (i
=0; i
< csg_num_brushes
; ++i
) {
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;
231 if (br
>= MAX_FACE_BRUSHES
)
232 Error(1, "RegisterFace: MAX_FACE_BRUSHES exceeded.\n");
236 ref_locs
[br
] = Realloc(ref_locs
[br
], sizeof(SurfaceRef
) * ref_count
[br
]);
238 ref_locs
[br
] = Malloc(sizeof(SurfaceRef
));
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
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
;
277 clear_surface_cache();
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
],
284 p
, surface
, vertex
);
287 void ReassignTexture(int br
, BOOL texture_only
)
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
;
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
;
309 int n
= (poly
->ph
[0] == ph
);
310 if (poly
->ph
[n
] && IS_MARKED(poly
->ph
[n
]->leaf
)) {
313 poly
->brface
= find_brface_from_poly(poly
);
315 if (poly
->ph
[0] == ph
)
316 poly
= poly
->ph_next
[0];
318 poly
= poly
->ph_next
[1];
319 } while (poly
!= first
);
323 void assign_pinfo(PortalPolyhedron
*ph
)
325 PortalPolygon
*poly
, *first
;
327 // visit all polygons and compute their info
329 first
= poly
= ph
->poly
;
332 int n
= (poly
->ph
[0] == ph
);
334 x
= Malloc(sizeof(*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
;
342 x
->brface
= poly
->brface
;
346 if (poly
->ph
[0] == ph
)
347 poly
= poly
->ph_next
[0];
349 poly
= poly
->ph_next
[1];
350 } while (poly
!= first
);
353 void free_pinfo(PortalPolyhedron
*ph
)
355 PortalPolygon
*poly
, *first
;
357 first
= poly
= ph
->poly
;
364 if (poly
->ph
[0] == ph
)
365 poly
= poly
->ph_next
[0];
367 poly
= poly
->ph_next
[1];
368 } while (poly
!= first
);
371 // used by level optimizer
373 // ick, we have to write it out the old way
374 void write_cell(FILE *f
, PortalCell
*p
)
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
);
420 void write_cell(FILE *f
, PortalCell
*p
)
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
);
439 static void CheckPolyhedronFinal(PortalPolyhedron
*ph
)
441 CheckPolyhedron(ph
, " (after splitting)");
445 // Mark all the nodes which are open
446 static void MarkNode(BspNode
*b
)
449 if (b
->medium
== NO_MEDIUM
) Error(1, "Bad medium in MarkNode.\n");
450 if (b
->medium
== MEDIA_SOLID
)
454 if (((PortalPolyhedron
*) b
->ph
)->poly
)
465 MarkNode(b
->outside
);
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
)
482 traverse_func((PortalPolyhedron
*) b
->ph
);
484 PortalTraverseVirtualLeavesRaw(b
->inside
);
485 PortalTraverseVirtualLeavesRaw(b
->outside
);
489 static void PortalTraverseVirtualLeaves(BspNode
*b
, void (*tfunc
)(PortalPolyhedron
*))
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
)
505 sprintf(buf
, "Portalize: %s", s
);
507 sprintf(buf
, "Port pass %d: %s", pass
, s
);
509 sprintf(buf
, "Port edge-merge pass: %s", s
);
511 sprintf(buf
, "Port edge-merge pass %d: %s", -pass
, s
);
515 int bsp_num_planes
=3; // 3 axis aligned start planes
517 static void PortalizeTree(void *tree
, Real world_size
)
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
534 for (i
=0; i
< bsp_num_planes
; ++i
) {
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
;
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
);
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
);
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
565 Status("Port: analysis");
567 MarkNode((BspNode
*) tree
);
569 PortalTraverseVirtualLeaves((BspNode
*) tree
, CheckPolyhedronFinal
);
572 PortalTraverseVirtualLeaves((BspNode
*) tree
, compute_brface
);
573 stat_edge_merge
= stat_poly_merge
= 0;
578 num_cells
= num_points
= num_poly
= num_portal
= 0;
581 cur_dummy_nodes
= num_dummy_nodes
;
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
;
610 post_edge_merge
= TRUE
;
613 if (pass
> 0) ++pass
; else --pass
;
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
)
648 RecodeNode(b
->inside
);
649 RecodeNode(b
->outside
);
650 if (merge_nodes
&& b
->inside
->medium
== b
->outside
->medium
)
651 b
->medium
= b
->inside
->medium
;
653 b
->medium
= NO_MEDIUM
;
654 if (b
->inside
->medium
!= NO_MEDIUM
)
656 if (b
->outside
->medium
!= NO_MEDIUM
)
662 extern void init_csgmedia(void);
664 void csg_memory_init(void)
666 MakeHep(&Nodes
, sizeof(BspNode
));
668 portalize_mem_init();
671 void csg_memory_reset(void)
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
)
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
);
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
);
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();
718 void load_csg_internal_database(CSGReadWriteFunc func
)
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
);
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
);
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)
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
))
769 if (sourceIndex
+ 1 > highest
&& map
[sourceIndex
] == sourceIndex
)
770 highest
= sourceIndex
+ 1;
773 { // SWAP EVERYTHING... ICKY-POO (tm)
774 SurfaceRef
*tmp_sref
;
775 int destIndex
= map
[sourceIndex
];
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
)
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
)
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
)
823 if (p
->a
*q
->a
+ p
->b
*q
->b
+ p
->c
*q
->c
<= -VEC_DOT_ONE
)
828 int find_plane(BspPlane
*p
)
831 for (i
=0; i
< bsp_num_planes
; ++i
)
832 if (eq_planes(&all_planes
[i
], p
))
837 int plane_brush(BspPlane
*p
)
840 for (i
=0; i
< bsp_num_planes
; ++i
)
841 if (eq_planes(&all_planes
[i
], p
))
842 return plane_brushid
[i
];
847 int find_plane_2(BspPlane
*p
)
853 if (i
>= 0) return i
;
858 for (i
=0; i
< bsp_num_planes
; ++i
) {
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
) {
871 mprintf("Warning: converting optimized plane to nearest match.\n");
876 #define PEQ(x,y) (fabs((x)-(y)) < REAL_EPSILON)
878 void show_match_plane(BspPlane
*p
)
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
);
897 i
= bsp_num_planes
++;
899 Error(1, "Too many unique planes; increase MAX_PLANES.\n");
901 plane_brushid
[i
] = cur_brush
;
902 //mprintf("New plane: %g %g %g %g\n",p->a,p->b,p->c,p->d);
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)
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
);
922 int find_normal(BspPlane
*p
)
925 for (i
=0; i
< bsp_num_planes
; ++i
)
926 if (eq_normals(&all_planes
[i
], p
))
931 int normal_brush(BspPlane
*p
)
934 for (i
=0; i
< bsp_num_planes
; ++i
)
935 if (eq_normals(&all_planes
[i
], p
))
936 return plane_brushid
[i
];
940 extern void find_matched_normal(BspPlane
*p
)
942 int i
= find_normal(p
);
944 BspPlane
*q
= &all_planes
[i
], old
= *p
;
945 if (p
->a
*q
->a
+ p
->b
*q
->b
+ p
->c
*q
->c
> 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
);
965 extern void free_csg_data(void *head
);
966 void init_csg_mem(void)
972 CSGfree
= free_csg_data
;
981 extern void *BspReadTree(FILE *f
);
984 void init_csg_internal_database(void)
989 FILE *f
= fopen("bsp.out", "r");
991 mprintf("No bsp.out file found.\n");
992 tree
= BspMakeTree();
994 tree
= BspReadTree(f
);
996 tree
= BspMakeTree();
1000 tree
= BspMakeTree();
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
;
1015 recursive_recompute_node(tree
);
1017 Error(1, "Active list non-empty after resetting csg medium.\n");
1022 void cid_register_start(void)
1028 extern void clear_surface_cache(void);
1029 void free_portal_database(void)
1031 clear_surface_cache();
1032 reset_dynamic_lights();
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
1061 RecodeNode((BspNode
*) tree
);
1063 mprintf("RAW cell count: %d\n", leaf_count
);
1066 FILE *f
= fopen("bsp.out", "r");
1068 BspCompareNode((BspNode
*) tree
, f
);
1074 PortalizeTree(tree
, 1000.0);
1076 // And free it because it is no longer needed
1080 AllocGetLimits(&alloc_limits
);
1081 AllocSetAllocCap((alloc_limits
.totalAlloc
> old_alloc_cap
) ?
1082 alloc_limits
.totalAlloc
: