]> icculus.org git repositories - btb/d2x.git/blob - main/editor/seguvs.c
imported missing editor files from d1x
[btb/d2x.git] / main / editor / seguvs.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13  /*
14  * $Source: /cvs/cvsroot/d2x/main/editor/seguvs.c,v $
15  * $Revision: 1.1 $
16  * $Author: btb $
17  * $Date: 2004-12-19 13:54:27 $
18  * 
19  * u,v coordinate computation for segment faces
20  * 
21  * $Log: not supported by cvs2svn $
22  * Revision 1.2  2003/03/09 06:34:09  donut
23  * change byte typedef to sbyte to avoid conflict with win32 byte which is unsigned
24  *
25  * Revision 1.1.1.1  1999/06/14 22:04:31  donut
26  * Import of d1x 1.37 source.
27  *
28  * Revision 2.1  1995/05/08  10:49:34  mike
29  * fix lighting bug: oblong segments could be very dark.
30  * 
31  * Revision 2.0  1995/02/27  11:36:37  john
32  * Version 2.0. Ansi-fied.
33  * 
34  * Revision 1.84  1994/11/27  23:17:18  matt
35  * Made changes for new mprintf calling convention
36  * 
37  * Revision 1.83  1994/11/17  14:48:02  mike
38  * validation functions moved from editor to game.
39  * 
40  * Revision 1.82  1994/10/15  19:08:26  mike
41  * Disable exhaustive search mprintfs in find_point_seg during lighting.
42  * 
43  * Revision 1.81  1994/08/25  21:55:50  mike
44  * IS_CHILD stuff.
45  * 
46  * Revision 1.80  1994/08/04  19:13:22  matt
47  * Changed a bunch of vecmat calls to use multiple-function routines, and to
48  * allow the use of C macros for some functions
49  * 
50  * Revision 1.79  1994/08/03  10:31:33  mike
51  * Texture map propagation without uv assignment.
52  * 
53  * Revision 1.78  1994/08/01  13:31:12  matt
54  * Made fvi() check holes in transparent walls, and changed fvi() calling
55  * parms to take all input data in query structure.
56  * 
57  * Revision 1.77  1994/07/08  14:31:24  matt
58  * New parms for FVI
59  * 
60  * Revision 1.76  1994/06/23  14:01:04  mike
61  * Fix cache bug which caused some vertices to not get light, mainly
62  * noticeable at joints which had doors.
63  * 
64  * Revision 1.75  1994/06/22  17:33:11  mike
65  * Make position of light (which is always towards center of segment from
66  * actual light panel) constant, not dependent on segment size, which fixes
67  * bug of dark light panels in very large segments.
68  * 
69  * Revision 1.74  1994/06/21  18:58:18  mike
70  * Fix stupid bug in light propagation, was using wrong vector in fvi caching.
71  * 
72  * Revision 1.73  1994/06/20  11:20:24  mike
73  * Fix stupid lighting bug introduced when I went to cached fvi results.
74  * 
75  * Revision 1.72  1994/06/19  16:26:37  mike
76  * Speed up lighting by storing and hashing fvi results.
77  * 
78  * Revision 1.71  1994/06/17  16:05:56  mike
79  * Support optional quick lighting propagation: no find_vector_intersection.
80  * 
81  * Revision 1.70  1994/06/15  15:42:30  mike
82  * Propagate static_light.
83  * 
84  * Revision 1.69  1994/06/14  16:59:37  mike
85  * Fix references to tmap_num2, must strip off orientation bits.
86  * 
87  * Revision 1.68  1994/06/09  09:58:58  matt
88  * Moved find_vector_intersection() from physics.c to new file fvi.c
89  * 
90  * 
91  * Revision 1.67  1994/06/08  18:14:02  mike
92  * mprintf a dot in light casting.
93  * 
94  * Revision 1.66  1994/06/08  14:37:45  mike
95  * double static light value in going from value (a short) to static_light (a fix).
96  * 
97  * Revision 1.65  1994/06/08  14:29:44  matt
98  * Added static_light field to segment structure, and padded side struct
99  * to be longword aligned.
100  * 
101  * Revision 1.64  1994/06/08  11:45:24  mike
102  * New, supercool, superslow lighting function.
103  * 
104  * Revision 1.63  1994/06/07  09:38:11  mike
105  * Make lighting function yet better by calling find_vector_intersection.
106  * 
107  * Revision 1.62  1994/06/06  13:14:33  mike
108  * Make illusory walls cast light.
109  * 
110  * Revision 1.61  1994/06/05  20:39:47  mike
111  * Add new distance and dot product based lighting function.
112  * 
113  * Revision 1.60  1994/05/31  12:31:18  mike
114  * fix bugs in lighting, though it's not perfect, will be changing all
115  * lighting to be distance based.  Bug had to do with not handling one
116  * of the return values from WALL_IS_DOORWAY, so assuming light couldn't
117  * be recursively propagated almost all the time.
118  * 
119  * Revision 1.59  1994/05/19  23:35:26  mike
120  * Support uv coordinates in range 0..1.0.
121  * 
122  * Revision 1.58  1994/05/19  12:10:21  matt
123  * Use new vecmat macros and globals
124  * 
125  * Revision 1.57  1994/05/04  19:15:53  mike
126  * Error checking for degenerate segments.
127  * 
128  * Revision 1.56  1994/05/03  11:02:34  mike
129  * Change how default texture map assignment works; now pixels are constant size.
130  * 
131  * Revision 1.55  1994/04/28  23:25:26  yuan
132  * Obliterated warnings.
133  * 
134  */
135
136
137 #ifdef RCS
138 static char rcsid[] = "$Id: seguvs.c,v 1.1 2004-12-19 13:54:27 btb Exp $";
139 #endif
140
141 #include <stdio.h>
142 #include <stdlib.h>
143 #include <stdarg.h>
144 #include <math.h>
145 #include <string.h>
146
147 #include "inferno.h"
148 #include "segment.h"
149 #include "editor/editor.h"
150
151 #include "gameseg.h"
152
153 #include "fix.h"
154 #include "mono.h"
155 #include "error.h"
156
157 #include "wall.h"
158 #include "editor/kdefs.h"
159 #include "bm.h"         //      Needed for TmapInfo
160 #include        "effects.h"     //      Needed for effects_bm_num
161 #include "fvi.h"
162
163 void cast_all_light_in_mine(int quick_flag);
164 //--rotate_uvs-- vms_vector Rightvec;
165
166 //      ---------------------------------------------------------------------------------------------
167 //      Returns approximate area of a side
168 fix area_on_side(side *sidep)
169 {
170         fix     du,dv,width,height;
171
172         du = sidep->uvls[1].u - sidep->uvls[0].u;
173         dv = sidep->uvls[1].v - sidep->uvls[0].v;
174
175         width = fix_sqrt(fixmul(du,du) + fixmul(dv,dv));
176
177         du = sidep->uvls[3].u - sidep->uvls[0].u;
178         dv = sidep->uvls[3].v - sidep->uvls[0].v;
179
180         height = fix_sqrt(fixmul(du,du) + fixmul(dv,dv));
181
182         return fixmul(width, height);
183 }
184
185 //      -------------------------------------------------------------------------------------------
186 //      DEBUG function -- callable from debugger.
187 //      Returns approximate area of all sides which get mapped (ie, are not a connection).
188 //      I wrote this because I was curious how much memory would be required to texture map all
189 //      sides individually with custom artwork.  For demo1.min on 2/18/94, it would be about 5 meg.
190 int area_on_all_sides(void)
191 {
192         int     i,s;
193         int     total_area = 0;
194
195         for (i=0; i<=Highest_segment_index; i++) {
196                 segment *segp = &Segments[i];
197
198                 for (s=0; s<MAX_SIDES_PER_SEGMENT; s++)
199                         if (!IS_CHILD(segp->children[s]))
200                                 total_area += f2i(area_on_side(&segp->sides[s]));
201         }
202
203         return total_area;
204 }
205
206 fix average_connectivity(void)
207 {
208         int     i,s;
209         int     total_sides = 0, total_mapped_sides = 0;
210
211         for (i=0; i<=Highest_segment_index; i++) {
212                 segment *segp = &Segments[i];
213
214                 for (s=0; s<MAX_SIDES_PER_SEGMENT; s++) {
215                         if (!IS_CHILD(segp->children[s]))
216                                 total_mapped_sides++;
217                         total_sides++;
218                 }
219         }
220
221         return 6 * fixdiv(total_mapped_sides, total_sides);
222 }
223
224 #define MAX_LIGHT_SEGS 16
225
226 //      ---------------------------------------------------------------------------------------------
227 //      Scan all polys in all segments, return average light value for vnum.
228 //      segs = output array for segments containing vertex, terminated by -1.
229 fix get_average_light_at_vertex(int vnum, short *segs)
230 {
231         int     segnum, relvnum, sidenum;
232         fix     total_light;
233         int     num_occurrences;
234 //      #ifndef NDEBUG //Removed this ifdef because the version of Assert that I used to get it to compile doesn't work without this symbol. -KRB
235         short   *original_segs;
236
237         original_segs = segs;
238 //      #endif
239
240
241         num_occurrences = 0;
242         total_light = 0;
243
244         for (segnum=0; segnum<=Highest_segment_index; segnum++) {
245                 segment *segp = &Segments[segnum];
246                 short *vp = segp->verts;
247
248                 for (relvnum=0; relvnum<MAX_VERTICES_PER_SEGMENT; relvnum++)
249                         if (*vp++ == vnum)
250                                 break;
251
252                 if (relvnum < MAX_VERTICES_PER_SEGMENT) {
253
254                         *segs++ = segnum;
255                         Assert(segs - original_segs < MAX_LIGHT_SEGS);
256
257                         for (sidenum=0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++) {
258                                 if (!IS_CHILD(segp->children[sidenum])) {
259                                         side    *sidep = &segp->sides[sidenum];
260                                         sbyte   *vp = Side_to_verts[sidenum];
261                                         int     v;
262
263                                         for (v=0; v<4; v++)
264                                                 if (*vp++ == relvnum) {
265                                                         total_light += sidep->uvls[v].l;
266                                                         num_occurrences++;
267                                                 }
268                                 }       // end if
269                         }       // end sidenum
270                 }
271         }       // end segnum
272
273         *segs = -1;
274
275         if (num_occurrences)
276                 return total_light/num_occurrences;
277         else
278                 return 0;
279
280 }
281
282 void set_average_light_at_vertex(int vnum)
283 {
284         int     relvnum, sidenum;
285         short   Segment_indices[MAX_LIGHT_SEGS];
286         int     segind;
287
288         fix average_light;
289
290         average_light = get_average_light_at_vertex(vnum, Segment_indices);
291
292         if (!average_light)
293                 return;
294
295         segind = 0;
296         while (Segment_indices[segind] != -1) {
297                 int segnum = Segment_indices[segind++];
298
299                 segment *segp = &Segments[segnum];
300
301                 for (relvnum=0; relvnum<MAX_VERTICES_PER_SEGMENT; relvnum++)
302                         if (segp->verts[relvnum] == vnum)
303                                 break;
304
305                 if (relvnum < MAX_VERTICES_PER_SEGMENT) {
306                         for (sidenum=0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++) {
307                                 if (!IS_CHILD(segp->children[sidenum])) {
308                                         side *sidep = &segp->sides[sidenum];
309                                         sbyte   *vp = Side_to_verts[sidenum];
310                                         int     v;
311
312                                         for (v=0; v<4; v++)
313                                                 if (*vp++ == relvnum)
314                                                         sidep->uvls[v].l = average_light;
315                                 }       // end if
316                         }       // end sidenum
317                 }       // end if
318         }       // end while
319
320         Update_flags |= UF_WORLD_CHANGED;
321 }
322
323 void set_average_light_on_side(segment *segp, int sidenum)
324 {
325         int     v;
326
327         if (!IS_CHILD(segp->children[sidenum]))
328                 for (v=0; v<4; v++) {
329 //                      mprintf((0,"Vertex %i\n", segp->verts[Side_to_verts[side][v]]));
330                         set_average_light_at_vertex(segp->verts[Side_to_verts[sidenum][v]]);
331                 }
332
333 }
334
335 int set_average_light_on_curside(void)
336 {
337         set_average_light_on_side(Cursegp, Curside);
338         return 0;
339 }
340
341 //      -----------------------------------------------------------------------------------------
342 void set_average_light_on_all_fast(void)
343 {
344         int     s,v,relvnum;
345         fix     al;
346         int     alc;
347         int     seglist[MAX_LIGHT_SEGS];
348         int     *segptr;
349
350         set_vertex_counts();
351
352         //      Set total light value for all vertices in array average_light.
353         for (v=0; v<=Highest_vertex_index; v++) {
354                 al = 0;
355                 alc = 0;
356
357                 if (Vertex_active[v]) {
358                         segptr = seglist;
359
360                         for (s=0; s<=Highest_segment_index; s++) {
361                                 segment *segp = &Segments[s];
362                                 for (relvnum=0; relvnum<MAX_VERTICES_PER_SEGMENT; relvnum++)
363                                         if (segp->verts[relvnum] == v)
364                                                 break;
365
366                                         if (relvnum != MAX_VERTICES_PER_SEGMENT) {
367                                                 int             si;
368
369                                                 *segptr++ = s;                  // Note this segment in list, so we can process it below.
370                                                 Assert(segptr - seglist < MAX_LIGHT_SEGS);
371
372                                                 for (si=0; si<MAX_SIDES_PER_SEGMENT; si++) {
373                                                         if (!IS_CHILD(segp->children[si])) {
374                                                                 side    *sidep = &segp->sides[si];
375                                                                 sbyte   *vp = Side_to_verts[si];
376                                                                 int     vv;
377
378                                                                 for (vv=0; vv<4; vv++)
379                                                                         if (*vp++ == relvnum) {
380                                                                                 al += sidep->uvls[vv].l;
381                                                                                 alc++;
382                                                                         }
383                                                         }       // if (segp->children[si == -1) {
384                                                 }       // for (si=0...
385                                         }       // if (relvnum != ...
386                         }       // for (s=0; ...
387
388                         *segptr = -1;
389
390                         //      Now, divide average_light by number of number of occurrences for each vertex
391                         if (alc)
392                                 al /= alc;
393                         else
394                                 al = 0;
395
396                         segptr = seglist;
397                         while (*segptr != -1) {
398                                 int             segnum = *segptr++;
399                                 segment *segp = &Segments[segnum];
400                                 int             sidenum;
401
402                                 for (relvnum=0; relvnum<MAX_VERTICES_PER_SEGMENT; relvnum++)
403                                         if (segp->verts[relvnum] == v)
404                                                 break;
405
406                                 Assert(relvnum < MAX_VERTICES_PER_SEGMENT);     // IMPOSSIBLE! This segment is in seglist, but vertex v does not occur!
407                                 for (sidenum=0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++) {
408                                         int     wid_result;
409                                         wid_result = WALL_IS_DOORWAY(segp, sidenum);
410                                         if ((wid_result != WID_FLY_FLAG) && (wid_result != WID_NO_WALL)) {
411                                                 side *sidep = &segp->sides[sidenum];
412                                                 sbyte   *vp = Side_to_verts[sidenum];
413                                                 int     v;
414
415                                                 for (v=0; v<4; v++)
416                                                         if (*vp++ == relvnum)
417                                                                 sidep->uvls[v].l = al;
418                                         }       // end if
419                                 }       // end sidenum
420                         }       // end while
421
422                 }       // if (Vertex_active[v]...
423
424         }       // for (v=0...
425
426 }
427
428 extern int Doing_lighting_hack_flag;    //      If set, don't mprintf warning messages in gameseg.c/find_point_seg
429 int set_average_light_on_all(void)
430 {
431 //      set_average_light_on_all_fast();
432
433         Doing_lighting_hack_flag = 1;
434         cast_all_light_in_mine(0);
435         Doing_lighting_hack_flag = 0;
436         Update_flags |= UF_WORLD_CHANGED;
437
438 //      int seg, side;
439
440 //      for (seg=0; seg<=Highest_segment_index; seg++)
441 //              for (side=0; side<MAX_SIDES_PER_SEGMENT; side++)
442 //                      if (Segments[seg].segnum != -1)
443 //                              set_average_light_on_side(&Segments[seg], side);
444         return 0;
445 }
446
447 int set_average_light_on_all_quick(void)
448 {
449         cast_all_light_in_mine(1);
450         Update_flags |= UF_WORLD_CHANGED;
451
452         return 0;
453 }
454
455 //      ---------------------------------------------------------------------------------------------
456 fix compute_uv_dist(uvl *uv0, uvl *uv1)
457 {
458         vms_vector      v0,v1;
459
460         v0.x = uv0->u;
461         v0.y = 0;
462         v0.z = uv0->v;
463
464         v1.x = uv1->u;
465         v1.y = 0;
466         v1.z = uv1->v;
467
468         return vm_vec_dist(&v0,&v1);
469 }
470
471 //      ---------------------------------------------------------------------------------------------
472 //      Given a polygon, compress the uv coordinates so that they are as close to 0 as possible.
473 //      Do this by adding a constant u and v to each uv pair.
474 void compress_uv_coordinates(side *sidep)
475 {
476         int     v;
477         fix     uc, vc;
478
479         uc = 0;
480         vc = 0;
481
482         for (v=0; v<4; v++) {
483                 uc += sidep->uvls[v].u;
484                 vc += sidep->uvls[v].v;
485         }
486
487         uc /= 4;
488         vc /= 4;
489         uc = uc & 0xffff0000;
490         vc = vc & 0xffff0000;
491
492         for (v=0; v<4; v++) {
493                 sidep->uvls[v].u -= uc;
494                 sidep->uvls[v].v -= vc;
495         }
496
497 }
498
499 //      ---------------------------------------------------------------------------------------------
500 void compress_uv_coordinates_on_side(side *sidep)
501 {
502         compress_uv_coordinates(sidep);
503 }
504
505 //      ---------------------------------------------------------------------------------------------
506 void validate_uv_coordinates_on_side(segment *segp, int sidenum)
507 {
508 //      int                     v;
509 //      fix                     uv_dist,threed_dist;
510 //      vms_vector      tvec;
511 //      fix                     dist_ratios[MAX_VERTICES_PER_POLY];
512         side                    *sidep = &segp->sides[sidenum];
513 //      sbyte                   *vp = Side_to_verts[sidenum];
514
515 //      This next hunk doesn't seem to affect anything. @mk, 02/13/94
516 //      for (v=1; v<4; v++) {
517 //              uv_dist = compute_uv_dist(&sidep->uvls[v],&sidep->uvls[0]);
518 //              threed_dist = vm_vec_mag(vm_vec_sub(&tvec,&Vertices[segp->verts[vp[v]],&Vertices[vp[0]]));
519 //              dist_ratios[v-1] = fixdiv(uv_dist,threed_dist);
520 //      }
521
522         compress_uv_coordinates_on_side(sidep);
523 }
524
525 void compress_uv_coordinates_in_segment(segment *segp)
526 {
527         int     side;
528
529         for (side=0; side<MAX_SIDES_PER_SEGMENT; side++)
530                 compress_uv_coordinates_on_side(&segp->sides[side]);
531 }
532
533 void compress_uv_coordinates_all(void)
534 {
535         int     seg;
536
537         for (seg=0; seg<=Highest_segment_index; seg++)
538                 if (Segments[seg].segnum != -1)
539                         compress_uv_coordinates_in_segment(&Segments[seg]);
540 }
541
542 void check_lighting_side(segment *sp, int sidenum)
543 {
544         int     v;
545         side    *sidep = &sp->sides[sidenum];
546
547         for (v=0; v<4; v++)
548                 if ((sidep->uvls[v].l > F1_0*16) || (sidep->uvls[v].l < 0))
549                         Int3(); //mprintf(0,"Bogus lighting value in segment %i, side %i, vert %i = %x\n",sp-Segments, side, v, sidep->uvls[v].l);
550 }
551
552 void check_lighting_segment(segment *segp)
553 {
554         int     side;
555
556         for (side=0; side<MAX_SIDES_PER_SEGMENT; side++)
557                 check_lighting_side(segp, side);
558 }
559
560 //      Flag bogus lighting values.
561 void check_lighting_all(void)
562 {
563         int     seg;
564
565         for (seg=0; seg<=Highest_segment_index; seg++)
566                 if (Segments[seg].segnum != -1)
567                         check_lighting_segment(&Segments[seg]);
568 }
569
570 void assign_default_lighting_on_side(segment *segp, int sidenum)
571 {
572         int     v;
573         side    *sidep = &segp->sides[sidenum];
574
575         for (v=0; v<4; v++)
576                 sidep->uvls[v].l = DEFAULT_LIGHTING;
577 }
578
579 void assign_default_lighting(segment *segp)
580 {
581         int     sidenum;
582
583         for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++)
584                 assign_default_lighting_on_side(segp, sidenum);
585 }
586
587 void assign_default_lighting_all(void)
588 {
589         int     seg;
590
591         for (seg=0; seg<=Highest_segment_index; seg++)
592                 if (Segments[seg].segnum != -1)
593                         assign_default_lighting(&Segments[seg]);
594 }
595
596 //      ---------------------------------------------------------------------------------------------
597 void validate_uv_coordinates(segment *segp)
598 {
599         int     s;
600
601         for (s=0; s<MAX_SIDES_PER_SEGMENT; s++)
602                 validate_uv_coordinates_on_side(segp,s);
603
604 }
605
606 //      ---------------------------------------------------------------------------------------------
607 //      For all faces in side, copy uv coordinates from uvs array to face.
608 void copy_uvs_from_side_to_faces(segment *segp, int sidenum, uvl uvls[])
609 {
610         int     v;
611         side    *sidep = &segp->sides[sidenum];
612
613         for (v=0; v<4; v++)
614                 sidep->uvls[v] = uvls[v];
615
616 }
617
618 #ifdef __WATCOMC__
619 fix zhypot(fix a,fix b);
620 #pragma aux zhypot parm [eax] [ebx] value [eax] modify [eax ebx ecx edx] = \
621         "imul   eax" \
622         "xchg eax,ebx" \
623         "mov    ecx,edx" \
624         "imul eax" \
625         "add    eax,ebx" \
626         "adc    edx,ecx" \
627         "call   quad_sqrt";
628 #else
629 fix zhypot(fix a,fix b) {
630         double x = (double)a / 65536;
631         double y = (double)b / 65536;
632         return (long)(sqrt(x * x + y * y) * 65536);
633 }
634 #endif
635
636 //      ---------------------------------------------------------------------------------------------
637 //      Assign lighting value to side, a function of the normal vector.
638 void assign_light_to_side(segment *sp, int sidenum)
639 {
640         int     v;
641         side    *sidep = &sp->sides[sidenum];
642
643         for (v=0; v<4; v++)
644                 sidep->uvls[v].l = DEFAULT_LIGHTING;
645 }
646
647 fix     Stretch_scale_x = F1_0;
648 fix     Stretch_scale_y = F1_0;
649
650 //      ---------------------------------------------------------------------------------------------
651 //      Given u,v coordinates at two vertices, assign u,v coordinates to other two vertices on a side.
652 //      (Actually, assign them to the coordinates in the faces.)
653 //      va, vb = face-relative vertex indices corresponding to uva, uvb.  Ie, they are always in 0..3 and should be looked up in
654 //      Side_to_verts[side] to get the segment relative index.
655 void assign_uvs_to_side(segment *segp, int sidenum, uvl *uva, uvl *uvb, int va, int vb)
656 {
657         int                     vlo,vhi,v0,v1,v2,v3;
658         vms_vector      fvec,rvec,tvec;
659         vms_matrix      rotmat;
660         uvl                     uvls[4],ruvmag,fuvmag,uvlo,uvhi;
661         fix                     fmag,mag01;
662         sbyte                   *vp;
663
664         Assert( (va<4) && (vb<4) );
665         Assert((abs(va - vb) == 1) || (abs(va - vb) == 3));             // make sure the verticies specify an edge
666
667         vp = (sbyte *)&Side_to_verts[sidenum];
668
669         // We want vlo precedes vhi, ie vlo < vhi, or vlo = 3, vhi = 0
670         if (va == ((vb + 1) % 4)) {             // va = vb + 1
671                 vlo = vb;
672                 vhi = va;
673                 uvlo = *uvb;
674                 uvhi = *uva;
675         } else {
676                 vlo = va;
677                 vhi = vb;
678                 uvlo = *uva;
679                 uvhi = *uvb;
680         }
681
682         Assert(((vlo+1) % 4) == vhi);   // If we are on an edge, then uvhi is one more than uvlo (mod 4)
683         uvls[vlo] = uvlo;
684         uvls[vhi] = uvhi;
685
686         // Now we have vlo precedes vhi, compute vertices ((vhi+1) % 4) and ((vhi+2) % 4)
687
688         // Assign u,v scale to a unit length right vector.
689         fmag = zhypot(uvhi.v - uvlo.v,uvhi.u - uvlo.u);
690         if (fmag < 64) {                // this is a fix, so 64 = 1/1024
691                 mprintf((0,"Warning: fmag = %7.3f, using approximate u,v values\n",f2fl(fmag)));
692                 ruvmag.u = F1_0*256;
693                 ruvmag.v = F1_0*256;
694                 fuvmag.u = F1_0*256;
695                 fuvmag.v = F1_0*256;
696         } else {
697                 ruvmag.u = uvhi.v - uvlo.v;
698                 ruvmag.v = uvlo.u - uvhi.u;
699
700                 fuvmag.u = uvhi.u - uvlo.u;
701                 fuvmag.v = uvhi.v - uvlo.v;
702         }
703
704         v0 = segp->verts[vp[vlo]];
705         v1 = segp->verts[vp[vhi]];
706         v2 = segp->verts[vp[(vhi+1)%4]];
707         v3 = segp->verts[vp[(vhi+2)%4]];
708
709         //      Compute right vector by computing orientation matrix from:
710         //              forward vector = vlo:vhi
711         //                right vector = vlo:(vhi+2) % 4
712         vm_vec_sub(&fvec,&Vertices[v1],&Vertices[v0]);
713         vm_vec_sub(&rvec,&Vertices[v3],&Vertices[v0]);
714
715         if (((fvec.x == 0) && (fvec.y == 0) && (fvec.z == 0)) || ((rvec.x == 0) && (rvec.y == 0) && (rvec.z == 0))) {
716                 mprintf((1, "Trapped null vector in assign_uvs_to_side, using identity matrix.\n"));
717                 rotmat = vmd_identity_matrix;
718         } else
719                 vm_vector_2_matrix(&rotmat,&fvec,0,&rvec);
720
721         rvec = rotmat.rvec; vm_vec_negate(&rvec);
722         fvec = rotmat.fvec;
723
724         // mprintf((0, "va = %i, vb = %i\n", va, vb));
725         mag01 = vm_vec_dist(&Vertices[v1],&Vertices[v0]);
726         if ((va == 0) || (va == 2))
727                 mag01 = fixmul(mag01, Stretch_scale_x);
728         else
729                 mag01 = fixmul(mag01, Stretch_scale_y);
730
731         if (mag01 < F1_0/1024 )
732                 editor_status("U, V bogosity in segment #%i, probably on side #%i.  CLEAN UP YOUR MESS!", segp-Segments, sidenum);
733         else {
734                 vm_vec_sub(&tvec,&Vertices[v2],&Vertices[v1]);
735                 uvls[(vhi+1)%4].u = uvhi.u + 
736                         fixdiv(fixmul(ruvmag.u,vm_vec_dotprod(&rvec,&tvec)),mag01) +
737                         fixdiv(fixmul(fuvmag.u,vm_vec_dotprod(&fvec,&tvec)),mag01);
738
739                 uvls[(vhi+1)%4].v = uvhi.v + 
740                         fixdiv(fixmul(ruvmag.v,vm_vec_dotprod(&rvec,&tvec)),mag01) +
741                         fixdiv(fixmul(fuvmag.v,vm_vec_dotprod(&fvec,&tvec)),mag01);
742
743
744                 vm_vec_sub(&tvec,&Vertices[v3],&Vertices[v0]);
745                 uvls[(vhi+2)%4].u = uvlo.u + 
746                         fixdiv(fixmul(ruvmag.u,vm_vec_dotprod(&rvec,&tvec)),mag01) +
747                         fixdiv(fixmul(fuvmag.u,vm_vec_dotprod(&fvec,&tvec)),mag01);
748
749                 uvls[(vhi+2)%4].v = uvlo.v + 
750                         fixdiv(fixmul(ruvmag.v,vm_vec_dotprod(&rvec,&tvec)),mag01) +
751                         fixdiv(fixmul(fuvmag.v,vm_vec_dotprod(&fvec,&tvec)),mag01);
752
753                 uvls[(vhi+1)%4].l = uvhi.l;
754                 uvls[(vhi+2)%4].l = uvlo.l;
755
756                 copy_uvs_from_side_to_faces(segp, sidenum, uvls);
757         }
758 }
759
760
761 int Vmag = VMAG;
762
763 // -----------------------------------------------------------------------------------------------------------
764 //      Assign default uvs to side.
765 //      This means:
766 //              v0 = 0,0
767 //              v1 = k,0 where k is 3d size dependent
768 //      v2, v3 assigned by assign_uvs_to_side
769 void assign_default_uvs_to_side(segment *segp,int side)
770 {
771         uvl                     uv0,uv1;
772         sbyte                   *vp;
773
774         uv0.u = 0;
775         uv0.v = 0;
776
777         vp = Side_to_verts[side];
778
779         uv1.u = 0;
780         uv1.v = Num_tilings * fixmul(Vmag, vm_vec_dist(&Vertices[segp->verts[vp[1]]],&Vertices[segp->verts[vp[0]]]));
781
782         assign_uvs_to_side(segp, side, &uv0, &uv1, 0, 1);
783 }
784
785 // -----------------------------------------------------------------------------------------------------------
786 //      Assign default uvs to side.
787 //      This means:
788 //              v0 = 0,0
789 //              v1 = k,0 where k is 3d size dependent
790 //      v2, v3 assigned by assign_uvs_to_side
791 void stretch_uvs_from_curedge(segment *segp, int side)
792 {
793         uvl                     uv0,uv1;
794         int                     v0, v1;
795
796         v0 = Curedge;
797         v1 = (v0 + 1) % 4;
798
799         uv0.u = segp->sides[side].uvls[v0].u;
800         uv0.v = segp->sides[side].uvls[v0].v;
801
802         uv1.u = segp->sides[side].uvls[v1].u;
803         uv1.v = segp->sides[side].uvls[v1].v;
804
805         assign_uvs_to_side(segp, side, &uv0, &uv1, v0, v1);
806 }
807
808 // --------------------------------------------------------------------------------------------------------------
809 //      Assign default uvs to a segment.
810 void assign_default_uvs_to_segment(segment *segp)
811 {
812         int     s;
813
814         for (s=0; s<MAX_SIDES_PER_SEGMENT; s++) {
815                 assign_default_uvs_to_side(segp,s);
816                 assign_light_to_side(segp, s);
817         }
818 }
819
820
821 // -- mk021394 -- // --------------------------------------------------------------------------------------------------------------
822 // -- mk021394 -- //    Find the face:poly:vertex index in base_seg:base_common_side which is segment relative vertex v1
823 // -- mk021394 -- //    This very specific routine is subsidiary to med_assign_uvs_to_side.
824 // -- mk021394 -- void get_face_and_vert(segment *base_seg, int base_common_side, int v1, int *ff, int *vv, int *pi)
825 // -- mk021394 -- {
826 // -- mk021394 --       int     p,f,v;
827 // -- mk021394 -- 
828 // -- mk021394 --       for (f=0; f<base_seg->sides[base_common_side].num_faces; f++) {
829 // -- mk021394 --               face *fp = &base_seg->sides[base_common_side].faces[f];
830 // -- mk021394 --               for (p=0; p<fp->num_polys; p++) {
831 // -- mk021394 --                       poly *pp = &fp->polys[p];
832 // -- mk021394 --                       for (v=0; v<pp->num_vertices; v++)
833 // -- mk021394 --                               if (pp->verts[v] == v1) {
834 // -- mk021394 --                                       *ff = f;
835 // -- mk021394 --                                       *vv = v;
836 // -- mk021394 --                                       *pi = p;
837 // -- mk021394 --                                       return;
838 // -- mk021394 --                               }
839 // -- mk021394 --               }
840 // -- mk021394 --       }
841 // -- mk021394 -- 
842 // -- mk021394 --       Assert(0);      // Error -- Couldn't find face:vertex which matched vertex v1 on base_seg:base_common_side
843 // -- mk021394 -- }
844
845 // -- mk021394 -- // --------------------------------------------------------------------------------------------------------------
846 // -- mk021394 -- //    Find the vertex index in base_seg:base_common_side which is segment relative vertex v1
847 // -- mk021394 -- //    This very specific routine is subsidiary to med_assign_uvs_to_side.
848 // -- mk021394 -- void get_side_vert(segment *base_seg,int base_common_side,int v1,int *vv)
849 // -- mk021394 -- {
850 // -- mk021394 --       int     p,f,v;
851 // -- mk021394 -- 
852 // -- mk021394 --       Assert((base_seg->sides[base_common_side].tri_edge == 0) || (base_seg->sides[base_common_side].tri_edge == 1));
853 // -- mk021394 --       Assert(base_seg->sides[base_common_side].num_faces <= 2);
854 // -- mk021394 -- 
855 // -- mk021394 --       for (f=0; f<base_seg->sides[base_common_side].num_faces; f++) {
856 // -- mk021394 --               face *fp = &base_seg->sides[base_common_side].faces[f];
857 // -- mk021394 --               for (p=0; p<fp->num_polys; p++) {
858 // -- mk021394 --                       poly    *pp = &fp->polys[p];
859 // -- mk021394 --                       for (v=0; v<pp->num_vertices; v++)
860 // -- mk021394 --                               if (pp->verts[v] == v1) {
861 // -- mk021394 --                                       if (pp->num_vertices == 4) {
862 // -- mk021394 --                                               *vv = v;
863 // -- mk021394 --                                               return;
864 // -- mk021394 --                                       }
865 // -- mk021394 -- 
866 // -- mk021394 --                                       if (base_seg->sides[base_common_side].tri_edge == 0) {  // triangulated 012, 023, so if f==0, *vv = v, if f==1, *vv = v if v=0, else v+1
867 // -- mk021394 --                                               if ((f == 1) && (v > 0))
868 // -- mk021394 --                                                       v++;
869 // -- mk021394 --                                               *vv = v;
870 // -- mk021394 --                                               return;
871 // -- mk021394 --                                       } else {                                                                // triangulated 013, 123
872 // -- mk021394 --                                               if (f == 0) {
873 // -- mk021394 --                                                       if (v == 2)
874 // -- mk021394 --                                                               v++;
875 // -- mk021394 --                                               } else
876 // -- mk021394 --                                                       v++;
877 // -- mk021394 --                                               *vv = v;
878 // -- mk021394 --                                               return;
879 // -- mk021394 --                                       }
880 // -- mk021394 --                               }
881 // -- mk021394 --               }
882 // -- mk021394 --       }
883 // -- mk021394 -- 
884 // -- mk021394 --       Assert(0);      // Error -- Couldn't find face:vertex which matched vertex v1 on base_seg:base_common_side
885 // -- mk021394 -- }
886
887 //--rotate_uvs-- // --------------------------------------------------------------------------------------------------------------
888 //--rotate_uvs-- //     Rotate uvl coordinates uva, uvb about their center point by heading
889 //--rotate_uvs-- void rotate_uvs(uvl *uva, uvl *uvb, vms_vector *rvec)
890 //--rotate_uvs-- {
891 //--rotate_uvs--        uvl     uvc, uva1, uvb1;
892 //--rotate_uvs-- 
893 //--rotate_uvs--        uvc.u = (uva->u + uvb->u)/2;
894 //--rotate_uvs--        uvc.v = (uva->v + uvb->v)/2;
895 //--rotate_uvs-- 
896 //--rotate_uvs--        uva1.u = fixmul(uva->u - uvc.u, rvec->x) - fixmul(uva->v - uvc.v, rvec->z);
897 //--rotate_uvs--        uva1.v = fixmul(uva->u - uvc.u, rvec->z) + fixmul(uva->v - uvc.v, rvec->x);
898 //--rotate_uvs-- 
899 //--rotate_uvs--        uva->u = uva1.u + uvc.u;
900 //--rotate_uvs--        uva->v = uva1.v + uvc.v;
901 //--rotate_uvs-- 
902 //--rotate_uvs--        uvb1.u = fixmul(uvb->u - uvc.u, rvec->x) - fixmul(uvb->v - uvc.v, rvec->z);
903 //--rotate_uvs--        uvb1.v = fixmul(uvb->u - uvc.u, rvec->z) + fixmul(uvb->v - uvc.v, rvec->x);
904 //--rotate_uvs-- 
905 //--rotate_uvs--        uvb->u = uvb1.u + uvc.u;
906 //--rotate_uvs--        uvb->v = uvb1.v + uvc.v;
907 //--rotate_uvs-- }
908
909
910 // --------------------------------------------------------------------------------------------------------------
911 void med_assign_uvs_to_side(segment *con_seg, int con_common_side, segment *base_seg, int base_common_side, int abs_id1, int abs_id2)
912 {
913         uvl             uv1,uv2;
914         int             v,bv1,bv2, vv1, vv2;
915         int             cv1=0, cv2=0;
916
917         bv1 = -1;       bv2 = -1;
918
919         // Find which vertices in segment match abs_id1, abs_id2
920         for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) {
921                 if (base_seg->verts[v] == abs_id1)
922                         bv1 = v;
923                 if (base_seg->verts[v] == abs_id2)
924                         bv2 = v;
925                 if (con_seg->verts[v] == abs_id1)
926                         cv1 = v;
927                 if (con_seg->verts[v] == abs_id2)
928                         cv2 = v;
929         }
930
931         //      Now, bv1, bv2 are segment relative vertices in base segment which are the same as absolute vertices abs_id1, abs_id2
932         //           cv1, cv2 are segment relative vertices in conn segment which are the same as absolute vertices abs_id1, abs_id2
933
934         Assert((bv1 != -1) && (bv2 != -1) && (cv1 != -1) && (cv2 != -1));
935         Assert((uv1.u != uv2.u) || (uv1.v != uv2.v));
936
937         //      Now, scan 4 vertices in base side and 4 vertices in connected side.
938         //      Set uv1, uv2 to uv coordinates from base side which correspond to vertices bv1, bv2.
939         //      Set vv1, vv2 to relative vertex ids (in 0..3) in connecting side which correspond to cv1, cv2
940         vv1 = -1;       vv2 = -1;
941         for (v=0; v<4; v++) {
942                 if (bv1 == Side_to_verts[base_common_side][v])
943                         uv1 = base_seg->sides[base_common_side].uvls[v];
944
945                 if (bv2 == Side_to_verts[base_common_side][v])
946                         uv2 = base_seg->sides[base_common_side].uvls[v];
947
948                 if (cv1 == Side_to_verts[con_common_side][v])
949                         vv1 = v;
950
951                 if (cv2 == Side_to_verts[con_common_side][v])
952                         vv2 = v;
953         }
954
955         Assert( (vv1 != -1) && (vv2 != -1) );
956         assign_uvs_to_side(con_seg, con_common_side, &uv1, &uv2, vv1, vv2);
957 }
958
959
960 // -----------------------------------------------------------------------------
961 //      Given a base and a connecting segment, a side on each of those segments and two global vertex ids,
962 //      determine which side in each of the segments shares those two vertices.
963 //      This is used to propagate a texture map id to a connecting segment in an expected and desired way.
964 //      Since we can attach any side of a segment to any side of another segment, and do so in each case in
965 //      four different rotations (for a total of 6*6*4 = 144 ways), not having this nifty function will cause
966 //      great confusion.
967 void get_side_ids(segment *base_seg, segment *con_seg, int base_side, int con_side, int abs_id1, int abs_id2, int *base_common_side, int *con_common_side)
968 {
969         char            *base_vp,*con_vp;
970         int             v0,side;
971
972         *base_common_side = -1;
973
974         //      Find side in base segment which contains the two global vertex ids.
975         for (side=0; side<MAX_SIDES_PER_SEGMENT; side++) {
976                 if (side != base_side) {
977                         base_vp = Side_to_verts[side];
978                         for (v0=0; v0<4; v0++)
979                                 if (((base_seg->verts[(int) base_vp[v0]] == abs_id1) && (base_seg->verts[(int) base_vp[(v0+1) % 4]] == abs_id2)) || ((base_seg->verts[(int) base_vp[v0]] == abs_id2) && (base_seg->verts[(int)base_vp[ (v0+1) % 4]] == abs_id1))) {
980                                         Assert(*base_common_side == -1);                // This means two different sides shared the same edge with base_side == impossible!
981                                         *base_common_side = side;
982                                 }
983                 }
984         }
985
986         // Note: For connecting segment, process vertices in reversed order.
987         *con_common_side = -1;
988
989         //      Find side in connecting segment which contains the two global vertex ids.
990         for (side=0; side<MAX_SIDES_PER_SEGMENT; side++) {
991                 if (side != con_side) {
992                         con_vp = Side_to_verts[side];
993                         for (v0=0; v0<4; v0++)
994                                 if (((con_seg->verts[(int) con_vp[(v0 + 1) % 4]] == abs_id1) && (con_seg->verts[(int) con_vp[v0]] == abs_id2)) || ((con_seg->verts[(int) con_vp[(v0 + 1) % 4]] == abs_id2) && (con_seg->verts[(int) con_vp[v0]] == abs_id1))) {
995                                         Assert(*con_common_side == -1);         // This means two different sides shared the same edge with con_side == impossible!
996                                         *con_common_side = side;
997                                 }
998                 }
999         }
1000
1001 // mprintf((0,"side %3i adjacent to side %3i\n",*base_common_side,*con_common_side));
1002
1003         Assert((*base_common_side != -1) && (*con_common_side != -1));
1004 }
1005
1006 // -----------------------------------------------------------------------------
1007 //      Propagate texture map u,v coordinates from base_seg:base_side to con_seg:con_side.
1008 //      The two vertices abs_id1 and abs_id2 are the only two vertices common to the two sides.
1009 //      If uv_only_flag is 1, then don't assign texture map ids, only update the uv coordinates
1010 //      If uv_only_flag is -1, then ONLY assign texture map ids, don't update the uv coordinates
1011 void propagate_tmaps_to_segment_side(segment *base_seg, int base_side, segment *con_seg, int con_side, int abs_id1, int abs_id2, int uv_only_flag)
1012 {
1013         int             base_common_side,con_common_side;
1014         int             tmap_num;
1015
1016         Assert ((uv_only_flag == -1) || (uv_only_flag == 0) || (uv_only_flag == 1));
1017
1018         // Set base_common_side = side in base_seg which contains edge abs_id1:abs_id2
1019         // Set con_common_side = side in con_seg which contains edge abs_id1:abs_id2
1020         if (base_seg != con_seg)
1021                 get_side_ids(base_seg, con_seg, base_side, con_side, abs_id1, abs_id2, &base_common_side, &con_common_side);
1022         else {
1023                 base_common_side = base_side;
1024                 con_common_side = con_side;
1025         }
1026
1027         // Now, all faces in con_seg which are on side con_common_side get their tmap_num set to whatever tmap is assigned
1028         // to whatever face I find which is on side base_common_side.
1029         // First, find tmap_num for base_common_side.  If it doesn't exist (ie, there is a connection there), look at the segment
1030         // that is connected through it.
1031         if (!IS_CHILD(con_seg->children[con_common_side])) {
1032                 if (!IS_CHILD(base_seg->children[base_common_side])) {
1033                         // There is at least one face here, so get the tmap_num from there.
1034                         tmap_num = base_seg->sides[base_common_side].tmap_num;
1035
1036                         // Now assign all faces in the connecting segment on side con_common_side to tmap_num.
1037                         if ((uv_only_flag == -1) || (uv_only_flag == 0))
1038                                 con_seg->sides[con_common_side].tmap_num = tmap_num;
1039
1040                         if (uv_only_flag != -1)
1041                                 med_assign_uvs_to_side(con_seg, con_common_side, base_seg, base_common_side, abs_id1, abs_id2);
1042
1043                 } else {                        // There are no faces here, there is a connection, trace through the connection.
1044                         int     cside;
1045
1046                         cside = find_connect_side(base_seg, &Segments[base_seg->children[base_common_side]]);
1047                         propagate_tmaps_to_segment_side(&Segments[base_seg->children[base_common_side]], cside, con_seg, con_side, abs_id1, abs_id2, uv_only_flag);
1048                 }
1049         }
1050
1051 }
1052
1053 sbyte   Edge_between_sides[MAX_SIDES_PER_SEGMENT][MAX_SIDES_PER_SEGMENT][2] = {
1054 //              left            top             right           bottom  back            front
1055         { {-1,-1}, { 3, 7}, {-1,-1}, { 2, 6}, { 6, 7}, { 2, 3} },       // left
1056         { { 3, 7}, {-1,-1}, { 0, 4}, {-1,-1}, { 4, 7}, { 0, 3} },       // top
1057         { {-1,-1}, { 0, 4}, {-1,-1}, { 1, 5}, { 4, 5}, { 0, 1} },       // right
1058         { { 2, 6}, {-1,-1}, { 1, 5}, {-1,-1}, { 5, 6}, { 1, 2} },       // bottom
1059         { { 6, 7}, { 4, 7}, { 4, 5}, { 5, 6}, {-1,-1}, {-1,-1} },       // back
1060         { { 2, 3}, { 0, 3}, { 0, 1}, { 1, 2}, {-1,-1}, {-1,-1} }};      // front
1061
1062 // -----------------------------------------------------------------------------
1063 //      Propagate texture map u,v coordinates to base_seg:back_side from base_seg:some-other-side
1064 //      There is no easy way to figure out which side is adjacent to another side along some edge, so we do a bit of searching.
1065 void med_propagate_tmaps_to_back_side(segment *base_seg, int back_side, int uv_only_flag)
1066 {
1067         int     v1=0,v2=0;
1068         int     s,ss,tmap_num,back_side_tmap;
1069
1070         if (IS_CHILD(base_seg->children[back_side]))
1071                 return;         // connection, so no sides here.
1072
1073         //      Scan all sides, look for an occupied side which is not back_side or Side_opposite[back_side]
1074         for (s=0; s<MAX_SIDES_PER_SEGMENT; s++)
1075                 if ((s != back_side) && (s != Side_opposite[back_side])) {
1076                         v1 = Edge_between_sides[s][back_side][0];
1077                         v2 = Edge_between_sides[s][back_side][1];
1078                         goto found1;
1079                 }
1080         Assert(0);              // Error -- couldn't find edge != back_side and Side_opposite[back_side]
1081 found1: ;
1082         Assert( (v1 != -1) && (v2 != -1));              // This means there was no shared edge between the two sides.
1083
1084         propagate_tmaps_to_segment_side(base_seg, s, base_seg, back_side, base_seg->verts[v1], base_seg->verts[v2], uv_only_flag);
1085
1086         //      Assign an unused tmap id to the back side.
1087         //      Note that this can get undone by the caller if this was not part of a new attach, but a rotation or a scale (which
1088         //      both do attaches).
1089         //      First see if tmap on back side is anywhere else.
1090         if (!uv_only_flag) {
1091                 back_side_tmap = base_seg->sides[back_side].tmap_num;
1092                 for (s=0; s<MAX_SIDES_PER_SEGMENT; s++) {
1093                         if (s != back_side)
1094                                 if (base_seg->sides[s].tmap_num == back_side_tmap) {
1095                                         for (tmap_num=0; tmap_num < MAX_SIDES_PER_SEGMENT; tmap_num++) {
1096                                                 for (ss=0; ss<MAX_SIDES_PER_SEGMENT; ss++)
1097                                                         if (ss != back_side)
1098                                                                 if (base_seg->sides[ss].tmap_num == New_segment.sides[tmap_num].tmap_num)
1099                                                                         goto found2;            // current texture map (tmap_num) is used on current (ss) side, so try next one
1100                                                 // Current texture map (tmap_num) has not been used, assign to all faces on back_side.
1101                                                 base_seg->sides[back_side].tmap_num = New_segment.sides[tmap_num].tmap_num;
1102                                                 goto done1;
1103                                         found2: ;
1104                                         }
1105                                 }
1106                 }
1107         done1: ;
1108         }
1109
1110 }
1111
1112 int fix_bogus_uvs_on_side(void)
1113 {
1114         med_propagate_tmaps_to_back_side(Cursegp, Curside, 1);
1115         return 0;
1116 }
1117
1118 void fix_bogus_uvs_on_side1(segment *sp, int sidenum, int uvonly_flag)
1119 {
1120         side    *sidep = &sp->sides[sidenum];
1121
1122         if ((sidep->uvls[0].u == 0) && (sidep->uvls[1].u == 0) && (sidep->uvls[2].u == 0)) {
1123                 mprintf((0,"Found bogus segment %i, side %i\n", sp-Segments, sidenum));
1124                 med_propagate_tmaps_to_back_side(sp, sidenum, uvonly_flag);
1125         }
1126 }
1127
1128 void fix_bogus_uvs_seg(segment *segp)
1129 {
1130         int     s;
1131
1132         for (s=0; s<MAX_SIDES_PER_SEGMENT; s++) {
1133                 if (!IS_CHILD(segp->children[s]))
1134                         fix_bogus_uvs_on_side1(segp, s, 1);
1135         }
1136 }
1137
1138 int fix_bogus_uvs_all(void)
1139 {
1140         int     seg;
1141
1142         for (seg=0; seg<=Highest_segment_index; seg++)
1143                 if (Segments[seg].segnum != -1)
1144                         fix_bogus_uvs_seg(&Segments[seg]);
1145         return 0;
1146 }
1147
1148 // -----------------------------------------------------------------------------
1149 //      Propagate texture map u,v coordinates to base_seg:back_side from base_seg:some-other-side
1150 //      There is no easy way to figure out which side is adjacent to another side along some edge, so we do a bit of searching.
1151 void med_propagate_tmaps_to_any_side(segment *base_seg, int back_side, int tmap_num, int uv_only_flag)
1152 {
1153         int     v1=0,v2=0;
1154         int     s;
1155
1156         //      Scan all sides, look for an occupied side which is not back_side or Side_opposite[back_side]
1157         for (s=0; s<MAX_SIDES_PER_SEGMENT; s++)
1158                 if ((s != back_side) && (s != Side_opposite[back_side])) {
1159                         v1 = Edge_between_sides[s][back_side][0];
1160                         v2 = Edge_between_sides[s][back_side][1];
1161                         goto found1;
1162                 }
1163         Assert(0);              // Error -- couldn't find edge != back_side and Side_opposite[back_side]
1164 found1: ;
1165         Assert( (v1 != -1) && (v2 != -1));              // This means there was no shared edge between the two sides.
1166
1167         propagate_tmaps_to_segment_side(base_seg, s, base_seg, back_side, base_seg->verts[v1], base_seg->verts[v2], uv_only_flag);
1168
1169         base_seg->sides[back_side].tmap_num = tmap_num;
1170
1171 }
1172
1173 // -----------------------------------------------------------------------------
1174 //      Segment base_seg is connected through side base_side to segment con_seg on con_side.
1175 //      For all walls in con_seg, find the wall in base_seg which shares an edge.  Copy tmap_num
1176 //      from that side in base_seg to the wall in con_seg.  If the wall in base_seg is not present
1177 //      (ie, there is another segment connected through it), follow the connection through that
1178 //      segment to get the wall in the connected segment which shares the edge, and get tmap_num from there.
1179 void propagate_tmaps_to_segment_sides(segment *base_seg, int base_side, segment *con_seg, int con_side, int uv_only_flag)
1180 {
1181         char            *base_vp,*con_vp;
1182         short           abs_id1,abs_id2;
1183         int             v;
1184
1185         base_vp = Side_to_verts[base_side];
1186         con_vp = Side_to_verts[con_side];
1187
1188         // Do for each edge on connecting face.
1189         for (v=0; v<4; v++) {
1190                 abs_id1 = base_seg->verts[(int) base_vp[v]];
1191                 abs_id2 = base_seg->verts[(int) base_vp[(v+1) % 4]];
1192                 propagate_tmaps_to_segment_side(base_seg, base_side, con_seg, con_side, abs_id1, abs_id2, uv_only_flag);
1193         }
1194
1195 }
1196
1197 // -----------------------------------------------------------------------------
1198 //      Propagate texture maps in base_seg to con_seg.
1199 //      For each wall in con_seg, find the wall in base_seg which shared an edge.  Copy tmap_num from that
1200 //      wall in base_seg to the wall in con_seg.  If the wall in base_seg is not present, then look at the
1201 //      segment connected through base_seg through the wall.  The wall with a common edge is the new wall
1202 //      of interest.  Continue searching in this way until a wall of interest is present.
1203 void med_propagate_tmaps_to_segments(segment *base_seg,segment *con_seg, int uv_only_flag)
1204 {
1205         int             s;
1206
1207 // mprintf((0,"Propagating segments from %i to %i\n",base_seg-Segments,con_seg-Segments));
1208         for (s=0; s<MAX_SIDES_PER_SEGMENT; s++)
1209                 if (base_seg->children[s] == con_seg-Segments)
1210                         propagate_tmaps_to_segment_sides(base_seg, s, con_seg, find_connect_side(base_seg, con_seg), uv_only_flag);
1211
1212         con_seg->static_light = base_seg->static_light;
1213
1214         validate_uv_coordinates(con_seg);
1215 }
1216
1217
1218 // -------------------------------------------------------------------------------
1219 //      Copy texture map uvs from srcseg to destseg.
1220 //      If two segments have different face structure (eg, destseg has two faces on side 3, srcseg has only 1)
1221 //      then assign uvs according to side vertex id, not face vertex id.
1222 void copy_uvs_seg_to_seg(segment *destseg,segment *srcseg)
1223 {
1224         int     s;
1225
1226         for (s=0; s<MAX_SIDES_PER_SEGMENT; s++) {
1227                 destseg->sides[s].tmap_num = srcseg->sides[s].tmap_num;
1228                 destseg->sides[s].tmap_num2 = srcseg->sides[s].tmap_num2;
1229         }
1230
1231         destseg->static_light = srcseg->static_light;
1232 }
1233
1234 //      _________________________________________________________________________________________________________________________
1235 //      Maximum distance between a segment containing light to a segment to receive light.
1236 #define LIGHT_DISTANCE_THRESHOLD        (F1_0*80)
1237 fix     Magical_light_constant = (F1_0*16);
1238
1239 // int  Seg0, Seg1;
1240
1241 //int   Bugseg = 27;
1242
1243 typedef struct {
1244         sbyte                   flag, hit_type;
1245         vms_vector      vector;
1246 } hash_info;
1247
1248 #define FVI_HASH_SIZE 8
1249 #define FVI_HASH_AND_MASK (FVI_HASH_SIZE - 1)
1250
1251 //      Note: This should be malloced.
1252 //                      Also, the vector should not be 12 bytes, you should only care about some smaller portion of it.
1253 hash_info       fvi_cache[FVI_HASH_SIZE];
1254 int     Hash_hits=0, Hash_retries=0, Hash_calcs=0;
1255
1256 //      -----------------------------------------------------------------------------------------
1257 //      Set light from a light source.
1258 //      Light incident on a surface is defined by the light incident at its points.
1259 //      Light at a point = K * (V . N) / d
1260 //      where:
1261 //              K = some magical constant to make everything look good
1262 //              V = normalized vector from light source to point
1263 //              N = surface normal at point
1264 //              d = distance from light source to point
1265 //      (Note that the above equation can be simplified to K * (VV . N) / d^2 where VV = non-normalized V)
1266 //      Light intensity emitted from a light source is defined to be cast from four points.
1267 //      These four points are 1/64 of the way from the corners of the light source to the center
1268 //      of its segment.  By assuming light is cast from these points, rather than from on the
1269 //      light surface itself, light will be properly cast on the light surface.  Otherwise, the
1270 //      vector V would be the null vector.
1271 //      If quick_light set, then don't use find_vector_intersection
1272 void cast_light_from_side(segment *segp, int light_side, fix light_intensity, int quick_light)
1273 {
1274         vms_vector      segment_center;
1275         int                     segnum,sidenum,vertnum, lightnum;
1276
1277         compute_segment_center(&segment_center, segp);
1278
1279 //mprintf((0, "From [%i %i %7.3f]:  ", segp-Segments, light_side, f2fl(light_intensity)));
1280
1281         //      Do for four lights, one just inside each corner of side containing light.
1282         for (lightnum=0; lightnum<4; lightnum++) {
1283                 int                     light_vertex_num, i;
1284                 vms_vector      vector_to_center;
1285                 vms_vector      light_location;
1286                 // fix                  inverse_segment_magnitude;
1287
1288                 light_vertex_num = segp->verts[Side_to_verts[light_side][lightnum]];
1289                 light_location = Vertices[light_vertex_num];
1290
1291
1292         //      New way, 5/8/95: Move towards center irrespective of size of segment.
1293         vm_vec_sub(&vector_to_center, &segment_center, &light_location);
1294         vm_vec_normalize_quick(&vector_to_center);
1295         vm_vec_add2(&light_location, &vector_to_center);
1296
1297 // -- Old way, before 5/8/95 --         // -- This way was kind of dumb.  In larger segments, you move LESS towards the center.
1298 // -- Old way, before 5/8/95 --         //    Main problem, though, is vertices don't illuminate themselves well in oblong segments because the dot product is small.
1299 // -- Old way, before 5/8/95 --         vm_vec_sub(&vector_to_center, &segment_center, &light_location);
1300 // -- Old way, before 5/8/95 --         inverse_segment_magnitude = fixdiv(F1_0/5, vm_vec_mag(&vector_to_center));
1301 // -- Old way, before 5/8/95 --         vm_vec_scale_add(&light_location, &light_location, &vector_to_center, inverse_segment_magnitude);
1302
1303                 for (segnum=0; segnum<=Highest_segment_index; segnum++) {
1304                         segment         *rsegp = &Segments[segnum];
1305                         vms_vector      r_segment_center;
1306                         fix                     dist_to_rseg;
1307
1308                         for (i=0; i<FVI_HASH_SIZE; i++)
1309                                 fvi_cache[i].flag = 0;
1310
1311                         //      efficiency hack (I hope!), for faraway segments, don't check each point.
1312                         compute_segment_center(&r_segment_center, rsegp);
1313                         dist_to_rseg = vm_vec_dist_quick(&r_segment_center, &segment_center);
1314
1315                         if (dist_to_rseg <= LIGHT_DISTANCE_THRESHOLD) {
1316                                 for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
1317                                         if (WALL_IS_DOORWAY(rsegp, sidenum) != WID_NO_WALL) {
1318                                                 side                    *rsidep = &rsegp->sides[sidenum];
1319                                                 vms_vector      *side_normalp = &rsidep->normals[0];    //      kinda stupid? always use vector 0.
1320
1321 //mprintf((0, "[%i %i], ", rsegp-Segments, sidenum));
1322                                                 for (vertnum=0; vertnum<4; vertnum++) {
1323                                                         fix                     distance_to_point, light_at_point, light_dot;
1324                                                         vms_vector      vert_location, vector_to_light;
1325                                                         int                     abs_vertnum;
1326
1327                                                         abs_vertnum = rsegp->verts[Side_to_verts[sidenum][vertnum]];
1328                                                         vert_location = Vertices[abs_vertnum];
1329                                                         distance_to_point = vm_vec_dist_quick(&vert_location, &light_location);
1330                                                         vm_vec_sub(&vector_to_light, &light_location, &vert_location);
1331                                                         vm_vec_normalize(&vector_to_light);
1332
1333                                                         //      Hack: In oblong segments, it's possible to get a very small dot product
1334                                                         //      but the light source is very nearby (eg, illuminating light itself!).
1335                                                         light_dot = vm_vec_dot(&vector_to_light, side_normalp);
1336                                                         if (distance_to_point < F1_0)
1337                                                                 if (light_dot > 0)
1338                                                                         light_dot = (light_dot + F1_0)/2;
1339
1340                                                         if (light_dot > 0) {
1341                                                                 light_at_point = fixdiv(fixmul(light_dot, light_dot), distance_to_point);
1342                                                                 light_at_point = fixmul(light_at_point, Magical_light_constant);
1343                                                                 if (light_at_point >= 0) {
1344                                                                         fvi_info        hit_data;
1345                                                                         int             hit_type;
1346                                                                         vms_vector      vert_location_1, r_vector_to_center;
1347                                                                         fix             inverse_segment_magnitude;
1348
1349                                                                         vm_vec_sub(&r_vector_to_center, &r_segment_center, &vert_location);
1350                                                                         inverse_segment_magnitude = fixdiv(F1_0/3, vm_vec_mag(&r_vector_to_center));
1351                                                                         vm_vec_scale_add(&vert_location_1, &vert_location, &r_vector_to_center, inverse_segment_magnitude);
1352                                                                         vert_location = vert_location_1;
1353
1354 //if ((segp-Segments == 199) && (rsegp-Segments==199))
1355 //      Int3();
1356 // Seg0 = segp-Segments;
1357 // Seg1 = rsegp-Segments;
1358                                                                         if (!quick_light) {
1359                                                                                 int hash_value = Side_to_verts[sidenum][vertnum];
1360                                                                                 hash_info       *hashp = &fvi_cache[hash_value];
1361                                                                                 while (1) {
1362                                                                                         if (hashp->flag) {
1363                                                                                                 if ((hashp->vector.x == vector_to_light.x) && (hashp->vector.y == vector_to_light.y) && (hashp->vector.z == vector_to_light.z)) {
1364 //mprintf((0, "{CACHE %4x} ", hash_value));
1365                                                                                                         hit_type = hashp->hit_type;
1366                                                                                                         Hash_hits++;
1367                                                                                                         break;
1368                                                                                                 } else {
1369                                                                                                         Int3(); // How is this possible?  Should be no hits!
1370                                                                                                         Hash_retries++;
1371                                                                                                         hash_value = (hash_value+1) & FVI_HASH_AND_MASK;
1372                                                                                                         hashp = &fvi_cache[hash_value];
1373                                                                                                 }
1374                                                                                         } else {
1375 //mprintf((0, "\nH:%04x ", hash_value));
1376                                                                                                 fvi_query fq;
1377
1378                                                                                                 Hash_calcs++;
1379                                                                                                 hashp->vector = vector_to_light;
1380                                                                                                 hashp->flag = 1;
1381
1382                                                                                                 fq.p0                                           = &light_location;
1383                                                                                                 fq.startseg                             = segp-Segments;
1384                                                                                                 fq.p1                                           = &vert_location;
1385                                                                                                 fq.rad                                  = 0;
1386                                                                                                 fq.thisobjnum                   = -1;
1387                                                                                                 fq.ignore_obj_list      = NULL;
1388                                                                                                 fq.flags                                        = 0;
1389
1390                                                                                                 hit_type = find_vector_intersection(&fq,&hit_data);
1391                                                                                                 hashp->hit_type = hit_type;
1392                                                                                                 break;
1393                                                                                         }
1394                                                                                 }
1395                                                                         } else
1396                                                                                 hit_type = HIT_NONE;
1397 //mprintf((0, "hit=%i ", hit_type));
1398                                                                         switch (hit_type) {
1399                                                                                 case HIT_NONE:
1400                                                                                         light_at_point = fixmul(light_at_point, light_intensity);
1401                                                                                         rsidep->uvls[vertnum].l += light_at_point;
1402 //mprintf((0, "(%5.2f) ", f2fl(light_at_point)));
1403                                                                                         if (rsidep->uvls[vertnum].l > F1_0)
1404                                                                                                 rsidep->uvls[vertnum].l = F1_0;
1405                                                                                         break;
1406                                                                                 case HIT_WALL:
1407                                                                                         break;
1408                                                                                 case HIT_OBJECT:
1409                                                                                         Int3(); // Hit object, should be ignoring objects!
1410                                                                                         break;
1411                                                                                 case HIT_BAD_P0:
1412                                                                                         Int3(); //      Ugh, this thing again, what happened, what does it mean?
1413                                                                                         break;
1414                                                                         }
1415                                                                 }       //      end if (light_at_point...
1416                                                         }       // end if (light_dot >...
1417                                                 }       //      end for (vertnum=0...
1418                                         }       //      end if (rsegp...
1419                                 }       //      end for (sidenum=0...
1420                         }       //      end if (dist_to_rseg...
1421
1422                 }       //      end for (segnum=0...
1423
1424         }       //      end for (lightnum=0...
1425
1426 //mprintf((0, "\n"));
1427 }
1428
1429
1430 //      ------------------------------------------------------------------------------------------
1431 //      Zero all lighting values.
1432 void calim_zero_light_values(void)
1433 {
1434         int     segnum, sidenum, vertnum;
1435
1436         for (segnum=0; segnum<=Highest_segment_index; segnum++) {
1437                 segment *segp = &Segments[segnum];
1438                 for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
1439                         side    *sidep = &segp->sides[sidenum];
1440                         for (vertnum=0; vertnum<4; vertnum++)
1441                                 sidep->uvls[vertnum].l = F1_0/64;       // Put a tiny bit of light here.
1442                 }
1443                 Segments[segnum].static_light = F1_0/64;
1444         }
1445 }
1446
1447
1448 //      ------------------------------------------------------------------------------------------
1449 //      Used in setting average light value in a segment, cast light from a side to the center
1450 //      of all segments.
1451 void cast_light_from_side_to_center(segment *segp, int light_side, fix light_intensity, int quick_light)
1452 {
1453         vms_vector      segment_center;
1454         int                     segnum, lightnum;
1455
1456         compute_segment_center(&segment_center, segp);
1457
1458         //      Do for four lights, one just inside each corner of side containing light.
1459         for (lightnum=0; lightnum<4; lightnum++) {
1460                 int                     light_vertex_num;
1461                 vms_vector      vector_to_center;
1462                 vms_vector      light_location;
1463
1464                 light_vertex_num = segp->verts[Side_to_verts[light_side][lightnum]];
1465                 light_location = Vertices[light_vertex_num];
1466                 vm_vec_sub(&vector_to_center, &segment_center, &light_location);
1467                 vm_vec_scale_add(&light_location, &light_location, &vector_to_center, F1_0/64);
1468
1469                 for (segnum=0; segnum<=Highest_segment_index; segnum++) {
1470                         segment         *rsegp = &Segments[segnum];
1471                         vms_vector      r_segment_center;
1472                         fix                     dist_to_rseg;
1473 //if ((segp == &Segments[Bugseg]) && (rsegp == &Segments[Bugseg]))
1474 //      Int3();
1475                         compute_segment_center(&r_segment_center, rsegp);
1476                         dist_to_rseg = vm_vec_dist_quick(&r_segment_center, &segment_center);
1477
1478                         if (dist_to_rseg <= LIGHT_DISTANCE_THRESHOLD) {
1479                                 fix     light_at_point;
1480                                 if (dist_to_rseg > F1_0)
1481                                         light_at_point = fixdiv(Magical_light_constant, dist_to_rseg);
1482                                 else
1483                                         light_at_point = Magical_light_constant;
1484
1485                                 if (light_at_point >= 0) {
1486                                         int             hit_type;
1487
1488                                         if (!quick_light) {
1489                                                 fvi_query fq;
1490                                                 fvi_info        hit_data;
1491
1492                                                 fq.p0                                           = &light_location;
1493                                                 fq.startseg                             = segp-Segments;
1494                                                 fq.p1                                           = &r_segment_center;
1495                                                 fq.rad                                  = 0;
1496                                                 fq.thisobjnum                   = -1;
1497                                                 fq.ignore_obj_list      = NULL;
1498                                                 fq.flags                                        = 0;
1499
1500                                                 hit_type = find_vector_intersection(&fq,&hit_data);
1501                                         }
1502                                         else
1503                                                 hit_type = HIT_NONE;
1504
1505                                         switch (hit_type) {
1506                                                 case HIT_NONE:
1507                                                         light_at_point = fixmul(light_at_point, light_intensity);
1508                                                         if (light_at_point >= F1_0)
1509                                                                 light_at_point = F1_0-1;
1510                                                         rsegp->static_light += light_at_point;
1511                                                         if (segp->static_light < 0)     // if it went negative, saturate
1512                                                                 segp->static_light = 0;
1513                                                         break;
1514                                                 case HIT_WALL:
1515                                                         break;
1516                                                 case HIT_OBJECT:
1517                                                         Int3(); // Hit object, should be ignoring objects!
1518                                                         break;
1519                                                 case HIT_BAD_P0:
1520                                                         Int3(); //      Ugh, this thing again, what happened, what does it mean?
1521                                                         break;
1522                                         }
1523                                 }       //      end if (light_at_point...
1524                         }       //      end if (dist_to_rseg...
1525
1526                 }       //      end for (segnum=0...
1527
1528         }       //      end for (lightnum=0...
1529
1530 }
1531
1532 //      ------------------------------------------------------------------------------------------
1533 //      Process all lights.
1534 void calim_process_all_lights(int quick_light)
1535 {
1536         int     segnum, sidenum;
1537
1538         for (segnum=0; segnum<=Highest_segment_index; segnum++) {
1539                 segment *segp = &Segments[segnum];
1540                 mprintf((0, "."));
1541                 for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
1542                         // if (!IS_CHILD(segp->children[sidenum])) {
1543                         if (WALL_IS_DOORWAY(segp, sidenum) != WID_NO_WALL) {
1544                                 side    *sidep = &segp->sides[sidenum];
1545                                 fix     light_intensity;
1546
1547                                 light_intensity = TmapInfo[sidep->tmap_num].lighting + TmapInfo[sidep->tmap_num2 & 0x3fff].lighting;
1548
1549 //                              if (segp->sides[sidenum].wall_num != -1) {
1550 //                                      int     wall_num, bitmap_num, effect_num;
1551 //                                      wall_num = segp->sides[sidenum].wall_num;
1552 //                                      effect_num = Walls[wall_num].type;
1553 //                                      bitmap_num = effects_bm_num[effect_num];
1554 //
1555 //                                      light_intensity += TmapInfo[bitmap_num].lighting;
1556 //                              }
1557
1558                                 if (light_intensity) {
1559                                         light_intensity /= 4;                   // casting light from four spots, so divide by 4.
1560                                         cast_light_from_side(segp, sidenum, light_intensity, quick_light);
1561                                         cast_light_from_side_to_center(segp, sidenum, light_intensity, quick_light);
1562                                 }
1563                         }
1564                 }
1565         }
1566 }
1567
1568 //      ------------------------------------------------------------------------------------------
1569 //      Apply static light in mine.
1570 //      First, zero all light values.
1571 //      Then, for all light sources, cast their light.
1572 void cast_all_light_in_mine(int quick_flag)
1573 {
1574
1575         validate_segment_all();
1576
1577         calim_zero_light_values();
1578
1579         calim_process_all_lights(quick_flag);
1580
1581 }
1582
1583 // int  Fvit_num = 1000;
1584 // 
1585 // fix find_vector_intersection_test(void)
1586 // {
1587 //      int             i;
1588 //      fvi_info        hit_data;
1589 //      int             p0_seg, p1_seg, this_objnum, ignore_obj, check_obj_flag;
1590 //      fix             rad;
1591 //      int             start_time = timer_get_milliseconds();;
1592 //      vms_vector      p0,p1;
1593 // 
1594 //      ignore_obj = 1;
1595 //      check_obj_flag = 0;
1596 //      this_objnum = -1;
1597 //      rad = F1_0/4;
1598 // 
1599 //      for (i=0; i<Fvit_num; i++) {
1600 //              p0_seg = d_rand()*(Highest_segment_index+1)/32768;
1601 //              compute_segment_center(&p0, &Segments[p0_seg]);
1602 // 
1603 //              p1_seg = d_rand()*(Highest_segment_index+1)/32768;
1604 //              compute_segment_center(&p1, &Segments[p1_seg]);
1605 // 
1606 //              find_vector_intersection(&hit_data, &p0, p0_seg, &p1, rad, this_objnum, ignore_obj, check_obj_flag);
1607 //      }
1608 // 
1609 //      return timer_get_milliseconds() - start_time;
1610 // }
1611
1612 vms_vector      Normals[MAX_SEGMENTS*12];
1613
1614 int     Normal_nearness = 4;
1615
1616 int normal_near(vms_vector *v1, vms_vector *v2)
1617 {
1618         if (abs(v1->x - v2->x) < Normal_nearness)
1619                 if (abs(v1->y - v2->y) < Normal_nearness)
1620                         if (abs(v1->z - v2->z) < Normal_nearness)
1621                                 return 1;
1622         return 0;
1623 }
1624
1625 int     Total_normals=0;
1626 int     Diff_normals=0;
1627
1628 void print_normals(void)
1629 {
1630         int                     i,j,s,n,nn;
1631         // vms_vector   *normal;
1632         int                     num_normals=0;
1633
1634         Total_normals = 0;
1635         Diff_normals = 0;
1636
1637         for (i=0; i<=Highest_segment_index; i++)
1638                 for (s=0; s<6; s++) {
1639                         if (Segments[i].sides[s].type == SIDE_IS_QUAD)
1640                                 nn=1;
1641                         else
1642                                 nn=2;
1643                         for (n=0; n<nn; n++) {
1644                                 for (j=0; j<num_normals; j++)
1645                                         if (normal_near(&Segments[i].sides[s].normals[n],&Normals[j]))
1646                                                 break;
1647                                 if (j == num_normals) {
1648                                         Normals[num_normals++] = Segments[i].sides[s].normals[n];
1649                                         Diff_normals++;
1650                                 }
1651                                 Total_normals++;
1652                         }
1653                 }
1654
1655 }
1656