Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * Portions of this file are copyright Rebirth contributors and licensed as
3
 * described in COPYING.txt.
4
 * Portions of this file are copyright Parallax Software and licensed
5
 * according to the Parallax license below.
6
 * See COPYING.txt for license details.
7
 
8
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9
SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12
IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14
FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17
COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18
*/
19
 
20
/*
21
 *
22
 * Interrogation functions for segment data structure.
23
 *
24
 */
25
 
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <math.h>
29
#include <string.h>
30
#include "key.h"
31
#include "gr.h"
32
#include "inferno.h"
33
#include "segment.h"
34
#include "editor.h"
35
#include "editor/esegment.h"
36
#include "dxxerror.h"
37
#include "object.h"
38
#include "gameseg.h"
39
#include "render.h"
40
#include "game.h"
41
#include "wall.h"
42
#include "switch.h"
43
#include "fuelcen.h"
44
#include "cntrlcen.h"
45
#include "seguvs.h"
46
#include "gameseq.h"
47
#include "kdefs.h"
48
 
49
#include "medwall.h"
50
#include "hostage.h"
51
 
52
#include "compiler-range_for.h"
53
#include "d_range.h"
54
#include "d_enumerate.h"
55
#include "d_zip.h"
56
#include "segiter.h"
57
 
58
int     Do_duplicate_vertex_check = 0;          // Gets set to 1 in med_create_duplicate_vertex, means to check for duplicate vertices in compress_mine
59
 
60
//      Remap all vertices in polygons in a segment through translation table xlate_verts.
61
int ToggleBottom(void)
62
{
63
        Render_only_bottom = !Render_only_bottom;
64
        Update_flags = UF_WORLD_CHANGED;
65
        return 0;
66
}
67
 
68
// -------------------------------------------------------------------------------
69
//      Return number of times vertex vi appears in all segments.
70
//      This function can be used to determine whether a vertex is used exactly once in
71
//      all segments, in which case it can be freely moved because it is not connected
72
//      to any other segment.
73
static int med_vertex_count(int vi)
74
{
75
        int             count;
76
 
77
        count = 0;
78
 
79
        range_for (auto &s, Segments)
80
        {
81
                auto sp = &s;
82
                if (sp->segnum != segment_none)
83
                        range_for (auto &v, s.verts)
84
                                if (v == vi)
85
                                        count++;
86
        }
87
 
88
        return count;
89
}
90
 
91
// -------------------------------------------------------------------------------
92
int is_free_vertex(int vi)
93
{
94
        return med_vertex_count(vi) == 1;
95
}
96
 
97
// -------------------------------------------------------------------------------
98
//      Return true if one fixed point number is very close to another, else return false.
99
static int fnear(fix f1, fix f2)
100
{
101
        return (abs(f1 - f2) <= FIX_EPSILON);
102
}
103
 
104
// -------------------------------------------------------------------------------
105
static int vnear(const vms_vector &vp1, const vms_vector &vp2)
106
{
107
        return fnear(vp1.x, vp2.x) && fnear(vp1.y, vp2.y) && fnear(vp1.z, vp2.z);
108
}
109
 
110
// -------------------------------------------------------------------------------
111
//      Add the vertex *vp to the global list of vertices, return its index.
112
//      Search until a matching vertex is found (has nearly the same coordinates) or until Num_vertices
113
// vertices have been looked at without a match.  If no match, add a new vertex.
114
int med_add_vertex(const vertex &vp)
115
{
116
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
117
        int     count;                                  // number of used vertices found, for loops exits when count == Num_vertices
118
 
119
//      set_vertex_counts();
120
 
121
        const auto Num_vertices = LevelSharedVertexState.Num_vertices;
122
        Assert(Num_vertices < MAX_SEGMENT_VERTICES);
123
 
124
        count = 0;
125
        unsigned free_index = UINT32_MAX;
126
        auto &Vertices = LevelSharedVertexState.get_vertices();
127
        auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
128
        for (unsigned v = 0; v < MAX_SEGMENT_VERTICES && count < Num_vertices; ++v)
129
                if (Vertex_active[v]) {
130
                        count++;
131
                        if (vnear(vp, Vertices.vcptr(v))) {
132
                                return v;
133
                        }
134
                } else if (free_index == UINT32_MAX)
135
                        free_index = v;                                 // we want free_index to be the first free slot to add a vertex
136
 
137
        if (free_index == UINT32_MAX)
138
                free_index = Num_vertices;
139
 
140
        while (Vertex_active[free_index] && (free_index < MAX_VERTICES))
141
                free_index++;
142
 
143
        Assert(free_index < MAX_VERTICES);
144
 
145
        *Vertices.vmptr(free_index) = vp;
146
        Vertex_active[free_index] = 1;
147
 
148
        ++LevelSharedVertexState.Num_vertices;
149
 
150
        if (Vertices.get_count() - 1 < free_index)
151
                Vertices.set_count(free_index + 1);
152
 
153
        return free_index;
154
}
155
 
156
namespace dsx {
157
 
158
// ------------------------------------------------------------------------------------------
159
//      Returns the index of a free segment.
160
//      Scans the Segments array.
161
segnum_t get_free_segment_number(segment_array &Segments)
162
{
163
        for (segnum_t segnum=0; segnum<MAX_SEGMENTS; segnum++)
164
                if (Segments[segnum].segnum == segment_none) {
165
                        ++ LevelSharedSegmentState.Num_segments;
166
                        if (segnum > Highest_segment_index)
167
                                Segments.set_count(segnum + 1);
168
                        return segnum;
169
                }
170
 
171
        Assert(0);
172
 
173
        return 0;
174
}
175
 
176
// -------------------------------------------------------------------------------
177
//      Create a new segment, duplicating exactly, including vertex ids and children, the passed segment.
178
segnum_t med_create_duplicate_segment(segment_array &Segments, const segment &sp)
179
{
180
        const auto segnum = get_free_segment_number(Segments);
181
 
182
        auto &nsp = *Segments.vmptr(segnum);
183
        nsp = sp;
184
        nsp.objects = object_none;
185
 
186
        return segnum;
187
}
188
 
189
}
190
 
191
// -------------------------------------------------------------------------------
192
//      Add the vertex *vp to the global list of vertices, return its index.
193
//      This is the same as med_add_vertex, except that it does not search for the presence of the vertex.
194
int med_create_duplicate_vertex(const vertex &vp)
195
{
196
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
197
        const auto Num_vertices = LevelSharedVertexState.Num_vertices;
198
        Assert(Num_vertices < MAX_SEGMENT_VERTICES);
199
 
200
        Do_duplicate_vertex_check = 1;
201
 
202
        unsigned free_index = Num_vertices;
203
 
204
        auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
205
        while (Vertex_active[free_index] && (free_index < MAX_VERTICES))
206
                free_index++;
207
 
208
        Assert(free_index < MAX_VERTICES);
209
 
210
        auto &Vertices = LevelSharedVertexState.get_vertices();
211
        *Vertices.vmptr(free_index) = vp;
212
        Vertex_active[free_index] = 1;
213
 
214
        ++LevelSharedVertexState.Num_vertices;
215
 
216
        if (Vertices.get_count() - 1 < free_index)
217
                Vertices.set_count(free_index + 1);
218
 
219
        return free_index;
220
}
221
 
222
 
223
// -------------------------------------------------------------------------------
224
//      Set the vertex *vp at index vnum in the global list of vertices, return its index (just for compatibility).
225
int med_set_vertex(const unsigned vnum, const vertex &vp)
226
{
227
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
228
        auto &Vertices = LevelSharedVertexState.get_vertices();
229
        *Vertices.vmptr(vnum) = vp;
230
 
231
        // Just in case this vertex wasn't active, mark it as active.
232
        auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
233
        if (!Vertex_active[vnum]) {
234
                Vertex_active[vnum] = 1;
235
                ++LevelSharedVertexState.Num_vertices;
236
                if ((vnum > Vertices.get_count() - 1) && (vnum < NEW_SEGMENT_VERTICES)) {
237
                        Vertices.set_count(vnum + 1);
238
                }
239
        }
240
 
241
        return vnum;
242
}
243
 
