1 /* $Id: gameseg.c,v 1.8 2004-05-22 01:06:19 btb Exp $ */
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
17 * Functions moved from segment.c to make editor separable from game.
20 * Revision 1.9 1995/11/08 16:26:04 allender
21 * minor bug fix in find_connected_distance
23 * Revision 1.8 1995/10/12 17:36:55 allender
24 * made trace_segs only recurse 100 times max
26 * Revision 1.7 1995/10/11 18:29:01 allender
27 * removed Int3 from trace_segs
29 * Revision 1.6 1995/10/11 14:13:54 allender
30 * put in stack check code into trace-segs
32 * Revision 1.5 1995/09/23 09:40:25 allender
33 * put in casts in extract_shortpos to try and solve shortpos problem
36 * Revision 1.4 1995/09/20 14:26:50 allender
37 * added flag to swap bytes on extract shortpot
39 * Revision 1.3 1995/08/12 12:01:27 allender
40 * added flag to create_shortpos to swap bytes
42 * Revision 1.2 1995/06/06 10:42:07 allender
43 * made shortpos routines swap bytes when extracting and making shortpos structures
45 * Revision 1.1 1995/05/16 15:25:46 allender
48 * Revision 2.2 1995/03/20 18:15:39 john
49 * Added code to not store the normals in the segment structure.
51 * Revision 2.1 1995/03/08 12:11:39 allender
52 * fix shortpos reading/writing
54 * Revision 2.0 1995/02/27 11:29:21 john
55 * New version 2.0, which has no anonymous unions, builds with
56 * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
58 * Revision 1.78 1995/02/22 13:52:22 allender
59 * remove anonymous unions from object structure
61 * Revision 1.77 1995/02/22 13:24:47 john
62 * Removed the vecmat anonymous unions.
64 * Revision 1.76 1995/02/13 20:35:01 john
67 * Revision 1.75 1995/02/09 13:10:51 mike
68 * remove an annoying mprintf.
70 * Revision 1.74 1995/02/05 17:49:28 rob
71 * Added assert to gameseg.c.
73 * Revision 1.73 1995/02/02 00:49:26 mike
74 * new automap segment-depth functionality.
76 * Revision 1.72 1995/01/16 21:06:51 mike
77 * Move function pick_random_point_in_segment from fireball.c to gameseg.c.
79 * Revision 1.71 1994/12/21 19:54:32 matt
80 * Added error checking
82 * Revision 1.70 1994/12/11 21:34:09 matt
83 * Changed assert() to int3()
85 * Revision 1.69 1994/12/01 21:04:37 matt
86 * Several important changes:
87 * (1) Checking against triangulated sides has been standardized a bit
88 * (2) Code has been added to de-triangulate some sides
89 * (3) BIG ONE: the tolerance for checking a point against a plane has
90 * been drastically relaxed
93 * Revision 1.67 1994/11/27 23:12:21 matt
94 * Made changes for new mprintf calling convention
96 * Revision 1.66 1994/11/26 22:51:40 matt
97 * Removed editor-only fields from segment structure when editor is compiled
98 * out, and padded segment structure to even multiple of 4 bytes.
100 * Revision 1.65 1994/11/22 16:55:38 mike
101 * use memset in place of loop to clear array.
103 * Revision 1.64 1994/11/19 15:20:37 mike
104 * rip out unused code and data
106 * Revision 1.63 1994/11/18 18:31:48 matt
107 * Fixed code again (and maybe for real)
109 * Revision 1.62 1994/11/18 16:54:24 matt
110 * Fixed extract_orient_from_segment()
112 * Revision 1.61 1994/11/17 14:56:50 mike
113 * moved segment validation functions from editor to main.
115 * Revision 1.60 1994/11/16 23:38:53 mike
116 * new improved boss teleportation behavior.
118 * Revision 1.59 1994/10/30 14:12:46 mike
119 * rip out local segments stuff.
121 * Revision 1.58 1994/10/27 10:53:39 matt
122 * Made connectivity error checking put up warning if errors found
124 * Revision 1.57 1994/10/25 21:19:26 mike
127 * Revision 1.56 1994/10/25 11:26:09 mike
128 * *** empty log message ***
130 * Revision 1.55 1994/10/22 22:36:08 matt
131 * Improved error finding routine
133 * Revision 1.54 1994/10/22 18:56:51 matt
134 * Fixed obscure bug in segment trace code
135 * Added error find routine, check_segment_connections()
137 * Revision 1.53 1994/10/17 14:05:19 matt
138 * Don't give recursion assert if doing lighting
140 * Revision 1.52 1994/10/15 19:03:48 mike
141 * Don't do exhaustive search in smooth lighting.
143 * Revision 1.51 1994/10/12 09:46:44 mike
144 * Add debug code for trapping exhaustive searches.
146 * Revision 1.50 1994/10/11 20:50:41 matt
147 * Made find_point_seg() take -1 as segnum, meaning to search all segments
149 * Revision 1.49 1994/10/11 17:40:31 matt
150 * Fixed bug that caused segment trace to only go through sides you can fly through
152 * Revision 1.48 1994/10/10 14:48:16 matt
153 * Fixed mistake that caused odd pauses and occasional int3's
155 * Revision 1.47 1994/10/09 23:50:41 matt
156 * Made find_hitpoint_uv() work with triangulated sides
158 * Revision 1.46 1994/10/08 23:06:52 matt
159 * trace_segs() didn't know about external walls
161 * Revision 1.45 1994/10/07 22:18:57 mike
162 * Put in asserts to trap bad segnums.
164 * Revision 1.44 1994/10/06 14:08:07 matt
165 * Added new function, extract_orient_from_segment()
167 * Revision 1.43 1994/10/04 16:24:11 mike
168 * Set global Connected_segment_distance for debug reasons for aipath.c.
170 * Revision 1.42 1994/10/04 09:18:42 mike
171 * Comment out a variable definition, preventing a warning message.
173 * Revision 1.41 1994/10/03 23:43:42 mike
174 * Put in a warning for overrunning point_segs buffer.
176 * Revision 1.40 1994/10/03 20:55:43 rob
177 * Added velocity to shortpos.
179 * Revision 1.39 1994/09/27 11:46:06 rob
180 * re-fixed that same bug (ugh).
182 * Revision 1.38 1994/09/27 10:10:51 rob
183 * Fixed bug in extract_shortpos (obj_relink added).
185 * Revision 1.37 1994/09/25 23:41:02 matt
186 * Changed the object load & save code to read/write the structure fields one
187 * at a time (rather than the whole structure at once). This mean that the
188 * object structure can be changed without breaking the load/save functions.
189 * As a result of this change, the local_object data can be and has been
190 * incorporated into the object array. Also, timeleft is now a property
191 * of all objects, and the object structure has been otherwise cleaned up.
193 * Revision 1.36 1994/09/22 19:03:05 mike
194 * Add shortpos manipulation functions create_shortpos and extract_shortpos.
196 * Revision 1.35 1994/09/19 21:21:16 mike
197 * Minor optimization to find_connected_distance.
199 * Revision 1.34 1994/09/19 21:05:25 mike
200 * Write function find_connected_distance,
201 * returns distance between two points as travellable through the mine.
203 * Revision 1.33 1994/08/30 15:07:15 matt
204 * Changed find_point_seg() to deal with some infinite recursion problems.
206 * Revision 1.32 1994/08/11 18:58:32 mike
207 * Use ints in place of shorts for optimization.
209 * Revision 1.31 1994/08/04 00:20:09 matt
210 * Cleaned up fvi & physics error handling; put in code to make sure objects
211 * are in correct segment; simplified segment finding for objects and points
213 * Revision 1.30 1994/08/03 16:46:12 mike
216 * Revision 1.29 1994/08/02 20:41:31 matt
217 * Fixed bug in get_side_verts()
219 * Revision 1.28 1994/08/02 19:04:25 matt
220 * Cleaned up vertex list functions
222 * Revision 1.27 1994/08/01 10:39:44 matt
223 * find_new_seg() now will look through any kind of wall but a totally solid one
225 * Revision 1.26 1994/07/28 19:15:59 matt
226 * Fixed yet another bug in get_seg_masks()
236 #include <string.h> // for memset()
249 #include "byteswap.h"
252 static char rcsid[] = "$Id: gameseg.c,v 1.8 2004-05-22 01:06:19 btb Exp $";
255 // How far a point can be from a plane, and still be "in" the plane
256 #define PLANE_DIST_TOLERANCE 250
258 dl_index Dl_indices[MAX_DL_INDICES];
259 delta_light Delta_lights[MAX_DELTA_LIGHTS];
260 int Num_static_lights;
262 // ------------------------------------------------------------------------------------------
263 // Compute the center point of a side of a segment.
264 // The center point is defined to be the average of the 4 points defining the side.
265 void compute_center_point_on_side(vms_vector *vp,segment *sp,int side)
272 vm_vec_add2(vp,&Vertices[sp->verts[Side_to_verts[side][v]]]);
274 vm_vec_scale(vp,F1_0/4);
277 // ------------------------------------------------------------------------------------------
278 // Compute segment center.
279 // The center point is defined to be the average of the 8 points defining the segment.
280 void compute_segment_center(vms_vector *vp,segment *sp)
287 vm_vec_add2(vp,&Vertices[sp->verts[v]]);
289 vm_vec_scale(vp,F1_0/8);
292 // -----------------------------------------------------------------------------
293 // Given two segments, return the side index in the connecting segment which connects to the base segment
294 // Optimized by MK on 4/21/94 because it is a 2% load.
295 int find_connect_side(segment *base_seg, segment *con_seg)
298 short base_seg_num = base_seg - Segments;
299 short *childs = con_seg->children;
301 for (s=0; s<MAX_SIDES_PER_SEGMENT; s++) {
302 if (*childs++ == base_seg_num)
307 // legal to return -1, used in object_move_one(), mk, 06/08/94: Assert(0); // Illegal -- there is no connecting side between these two segments
312 // -----------------------------------------------------------------------------------
313 // Given a side, return the number of faces
314 int get_num_faces(side *sidep)
316 switch (sidep->type) {
325 Error("Illegal type = %i\n", sidep->type);
331 // Fill in array with four absolute point numbers for a given side
332 void get_side_verts(short *vertlist,int segnum,int sidenum)
335 sbyte *sv = Side_to_verts[sidenum];
336 short *vp = Segments[segnum].verts;
339 vertlist[i] = vp[sv[i]];
344 // -----------------------------------------------------------------------------------
345 // Create all vertex lists (1 or 2) for faces on a side.
347 // num_faces number of lists
348 // vertices vertices in all (1 or 2) faces
349 // If there is one face, it has 4 vertices.
350 // If there are two faces, they both have three vertices, so face #0 is stored in vertices 0,1,2,
351 // face #1 is stored in vertices 3,4,5.
352 // Note: these are not absolute vertex numbers, but are relative to the segment
353 // Note: for triagulated sides, the middle vertex of each trianle is the one NOT
354 // adjacent on the diagonal edge
355 void create_all_vertex_lists(int *num_faces, int *vertices, int segnum, int sidenum)
357 side *sidep = &Segments[segnum].sides[sidenum];
358 int *sv = Side_to_verts_int[sidenum];
360 Assert((segnum <= Highest_segment_index) && (segnum >= 0));
361 Assert((sidenum >= 0) && (sidenum < 6));
363 switch (sidep->type) {
384 //IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS()
385 //CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
398 //IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS()
399 //CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
402 Error("Illegal side type (1), type = %i, segment # = %i, side # = %i\n Please report this bug.\n", sidep->type, segnum, sidenum);
409 // -----------------------------------------------------------------------------------
410 // Like create all vertex lists, but returns the vertnums (relative to
411 // the side) for each of the faces that make up the side.
412 // If there is one face, it has 4 vertices.
413 // If there are two faces, they both have three vertices, so face #0 is stored in vertices 0,1,2,
414 // face #1 is stored in vertices 3,4,5.
415 void create_all_vertnum_lists(int *num_faces, int *vertnums, int segnum, int sidenum)
417 side *sidep = &Segments[segnum].sides[sidenum];
419 Assert((segnum <= Highest_segment_index) && (segnum >= 0));
421 switch (sidep->type) {
442 //IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS()
443 //CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
456 //IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS()
457 //CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
460 Error("Illegal side type (2), type = %i, segment # = %i, side # = %i\n Please report this bug.\n", sidep->type, segnum, sidenum);
467 // like create_all_vertex_lists(), but generate absolute point numbers
468 void create_abs_vertex_lists(int *num_faces, int *vertices, int segnum, int sidenum, char *calling_file, int calling_linenum)
470 short *vp = Segments[segnum].verts;
471 side *sidep = &Segments[segnum].sides[sidenum];
472 int *sv = Side_to_verts_int[sidenum];
474 Assert((segnum <= Highest_segment_index) && (segnum >= 0));
476 switch (sidep->type) {
479 vertices[0] = vp[sv[0]];
480 vertices[1] = vp[sv[1]];
481 vertices[2] = vp[sv[2]];
482 vertices[3] = vp[sv[3]];
489 vertices[0] = vp[sv[0]];
490 vertices[1] = vp[sv[1]];
491 vertices[2] = vp[sv[2]];
493 vertices[3] = vp[sv[2]];
494 vertices[4] = vp[sv[3]];
495 vertices[5] = vp[sv[0]];
497 //IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS(),
498 //CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
503 vertices[0] = vp[sv[3]];
504 vertices[1] = vp[sv[0]];
505 vertices[2] = vp[sv[1]];
507 vertices[3] = vp[sv[1]];
508 vertices[4] = vp[sv[2]];
509 vertices[5] = vp[sv[3]];
511 //IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS()
512 //CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
515 Error("Illegal side type (3), type = %i, segment # = %i, side # = %i caller:%s:%i\n Please report this bug.\n", sidep->type, segnum, sidenum, calling_file, calling_linenum);
522 //returns 3 different bitmasks with info telling if this sphere is in
523 //this segment. See segmasks structure for info on fields
524 segmasks get_seg_masks(vms_vector *checkp, int segnum, fix rad, char *calling_file, int calling_linenum)
526 int sn,facebit,sidebit;
533 Error("segnum == -1 in get_seg_masks()");
535 Assert((segnum <= Highest_segment_index) && (segnum >= 0));
537 seg = &Segments[segnum];
539 //check point against each side of segment. return bitmask
541 masks.sidemask = masks.facemask = masks.centermask = 0;
543 for (sn=0,facebit=sidebit=1;sn<6;sn++,sidebit<<=1) {
545 side *s = &seg->sides[sn];
550 // Get number of faces on this side, and at vertex_list, store vertices.
551 // If one face, then vertex_list indicates a quadrilateral.
552 // If two faces, then 0,1,2 define one triangle, 3,4,5 define the second.
553 create_abs_vertex_lists(&num_faces, vertex_list, segnum, sn, calling_file, calling_linenum);
555 //ok...this is important. If a side has 2 faces, we need to know if
556 //those faces form a concave or convex side. If the side pokes out,
557 //then a point is on the back of the side if it is behind BOTH faces,
558 //but if the side pokes in, a point is on the back if behind EITHER face.
562 int side_count,center_count;
564 vms_vector normals[2];
567 vertnum = min(vertex_list[0],vertex_list[2]);
570 get_side_normals(seg, sn, &normals[0], &normals[1] );
573 if (vertex_list[4] < vertex_list[1])
575 dist = vm_dist_to_plane(&Vertices[vertex_list[4]],&normals[0],&Vertices[vertnum]);
577 dist = vm_dist_to_plane(&Vertices[vertex_list[4]],&s->normals[0],&Vertices[vertnum]);
581 dist = vm_dist_to_plane(&Vertices[vertex_list[1]],&normals[1],&Vertices[vertnum]);
583 dist = vm_dist_to_plane(&Vertices[vertex_list[1]],&s->normals[1],&Vertices[vertnum]);
586 side_pokes_out = (dist > PLANE_DIST_TOLERANCE);
588 side_count = center_count = 0;
590 for (fn=0;fn<2;fn++,facebit<<=1) {
593 dist = vm_dist_to_plane(checkp, &normals[fn], &Vertices[vertnum]);
595 dist = vm_dist_to_plane(checkp, &s->normals[fn], &Vertices[vertnum]);
598 if (dist < -PLANE_DIST_TOLERANCE) //in front of face
601 if (dist-rad < -PLANE_DIST_TOLERANCE) {
602 masks.facemask |= facebit;
607 if (!side_pokes_out) { //must be behind both faces
610 masks.sidemask |= sidebit;
613 masks.centermask |= sidebit;
616 else { //must be behind at least one face
619 masks.sidemask |= sidebit;
622 masks.centermask |= sidebit;
628 else { //only one face on this side
635 //use lowest point number
637 vertnum = vertex_list[0];
639 if (vertex_list[i] < vertnum)
640 vertnum = vertex_list[i];
643 get_side_normal(seg, sn, 0, &normal );
644 dist = vm_dist_to_plane(checkp, &normal, &Vertices[vertnum]);
646 dist = vm_dist_to_plane(checkp, &s->normals[0], &Vertices[vertnum]);
650 if (dist < -PLANE_DIST_TOLERANCE)
651 masks.centermask |= sidebit;
653 if (dist-rad < -PLANE_DIST_TOLERANCE) {
654 masks.facemask |= facebit;
655 masks.sidemask |= sidebit;
667 //this was converted from get_seg_masks()...it fills in an array of 6
668 //elements for the distace behind each side, or zero if not behind
669 //only gets centermask, and assumes zero rad
670 ubyte get_side_dists(vms_vector *checkp,int segnum,fix *side_dists)
672 int sn,facebit,sidebit;
678 Assert((segnum <= Highest_segment_index) && (segnum >= 0));
681 Error("segnum == -1 in get_seg_dists()");
683 seg = &Segments[segnum];
685 //check point against each side of segment. return bitmask
689 for (sn=0,facebit=sidebit=1;sn<6;sn++,sidebit<<=1) {
691 side *s = &seg->sides[sn];
698 // Get number of faces on this side, and at vertex_list, store vertices.
699 // If one face, then vertex_list indicates a quadrilateral.
700 // If two faces, then 0,1,2 define one triangle, 3,4,5 define the second.
701 create_abs_vertex_lists(&num_faces, vertex_list, segnum, sn, __FILE__, __LINE__);
703 //ok...this is important. If a side has 2 faces, we need to know if
704 //those faces form a concave or convex side. If the side pokes out,
705 //then a point is on the back of the side if it is behind BOTH faces,
706 //but if the side pokes in, a point is on the back if behind EITHER face.
713 vms_vector normals[2];
716 vertnum = min(vertex_list[0],vertex_list[2]);
719 get_side_normals(seg, sn, &normals[0], &normals[1] );
722 if (vertex_list[4] < vertex_list[1])
724 dist = vm_dist_to_plane(&Vertices[vertex_list[4]],&normals[0],&Vertices[vertnum]);
726 dist = vm_dist_to_plane(&Vertices[vertex_list[4]],&s->normals[0],&Vertices[vertnum]);
730 dist = vm_dist_to_plane(&Vertices[vertex_list[1]],&normals[1],&Vertices[vertnum]);
732 dist = vm_dist_to_plane(&Vertices[vertex_list[1]],&s->normals[1],&Vertices[vertnum]);
735 side_pokes_out = (dist > PLANE_DIST_TOLERANCE);
739 for (fn=0;fn<2;fn++,facebit<<=1) {
742 dist = vm_dist_to_plane(checkp, &normals[fn], &Vertices[vertnum]);
744 dist = vm_dist_to_plane(checkp, &s->normals[fn], &Vertices[vertnum]);
747 if (dist < -PLANE_DIST_TOLERANCE) { //in front of face
749 side_dists[sn] += dist;
754 if (!side_pokes_out) { //must be behind both faces
756 if (center_count==2) {
758 side_dists[sn] /= 2; //get average
763 else { //must be behind at least one face
768 side_dists[sn] /= 2; //get average
775 else { //only one face on this side
783 //use lowest point number
785 vertnum = vertex_list[0];
787 if (vertex_list[i] < vertnum)
788 vertnum = vertex_list[i];
791 get_side_normal(seg, sn, 0, &normal );
792 dist = vm_dist_to_plane(checkp, &normal, &Vertices[vertnum]);
794 dist = vm_dist_to_plane(checkp, &s->normals[0], &Vertices[vertnum]);
797 if (dist < -PLANE_DIST_TOLERANCE) {
799 side_dists[sn] = dist;
813 //returns true if errors detected
814 int check_norms(int segnum,int sidenum,int facenum,int csegnum,int csidenum,int cfacenum)
818 n0 = &Segments[segnum].sides[sidenum].normals[facenum];
819 n1 = &Segments[csegnum].sides[csidenum].normals[cfacenum];
821 if (n0->x != -n1->x || n0->y != -n1->y || n0->z != -n1->z) {
822 mprintf((0,"Seg %x, side %d, norm %d doesn't match seg %x, side %d, norm %d:\n"
824 " %8x %8x %8x (negated)\n",
825 segnum,sidenum,facenum,csegnum,csidenum,cfacenum,
826 n0->x,n0->y,n0->z,-n1->x,-n1->y,-n1->z));
833 //heavy-duty error checking
834 int check_segment_connections(void)
839 for (segnum=0;segnum<=Highest_segment_index;segnum++) {
842 seg = &Segments[segnum];
844 for (sidenum=0;sidenum<6;sidenum++) {
848 int num_faces,csegnum,csidenum,con_num_faces;
849 int vertex_list[6],con_vertex_list[6];
851 s = &seg->sides[sidenum];
853 create_abs_vertex_lists(&num_faces, vertex_list, segnum, sidenum, __FILE__, __LINE__);
855 csegnum = seg->children[sidenum];
858 cseg = &Segments[csegnum];
859 csidenum = find_connect_side(seg,cseg);
861 if (csidenum == -1) {
862 mprintf((0,"Could not find connected side for seg %x back to seg %x, side %d\n",csegnum,segnum,sidenum));
867 cs = &cseg->sides[csidenum];
869 create_abs_vertex_lists(&con_num_faces, con_vertex_list, csegnum, csidenum, __FILE__, __LINE__);
871 if (con_num_faces != num_faces) {
872 mprintf((0,"Seg %x, side %d: num_faces (%d) mismatch with seg %x, side %d (%d)\n",segnum,sidenum,num_faces,csegnum,csidenum,con_num_faces));
876 if (num_faces == 1) {
879 for (t=0;t<4 && con_vertex_list[t]!=vertex_list[0];t++);
882 vertex_list[0] != con_vertex_list[t] ||
883 vertex_list[1] != con_vertex_list[(t+3)%4] ||
884 vertex_list[2] != con_vertex_list[(t+2)%4] ||
885 vertex_list[3] != con_vertex_list[(t+1)%4]) {
886 mprintf((0,"Seg %x, side %d: vertex list mismatch with seg %x, side %d\n"
889 segnum,sidenum,csegnum,csidenum,
890 vertex_list[0],vertex_list[1],vertex_list[2],vertex_list[3],
891 con_vertex_list[0],con_vertex_list[1],con_vertex_list[2],con_vertex_list[3]));
895 errors |= check_norms(segnum,sidenum,0,csegnum,csidenum,0);
900 if (vertex_list[1] == con_vertex_list[1]) {
902 if (vertex_list[4] != con_vertex_list[4] ||
903 vertex_list[0] != con_vertex_list[2] ||
904 vertex_list[2] != con_vertex_list[0] ||
905 vertex_list[3] != con_vertex_list[5] ||
906 vertex_list[5] != con_vertex_list[3]) {
907 mprintf((0,"Seg %x, side %d: vertex list mismatch with seg %x, side %d\n"
908 " %x %x %x %x %x %x\n"
909 " %x %x %x %x %x %x\n",
910 segnum,sidenum,csegnum,csidenum,
911 vertex_list[0],vertex_list[1],vertex_list[2],vertex_list[3],vertex_list[4],vertex_list[5],
912 con_vertex_list[0],con_vertex_list[1],con_vertex_list[2],con_vertex_list[3],con_vertex_list[4],con_vertex_list[5]));
913 mprintf((0,"Changing seg:side %4i:%i from %i to %i\n", csegnum, csidenum, Segments[csegnum].sides[csidenum].type, 5-Segments[csegnum].sides[csidenum].type));
914 Segments[csegnum].sides[csidenum].type = 5-Segments[csegnum].sides[csidenum].type;
916 errors |= check_norms(segnum,sidenum,0,csegnum,csidenum,0);
917 errors |= check_norms(segnum,sidenum,1,csegnum,csidenum,1);
922 if (vertex_list[1] != con_vertex_list[4] ||
923 vertex_list[4] != con_vertex_list[1] ||
924 vertex_list[0] != con_vertex_list[5] ||
925 vertex_list[5] != con_vertex_list[0] ||
926 vertex_list[2] != con_vertex_list[3] ||
927 vertex_list[3] != con_vertex_list[2]) {
928 mprintf((0,"Seg %x, side %d: vertex list mismatch with seg %x, side %d\n"
929 " %x %x %x %x %x %x\n"
930 " %x %x %x %x %x %x\n",
931 segnum,sidenum,csegnum,csidenum,
932 vertex_list[0],vertex_list[1],vertex_list[2],vertex_list[3],vertex_list[4],vertex_list[5],
933 con_vertex_list[0],con_vertex_list[1],con_vertex_list[2],con_vertex_list[3],con_vertex_list[4],vertex_list[5]));
934 mprintf((0,"Changing seg:side %4i:%i from %i to %i\n", csegnum, csidenum, Segments[csegnum].sides[csidenum].type, 5-Segments[csegnum].sides[csidenum].type));
935 Segments[csegnum].sides[csidenum].type = 5-Segments[csegnum].sides[csidenum].type;
937 errors |= check_norms(segnum,sidenum,0,csegnum,csidenum,1);
938 errors |= check_norms(segnum,sidenum,1,csegnum,csidenum,0);
946 // mprintf((0,"\n DONE \n"));
954 // Used to become a constant based on editor, but I wanted to be able to set
955 // this for omega blob find_point_seg calls. Would be better to pass a paremeter
956 // to the routine...--MK, 01/17/96
957 int Doing_lighting_hack_flag=0;
959 //figure out what seg the given point is in, tracing through segments
960 //returns segment number, or -1 if can't find segment
961 int trace_segs(vms_vector *p0,int oldsegnum)
967 int sidenum, bit, check, biggest_side;
968 static int trace_segs_callcount = 0; // how many times we called ourselves recursively
969 static ubyte visited [MAX_SEGMENTS];
971 Assert((oldsegnum <= Highest_segment_index) && (oldsegnum >= 0));
973 if (trace_segs_callcount >= Num_segments) {
974 con_printf (CON_DEBUG, "trace_segs: Segment not found\n");
975 mprintf ((0,"trace_segs (gameseg.c): Error: infinite loop\n"));
978 if (trace_segs_callcount == 0)
979 memset (visited, 0, sizeof (visited));
980 if (visited [oldsegnum])
982 visited [oldsegnum] = 1;
983 trace_segs_callcount++;
985 centermask = get_side_dists(p0,oldsegnum,side_dists); //check old segment
986 if (centermask == 0) { //we're in the old segment
987 trace_segs_callcount--;
988 return oldsegnum; //..say so
991 seg = &Segments[oldsegnum];
994 for (sidenum = 0, bit = 1; sidenum < 6; sidenum++, bit <<= 1)
995 if ((centermask & bit) && (seg->children[sidenum] > -1)
996 && side_dists[sidenum] < biggest_val) {
997 biggest_val = side_dists[sidenum];
998 biggest_side = sidenum;
1001 if (biggest_side == -1)
1004 side_dists[biggest_side] = 0;
1005 check = trace_segs(p0,seg->children[biggest_side]); //trace into adjacent segment
1006 if (check >= 0) //we've found a segment
1009 trace_segs_callcount--;
1010 return -1; //we haven't found a segment
1014 int Exhaustive_count=0, Exhaustive_failed_count=0;
1016 //Tries to find a segment for a point, in the following way:
1017 // 1. Check the given segment
1018 // 2. Recursively trace through attached segments
1019 // 3. Check all the segmentns
1020 //Returns segnum if found, or -1
1021 int find_point_seg(vms_vector *p,int segnum)
1025 //allow segnum==-1, meaning we have no idea what segment point is in
1026 Assert((segnum <= Highest_segment_index) && (segnum >= -1));
1029 newseg = trace_segs(p,segnum);
1031 if (newseg != -1) //we found a segment!
1035 //couldn't find via attached segs, so search all segs
1038 // This Doing_lighting_hack_flag thing added by mk because the hundreds of scrolling messages were
1039 // slowing down lighting, and in about 98% of cases, it would just return -1 anyway.
1040 // Matt: This really should be fixed, though. We're probably screwing up our lighting in a few places.
1041 if (!Doing_lighting_hack_flag) {
1042 mprintf((1,"Warning: doing exhaustive search to find point segment (%i times)\n", ++Exhaustive_count));
1044 for (newseg=0;newseg <= Highest_segment_index;newseg++)
1045 if (get_seg_masks(p, newseg, 0, __FILE__, __LINE__).centermask == 0)
1048 mprintf((1,"Warning: could not find point segment (%i times)\n", ++Exhaustive_failed_count));
1050 return -1; //no segment found
1056 //--repair-- // ------------------------------------------------------------------------------
1057 //--repair-- void clsd_repair_center(int segnum)
1059 //--repair-- int sidenum;
1061 //--repair-- // --- Set repair center bit for all repair center segments.
1062 //--repair-- if (Segments[segnum].special == SEGMENT_IS_REPAIRCEN) {
1063 //--repair-- Lsegments[segnum].special_type |= SS_REPAIR_CENTER;
1064 //--repair-- Lsegments[segnum].special_segment = segnum;
1067 //--repair-- // --- Set repair center bit for all segments adjacent to a repair center.
1068 //--repair-- for (sidenum=0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++) {
1069 //--repair-- int s = Segments[segnum].children[sidenum];
1071 //--repair-- if ( (s != -1) && (Segments[s].special==SEGMENT_IS_REPAIRCEN) ) {
1072 //--repair-- Lsegments[segnum].special_type |= SS_REPAIR_CENTER;
1073 //--repair-- Lsegments[segnum].special_segment = s;
1078 //--repair-- // ------------------------------------------------------------------------------
1079 //--repair-- // --- Set destination points for all Materialization centers.
1080 //--repair-- void clsd_materialization_center(int segnum)
1082 //--repair-- if (Segments[segnum].special == SEGMENT_IS_ROBOTMAKER) {
1087 //--repair-- int Lsegment_highest_segment_index, Lsegment_highest_vertex_index;
1089 //--repair-- // ------------------------------------------------------------------------------
1090 //--repair-- // Create data specific to mine which doesn't get written to disk.
1091 //--repair-- // Highest_segment_index and Highest_object_index must be valid.
1092 //--repair-- // 07/21: set repair center bit
1093 //--repair-- void create_local_segment_data(void)
1095 //--repair-- int segnum;
1097 //--repair-- // --- Initialize all Lsegments.
1098 //--repair-- for (segnum=0; segnum <= Highest_segment_index; segnum++) {
1099 //--repair-- Lsegments[segnum].special_type = 0;
1100 //--repair-- Lsegments[segnum].special_segment = -1;
1103 //--repair-- for (segnum=0; segnum <= Highest_segment_index; segnum++) {
1105 //--repair-- clsd_repair_center(segnum);
1106 //--repair-- clsd_materialization_center(segnum);
1110 //--repair-- // Set check variables.
1111 //--repair-- // In main game loop, make sure these are valid, else Lsegments is not valid.
1112 //--repair-- Lsegment_highest_segment_index = Highest_segment_index;
1113 //--repair-- Lsegment_highest_vertex_index = Highest_vertex_index;
1116 //--repair-- // ------------------------------------------------------------------------------------------
1117 //--repair-- // Sort of makes sure create_local_segment_data has been called for the currently executing mine.
1118 //--repair-- // It is not failsafe, as you will see if you look at the code.
1119 //--repair-- // Returns 1 if Lsegments appears valid, 0 if not.
1120 //--repair-- int check_lsegments_validity(void)
1122 //--repair-- return ((Lsegment_highest_segment_index == Highest_segment_index) && (Lsegment_highest_vertex_index == Highest_vertex_index));
1125 #define MAX_LOC_POINT_SEGS 64
1127 int Connected_segment_distance;
1129 #define MIN_CACHE_FCD_DIST (F1_0*80) // Must be this far apart for cache lookup to succeed. Recognizes small changes in distance matter at small distances.
1130 #define MAX_FCD_CACHE 8
1133 int seg0, seg1, csd;
1138 fcd_data Fcd_cache[MAX_FCD_CACHE];
1139 fix Last_fcd_flush_time;
1141 // ----------------------------------------------------------------------------------------------------------
1142 void flush_fcd_cache(void)
1148 for (i=0; i<MAX_FCD_CACHE; i++)
1149 Fcd_cache[i].seg0 = -1;
1152 // ----------------------------------------------------------------------------------------------------------
1153 void add_to_fcd_cache(int seg0, int seg1, int depth, fix dist)
1155 if (dist > MIN_CACHE_FCD_DIST) {
1156 Fcd_cache[Fcd_index].seg0 = seg0;
1157 Fcd_cache[Fcd_index].seg1 = seg1;
1158 Fcd_cache[Fcd_index].csd = depth;
1159 Fcd_cache[Fcd_index].dist = dist;
1163 if (Fcd_index >= MAX_FCD_CACHE)
1166 // -- mprintf((0, "Adding seg0=%i, seg1=%i to cache.\n", seg0, seg1));
1168 // If it's in the cache, remove it.
1171 for (i=0; i<MAX_FCD_CACHE; i++)
1172 if (Fcd_cache[i].seg0 == seg0)
1173 if (Fcd_cache[i].seg1 == seg1) {
1174 Fcd_cache[Fcd_index].seg0 = -1;
1181 // ----------------------------------------------------------------------------------------------------------
1182 // Determine whether seg0 and seg1 are reachable in a way that allows sound to pass.
1183 // Search up to a maximum depth of max_depth.
1184 // Return the distance.
1185 fix find_connected_distance(vms_vector *p0, int seg0, vms_vector *p1, int seg1, int max_depth, int wid_flag)
1189 int qtail = 0, qhead = 0;
1191 sbyte visited[MAX_SEGMENTS];
1192 seg_seg seg_queue[MAX_SEGMENTS];
1193 short depth[MAX_SEGMENTS];
1196 point_seg point_segs[MAX_LOC_POINT_SEGS];
1199 // If > this, will overrun point_segs buffer
1201 if (max_depth == -1) max_depth = 200;
1204 if (max_depth > MAX_LOC_POINT_SEGS-2) {
1205 mprintf((1, "Warning: In find_connected_distance, max_depth = %i, limited to %i\n", max_depth, MAX_LOC_POINT_SEGS-2));
1206 max_depth = MAX_LOC_POINT_SEGS-2;
1210 Connected_segment_distance = 0;
1211 return vm_vec_dist_quick(p0, p1);
1214 if ((conn_side = find_connect_side(&Segments[seg0], &Segments[seg1])) != -1) {
1215 if (WALL_IS_DOORWAY(&Segments[seg1], conn_side) & wid_flag) {
1216 Connected_segment_distance = 1;
1217 //mprintf((0, "\n"));
1218 return vm_vec_dist_quick(p0, p1);
1223 // Periodically flush cache.
1224 if ((GameTime - Last_fcd_flush_time > F1_0*2) || (GameTime < Last_fcd_flush_time)) {
1226 Last_fcd_flush_time = GameTime;
1229 // Can't quickly get distance, so see if in Fcd_cache.
1230 for (i=0; i<MAX_FCD_CACHE; i++)
1231 if ((Fcd_cache[i].seg0 == seg0) && (Fcd_cache[i].seg1 == seg1)) {
1232 Connected_segment_distance = Fcd_cache[i].csd;
1233 // -- mprintf((0, "In cache, seg0=%i, seg1=%i. Returning.\n", seg0, seg1));
1234 return Fcd_cache[i].dist;
1239 memset(visited, 0, Highest_segment_index+1);
1240 memset(depth, 0, sizeof(depth[0]) * (Highest_segment_index+1));
1243 visited[cur_seg] = 1;
1246 while (cur_seg != seg1) {
1247 segment *segp = &Segments[cur_seg];
1249 for (sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++) {
1253 if (WALL_IS_DOORWAY(segp, snum) & wid_flag) {
1254 int this_seg = segp->children[snum];
1256 if (!visited[this_seg]) {
1257 seg_queue[qtail].start = cur_seg;
1258 seg_queue[qtail].end = this_seg;
1259 visited[this_seg] = 1;
1260 depth[qtail++] = cur_depth+1;
1261 if (max_depth != -1) {
1262 if (depth[qtail-1] == max_depth) {
1263 Connected_segment_distance = 1000;
1264 add_to_fcd_cache(seg0, seg1, Connected_segment_distance, F1_0*1000);
1267 } else if (this_seg == seg1) {
1273 } // for (sidenum...
1275 if (qhead >= qtail) {
1276 Connected_segment_distance = 1000;
1277 add_to_fcd_cache(seg0, seg1, Connected_segment_distance, F1_0*1000);
1281 cur_seg = seg_queue[qhead].end;
1282 cur_depth = depth[qhead];
1286 } // while (cur_seg ...
1288 // Set qtail to the segment which ends at the goal.
1289 while (seg_queue[--qtail].end != seg1)
1291 Connected_segment_distance = 1000;
1292 add_to_fcd_cache(seg0, seg1, Connected_segment_distance, F1_0*1000);
1296 while (qtail >= 0) {
1297 int parent_seg, this_seg;
1299 this_seg = seg_queue[qtail].end;
1300 parent_seg = seg_queue[qtail].start;
1301 point_segs[num_points].segnum = this_seg;
1302 compute_segment_center(&point_segs[num_points].point,&Segments[this_seg]);
1305 if (parent_seg == seg0)
1308 while (seg_queue[--qtail].end != parent_seg)
1312 point_segs[num_points].segnum = seg0;
1313 compute_segment_center(&point_segs[num_points].point,&Segments[seg0]);
1316 if (num_points == 1) {
1317 Connected_segment_distance = num_points;
1318 return vm_vec_dist_quick(p0, p1);
1320 dist = vm_vec_dist_quick(p1, &point_segs[1].point);
1321 dist += vm_vec_dist_quick(p0, &point_segs[num_points-2].point);
1323 for (i=1; i<num_points-2; i++) {
1325 ndist = vm_vec_dist_quick(&point_segs[i].point, &point_segs[i+1].point);
1331 Connected_segment_distance = num_points;
1332 add_to_fcd_cache(seg0, seg1, num_points, dist);
1338 sbyte convert_to_byte(fix f)
1340 if (f >= 0x00010000)
1342 else if (f <= -0x00010000)
1345 return f >> MATRIX_PRECISION;
1348 #define VEL_PRECISION 12
1350 // Create a shortpos struct from an object.
1351 // Extract the matrix into byte values.
1352 // Create a position relative to vertex 0 with 1/256 normal "fix" precision.
1353 // Stuff segment in a short.
1354 void create_shortpos(shortpos *spp, object *objp, int swap_bytes)
1361 *sp++ = convert_to_byte(objp->orient.rvec.x);
1362 *sp++ = convert_to_byte(objp->orient.uvec.x);
1363 *sp++ = convert_to_byte(objp->orient.fvec.x);
1364 *sp++ = convert_to_byte(objp->orient.rvec.y);
1365 *sp++ = convert_to_byte(objp->orient.uvec.y);
1366 *sp++ = convert_to_byte(objp->orient.fvec.y);
1367 *sp++ = convert_to_byte(objp->orient.rvec.z);
1368 *sp++ = convert_to_byte(objp->orient.uvec.z);
1369 *sp++ = convert_to_byte(objp->orient.fvec.z);
1371 spp->xo = (objp->pos.x - Vertices[Segments[objp->segnum].verts[0]].x) >> RELPOS_PRECISION;
1372 spp->yo = (objp->pos.y - Vertices[Segments[objp->segnum].verts[0]].y) >> RELPOS_PRECISION;
1373 spp->zo = (objp->pos.z - Vertices[Segments[objp->segnum].verts[0]].z) >> RELPOS_PRECISION;
1375 spp->segment = objp->segnum;
1377 spp->velx = (objp->mtype.phys_info.velocity.x) >> VEL_PRECISION;
1378 spp->vely = (objp->mtype.phys_info.velocity.y) >> VEL_PRECISION;
1379 spp->velz = (objp->mtype.phys_info.velocity.z) >> VEL_PRECISION;
1381 // swap the short values for the big-endian machines.
1384 spp->xo = INTEL_SHORT(spp->xo);
1385 spp->yo = INTEL_SHORT(spp->yo);
1386 spp->zo = INTEL_SHORT(spp->zo);
1387 spp->segment = INTEL_SHORT(spp->segment);
1388 spp->velx = INTEL_SHORT(spp->velx);
1389 spp->vely = INTEL_SHORT(spp->vely);
1390 spp->velz = INTEL_SHORT(spp->velz);
1392 // mprintf((0, "Matrix: %08x %08x %08x %08x %08x %08x\n", objp->orient.m1,objp->orient.m2,objp->orient.m3,
1393 // spp->bytemat[0] << MATRIX_PRECISION,spp->bytemat[1] << MATRIX_PRECISION,spp->bytemat[2] << MATRIX_PRECISION));
1395 // mprintf((0, " %08x %08x %08x %08x %08x %08x\n", objp->orient.m4,objp->orient.m5,objp->orient.m6,
1396 // spp->bytemat[3] << MATRIX_PRECISION,spp->bytemat[4] << MATRIX_PRECISION,spp->bytemat[5] << MATRIX_PRECISION));
1398 // mprintf((0, " %08x %08x %08x %08x %08x %08x\n", objp->orient.m7,objp->orient.m8,objp->orient.m9,
1399 // spp->bytemat[6] << MATRIX_PRECISION,spp->bytemat[7] << MATRIX_PRECISION,spp->bytemat[8] << MATRIX_PRECISION));
1401 // mprintf((0, "Positn: %08x %08x %08x %08x %08x %08x\n", objp->pos.x, objp->pos.y, objp->pos.z,
1402 // (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x,
1403 // (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y,
1404 // (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z));
1405 // mprintf((0, "Segment: %3i %3i\n", objp->segnum, spp->segment));
1409 void extract_shortpos(object *objp, shortpos *spp, int swap_bytes)
1416 objp->orient.rvec.x = *sp++ << MATRIX_PRECISION;
1417 objp->orient.uvec.x = *sp++ << MATRIX_PRECISION;
1418 objp->orient.fvec.x = *sp++ << MATRIX_PRECISION;
1419 objp->orient.rvec.y = *sp++ << MATRIX_PRECISION;
1420 objp->orient.uvec.y = *sp++ << MATRIX_PRECISION;
1421 objp->orient.fvec.y = *sp++ << MATRIX_PRECISION;
1422 objp->orient.rvec.z = *sp++ << MATRIX_PRECISION;
1423 objp->orient.uvec.z = *sp++ << MATRIX_PRECISION;
1424 objp->orient.fvec.z = *sp++ << MATRIX_PRECISION;
1427 spp->xo = INTEL_SHORT(spp->xo);
1428 spp->yo = INTEL_SHORT(spp->yo);
1429 spp->zo = INTEL_SHORT(spp->zo);
1430 spp->segment = INTEL_SHORT(spp->segment);
1431 spp->velx = INTEL_SHORT(spp->velx);
1432 spp->vely = INTEL_SHORT(spp->vely);
1433 spp->velz = INTEL_SHORT(spp->velz);
1436 segnum = spp->segment;
1438 Assert((segnum >= 0) && (segnum <= Highest_segment_index));
1440 objp->pos.x = (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x;
1441 objp->pos.y = (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y;
1442 objp->pos.z = (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z;
1444 objp->mtype.phys_info.velocity.x = (spp->velx << VEL_PRECISION);
1445 objp->mtype.phys_info.velocity.y = (spp->vely << VEL_PRECISION);
1446 objp->mtype.phys_info.velocity.z = (spp->velz << VEL_PRECISION);
1448 obj_relink(objp-Objects, segnum);
1450 // mprintf((0, "Matrix: %08x %08x %08x %08x %08x %08x\n", objp->orient.m1,objp->orient.m2,objp->orient.m3,
1451 // spp->bytemat[0],spp->bytemat[1],spp->bytemat[2]));
1453 // mprintf((0, " %08x %08x %08x %08x %08x %08x\n", objp->orient.m4,objp->orient.m5,objp->orient.m6,
1454 // spp->bytemat[3],spp->bytemat[4],spp->bytemat[5]));
1456 // mprintf((0, " %08x %08x %08x %08x %08x %08x\n", objp->orient.m7,objp->orient.m8,objp->orient.m9,
1457 // spp->bytemat[6],spp->bytemat[7],spp->bytemat[8]));
1459 // mprintf((0, "Positn: %08x %08x %08x %08x %08x %08x\n", objp->pos.x, objp->pos.y, objp->pos.z,
1460 // (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x, (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y, (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z));
1461 // mprintf((0, "Segment: %3i %3i\n", objp->segnum, spp->segment));
1465 //--unused-- void test_shortpos(void)
1467 //--unused-- shortpos spp;
1469 //--unused-- create_shortpos(&spp, &Objects[0]);
1470 //--unused-- extract_shortpos(&Objects[0], &spp);
1474 // -----------------------------------------------------------------------------
1475 // Segment validation functions.
1476 // Moved from editor to game so we can compute surface normals at load time.
1477 // -------------------------------------------------------------------------------
1479 // ------------------------------------------------------------------------------------------
1480 // Extract a vector from a segment. The vector goes from the start face to the end face.
1481 // The point on each face is the average of the four points forming the face.
1482 void extract_vector_from_segment(segment *sp, vms_vector *vp, int start, int end)
1490 for (i=0; i<4; i++) {
1491 vm_vec_add2(&vs,&Vertices[sp->verts[Side_to_verts[start][i]]]);
1492 vm_vec_add2(&ve,&Vertices[sp->verts[Side_to_verts[end][i]]]);
1495 vm_vec_sub(vp,&ve,&vs);
1496 vm_vec_scale(vp,F1_0/4);
1500 //create a matrix that describes the orientation of the given segment
1501 void extract_orient_from_segment(vms_matrix *m,segment *seg)
1503 vms_vector fvec,uvec;
1505 extract_vector_from_segment(seg,&fvec,WFRONT,WBACK);
1506 extract_vector_from_segment(seg,&uvec,WBOTTOM,WTOP);
1508 //vector to matrix does normalizations and orthogonalizations
1509 vm_vector_2_matrix(m,&fvec,&uvec,NULL);
1513 // ------------------------------------------------------------------------------------------
1514 // Extract the forward vector from segment *sp, return in *vp.
1515 // The forward vector is defined to be the vector from the the center of the front face of the segment
1516 // to the center of the back face of the segment.
1517 void extract_forward_vector_from_segment(segment *sp,vms_vector *vp)
1519 extract_vector_from_segment(sp,vp,WFRONT,WBACK);
1522 // ------------------------------------------------------------------------------------------
1523 // Extract the right vector from segment *sp, return in *vp.
1524 // The forward vector is defined to be the vector from the the center of the left face of the segment
1525 // to the center of the right face of the segment.
1526 void extract_right_vector_from_segment(segment *sp,vms_vector *vp)
1528 extract_vector_from_segment(sp,vp,WLEFT,WRIGHT);
1531 // ------------------------------------------------------------------------------------------
1532 // Extract the up vector from segment *sp, return in *vp.
1533 // The forward vector is defined to be the vector from the the center of the bottom face of the segment
1534 // to the center of the top face of the segment.
1535 void extract_up_vector_from_segment(segment *sp,vms_vector *vp)
1537 extract_vector_from_segment(sp,vp,WBOTTOM,WTOP);
1541 void add_side_as_quad(segment *sp, int sidenum, vms_vector *normal)
1543 side *sidep = &sp->sides[sidenum];
1545 sidep->type = SIDE_IS_QUAD;
1548 normal = normal; //avoid compiler warning
1550 sidep->normals[0] = *normal;
1551 sidep->normals[1] = *normal;
1554 // If there is a connection here, we only formed the faces for the purpose of determining segment boundaries,
1555 // so don't generate polys, else they will get rendered.
1556 // if (sp->children[sidenum] != -1)
1557 // sidep->render_flag = 0;
1559 // sidep->render_flag = 1;
1564 // -------------------------------------------------------------------------------
1565 // Return v0, v1, v2 = 3 vertices with smallest numbers. If *negate_flag set, then negate normal after computation.
1566 // Note, you cannot just compute the normal by treating the points in the opposite direction as this introduces
1567 // small differences between normals which should merely be opposites of each other.
1568 void get_verts_for_normal(int va, int vb, int vc, int vd, int *v0, int *v1, int *v2, int *v3, int *negate_flag)
1573 // w is a list that shows how things got scrambled so we know if our normal is pointing backwards
1586 t = v[j]; v[j] = v[i]; v[i] = t;
1587 t = w[j]; w[j] = w[i]; w[i] = t;
1590 Assert((v[0] < v[1]) && (v[1] < v[2]) && (v[2] < v[3]));
1592 // Now, if for any w[i] & w[i+1]: w[i+1] = (w[i]+3)%4, then must swap
1598 if ( (((w[0]+3) % 4) == w[1]) || (((w[1]+3) % 4) == w[2]))
1605 // -------------------------------------------------------------------------------
1606 void add_side_as_2_triangles(segment *sp, int sidenum)
1609 sbyte *vs = Side_to_verts[sidenum];
1611 vms_vector vec_13; // vector from vertex 1 to vertex 3
1613 side *sidep = &sp->sides[sidenum];
1615 // Choose how to triangulate.
1617 // Always triangulate so segment is convex.
1618 // Use Matt's formula: Na . AD > 0, where ABCD are vertices on side, a is face formed by A,B,C, Na is normal from face a.
1619 // If not a wall, then triangulate so whatever is on the other side is triangulated the same (ie, between the same absoluate vertices)
1620 if (!IS_CHILD(sp->children[sidenum])) {
1621 vm_vec_normal(&norm, &Vertices[sp->verts[vs[0]]], &Vertices[sp->verts[vs[1]]], &Vertices[sp->verts[vs[2]]]);
1622 vm_vec_sub(&vec_13, &Vertices[sp->verts[vs[3]]], &Vertices[sp->verts[vs[1]]]);
1623 dot = vm_vec_dot(&norm, &vec_13);
1625 // Now, signifiy whether to triangulate from 0:2 or 1:3
1627 sidep->type = SIDE_IS_TRI_02;
1629 sidep->type = SIDE_IS_TRI_13;
1631 #ifndef COMPACT_SEGS
1632 // Now, based on triangulation type, set the normals.
1633 if (sidep->type == SIDE_IS_TRI_02) {
1634 vm_vec_normal(&norm, &Vertices[sp->verts[vs[0]]], &Vertices[sp->verts[vs[1]]], &Vertices[sp->verts[vs[2]]]);
1635 sidep->normals[0] = norm;
1636 vm_vec_normal(&norm, &Vertices[sp->verts[vs[0]]], &Vertices[sp->verts[vs[2]]], &Vertices[sp->verts[vs[3]]]);
1637 sidep->normals[1] = norm;
1639 vm_vec_normal(&norm, &Vertices[sp->verts[vs[0]]], &Vertices[sp->verts[vs[1]]], &Vertices[sp->verts[vs[3]]]);
1640 sidep->normals[0] = norm;
1641 vm_vec_normal(&norm, &Vertices[sp->verts[vs[1]]], &Vertices[sp->verts[vs[2]]], &Vertices[sp->verts[vs[3]]]);
1642 sidep->normals[1] = norm;
1646 int i,v[4], vsorted[4];
1650 v[i] = sp->verts[vs[i]];
1652 get_verts_for_normal(v[0], v[1], v[2], v[3], &vsorted[0], &vsorted[1], &vsorted[2], &vsorted[3], &negate_flag);
1654 if ((vsorted[0] == v[0]) || (vsorted[0] == v[2])) {
1655 sidep->type = SIDE_IS_TRI_02;
1656 #ifndef COMPACT_SEGS
1657 // Now, get vertices for normal for each triangle based on triangulation type.
1658 get_verts_for_normal(v[0], v[1], v[2], 32767, &vsorted[0], &vsorted[1], &vsorted[2], &vsorted[3], &negate_flag);
1659 vm_vec_normal(&norm, &Vertices[vsorted[0]], &Vertices[vsorted[1]], &Vertices[vsorted[2]]);
1661 vm_vec_negate(&norm);
1662 sidep->normals[0] = norm;
1664 get_verts_for_normal(v[0], v[2], v[3], 32767, &vsorted[0], &vsorted[1], &vsorted[2], &vsorted[3], &negate_flag);
1665 vm_vec_normal(&norm, &Vertices[vsorted[0]], &Vertices[vsorted[1]], &Vertices[vsorted[2]]);
1667 vm_vec_negate(&norm);
1668 sidep->normals[1] = norm;
1671 sidep->type = SIDE_IS_TRI_13;
1672 #ifndef COMPACT_SEGS
1673 // Now, get vertices for normal for each triangle based on triangulation type.
1674 get_verts_for_normal(v[0], v[1], v[3], 32767, &vsorted[0], &vsorted[1], &vsorted[2], &vsorted[3], &negate_flag);
1675 vm_vec_normal(&norm, &Vertices[vsorted[0]], &Vertices[vsorted[1]], &Vertices[vsorted[2]]);
1677 vm_vec_negate(&norm);
1678 sidep->normals[0] = norm;
1680 get_verts_for_normal(v[1], v[2], v[3], 32767, &vsorted[0], &vsorted[1], &vsorted[2], &vsorted[3], &negate_flag);
1681 vm_vec_normal(&norm, &Vertices[vsorted[0]], &Vertices[vsorted[1]], &Vertices[vsorted[2]]);
1683 vm_vec_negate(&norm);
1684 sidep->normals[1] = norm;
1693 if (v > PLANE_DIST_TOLERANCE)
1695 else if (v < -(PLANE_DIST_TOLERANCE+1)) //neg & pos round differently
1701 // -------------------------------------------------------------------------------
1702 void create_walls_on_side(segment *sp, int sidenum)
1704 int vm0, vm1, vm2, vm3, negate_flag;
1709 v0 = sp->verts[Side_to_verts[sidenum][0]];
1710 v1 = sp->verts[Side_to_verts[sidenum][1]];
1711 v2 = sp->verts[Side_to_verts[sidenum][2]];
1712 v3 = sp->verts[Side_to_verts[sidenum][3]];
1714 get_verts_for_normal(v0, v1, v2, v3, &vm0, &vm1, &vm2, &vm3, &negate_flag);
1716 vm_vec_normal(&vn, &Vertices[vm0], &Vertices[vm1], &Vertices[vm2]);
1717 dist_to_plane = abs(vm_dist_to_plane(&Vertices[vm3], &vn, &Vertices[vm0]));
1719 //if ((sp-Segments == 0x7b) && (sidenum == 3)) {
1720 // mprintf((0, "Verts = %3i %3i %3i %3i, negate flag = %3i, dist = %8x\n", vm0, vm1, vm2, vm3, negate_flag, dist_to_plane));
1721 // mprintf((0, " Normal = %8x %8x %8x\n", vn.x, vn.y, vn.z));
1722 // mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm0, Vertices[vm0].x, Vertices[vm0].y, Vertices[vm0].z));
1723 // mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm1, Vertices[vm1].x, Vertices[vm1].y, Vertices[vm1].z));
1724 // mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm2, Vertices[vm2].x, Vertices[vm2].y, Vertices[vm2].z));
1725 // mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm3, Vertices[vm3].x, Vertices[vm3].y, Vertices[vm3].z));
1728 //if ((sp-Segments == 0x86) && (sidenum == 5)) {
1729 // mprintf((0, "Verts = %3i %3i %3i %3i, negate flag = %3i, dist = %8x\n", vm0, vm1, vm2, vm3, negate_flag, dist_to_plane));
1730 // mprintf((0, " Normal = %8x %8x %8x\n", vn.x, vn.y, vn.z));
1731 // mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm0, Vertices[vm0].x, Vertices[vm0].y, Vertices[vm0].z));
1732 // mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm1, Vertices[vm1].x, Vertices[vm1].y, Vertices[vm1].z));
1733 // mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm2, Vertices[vm2].x, Vertices[vm2].y, Vertices[vm2].z));
1734 // mprintf((0, " Vert %3i = [%8x %8x %8x]\n", vm3, Vertices[vm3].x, Vertices[vm3].y, Vertices[vm3].z));
1740 if (dist_to_plane <= PLANE_DIST_TOLERANCE)
1741 add_side_as_quad(sp, sidenum, &vn);
1743 add_side_as_2_triangles(sp, sidenum);
1745 //this code checks to see if we really should be triangulated, and
1746 //de-triangulates if we shouldn't be.
1756 create_abs_vertex_lists(&num_faces, vertex_list, sp - Segments, sidenum, __FILE__, __LINE__);
1758 Assert(num_faces == 2);
1760 s = &sp->sides[sidenum];
1762 vertnum = min(vertex_list[0],vertex_list[2]);
1766 vms_vector normals[2];
1767 get_side_normals(sp, sidenum, &normals[0], &normals[1] );
1768 dist0 = vm_dist_to_plane(&Vertices[vertex_list[1]],&normals[1],&Vertices[vertnum]);
1769 dist1 = vm_dist_to_plane(&Vertices[vertex_list[4]],&normals[0],&Vertices[vertnum]);
1772 dist0 = vm_dist_to_plane(&Vertices[vertex_list[1]],&s->normals[1],&Vertices[vertnum]);
1773 dist1 = vm_dist_to_plane(&Vertices[vertex_list[4]],&s->normals[0],&Vertices[vertnum]);
1779 if (s0==0 || s1==0 || s0!=s1) {
1780 sp->sides[sidenum].type = SIDE_IS_QUAD; //detriangulate!
1781 #ifndef COMPACT_SEGS
1782 sp->sides[sidenum].normals[0] = vn;
1783 sp->sides[sidenum].normals[1] = vn;
1795 //#define CACHE_DEBUG 1
1796 #define MAX_CACHE_NORMALS 128
1797 #define CACHE_MASK 127
1799 typedef struct ncache_element {
1802 vms_vector normals[2];
1805 int ncache_initialized = 0;
1806 ncache_element ncache[MAX_CACHE_NORMALS];
1809 int ncache_counter = 0;
1810 int ncache_hits = 0;
1811 int ncache_misses = 0;
1817 ncache_initialized = 1;
1823 for (i=0; i<MAX_CACHE_NORMALS; i++ ) {
1824 ncache[i].segnum = -1;
1830 // -------------------------------------------------------------------------------
1831 int find_ncache_element( int segnum, int sidenum, int face_flags )
1835 if (!ncache_initialized) ncache_init();
1838 if (((++ncache_counter % 5000)==1) && (ncache_hits+ncache_misses > 0))
1839 mprintf(( 0, "NCACHE %d%% missed, H:%d, M:%d\n", (ncache_misses*100)/(ncache_hits+ncache_misses), ncache_hits, ncache_misses ));
1842 i = ((segnum<<2) ^ sidenum) & CACHE_MASK;
1844 if ((ncache[i].segnum == segnum) && ((ncache[i].sidenum&0xf)==sidenum) ) {
1849 f1 = ncache[i].sidenum>>4;
1850 if ( (f1&face_flags)==face_flags )
1853 uncached_get_side_normal( &Segments[segnum], sidenum, 1, &ncache[i].normals[1] );
1855 uncached_get_side_normal( &Segments[segnum], sidenum, 0, &ncache[i].normals[0] );
1856 ncache[i].sidenum |= face_flags<<4;
1863 switch( face_flags ) {
1865 uncached_get_side_normal( &Segments[segnum], sidenum, 0, &ncache[i].normals[0] );
1868 uncached_get_side_normal( &Segments[segnum], sidenum, 1, &ncache[i].normals[1] );
1871 uncached_get_side_normals(&Segments[segnum], sidenum, &ncache[i].normals[0], &ncache[i].normals[1] );
1874 ncache[i].segnum = segnum;
1875 ncache[i].sidenum = sidenum | (face_flags<<4);
1879 void get_side_normal(segment *sp, int sidenum, int face_num, vms_vector * vm )
1882 i = find_ncache_element( sp - Segments, sidenum, 1 << face_num );
1883 *vm = ncache[i].normals[face_num];
1886 uncached_get_side_normal(sp, sidenum, face_num, &tmp );
1887 Assert( tmp.x == vm->x );
1888 Assert( tmp.y == vm->y );
1889 Assert( tmp.z == vm->z );
1893 void get_side_normals(segment *sp, int sidenum, vms_vector * vm1, vms_vector * vm2 )
1896 i = find_ncache_element( sp - Segments, sidenum, 3 );
1897 *vm1 = ncache[i].normals[0];
1898 *vm2 = ncache[i].normals[1];
1902 uncached_get_side_normal(sp, sidenum, 0, &tmp );
1903 Assert( tmp.x == vm1->x );
1904 Assert( tmp.y == vm1->y );
1905 Assert( tmp.z == vm1->z );
1906 uncached_get_side_normal(sp, sidenum, 1, &tmp );
1907 Assert( tmp.x == vm2->x );
1908 Assert( tmp.y == vm2->y );
1909 Assert( tmp.z == vm2->z );
1914 void uncached_get_side_normal(segment *sp, int sidenum, int face_num, vms_vector * vm )
1916 int vm0, vm1, vm2, vm3, negate_flag;
1917 char *vs = Side_to_verts[sidenum];
1919 switch( sp->sides[sidenum].type ) {
1921 get_verts_for_normal(sp->verts[vs[0]], sp->verts[vs[1]], sp->verts[vs[2]], sp->verts[vs[3]], &vm0, &vm1, &vm2, &vm3, &negate_flag);
1922 vm_vec_normal(vm, &Vertices[vm0], &Vertices[vm1], &Vertices[vm2]);
1926 case SIDE_IS_TRI_02:
1927 if ( face_num == 0 )
1928 vm_vec_normal(vm, &Vertices[sp->verts[vs[0]]], &Vertices[sp->verts[vs[1]]], &Vertices[sp->verts[vs[2]]]);
1930 vm_vec_normal(vm, &Vertices[sp->verts[vs[0]]], &Vertices[sp->verts[vs[2]]], &Vertices[sp->verts[vs[3]]]);
1932 case SIDE_IS_TRI_13:
1933 if ( face_num == 0 )
1934 vm_vec_normal(vm, &Vertices[sp->verts[vs[0]]], &Vertices[sp->verts[vs[1]]], &Vertices[sp->verts[vs[3]]]);
1936 vm_vec_normal(vm, &Vertices[sp->verts[vs[1]]], &Vertices[sp->verts[vs[2]]], &Vertices[sp->verts[vs[3]]]);
1941 void uncached_get_side_normals(segment *sp, int sidenum, vms_vector * vm1, vms_vector * vm2 )
1943 int vvm0, vvm1, vvm2, vvm3, negate_flag;
1944 char *vs = Side_to_verts[sidenum];
1946 switch( sp->sides[sidenum].type ) {
1948 get_verts_for_normal(sp->verts[vs[0]], sp->verts[vs[1]], sp->verts[vs[2]], sp->verts[vs[3]], &vvm0, &vvm1, &vvm2, &vvm3, &negate_flag);
1949 vm_vec_normal(vm1, &Vertices[vvm0], &Vertices[vvm1], &Vertices[vvm2]);
1954 case SIDE_IS_TRI_02:
1955 vm_vec_normal(vm1, &Vertices[sp->verts[vs[0]]], &Vertices[sp->verts[vs[1]]], &Vertices[sp->verts[vs[2]]]);
1956 vm_vec_normal(vm2, &Vertices[sp->verts[vs[0]]], &Vertices[sp->verts[vs[2]]], &Vertices[sp->verts[vs[3]]]);
1958 case SIDE_IS_TRI_13:
1959 vm_vec_normal(vm1, &Vertices[sp->verts[vs[0]]], &Vertices[sp->verts[vs[1]]], &Vertices[sp->verts[vs[3]]]);
1960 vm_vec_normal(vm2, &Vertices[sp->verts[vs[1]]], &Vertices[sp->verts[vs[2]]], &Vertices[sp->verts[vs[3]]]);
1967 // -------------------------------------------------------------------------------
1968 void validate_removable_wall(segment *sp, int sidenum, int tmap_num)
1970 create_walls_on_side(sp, sidenum);
1972 sp->sides[sidenum].tmap_num = tmap_num;
1974 // assign_default_uvs_to_side(sp, sidenum);
1975 // assign_light_to_side(sp, sidenum);
1978 // -------------------------------------------------------------------------------
1979 // Make a just-modified segment side valid.
1980 void validate_segment_side(segment *sp, int sidenum)
1982 if (sp->sides[sidenum].wall_num == -1)
1983 create_walls_on_side(sp, sidenum);
1985 // create_removable_wall(sp, sidenum, sp->sides[sidenum].tmap_num);
1986 validate_removable_wall(sp, sidenum, sp->sides[sidenum].tmap_num);
1989 // If side doesn't have a child, then render wall. If it does have a child, but there is a temporary
1990 // wall there, then do render wall.
1991 // if (sp->children[sidenum] == -1)
1992 // sp->sides[sidenum].render_flag = 1;
1993 // else if (sp->sides[sidenum].wall_num != -1)
1994 // sp->sides[sidenum].render_flag = 1;
1996 // sp->sides[sidenum].render_flag = 0;
1999 extern int check_for_degenerate_segment(segment *sp);
2001 // -------------------------------------------------------------------------------
2002 // Make a just-modified segment valid.
2003 // check all sides to see how many faces they each should have (0,1,2)
2004 // create new vector normals
2005 void validate_segment(segment *sp)
2010 check_for_degenerate_segment(sp);
2013 for (side = 0; side < MAX_SIDES_PER_SEGMENT; side++)
2014 validate_segment_side(sp, side);
2016 // assign_default_uvs_to_segment(sp);
2019 // -------------------------------------------------------------------------------
2020 // Validate all segments.
2021 // Highest_segment_index must be set.
2022 // For all used segments (number <= Highest_segment_index), segnum field must be != -1.
2023 void validate_segment_all(void)
2027 for (s=0; s<=Highest_segment_index; s++)
2029 if (Segments[s].segnum != -1)
2031 validate_segment(&Segments[s]);
2036 for (s=Highest_segment_index+1; s<MAX_SEGMENTS; s++)
2037 if (Segments[s].segnum != -1) {
2039 mprintf((0, "Segment %i has invalid segnum. Bashing to -1. Silently bashing all others...", s));
2042 Segments[s].segnum = -1;
2046 mprintf((0, "%i fixed.\n", said));
2051 #ifndef COMPACT_SEGS
2052 if (check_segment_connections())
2053 Int3(); //Get Matt, si vous plait.
2059 // ------------------------------------------------------------------------------------------------------
2060 // Picks a random point in a segment like so:
2061 // From center, go up to 50% of way towards any of the 8 vertices.
2062 void pick_random_point_in_seg(vms_vector *new_pos, int segnum)
2067 compute_segment_center(new_pos, &Segments[segnum]);
2068 vnum = (d_rand() * MAX_VERTICES_PER_SEGMENT) >> 15;
2069 vm_vec_sub(&vec2, &Vertices[Segments[segnum].verts[vnum]], new_pos);
2070 vm_vec_scale(&vec2, d_rand()); // d_rand() always in 0..1/2
2071 vm_vec_add2(new_pos, &vec2);
2075 // ----------------------------------------------------------------------------------------------------------
2076 // Set the segment depth of all segments from start_seg in *segbuf.
2077 // Returns maximum depth value.
2078 int set_segment_depths(int start_seg, ubyte *segbuf)
2081 ubyte visited[MAX_SEGMENTS];
2082 int queue[MAX_SEGMENTS];
2091 for (i=0; i<=Highest_segment_index; i++)
2094 if (segbuf[start_seg] == 0)
2097 queue[tail++] = start_seg;
2098 visited[start_seg] = 1;
2099 segbuf[start_seg] = depth++;
2104 while (head < tail) {
2105 curseg = queue[head++];
2106 parent_depth = segbuf[curseg];
2108 for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
2111 childnum = Segments[curseg].children[i];
2113 if (segbuf[childnum])
2114 if (!visited[childnum]) {
2115 visited[childnum] = 1;
2116 segbuf[childnum] = parent_depth+1;
2117 queue[tail++] = childnum;
2122 return parent_depth+1;
2125 //these constants should match the ones in seguvs
2126 #define LIGHT_DISTANCE_THRESHOLD (F1_0*80)
2127 #define Magical_light_constant (F1_0*16)
2129 #define MAX_CHANGED_SEGS 30
2130 short changed_segs[MAX_CHANGED_SEGS];
2133 // ------------------------------------------------------------------------------------------
2134 //cast static light from a segment to nearby segments
2135 void apply_light_to_segment(segment *segp,vms_vector *segment_center, fix light_intensity,int recursion_depth)
2137 vms_vector r_segment_center;
2139 int i,segnum=segp-Segments,sidenum;
2141 for (i=0;i<n_changed_segs;i++)
2142 if (changed_segs[i] == segnum)
2145 if (i == n_changed_segs) {
2146 compute_segment_center(&r_segment_center, segp);
2147 dist_to_rseg = vm_vec_dist_quick(&r_segment_center, segment_center);
2149 if (dist_to_rseg <= LIGHT_DISTANCE_THRESHOLD) {
2151 if (dist_to_rseg > F1_0)
2152 light_at_point = fixdiv(Magical_light_constant, dist_to_rseg);
2154 light_at_point = Magical_light_constant;
2156 if (light_at_point >= 0) {
2157 segment2 *seg2p = &Segment2s[segnum];
2158 light_at_point = fixmul(light_at_point, light_intensity);
2159 if (light_at_point >= F1_0)
2160 light_at_point = F1_0-1;
2161 if (light_at_point <= -F1_0)
2162 light_at_point = -(F1_0-1);
2163 seg2p->static_light += light_at_point;
2164 if (seg2p->static_light < 0) // if it went negative, saturate
2165 seg2p->static_light = 0;
2166 } // end if (light_at_point...
2167 } // end if (dist_to_rseg...
2169 changed_segs[n_changed_segs++] = segnum;
2172 if (recursion_depth < 2)
2173 for (sidenum=0; sidenum<6; sidenum++) {
2174 if (WALL_IS_DOORWAY(segp,sidenum) & WID_RENDPAST_FLAG)
2175 apply_light_to_segment(&Segments[segp->children[sidenum]],segment_center,light_intensity,recursion_depth+1);
2181 extern object *old_viewer;
2183 //update the static_light field in a segment, which is used for object lighting
2184 //this code is copied from the editor routine calim_process_all_lights()
2185 void change_segment_light(int segnum,int sidenum,int dir)
2187 segment *segp = &Segments[segnum];
2189 if (WALL_IS_DOORWAY(segp, sidenum) & WID_RENDER_FLAG) {
2190 side *sidep = &segp->sides[sidenum];
2191 fix light_intensity;
2193 light_intensity = TmapInfo[sidep->tmap_num].lighting + TmapInfo[sidep->tmap_num2 & 0x3fff].lighting;
2195 light_intensity *= dir;
2199 if (light_intensity) {
2200 vms_vector segment_center;
2201 compute_segment_center(&segment_center, segp);
2202 apply_light_to_segment(segp,&segment_center,light_intensity,0);
2206 //this is a horrible hack to get around the horrible hack used to
2207 //smooth lighting values when an object moves between segments
2212 // ------------------------------------------------------------------------------------------
2213 // dir = +1 -> add light
2214 // dir = -1 -> subtract light
2215 // dir = 17 -> add 17x light
2216 // dir = 0 -> you are dumb
2217 void change_light(int segnum, int sidenum, int dir)
2221 for (i=0; i<Num_static_lights; i++) {
2222 if ((Dl_indices[i].segnum == segnum) && (Dl_indices[i].sidenum == sidenum)) {
2224 dlp = &Delta_lights[Dl_indices[i].index];
2226 for (j=0; j<Dl_indices[i].count; j++) {
2227 for (k=0; k<4; k++) {
2229 dl = dir * dlp->vert_light[k] * DL_SCALE;
2230 Assert((dlp->segnum >= 0) && (dlp->segnum <= Highest_segment_index));
2231 Assert((dlp->sidenum >= 0) && (dlp->sidenum < MAX_SIDES_PER_SEGMENT));
2232 new_l = (Segments[dlp->segnum].sides[dlp->sidenum].uvls[k].l += dl);
2234 Segments[dlp->segnum].sides[dlp->sidenum].uvls[k].l = 0;
2241 //recompute static light for segment
2242 change_segment_light(segnum,sidenum,dir);
2245 // Subtract light cast by a light source from all surfaces to which it applies light.
2246 // This is precomputed data, stored at static light application time in the editor (the slow lighting function).
2247 // returns 1 if lights actually subtracted, else 0
2248 int subtract_light(int segnum, int sidenum)
2250 if (Light_subtracted[segnum] & (1 << sidenum)) {
2251 //mprintf((0, "Warning: Trying to subtract light from a source twice!\n"));
2255 Light_subtracted[segnum] |= (1 << sidenum);
2256 change_light(segnum, sidenum, -1);
2260 // Add light cast by a light source from all surfaces to which it applies light.
2261 // This is precomputed data, stored at static light application time in the editor (the slow lighting function).
2262 // You probably only want to call this after light has been subtracted.
2263 // returns 1 if lights actually added, else 0
2264 int add_light(int segnum, int sidenum)
2266 if (!(Light_subtracted[segnum] & (1 << sidenum))) {
2267 //mprintf((0, "Warning: Trying to add light which has never been subtracted!\n"));
2271 Light_subtracted[segnum] &= ~(1 << sidenum);
2272 change_light(segnum, sidenum, 1);
2276 // Light_subtracted[i] contains bit indicators for segment #i.
2277 // If bit n (1 << n) is set, then side #n in segment #i has had light subtracted from original (editor-computed) value.
2278 ubyte Light_subtracted[MAX_SEGMENTS];
2280 // Parse the Light_subtracted array, turning on or off all lights.
2281 void apply_all_changed_light(void)
2285 for (i=0; i<=Highest_segment_index; i++) {
2286 for (j=0; j<MAX_SIDES_PER_SEGMENT; j++)
2287 if (Light_subtracted[i] & (1 << j))
2288 change_light(i, j, -1);
2292 //@@// Scans Light_subtracted bit array.
2293 //@@// For all light sources which have had their light subtracted, adds light back in.
2294 //@@void restore_all_lights_in_mine(void)
2298 //@@ for (i=0; i<Num_static_lights; i++) {
2299 //@@ int segnum, sidenum;
2300 //@@ delta_light *dlp;
2302 //@@ segnum = Dl_indices[i].segnum;
2303 //@@ sidenum = Dl_indices[i].sidenum;
2304 //@@ if (Light_subtracted[segnum] & (1 << sidenum)) {
2305 //@@ dlp = &Delta_lights[Dl_indices[i].index];
2307 //@@ Light_subtracted[segnum] &= ~(1 << sidenum);
2308 //@@ for (j=0; j<Dl_indices[i].count; j++) {
2309 //@@ for (k=0; k<4; k++) {
2311 //@@ dl = dlp->vert_light[k] * DL_SCALE;
2312 //@@ Assert((dlp->segnum >= 0) && (dlp->segnum <= Highest_segment_index));
2313 //@@ Assert((dlp->sidenum >= 0) && (dlp->sidenum < MAX_SIDES_PER_SEGMENT));
2314 //@@ Segments[dlp->segnum].sides[dlp->sidenum].uvls[k].l += dl;
2322 // Should call this whenever a new mine gets loaded.
2323 // More specifically, should call this whenever something global happens
2324 // to change the status of static light in the mine.
2325 void clear_light_subtracted(void)
2329 for (i=0; i<=Highest_segment_index; i++)
2330 Light_subtracted[i] = 0;
2334 // -----------------------------------------------------------------------------
2335 fix find_connected_distance_segments( int seg0, int seg1, int depth, int wid_flag)
2339 compute_segment_center(&p0, &Segments[seg0]);
2340 compute_segment_center(&p1, &Segments[seg1]);
2342 return find_connected_distance(&p0, seg0, &p1, seg1, depth, wid_flag);
2345 #define AMBIENT_SEGMENT_DEPTH 5
2347 // -----------------------------------------------------------------------------
2348 // Do a bfs from segnum, marking slots in marked_segs if the segment is reachable.
2349 void ambient_mark_bfs(int segnum, sbyte *marked_segs, int depth)
2356 marked_segs[segnum] = 1;
2358 for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
2359 int child = Segments[segnum].children[i];
2361 if (IS_CHILD(child) && (WALL_IS_DOORWAY(&Segments[segnum],i) & WID_RENDPAST_FLAG) && !marked_segs[child])
2362 ambient_mark_bfs(child, marked_segs, depth-1);
2367 // -----------------------------------------------------------------------------
2368 // Indicate all segments which are within audible range of falling water or lava,
2369 // and so should hear ambient gurgles.
2370 void set_ambient_sound_flags_common(int tmi_bit, int s2f_bit)
2373 sbyte marked_segs[MAX_SEGMENTS];
2375 // Now, all segments containing ambient lava or water sound makers are flagged.
2376 // Additionally flag all segments which are within range of them.
2377 for (i=0; i<=Highest_segment_index; i++) {
2379 Segment2s[i].s2_flags &= ~s2f_bit;
2382 // Mark all segments which are sources of the sound.
2383 for (i=0; i<=Highest_segment_index; i++) {
2384 segment *segp = &Segments[i];
2385 segment2 *seg2p = &Segment2s[i];
2387 for (j=0; j<MAX_SIDES_PER_SEGMENT; j++) {
2388 side *sidep = &segp->sides[j];
2390 if ((TmapInfo[sidep->tmap_num].flags & tmi_bit) || (TmapInfo[sidep->tmap_num2 & 0x3fff].flags & tmi_bit)) {
2391 if (!IS_CHILD(segp->children[j]) || (sidep->wall_num != -1)) {
2392 seg2p->s2_flags |= s2f_bit;
2393 marked_segs[i] = 1; // Say it's itself that it is close enough to to hear something.
2401 // Next mark all segments within N segments of a source.
2402 for (i=0; i<=Highest_segment_index; i++) {
2403 segment2 *seg2p = &Segment2s[i];
2405 if (seg2p->s2_flags & s2f_bit)
2406 ambient_mark_bfs(i, marked_segs, AMBIENT_SEGMENT_DEPTH);
2409 // Now, flip bits in all segments which can hear the ambient sound.
2410 for (i=0; i<=Highest_segment_index; i++)
2412 Segment2s[i].s2_flags |= s2f_bit;
2417 // -----------------------------------------------------------------------------
2418 // Indicate all segments which are within audible range of falling water or lava,
2419 // and so should hear ambient gurgles.
2420 // Bashes values in Segment2s array.
2421 void set_ambient_sound_flags(void)
2423 set_ambient_sound_flags_common(TMI_VOLATILE, S2F_AMBIENT_LAVA);
2424 set_ambient_sound_flags_common(TMI_WATER, S2F_AMBIENT_WATER);