244
namespace dsx {
245
 
246
// -------------------------------------------------------------------------------
247
void create_removable_wall(fvcvertptr &vcvertptr, const vmsegptridx_t sp, const unsigned sidenum, const unsigned tmap_num)
248
{
249
        create_walls_on_side(vcvertptr, sp, sidenum);
250
 
251
        sp->unique_segment::sides[sidenum].tmap_num = tmap_num;
252
 
253
        assign_default_uvs_to_side(sp, sidenum);
254
        assign_light_to_side(sp, sidenum);
255
}
256
 
257
#if 0
258
 
259
// ---------------------------------------------------------------------------------------------
260
//      Orthogonalize matrix smat, returning result in rmat.
261
//      Does not modify smat.
262
//      Uses Gram-Schmidt process.
263
//      See page 172 of Strang, Gilbert, Linear Algebra and its Applications
264
//      Matt -- This routine should be moved to the vector matrix library.
265
//      It IS legal for smat == rmat.
266
//      We should also have the functions:
267
//              mat_a = mat_b * scalar;                         // we now have mat_a = mat_a * scalar;
268
//              mat_a = mat_b + mat_c * scalar; // or maybe not, maybe this is not primitive
269
void make_orthogonal(vms_matrix *rmat,vms_matrix *smat)
270
{
271
        vms_matrix              tmat;
272
        vms_vector              tvec1,tvec2;
273
        float                           dot;
274
 
275
        // Copy source matrix to work area.
276
        tmat = *smat;
277
 
278
        // Normalize the three rows of the matrix tmat.
279
        vm_vec_normalize(&tmat.xrow);
280
        vm_vec_normalize(&tmat.yrow);
281
        vm_vec_normalize(&tmat.zrow);
282
 
283
        //      Now, compute the first vector.
284
        // This is very easy -- just copy the (normalized) source vector.
285
        rmat->zrow = tmat.zrow;
286
 
287
        // Now, compute the second vector.
288
        // From page 172 of Strang, we use the equation:
289
        //              b' = b - [transpose(q1) * b] * q1
290
        //      where:  b  = the second row of tmat
291
        //                              q1 = the first row of rmat
292
        //                              b' = the second row of rmat
293
 
294
        // Compute: transpose(q1) * b
295
        dot = vm_vec_dot(&rmat->zrow,&tmat.yrow);
296
 
297
        // Compute: b - dot * q1
298
        rmat->yrow.x = tmat.yrow.x - fixmul(dot,rmat->zrow.x);
299
        rmat->yrow.y = tmat.yrow.y - fixmul(dot,rmat->zrow.y);
300
        rmat->yrow.z = tmat.yrow.z - fixmul(dot,rmat->zrow.z);
301
 
302
        // Now, compute the third vector.
303
        // From page 173 of Strang, we use the equation:
304
        //              c' = c - (q1*c)*q1 - (q2*c)*q2
305
        //      where:  c  = the third row of tmat
306
        //                              q1 = the first row of rmat
307
        //                              q2 = the second row of rmat
308
        //                              c' = the third row of rmat
309
 
310
        // Compute: q1*c
311
        dot = vm_vec_dot(&rmat->zrow,&tmat.xrow);
312
 
313
        tvec1.x = fixmul(dot,rmat->zrow.x);
314
        tvec1.y = fixmul(dot,rmat->zrow.y);
315
        tvec1.z = fixmul(dot,rmat->zrow.z);
316
 
317
        // Compute: q2*c
318
        dot = vm_vec_dot(&rmat->yrow,&tmat.xrow);
319
 
320
        tvec2.x = fixmul(dot,rmat->yrow.x);
321
        tvec2.y = fixmul(dot,rmat->yrow.y);
322
        tvec2.z = fixmul(dot,rmat->yrow.z);
323
 
324
        vm_vec_sub(&rmat->xrow,vm_vec_sub(&rmat->xrow,&tmat.xrow,&tvec1),&tvec2);
325
}
326
 
327
#endif
328
 
329
// ------------------------------------------------------------------------------------------
330
// Given a segment, extract the rotation matrix which defines it.
331
// Do this by extracting the forward, right, up vectors and then making them orthogonal.
332
// In the process of making the vectors orthogonal, favor them in the order forward, up, right.
333
// This means that the forward vector will remain unchanged.
334
void med_extract_matrix_from_segment(const shared_segment &sp, vms_matrix &rotmat)
335
{
336
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
337
        vms_vector      forwardvec,upvec;
338
 
339
        auto &Vertices = LevelSharedVertexState.get_vertices();
340
        auto &vcvertptr = Vertices.vcptr;
341
        extract_forward_vector_from_segment(vcvertptr, sp, forwardvec);
342
        extract_up_vector_from_segment(vcvertptr, sp, upvec);
343
 
344
        if (((forwardvec.x == 0) && (forwardvec.y == 0) && (forwardvec.z == 0)) || ((upvec.x == 0) && (upvec.y == 0) && (upvec.z == 0))) {
345
                rotmat = vmd_identity_matrix;
346
                return;
347
        }
348
 
349
 
350
        vm_vector_2_matrix(rotmat, forwardvec, &upvec, nullptr);
351
 
352
#if 0
353
        vms_matrix      rm;
354
 
355
        extract_forward_vector_from_segment(sp,&rm.zrow);
356
        extract_right_vector_from_segment(sp,&rm.xrow);
357
        extract_up_vector_from_segment(sp,&rm.yrow);
358
 
359
        vm_vec_normalize(&rm.xrow);
360
        vm_vec_normalize(&rm.yrow);
361
        vm_vec_normalize(&rm.zrow);
362
 
363
        make_orthogonal(rotmat,&rm);
364
 
365
        vm_vec_normalize(&rotmat->xrow);
366
        vm_vec_normalize(&rotmat->yrow);
367
        vm_vec_normalize(&rotmat->zrow);
368
 
369
// *rotmat = rm; // include this line (and remove the call to make_orthogonal) if you don't want the matrix orthogonalized
370
#endif
371
}
372
 
373
}
374
 
375
// ------------------------------------------------------------------------------------------
376
//      Given a rotation matrix *rotmat which describes the orientation of a segment
377
//      and a side destside, return the rotation matrix which describes the orientation for the side.
378
void update_matrix_based_on_side(vms_matrix &rotmat,int destside)
379
{
380
        vms_angvec      rotvec;
381
 
382
        switch (destside) {
383
                case WLEFT:
384
                        vm_angvec_make(&rotvec,0,0,-16384);
385
                        rotmat = vm_matrix_x_matrix(rotmat, vm_angles_2_matrix(rotvec));
386
                        break;
387
 
388
                case WTOP:
389
                        vm_angvec_make(&rotvec,-16384,0,0);
390
                        rotmat = vm_matrix_x_matrix(rotmat, vm_angles_2_matrix(rotvec));
391
                        break;
392
 
393
                case WRIGHT:
394
                        vm_angvec_make(&rotvec,0,0,16384);
395
                        rotmat = vm_matrix_x_matrix(rotmat, vm_angles_2_matrix(rotvec));
396
                        break;
397
 
398
                case WBOTTOM:
399
                        vm_angvec_make(&rotvec,+16384,-32768,0);        // bank was -32768, but I think that was an erroneous compensation
400
                        rotmat = vm_matrix_x_matrix(rotmat, vm_angles_2_matrix(rotvec));
401
                        break;
402
 
403
                case WFRONT:
404
                        vm_angvec_make(&rotvec,0,0,-32768);
405
                        rotmat = vm_matrix_x_matrix(rotmat, vm_angles_2_matrix(rotvec));
406
                        break;
407
 
408
                case WBACK:
409
                        break;
410
        }
411
}
412
 
413
//      -------------------------------------------------------------------------------------
414
static void change_vertex_occurrences(int dest, int src)
415
{
416
        // Fix vertices in groups
417
        range_for (auto &g, partial_range(GroupList, num_groups))
418
                g.vertices.replace(src, dest);
419
 
420
        // now scan all segments, changing occurrences of src to dest
421
        range_for (const auto &&segp, vmsegptr)
422
        {
423
                if (segp->segnum != segment_none)
424
                        range_for (auto &v, segp->verts)
425
                                if (v == src)
426
                                        v = dest;
427
        }
428
}
429
 
430
// --------------------------------------------------------------------------------------------------
431
static void compress_vertices(void)
432
{
433
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
434
        const auto Num_vertices = LevelSharedVertexState.Num_vertices;
435
        auto &Vertices = LevelSharedVertexState.get_vertices();
436
        if (Vertices.get_count() == Num_vertices)
437
                return;
438
 
439
        unsigned vert = Vertices.get_count() - 1;       //MAX_SEGMENT_VERTICES-1;
440
 
441
        auto &vcvertptr = Vertices.vcptr;
442
        auto &vmvertptr = Vertices.vmptr;
443
        auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
444
        for (unsigned hole = 0; hole < vert; ++hole)
445
                if (!Vertex_active[hole]) {
446
                        // found an unused vertex which is a hole if a used vertex follows (not necessarily immediately) it.
447
                        for ( ; (vert>hole) && (!Vertex_active[vert]); vert--)
448
                                ;
449
 
450
                        if (vert > hole) {
451
 
452
                                // Ok, hole is the index of a hole, vert is the index of a vertex which follows it.
453
                                // Copy vert into hole, update pointers to it.
454
                                *vmvertptr(hole) = *vcvertptr(vert);
455
                                change_vertex_occurrences(hole, vert);
456
 
457
                                vert--;
458
                        }
459
                }
460
 
461
        Vertices.set_count(Num_vertices);
462
}
463
 
464
// --------------------------------------------------------------------------------------------------
465
static void compress_segments(void)
466
{
467
        auto &Objects = LevelUniqueObjectState.Objects;
468
        auto &vmobjptridx = Objects.vmptridx;
469
        if (Highest_segment_index == LevelSharedSegmentState.Num_segments - 1)
470
                return;
471
 
472
        segnum_t                hole,seg;
473
        seg = Highest_segment_index;
474
 
475
        auto &RobotCenters = LevelSharedRobotcenterState.RobotCenters;
476
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
477
        auto &vmwallptr = Walls.vmptr;
478
        for (hole=0; hole < seg; hole++)
479
                if (Segments[hole].segnum == segment_none) {
480
                        // found an unused segment which is a hole if a used segment follows (not necessarily immediately) it.
481
                        for ( ; (seg>hole) && (Segments[seg].segnum == segment_none); seg--)
482
                                ;
483
 
484
                        if (seg > hole) {
485
                                // Ok, hole is the index of a hole, seg is the index of a segment which follows it.
486
                                // Copy seg into hole, update pointers to it, update Cursegp, Markedsegp if necessary.
487
                                Segments[hole] = Segments[seg];
488
                                Segments[seg].segnum = segment_none;
489
 
490
                                if (Cursegp == &Segments[seg])
491
                                        Cursegp = imsegptridx(hole);
492
 
493
                                if (Markedsegp == &Segments[seg])
494
                                        Markedsegp = imsegptridx(hole);
495
 
496
                                // Fix segments in groups
497
                                range_for (auto &g, partial_range(GroupList, num_groups))
498
                                        g.segments.replace(seg, hole);
499
 
500
                                // Fix walls
501
                                range_for (const auto &&w, vmwallptr)
502
                                        if (w->segnum == seg)
503
                                                w->segnum = hole;
504
 
505
                                // Fix fuelcenters, robotcens, and triggers... added 2/1/95 -Yuan
506
                                range_for (auto &f, partial_range(LevelUniqueFuelcenterState.Station, LevelUniqueFuelcenterState.Num_fuelcenters))
507
                                        if (f.segnum == seg)
508
                                                f.segnum = hole;
509
 
510
                                range_for (auto &f, partial_range(RobotCenters, LevelSharedRobotcenterState.Num_robot_centers))
511
                                        if (f.segnum == seg)
512
                                                f.segnum = hole;
513
 
514
                                auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
515
                                auto &vmtrgptr = Triggers.vmptr;
516
                                range_for (const auto vt, vmtrgptr)
517
                                {
518
                                        auto &t = *vt;
519
                                        range_for (auto &l, partial_range(t.seg, t.num_links))
520
                                                if (l == seg)
521
                                                        l = hole;
522
                                }
523
 
524
                                auto &sp = *vmsegptr(hole);
525
                                range_for (auto &s, sp.children)
526
                                {
527
                                        if (IS_CHILD(s)) {
528
                                                // Find out on what side the segment connection to the former seg is on in *csegp.
529
                                                range_for (auto &t, vmsegptr(s)->children)
530
                                                {
531
                                                        if (t == seg) {
532
                                                                t = hole;                                       // It used to be connected to seg, so make it connected to hole
533
                                                        }
534
                                                }       // end for t
535
                                        }       // end if
536
                                }       // end for s
537
 
538
                                //Update object segment pointers
539
                                range_for (const auto objp, objects_in(sp, vmobjptridx, vmsegptr))
540
                                {
541
                                        Assert(objp->segnum == seg);
542
                                        objp->segnum = hole;
543
                                }
544
 
545
                                seg--;
546
 
547
                        }       // end if (seg > hole)
548
                }       // end if
549
 
550
        Segments.set_count(LevelSharedSegmentState.Num_segments);
551
        med_create_new_segment_from_cursegp();
552
 
553
}
554
 
555
 
556
// -------------------------------------------------------------------------------
557
//      Combine duplicate vertices.
558
//      If two vertices have the same coordinates, within some small tolerance, then assign
559
//      the same vertex number to the two vertices, freeing up one of the vertices.
560
void med_combine_duplicate_vertices(std::array<uint8_t, MAX_VERTICES> &vlp)
561
{
562
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
563
        auto &Vertices = LevelSharedVertexState.get_vertices();
564
        auto &vcvertptridx = Vertices.vcptridx;
565
        const auto &&range = make_range(vcvertptridx);
566
        // Note: ok to do to <, rather than <= because w for loop starts at v+1
567
        if (range.m_begin == range.m_end)
568
                return;
569
        for (auto i = range.m_begin;;)
570
        {
571
                const auto &&v = *i;
572
                if (++i == range.m_end)
573
                        return;
574
                if (vlp[v]) {
575
                        auto &vvp = *v;
576
                        auto subrange = range;
577
                        subrange.m_begin = i;
578
                        range_for (auto &&w, subrange)
579
                                if (vlp[w]) {   //      used to be Vertex_active[w]
580
                                        if (vnear(vvp, *w)) {
581
                                                change_vertex_occurrences(v, w);
582
                                        }
583
                                }
584
                }
585
        }
586
}
587
 
588
// ------------------------------------------------------------------------------
589
//      Compress mine at Segments and Vertices by squeezing out all holes.
590
//      If no holes (ie, an unused segment followed by a used segment), then no action.
591
//      If Cursegp or Markedsegp is a segment which gets moved to fill in a hole, then
592
//      they are properly updated.
593
void med_compress_mine(void)
594
{
595
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
596
        if (Do_duplicate_vertex_check) {
597
                auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
598
                med_combine_duplicate_vertices(Vertex_active);
599
                Do_duplicate_vertex_check = 0;
600
        }
601
 
602
        compress_segments();
603
        compress_vertices();
604
        set_vertex_counts();
605
 
606
        //--repair-- create_local_segment_data();
607
 
608
        //      This is necessary becuase a segment search (due to click in 3d window) uses the previous frame's
609
        //      segment information, which could get changed by this.
610
        Update_flags = UF_WORLD_CHANGED;
611
}
612
 
613
namespace dsx {
614
 
615
// ------------------------------------------------------------------------------------------
616
//      Copy texture map ids for each face in sseg to dseg.
617
static void copy_tmap_ids(unique_segment &dseg, const unique_segment &sseg)
618
{
619
        range_for (const auto &&z, zip(sseg.sides, dseg.sides))
620
        {
621
                auto &ds = std::get<1>(z);
622
                ds.tmap_num = std::get<0>(z).tmap_num;
623
                ds.tmap_num2 = 0;
624
        }
625
}
626
 
627
// ------------------------------------------------------------------------------------------
628
//      Attach a segment with a rotated orientation.
629
// Return value:
630
//  0 = successful attach
631
//  1 = No room in Segments[].
632
//  2 = No room in Vertices[].
633
//  3 = newside != WFRONT -- for now, the new segment must be attached at its (own) front side
634
//       4 = already a face attached on destseg:destside
635
static int med_attach_segment_rotated(const vmsegptridx_t destseg, const vmsegptr_t newseg, int destside, int newside,const vms_matrix &attmat)
636
{
637
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
638
        auto &Vertices = LevelSharedVertexState.get_vertices();
639
        vms_matrix      rotmat,rotmat2,rotmat3;
640
        vms_vector      forvec,upvec;
641
 
642
        // Return if already a face attached on this side.
643
        if (IS_CHILD(destseg->children[destside]))
644
                return 4;
645
 
646
        const auto segnum = get_free_segment_number(Segments);
647
 
648
        forvec = attmat.fvec;
649
        upvec = attmat.uvec;
650
 
651
        //      We are pretty confident we can add the segment.
652
        const auto &&nsp = destseg.absolute_sibling(segnum);
653
 
654
        nsp->segnum = segnum;
655
        nsp->objects = object_none;
656
        nsp->matcen_num = -1;
657
 
658
        // Copy group value.
659
        nsp->group = destseg->group;
660
 
661
        // Add segment to proper group list.
662
        if (nsp->group > -1)
663
                add_segment_to_group(nsp, nsp->group);
664
 
665
        // Copy the texture map ids.
666
        copy_tmap_ids(nsp,newseg);
667
 
668
        // clear all connections
669
        for (unsigned side = 0; side < MAX_SIDES_PER_SEGMENT; ++side)
670
        {
671
                nsp->children[side] = segment_none;
672
                nsp->shared_segment::sides[side].wall_num = wall_none; 
673
        }
674
 
675
        // Form the connection
676
        destseg->children[destside] = segnum;
677
//      destseg->sides[destside].render_flag = 0;
678
        nsp->children[newside] = destseg;
679
 
680
        // Copy vertex indices of the four vertices forming the joint
681
        auto &dvp = Side_to_verts[destside];
682
 
683
        // Set the vertex indices for the four vertices forming the front of the new side
684
        range_for (const unsigned v, xrange(4u))
685
                nsp->verts[v] = destseg->verts[static_cast<int>(dvp[v])];
686
 
687
        // The other 4 vertices must be created.
688
        // Their coordinates are determined by the 4 welded vertices and the vector from front
689
        // to back of the original *newseg.
690
 
691
        // Do lots of hideous matrix stuff, about 3/4 of which could probably be simplified out.
692
        med_extract_matrix_from_segment(destseg, rotmat);               // get orientation matrix for destseg (orthogonal rotation matrix)
693
        update_matrix_based_on_side(rotmat,destside);
694
        const auto rotmat1 = vm_vector_2_matrix(forvec,&upvec,nullptr);
695
        const auto rotmat4 = vm_matrix_x_matrix(rotmat,rotmat1);                        // this is the desired orientation of the new segment
696
        med_extract_matrix_from_segment(newseg, rotmat3);               // this is the current orientation of the new segment
697
        vm_transpose_matrix(rotmat3);                                                           // get the inverse of the current orientation matrix
698
        vm_matrix_x_matrix(rotmat2,rotmat4,rotmat3);                    // now rotmat2 takes the current segment to the desired orientation
699
 
700
        // Warning -- look at this line!
701
        vm_transpose_matrix(rotmat2);   // added 12:33 pm, 10/01/93
702
 
703
        // Compute and rotate the center point of the attaching face.
704
        auto &vcvertptr = Vertices.vcptr;
705
        const auto &&vc0 = compute_center_point_on_side(vcvertptr, newseg, newside);
706
        const auto vr = vm_vec_rotate(vc0,rotmat2);
707
 
708
        // Now rotate the free vertices in the segment
709
        std::array<vertex, 4> tvs;
710
        range_for (const unsigned v, xrange(4u))
711
                vm_vec_rotate(tvs[v], vcvertptr(newseg->verts[v + 4]), rotmat2);
712
 
713
        // Now translate the new segment so that the center point of the attaching faces are the same.
714
        const auto &&vc1 = compute_center_point_on_side(vcvertptr, destseg, destside);
715
        const auto xlate_vec = vm_vec_sub(vc1,vr);
716
 
717
        // Create and add the 4 new vertices.
718
        range_for (const unsigned v, xrange(4u))
719
        {
720
                vm_vec_add2(tvs[v],xlate_vec);
721
                nsp->verts[v+4] = med_add_vertex(tvs[v]);
722
        }
723
 
724
        set_vertex_counts();
725
 
726
        // Now all the vertices are in place.  Create the faces.
727
        validate_segment(vcvertptr, nsp);
728
 
729
        //      Say to not render at the joint.
730
//      destseg->sides[destside].render_flag = 0;
731
//      nsp->sides[newside].render_flag = 0;
732
 
733
        Cursegp = nsp;
734
 
735
        return  0;
736
}
737
 
738
 
739
// ------------------------------------------------------------------------------------------
740
// Attach side newside of newseg to side destside of destseg.
741
// Copies *newseg into global array Segments, increments Num_segments.
742
// Forms a weld between the two segments by making the new segment fit to the old segment.
743
// Updates number of faces per side if necessitated by new vertex coordinates.
744
//      Updates Cursegp.
745
// Return value:
746
//  0 = successful attach
747
//  1 = No room in Segments[].
748
//  2 = No room in Vertices[].
749
//  3 = newside != WFRONT -- for now, the new segment must be attached at its (own) front side
750
//       4 = already a face attached on side newside
751
int med_attach_segment(const vmsegptridx_t destseg, const vmsegptr_t newseg, int destside, int newside)
752
{
753
        int             rval;
754
        const auto ocursegp = Cursegp;
755
 
756
        vms_angvec      tang = {0,0,0};
757
        const auto &&rotmat = vm_angles_2_matrix(tang);
758
        rval = med_attach_segment_rotated(destseg,newseg,destside,newside,rotmat);
759
        med_propagate_tmaps_to_segments(ocursegp,Cursegp,0);
760
        med_propagate_tmaps_to_back_side(Cursegp, Side_opposite[newside],0);
761
        copy_uvs_seg_to_seg(vmsegptr(&New_segment), Cursegp);
762
 
763
        return rval;
764
}
765
 
766
}
767
 
768
// -------------------------------------------------------------------------------
769
//      Delete a vertex, sort of.
770
//      Decrement the vertex count.  If the count goes to 0, then the vertex is free (has been deleted).
771
static void delete_vertex(const unsigned v)
772
{
773
        Assert(v < MAX_VERTICES);                       // abort if vertex is not in array Vertices
774
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
775
        auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
776
        Assert(Vertex_active[v] >= 1);  // abort if trying to delete a non-existent vertex
777
 
778
        Vertex_active[v]--;
779
}
780
 
781
// -------------------------------------------------------------------------------
782
//      Update Num_vertices.
783
//      This routine should be called by anyone who calls delete_vertex.  It could be called in delete_vertex,
784
//      but then it would be called much more often than necessary, and it is a slow routine.
785
static void update_num_vertices(void)
786
{
787
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
788
        auto &Vertices = LevelSharedVertexState.get_vertices();
789
        // Now count the number of vertices.
790
        unsigned n = 0;
791
        auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
792
        range_for (const auto v, partial_range(Vertex_active, Vertices.get_count()))
793
                if (v)
794
                        ++n;
795
        LevelSharedVertexState.Num_vertices = n;
796
}
797
 
798
namespace dsx {
799
 
800
// -------------------------------------------------------------------------------
801
//      Set Vertex_active to number of occurrences of each vertex.
802
//      Set Num_vertices.
803
void set_vertex_counts(void)
804
{
805
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
806
        auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
807
        unsigned Num_vertices = 0;
808
 
809
        Vertex_active = {};
810
 
811
        // Count number of occurrences of each vertex.
812
        range_for (const auto &&segp, vmsegptr)
813
        {
814
                if (segp->segnum != segment_none)
815
                        range_for (auto &v, segp->verts)
816
                        {
817
                                if (!Vertex_active[v])
818
                                        Num_vertices++;
819
                                ++ Vertex_active[v];
820
                        }
821
        }
822
        LevelSharedVertexState.Num_vertices = Num_vertices;
823
}
824
 
825
// -------------------------------------------------------------------------------
826
//      Delete all vertices in segment *sp from the vertex list if they are not contained in another segment.
827
//      This is kind of a dangerous routine.  It modifies the global array Vertex_active, using the field as
828
//      a count.
829
static void delete_vertices_in_segment(const shared_segment &sp)
830
{
831
//      init_vertices();
832
        set_vertex_counts();
833
        // Subtract one count for each appearance of vertex in deleted segment
834
        range_for (auto &v, sp.verts)
835
                delete_vertex(v);
836
 
837
        update_num_vertices();
838
}
839
 
840
// -------------------------------------------------------------------------------
841
//      Delete segment *sp in Segments array.
842
// Return value:
843
//              0       successfully deleted.
844
//              1       unable to delete.
845
int med_delete_segment(const vmsegptridx_t sp)
846
{
847
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
848
        auto &Objects = LevelUniqueObjectState.Objects;
849
        auto &Vertices = LevelSharedVertexState.get_vertices();
850
        auto &vmobjptr = Objects.vmptr;
851
        auto &vmobjptridx = Objects.vmptridx;
852
        segnum_t segnum = sp;
853
        // Cannot delete segment if only segment.
854
        if (LevelSharedSegmentState.Num_segments == 1)
855
                return 1;
856
 
857
        // Don't try to delete if segment doesn't exist.
858
        if (sp->segnum == segment_none) {
859
                return 1;
860
        }
861
 
862
        // Delete its refueling center if it has one
863
        fuelcen_delete(sp);
864
 
865
        delete_vertices_in_segment(sp);
866
 
867
        -- LevelSharedSegmentState.Num_segments;
868
 
869
        // If deleted segment has walls on any side, wipe out the wall.
870
        for (unsigned side = 0; side < MAX_SIDES_PER_SEGMENT; ++side)
871
                if (sp->shared_segment::sides[side].wall_num != wall_none)
872
                        wall_remove_side(sp, side);
873
 
874
        auto &vcvertptr = Vertices.vcptr;
875
        // Find out what this segment was connected to and break those connections at the other end.
876
        range_for (auto &side, sp->children)
877
                if (IS_CHILD(side)) {
878
                        const auto &&csp = sp.absolute_sibling(side);
879
                        for (int s=0; s<MAX_SIDES_PER_SEGMENT; s++)
880
                                if (csp->children[s] == segnum) {
881
                                        csp->children[s] = segment_none;                                // this is the side of connection, break it
882
                                        validate_segment_side(vcvertptr, csp, s);                                       // we have converted a connection to a side so validate the segment
883
                                        med_propagate_tmaps_to_back_side(csp,s,0);
884
                                }
885
                        Cursegp = csp;
886
                        med_create_new_segment_from_cursegp();
887
                        copy_uvs_seg_to_seg(vmsegptr(&New_segment), Cursegp);
888
                }
889
 
890
        sp->segnum = segment_none;                                                                              // Mark segment as inactive.
891
 
892
        // If deleted segment = marked segment, then say there is no marked segment
893
        if (sp == Markedsegp)
894
                Markedsegp = segment_none;
895
 
896
        //      If deleted segment = a Group segment ptr, then wipe it out.
897
        range_for (auto &s, partial_range(Groupsegp, num_groups))
898
                if (s == sp)
899
                        s = nullptr;
900
 
901
        // If deleted segment = group segment, wipe it off the group list.
902
        if (sp->group > -1)
903
                        delete_segment_from_group(sp, sp->group);
904
 
905
        // If we deleted something which was not connected to anything, must now select a new current segment.
906
        if (Cursegp == sp)
907
                for (segnum_t s=0; s<MAX_SEGMENTS; s++)
908
                        if ((Segments[s].segnum != segment_none) && (s!=segnum) ) {
909
                                Cursegp = imsegptridx(s);
910
                                med_create_new_segment_from_cursegp();
911
                        break;
912
                        }
913
 
914
        // If deleted segment contains objects, wipe out all objects
915
                range_for (const auto objnum, objects_in(*sp, vmobjptridx, vmsegptr))
916
                {
917
                        //if an object is in the seg, delete it
918
                        //if the object is the player, move to new curseg
919
                        if (objnum == ConsoleObject)    {
920
                                compute_segment_center(vcvertptr, ConsoleObject->pos,Cursegp);
921
                                obj_relink(vmobjptr, vmsegptr, objnum, Cursegp);
922
                        } else
923
                                obj_delete(LevelUniqueObjectState, Segments, objnum);
924
                }
925
 
926
        // Make sure everything deleted ok...
927
        Assert( sp->objects==object_none );
928
 
929
        // If we are leaving many holes in Segments or Vertices, then compress mine, because it is inefficient to be that way
930
//      if ((Highest_segment_index > Num_segments+4) || (Highest_vertex_index > Num_vertices+4*8))
931
//              med_compress_mine();
932
 
933
        return 0;
934
}
935
 
936
// ------------------------------------------------------------------------------------------
937
//      Copy texture maps from sseg to dseg
938
static void copy_tmaps_to_segment(segment &dstseg, const segment &srcseg)
939
{
940
        shared_segment &shared_dst_seg = dstseg;
941
        unique_segment &unique_dst_seg = dstseg;
942
        const shared_segment &shared_src_seg = srcseg;
943
        const unique_segment &unique_src_seg = srcseg;
944
        range_for (const auto &&z, zip(shared_src_seg.sides, shared_dst_seg.sides, unique_src_seg.sides, unique_dst_seg.sides))
945
        {
946
                auto &shared_src_side = std::get<0>(z);
947
                auto &shared_dst_side = std::get<1>(z);
948
                auto &unique_src_side = std::get<2>(z);
949
                auto &unique_dst_side = std::get<3>(z);
950
                shared_dst_side.set_type(shared_src_side.get_type());
951
                unique_dst_side.tmap_num = unique_src_side.tmap_num;
952
                unique_dst_side.tmap_num2 = unique_src_side.tmap_num2;
953
        }
954
 
955
}
956
 
957
// ------------------------------------------------------------------------------------------
958
// Rotate the segment *seg by the pitch, bank, heading defined by *rot, destructively
959
// modifying its four free vertices in the global array Vertices.
960
// It is illegal to rotate a segment which has connectivity != 1.
961
// Pitch, bank, heading are about the point which is the average of the four points
962
// forming the side of connection.
963
// Return value:
964
//  0 = successful rotation
965
//  1 = Connectivity makes rotation illegal (connected to 0 or 2+ segments)
966
//  2 = Rotation causes degeneracy, such as self-intersecting segment.
967
//       3 = Unable to rotate because not connected to exactly 1 segment.
968
int med_rotate_segment(const vmsegptridx_t seg, const vms_matrix &rotmat)
969
{
970
        int             newside=0,destside;
971
        int             count;
972
 
973
        // Find side of attachment
974
        count = 0;
975
        range_for (const auto &&es, enumerate(seg->children))
976
                if (IS_CHILD(es.value))
977
                {
978
                        count++;
979
                        newside = es.idx;
980
                }
981
 
982
        // Return if passed in segment is connected to other than 1 segment.
983
        if (count != 1)
984
                return 3;
985
 
986
        const auto &&destseg = seg.absolute_sibling(seg->children[newside]);
987
 
988
        destside = 0;
989
        while (destside < MAX_SIDES_PER_SEGMENT && destseg->children[destside] != seg)
990
                destside++;
991
 
992
        // Before deleting the segment, copy its texture maps to New_segment
993
        copy_tmaps_to_segment(vmsegptr(&New_segment), seg);
994
 
995
        if (Curside == WFRONT)
996
                Curside = WBACK;
997
 
998
        med_attach_segment_rotated(destseg, vmsegptr(&New_segment), destside, AttachSide, rotmat);
999
 
1000
        //      Save tmap_num on each side to restore after call to med_propagate_tmaps_to_segments and _back_side
1001
        //      which will change the tmap nums.
1002
        std::array<int16_t, MAX_SIDES_PER_SEGMENT> side_tmaps;
1003
        range_for (const auto &&z, zip(side_tmaps, seg->unique_segment::sides))
1004
        {
1005
                const unique_side &us = std::get<1>(z);
1006
                std::get<0>(z) = us.tmap_num;
1007
        }
1008
 
1009
        auto back_side = Side_opposite[find_connect_side(destseg, seg)];
1010
 
1011
        med_propagate_tmaps_to_segments(destseg, seg,0);
1012
        med_propagate_tmaps_to_back_side(seg, back_side,0);
1013
 
1014
        for (const auto &&[idx, side_tmap, us] : enumerate(zip(side_tmaps, seg->unique_segment::sides)))
1015
                if (idx != back_side)
1016
                {
1017
                        us.tmap_num = side_tmap;
1018
                }
1019
 
1020
        return  0;
1021
}
1022
 
1023
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
1024
 
1025
// ----------------------------------------------------------------------------
1026
//      Compute the sum of the distances between the four pairs of points.
1027
//      The connections are:
1028
//              firstv1 : 0             (firstv1+1)%4 : 1               (firstv1+2)%4 : 2               (firstv1+3)%4 : 3
1029
static fix seg_seg_vertex_distsum(const vcsegptr_t seg1, const unsigned side1, const vcsegptr_t seg2, const unsigned side2, const unsigned firstv1)
1030
{
1031
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1032
        auto &Vertices = LevelSharedVertexState.get_vertices();
1033
        fix     distsum;
1034
 
1035
        distsum = 0;
1036
        auto &vcvertptr = Vertices.vcptr;
1037
        range_for (const unsigned secondv, xrange(4u))
1038
        {
1039
                const unsigned firstv = (4 - secondv + (3 - firstv1)) % 4;
1040
                distsum += vm_vec_dist(vcvertptr(seg1->verts[Side_to_verts[side1][firstv]]),vcvertptr(seg2->verts[Side_to_verts[side2][secondv]]));
1041
        }
1042
 
1043
        return distsum;
1044
 
1045
}
1046
 
1047
// ----------------------------------------------------------------------------
1048
//      Determine how to connect two segments together with the least amount of twisting.
1049
//      Returns vertex index in 0..3 on first segment.  Assumed ordering of vertices
1050
//      on second segment is 0,1,2,3.
1051
//      So, if return value is 2, connect 2:0 3:1 0:2 1:3.
1052
//      Theory:
1053
//              We select an ordering of vertices for connection.  For the first pair of vertices to be connected,
1054
//              compute the vector.  For the three remaining pairs of vertices, compute the vectors from one vertex
1055
//              to the other.  Compute the dot products of these vectors with the original vector.  Add them up.
1056
//              The close we are to 3, the better fit we have.  Reason:  The largest value for the dot product is
1057
//              1.0, and this occurs for a parallel set of vectors.
1058
static int get_index_of_best_fit(const vcsegptr_t seg1, int side1, const vcsegptr_t seg2, int side2)
1059
{
1060
        int     firstv;
1061
        fix     min_distance;
1062
        int     best_index=0;
1063
 
1064
        min_distance = F1_0*30000;
1065
 
1066
        for (firstv=0; firstv<4; firstv++) {
1067
                fix t;
1068
                t = seg_seg_vertex_distsum(seg1, side1, seg2, side2, firstv);
1069
                if (t <= min_distance) {
1070
                        min_distance = t;
1071
                        best_index = firstv;
1072
                }
1073
        }
1074
 
1075
        return best_index;
1076
 
1077
}
1078
 
1079
 
1080
#define MAX_VALIDATIONS 50
1081
 
1082
// ----------------------------------------------------------------------------
1083
//      Remap uv coordinates in all sides in segment *sp which have a vertex in vp[4].
1084
//      vp contains absolute vertex indices.
1085
static void remap_side_uvs(const vmsegptridx_t sp, const std::array<int, 4> &vp)
1086
{
1087
        range_for (const auto &&es, enumerate(Side_to_verts))
1088
        {
1089
                range_for (const auto v, es.value)
1090
                        range_for (auto &i, vp) // scan each vertex in vp[4]
1091
                                if (v == i) {
1092
                                        assign_default_uvs_to_side(sp, es.idx);                                 // Side s needs to be remapped
1093
                                        goto next_side;
1094
                                }
1095
next_side: ;
1096
        }
1097
}
1098
 
1099
// ----------------------------------------------------------------------------
1100
//      Modify seg2 to share side2 with seg1:side1.  This forms a connection between
1101
//      two segments without creating a new segment.  It modifies seg2 by sharing
1102
//      vertices from seg1.  seg1 is not modified.  Four vertices from seg2 are
1103
//      deleted.
1104
//      Return code:
1105
//              0                       joint formed
1106
//              1                       -- no, this is legal! -- unable to form joint because one or more vertices of side2 is not free
1107
//              2                       unable to form joint because side1 is already used
1108
int med_form_joint(const vmsegptridx_t seg1, int side1, const vmsegptridx_t seg2, int side2)
1109
{
1110
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1111
        auto &Vertices = LevelSharedVertexState.get_vertices();
1112
        int             bfi,v,s1;
1113
        std::array<int, 4> lost_vertices, remap_vertices;
1114
        std::array<segnum_t, MAX_VALIDATIONS> validation_list;
1115
        uint_fast32_t nv;
1116
 
1117
        //      Make sure that neither side is connected.
1118
        if (IS_CHILD(seg1->children[side1]) || IS_CHILD(seg2->children[side2]))
1119
                return 2;
1120
 
1121
        // Make sure there is no wall there 
1122
        if ((seg1->shared_segment::sides[side1].wall_num != wall_none) || (seg2->shared_segment::sides[side2].wall_num != wall_none))
1123
                return 2;
1124
 
1125
        //      We can form the joint.  Find the best orientation of vertices.
1126
        bfi = get_index_of_best_fit(seg1, side1, seg2, side2);
1127
 
1128
        auto &vp1 = Side_to_verts[side1];
1129
        auto &vp2 = Side_to_verts[side2];
1130
 
1131
        //      Make a copy of the list of vertices in seg2 which will be deleted and set the
1132
        //      associated vertex number, so that all occurrences of the vertices can be replaced.
1133
        for (v=0; v<4; v++)
1134
                lost_vertices[v] = seg2->verts[static_cast<int>(vp2[v])];
1135
 
1136
        //      Now, for each vertex in lost_vertices, determine which vertex it maps to.
1137
        for (v=0; v<4; v++)
1138
                remap_vertices[3 - ((v + bfi) % 4)] = seg1->verts[static_cast<int>(vp1[v])];
1139
 
1140
        // Now, in all segments, replace all occurrences of vertices in lost_vertices with remap_vertices
1141
 
1142
        // Put the one segment we know are being modified into the validation list.
1143
        // Note: seg1 does not require a full validation, only a validation of the affected side.  Its vertices do not move.
1144
        nv = 1;
1145
        validation_list[0] = seg2;
1146
 
1147
        for (v=0; v<4; v++)
1148
                range_for (const auto &&segp, vmsegptridx)
1149
                {
1150
                        if (segp->segnum != segment_none)
1151
                                range_for (auto &sv, segp->verts)
1152
                                        if (sv == lost_vertices[v]) {
1153
                                                sv = remap_vertices[v];
1154
                                                // Add segment to list of segments to be validated.
1155
                                                for (s1=0; s1<nv; s1++)
1156
                                                        if (validation_list[s1] == segp)
1157
                                                                break;
1158
                                                if (s1 == nv)
1159
                                                        validation_list[nv++] = segp;
1160
                                                Assert(nv < MAX_VALIDATIONS);
1161
                                        }
1162
                }
1163
 
1164
        //      Form new connections.
1165
        seg1->children[side1] = seg2;
1166
        seg2->children[side2] = seg1;
1167
 
1168
        // validate all segments
1169
        auto &vcvertptr = Vertices.vcptr;
1170
        validate_segment_side(vcvertptr, seg1, side1);
1171
        range_for (auto &s, partial_const_range(validation_list, nv))
1172
        {
1173
                const auto &&segp = seg1.absolute_sibling(s);
1174
                validate_segment(vcvertptr, segp);
1175
                remap_side_uvs(segp, remap_vertices);   // remap uv coordinates on sides which were reshaped (ie, have a vertex in lost_vertices)
1176
                warn_if_concave_segment(segp);
1177
        }
1178
 
1179
        set_vertex_counts();
1180
 
1181
        return 0;
1182
}
1183
 
1184
// ----------------------------------------------------------------------------
1185
//      Create a new segment and use it to form a bridge between two existing segments.
1186
//      Specify two segment:side pairs.  If either segment:side is not open (ie, segment->children[side] != -1)
1187
//      then it is not legal to form the brider.
1188
//      Return:
1189
//              0       bridge segment formed
1190
//              1       unable to form bridge because one (or both) of the sides is not open.
1191
//      Note that no new vertices are created by this process.
1192
int med_form_bridge_segment(const vmsegptridx_t seg1, int side1, const vmsegptridx_t seg2, int side2)
1193
{
1194
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1195
        auto &Vertices = LevelSharedVertexState.get_vertices();
1196
        int                     v,bfi;
1197
 
1198
        if (IS_CHILD(seg1->children[side1]) || IS_CHILD(seg2->children[side2]))
1199
                return 1;
1200
 
1201
        const auto &&bs = seg1.absolute_sibling(get_free_segment_number(Segments));
1202
        shared_segment &sbs = *bs;
1203
        sbs.segnum = bs;
1204
        bs->objects = object_none;
1205
 
1206
        // Copy vertices from seg2 into last 4 vertices of bridge segment.
1207
        {
1208
        auto &sv = Side_to_verts[side2];
1209
        for (v=0; v<4; v++)
1210
                sbs.verts[(3-v)+4] = seg2->verts[static_cast<int>(sv[v])];
1211
        }
1212
 
1213
        // Copy vertices from seg1 into first 4 vertices of bridge segment.
1214
        bfi = get_index_of_best_fit(seg1, side1, seg2, side2);
1215
 
1216
        {
1217
        auto &sv = Side_to_verts[side1];
1218
        for (v=0; v<4; v++)
1219
                bs->verts[(v + bfi) % 4] = seg1->verts[static_cast<int>(sv[v])];
1220
        }
1221
 
1222
        // Form connections to children, first initialize all to unconnected.
1223
        range_for (const auto &&z, zip(sbs.children, sbs.sides))
1224
        {
1225
                std::get<0>(z) = segment_none;
1226
                std::get<1>(z).wall_num = wall_none;
1227
        }
1228
 
1229
        // Now form connections between segments.
1230
 
1231
        bs->children[AttachSide] = seg1;
1232
        bs->children[Side_opposite[AttachSide]] = seg2;
1233
 
1234
        seg1->children[side1] = bs; //seg2 - Segments;
1235
        seg2->children[side2] = bs; //seg1 - Segments;
1236
 
1237
        //      Validate bridge segment, and if degenerate, clean up mess.
1238
        Degenerate_segment_found = 0;
1239
 
1240
        auto &vcvertptr = Vertices.vcptr;
1241
        validate_segment(vcvertptr, bs);
1242
 
1243
        if (Degenerate_segment_found) {
1244
                seg1->children[side1] = segment_none;
1245
                seg2->children[side2] = segment_none;
1246
                bs->children[AttachSide] = segment_none;
1247
                bs->children[static_cast<int>(Side_opposite[AttachSide])] = segment_none;
1248
                if (med_delete_segment(bs)) {
1249
                        Int3();
1250
                }
1251
                editor_status("Bridge segment would be degenerate, not created.\n");
1252
                return 1;
1253
        } else {
1254
                validate_segment(vcvertptr, seg1);      // used to only validate side, but segment does more error checking: ,side1);
1255
                validate_segment(vcvertptr, seg2);      // ,side2);
1256
                med_propagate_tmaps_to_segments(seg1,bs,0);
1257
 
1258
                editor_status("Bridge segment formed.");
1259
                warn_if_concave_segment(bs);
1260
                return 0;
1261
        }
1262
}
1263
 
1264
// -------------------------------------------------------------------------------
1265
//      Create a segment given center, dimensions, rotation matrix.
1266
//      Note that the created segment will always have planar sides and rectangular cross sections.
1267
//      It will be created with walls on all sides, ie not connected to anything.
1268
void med_create_segment(const vmsegptridx_t sp,fix cx, fix cy, fix cz, fix length, fix width, fix height, const vms_matrix &mp)
1269
{
1270
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1271
        auto &Vertices = LevelSharedVertexState.get_vertices();
1272
        int                     f;
1273
        ++ LevelSharedSegmentState.Num_segments;
1274
 
1275
        sp->segnum = 1;                                         // What to put here?  I don't know.
1276
 
1277
        // Form connections to children, of which it has none.
1278
        for (unsigned i = 0; i < MAX_SIDES_PER_SEGMENT; ++i)
1279
        {
1280
                sp->children[i] = segment_none;
1281
//              sp->sides[i].render_flag = 0;
1282
                sp->shared_segment::sides[i].wall_num  = wall_none;
1283
        }
1284
 
1285
        sp->group = -1;
1286
        sp->matcen_num = -1;
1287
 
1288
        //      Create relative-to-center vertices, which are the rotated points on the box defined by length, width, height
1289
        sp->verts[0] = med_add_vertex(vertex{vm_vec_rotate({+width/2, +height/2, -length/2}, mp)});
1290
        sp->verts[1] = med_add_vertex(vertex{vm_vec_rotate({+width/2, -height/2, -length/2}, mp)});
1291
        sp->verts[2] = med_add_vertex(vertex{vm_vec_rotate({-width/2, -height/2, -length/2}, mp)});
1292
        sp->verts[3] = med_add_vertex(vertex{vm_vec_rotate({-width/2, +height/2, -length/2}, mp)});
1293
        sp->verts[4] = med_add_vertex(vertex{vm_vec_rotate({+width/2, +height/2, +length/2}, mp)});
1294
        sp->verts[5] = med_add_vertex(vertex{vm_vec_rotate({+width/2, -height/2, +length/2}, mp)});
1295
        sp->verts[6] = med_add_vertex(vertex{vm_vec_rotate({-width/2, -height/2, +length/2}, mp)});
1296
        sp->verts[7] = med_add_vertex(vertex{vm_vec_rotate({-width/2, +height/2, +length/2}, mp)});
1297
 
1298
        // Now create the vector which is the center of the segment and add that to all vertices.
1299
        const vms_vector cv{cx, cy, cz};
1300
 
1301
        //      Now, add the center to all vertices, placing the segment in 3 space.
1302
        auto &vmvertptr = Vertices.vmptr;
1303
        range_for (auto &i, sp->verts)
1304
                vm_vec_add2(vmvertptr(i), cv);
1305
 
1306
        //      Set scale vector.
1307
//      sp->scale.x = width;
1308
//      sp->scale.y = height;
1309
//      sp->scale.z = length;
1310
 
1311
        //      Add faces to all sides.
1312
        auto &vcvertptr = Vertices.vcptr;
1313
        for (f=0; f<MAX_SIDES_PER_SEGMENT; f++)
1314
                create_walls_on_side(vcvertptr, sp, f);
1315
 
1316
        sp->objects = object_none;              //no objects in this segment
1317
 
1318
        // Assume nothing special about this segment
1319
        sp->special = 0;
1320
        sp->station_idx = station_none;
1321
        sp->static_light = 0;
1322
        sp->matcen_num = -1;
1323
 
1324
        copy_tmaps_to_segment(sp, vcsegptr(&New_segment));
1325
 
1326
        assign_default_uvs_to_segment(sp);
1327
}
1328
 
1329
// ----------------------------------------------------------------------------------------------
1330
//      Create New_segment using a specified scale factor.
1331
void med_create_new_segment(const vms_vector &scale)
1332
{
1333
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1334
        int                     t;
1335
        const auto &&sp = vmsegptridx(&New_segment);
1336
        fix                     length,width,height;
1337
 
1338
        length = scale.z;
1339
        width = scale.x;
1340
        height = scale.y;
1341
 
1342
        sp->segnum = 1;                                         // What to put here?  I don't know.
1343
 
1344
        //      Create relative-to-center vertices, which are the points on the box defined by length, width, height
1345
        t = LevelSharedVertexState.Num_vertices;
1346
        sp->verts[0] = med_set_vertex(NEW_SEGMENT_VERTICES+0,{+width/2,+height/2,-length/2});
1347
        sp->verts[1] = med_set_vertex(NEW_SEGMENT_VERTICES+1,{+width/2,-height/2,-length/2});
1348
        sp->verts[2] = med_set_vertex(NEW_SEGMENT_VERTICES+2,{-width/2,-height/2,-length/2});
1349
        sp->verts[3] = med_set_vertex(NEW_SEGMENT_VERTICES+3,{-width/2,+height/2,-length/2});
1350
        sp->verts[4] = med_set_vertex(NEW_SEGMENT_VERTICES+4,{+width/2,+height/2,+length/2});
1351
        sp->verts[5] = med_set_vertex(NEW_SEGMENT_VERTICES+5,{+width/2,-height/2,+length/2});
1352
        sp->verts[6] = med_set_vertex(NEW_SEGMENT_VERTICES+6,{-width/2,-height/2,+length/2});
1353
        sp->verts[7] = med_set_vertex(NEW_SEGMENT_VERTICES+7,{-width/2,+height/2,+length/2});
1354
        LevelSharedVertexState.Num_vertices = t;
1355
 
1356
//      sp->scale = *scale;
1357
 
1358
        auto &Vertices = LevelSharedVertexState.get_vertices();
1359
        auto &vcvertptr = Vertices.vcptr;
1360
        // Form connections to children, of which it has none, init faces and tmaps.
1361
        for (const auto &&[s, child, ss, us] : enumerate(zip(sp->children, sp->shared_segment::sides, sp->unique_segment::sides)))
1362
        {
1363
                child = segment_none;
1364
                ss.wall_num = wall_none;
1365
                create_walls_on_side(vcvertptr, sp, s);
1366
                us.tmap_num = s + 1;                                    // assign some stupid old tmap to this side.
1367
                us.tmap_num2 = 0;
1368
        }
1369
 
1370
        Seg_orientation = {};
1371
 
1372
        sp->objects = object_none;              //no objects in this segment
1373
 
1374
        assign_default_uvs_to_segment(sp);
1375
 
1376
        // Assume nothing special about this segment
1377
        sp->special = 0;
1378
        sp->station_idx = station_none;
1379
        sp->static_light = 0;
1380
        sp->matcen_num = -1;
1381
}
1382
 
1383
// -------------------------------------------------------------------------------
1384
void med_create_new_segment_from_cursegp(void)
1385
{
1386
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1387
        auto &Vertices = LevelSharedVertexState.get_vertices();
1388
        vms_vector      scalevec;
1389
        vms_vector      uvec, rvec, fvec;
1390
 
1391
        med_extract_up_vector_from_segment_side(Cursegp, Curside, uvec);
1392
        med_extract_right_vector_from_segment_side(Cursegp, Curside, rvec);
1393
        auto &vcvertptr = Vertices.vcptr;
1394
        extract_forward_vector_from_segment(vcvertptr, Cursegp, fvec);
1395
 
1396
        scalevec.x = vm_vec_mag(rvec);
1397
        scalevec.y = vm_vec_mag(uvec);
1398
        scalevec.z = vm_vec_mag(fvec);
1399
        med_create_new_segment(scalevec);
1400
}
1401
 
1402
// -------------------------------------------------------------------------------
1403
//      Initialize all vertices to inactive status.
1404
void init_all_vertices(void)
1405
{
1406
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1407
        auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
1408
        Vertex_active = {};
1409
        range_for (auto &s, Segments)
1410
                s.segnum = segment_none;
1411
}
1412
 
1413
// -----------------------------------------------------------------------------
1414
//      Create coordinate axes in orientation of specified segment, stores vertices at *vp.
1415
void create_coordinate_axes_from_segment(const vmsegptr_t sp, std::array<unsigned, 16> &vertnums)
1416
{
1417
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1418
        auto &Vertices = LevelSharedVertexState.get_vertices();
1419
        vms_matrix      rotmat;
1420
        vms_vector t;
1421
 
1422
        med_extract_matrix_from_segment(sp, rotmat);
1423
 
1424
        auto &vcvertptr = Vertices.vcptr;
1425
        auto &vmvertptr = Vertices.vmptr;
1426
        const auto &&v0 = vmvertptr(vertnums[0]);
1427
        compute_segment_center(vcvertptr, v0, sp);
1428
 
1429
        t = rotmat.rvec;
1430
        vm_vec_scale(t,i2f(32));
1431
        vm_vec_add(vmvertptr(vertnums[1]), v0, t);
1432
 
1433
        t = rotmat.uvec;
1434
        vm_vec_scale(t,i2f(32));
1435
        vm_vec_add(vmvertptr(vertnums[2]), v0, t);
1436
 
1437
        t = rotmat.fvec;
1438
        vm_vec_scale(t,i2f(32));
1439
        vm_vec_add(vmvertptr(vertnums[3]), v0, t);
1440
}
1441
 
1442
// -----------------------------------------------------------------------------
1443
//      Determine if a segment is concave. Returns true if concave
1444
static int check_seg_concavity(const vcsegptr_t s)
1445
{
1446
        vms_vector n0;
1447
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1448
        auto &Vertices = LevelSharedVertexState.get_vertices();
1449
        auto &vcvertptr = Vertices.vcptr;
1450
        range_for (auto &sn, Side_to_verts)
1451
                for (unsigned vn = 0; vn <= 4; ++vn)
1452
                {
1453
                        const auto n1 = vm_vec_normal(
1454
                                vcvertptr(s->verts[sn[vn % 4]]),
1455
                                vcvertptr(s->verts[sn[(vn + 1) % 4]]),
1456
                                vcvertptr(s->verts[sn[(vn + 2) % 4]]));
1457
 
1458
                        //vm_vec_normalize(&n1);
1459
 
1460
                        if (vn>0) if (vm_vec_dot(n0,n1) < f0_5) return 1;
1461
 
1462
                        n0 = n1;
1463
                }
1464
 
1465
        return 0;
1466
}
1467
 
1468
 
1469
// -----------------------------------------------------------------------------
1470
//      Find all concave segments and add to list
1471
void find_concave_segs()
1472
{
1473
        Warning_segs.clear();
1474
 
1475
        range_for (const auto &&s, vcsegptridx)
1476
                if (s->segnum != segment_none)
1477
                        if (check_seg_concavity(s))
1478
                                Warning_segs.emplace_back(s);
1479
}
1480
 
1481
 
1482
// -----------------------------------------------------------------------------
1483
void warn_if_concave_segments(void)
1484
{
1485
        find_concave_segs();
1486
 
1487
        if (!Warning_segs.empty())
1488
        {
1489
                editor_status_fmt("*** WARNING *** %d concave segments in mine! *** WARNING ***", Warning_segs.size());
1490
    }
1491
}
1492
 
1493
// -----------------------------------------------------------------------------
1494
//      Check segment s, if concave, warn
1495
void warn_if_concave_segment(const vmsegptridx_t s)
1496
{
1497
        int     result;
1498
 
1499
        result = check_seg_concavity(s);
1500
 
1501
        if (result) {
1502
                Warning_segs.emplace_back(s);
1503
 
1504
                        editor_status("*** WARNING *** New segment is concave! *** WARNING ***");
1505
        } //else
1506
        //editor_status("");
1507
}
1508
 
1509
 
1510
// -------------------------------------------------------------------------------
1511
//      Find segment adjacent to sp:side.
1512
//      Adjacent means a segment which shares all four vertices.
1513
//      Return true if segment found and fill in segment in adj_sp and side in adj_side.
1514
//      Return false if unable to find, in which case adj_sp and adj_side are undefined.
1515
int med_find_adjacent_segment_side(const vmsegptridx_t sp, int side, imsegptridx_t &adj_sp, int *adj_side)
1516
{
1517
        std::array<int, 4> abs_verts;
1518
 
1519
        //      Stuff abs_verts[4] array with absolute vertex indices
1520
        range_for (const unsigned v, xrange(4u))
1521
                abs_verts[v] = sp->verts[Side_to_verts[side][v]];
1522
 
1523
        //      Scan all segments, looking for a segment which contains the four abs_verts
1524
        range_for (const auto &&segp, vmsegptridx)
1525
        {
1526
                if (segp != sp)
1527
                {
1528
                        range_for (auto &v, abs_verts)
1529
                        {                                                                                               // do for each vertex in abs_verts
1530
                                range_for (auto &vv, segp->verts) // do for each vertex in segment
1531
                                        if (v == vv)
1532
                                                goto fass_found1;                                                                                       // Current vertex (indexed by v) is present in segment, try next
1533
                                goto fass_next_seg;                                                                                             // This segment doesn't contain the vertex indexed by v
1534
                        fass_found1: ;
1535
                        }               // end for v
1536
 
1537
                        //      All four vertices in sp:side are present in segment seg.
1538
                        //      Determine side and return
1539
                        range_for (const auto &&es, enumerate(Side_to_verts))
1540
                        {
1541
                                range_for (const auto v, es.value)
1542
                                {
1543
                                        range_for (auto &vv, abs_verts)
1544
                                        {
1545
                                                if (segp->verts[v] == vv)
1546
                                                        goto fass_found2;
1547
                                        }
1548
                                        goto fass_next_side;                                                                                    // Couldn't find vertex v in current side, so try next side.
1549
                                fass_found2: ;
1550
                                }
1551
                                // Found all four vertices in current side.  We are done!
1552
                                adj_sp = segp;
1553
                                *adj_side = es.idx;
1554
                                return 1;
1555
                        fass_next_side: ;
1556
                        }
1557
                        Assert(0);      // Impossible -- we identified this segment as containing all 4 vertices of side "side", but we couldn't find them.
1558
                        return 0;
1559
                fass_next_seg: ;
1560
                }
1561
        }
1562
 
1563
        return 0;
1564
}
1565
 
1566
 
1567
#define JOINT_THRESHOLD 10000*F1_0              // (Huge threshold)
1568
 
1569
// -------------------------------------------------------------------------------
1570
//      Find segment closest to sp:side.
1571
//      Return true if segment found and fill in segment in adj_sp and side in adj_side.
1572
//      Return false if unable to find, in which case adj_sp and adj_side are undefined.
1573
int med_find_closest_threshold_segment_side(const vmsegptridx_t sp, int side, imsegptridx_t &adj_sp, int *adj_side, fix threshold)
1574
{
1575
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1576
        auto &Vertices = LevelSharedVertexState.get_vertices();
1577
        fix                     current_dist, closest_seg_dist;
1578
 
1579
        if (IS_CHILD(sp->children[side]))
1580
                return 0;
1581
 
1582
        auto &vcvertptr = Vertices.vcptr;
1583
        const auto &&vsc = compute_center_point_on_side(vcvertptr, sp, side);
1584
 
1585
        closest_seg_dist = JOINT_THRESHOLD;
1586
 
1587
        //      Scan all segments, looking for a segment which contains the four abs_verts
1588
        range_for (const auto &&segp, vmsegptridx)
1589
        {
1590
                if (segp != sp)
1591
                        range_for (const auto &&es, enumerate(segp->children))
1592
                        {
1593
                                if (!IS_CHILD(es.value))
1594
                                {
1595
                                        const auto &&vtc = compute_center_point_on_side(vcvertptr, segp, es.idx);
1596
                                        current_dist = vm_vec_dist( vsc, vtc );
1597
                                        if (current_dist < closest_seg_dist) {
1598
                                                adj_sp = segp;
1599
                                                *adj_side = es.idx;
1600
                                                closest_seg_dist = current_dist;
1601
                                        }
1602
                                }
1603
                        }      
1604
        }
1605
 
1606
        if (closest_seg_dist < threshold)
1607
                return 1;
1608
        else
1609
                return 0;
1610
}
1611
 
1612
}