1 /* $Id: group.c,v 1.5 2005-01-25 20:46:55 schaffner Exp $ */
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
22 static char rcsid[] = "$Id: group.c,v 1.5 2005-01-25 20:46:55 schaffner Exp $";
39 #include "editor/editor.h"
44 #include "bm.h" // For MAX_TEXTURES.
51 void validate_selected_segments(void);
56 } group_top_fileinfo; // Should be same as first two fields below...
61 int header_offset; // Stuff common to game & editor
63 int editor_offset; // Editor specific stuff
83 int newsegment_offset;
89 group GroupList[MAX_GROUPS+1];
90 segment *Groupsegp[MAX_GROUPS+1];
91 int Groupside[MAX_GROUPS+1];
92 int Group_orientation[MAX_GROUPS+1];
96 extern void validate_segment_side(segment *sp, int sidenum);
98 // -- void swap_negate_columns(vms_matrix *rotmat, int col1, int col2)
100 // -- fix col1_1,col1_2,col1_3;
101 // -- fix col2_1,col2_2,col2_3;
103 // -- switch (col1) {
105 // -- col1_1 = rotmat->m1;
106 // -- col1_2 = rotmat->m2;
107 // -- col1_3 = rotmat->m3;
111 // -- col1_1 = rotmat->m4;
112 // -- col1_2 = rotmat->m5;
113 // -- col1_3 = rotmat->m6;
117 // -- col1_1 = rotmat->m7;
118 // -- col1_2 = rotmat->m8;
119 // -- col1_3 = rotmat->m9;
123 // -- switch (col2) {
125 // -- col2_1 = rotmat->m1;
126 // -- col2_2 = rotmat->m2;
127 // -- col2_3 = rotmat->m3;
131 // -- col2_1 = rotmat->m4;
132 // -- col2_2 = rotmat->m5;
133 // -- col2_3 = rotmat->m6;
137 // -- col2_1 = rotmat->m7;
138 // -- col2_2 = rotmat->m8;
139 // -- col2_3 = rotmat->m9;
143 // -- switch (col2) {
145 // -- rotmat->m1 = -col1_1;
146 // -- rotmat->m2 = -col1_2;
147 // -- rotmat->m3 = -col1_3;
151 // -- rotmat->m4 = -col1_1;
152 // -- rotmat->m5 = -col1_2;
153 // -- rotmat->m6 = -col1_3;
157 // -- rotmat->m7 = -col1_1;
158 // -- rotmat->m8 = -col1_2;
159 // -- rotmat->m9 = -col1_3;
163 // -- switch (col1) {
165 // -- rotmat->m1 = -col2_1;
166 // -- rotmat->m2 = -col2_2;
167 // -- rotmat->m3 = -col2_3;
171 // -- rotmat->m4 = -col2_1;
172 // -- rotmat->m5 = -col2_2;
173 // -- rotmat->m6 = -col2_3;
177 // -- rotmat->m7 = -col2_1;
178 // -- rotmat->m8 = -col2_2;
179 // -- rotmat->m9 = -col2_3;
185 // -- void swap_negate_rows(vms_matrix *rotmat, int row1, int row2)
187 // -- fix row1_1,row1_2,row1_3;
188 // -- fix row2_1,row2_2,row2_3;
190 // -- switch (row1) {
192 // -- row1_1 = rotmat->m1;
193 // -- row1_2 = rotmat->m4;
194 // -- row1_3 = rotmat->m7;
198 // -- row1_1 = rotmat->m2;
199 // -- row1_2 = rotmat->m5;
200 // -- row1_3 = rotmat->m8;
204 // -- row1_1 = rotmat->m3;
205 // -- row1_2 = rotmat->m6;
206 // -- row1_3 = rotmat->m9;
210 // -- switch (row2) {
212 // -- row2_1 = rotmat->m1;
213 // -- row2_2 = rotmat->m4;
214 // -- row2_3 = rotmat->m7;
218 // -- row2_1 = rotmat->m2;
219 // -- row2_2 = rotmat->m5;
220 // -- row2_3 = rotmat->m8;
224 // -- row2_1 = rotmat->m3;
225 // -- row2_2 = rotmat->m6;
226 // -- row2_3 = rotmat->m9;
230 // -- switch (row2) {
232 // -- rotmat->m1 = -row1_1;
233 // -- rotmat->m4 = -row1_2;
234 // -- rotmat->m7 = -row1_3;
238 // -- rotmat->m2 = -row1_1;
239 // -- rotmat->m5 = -row1_2;
240 // -- rotmat->m8 = -row1_3;
244 // -- rotmat->m3 = -row1_1;
245 // -- rotmat->m6 = -row1_2;
246 // -- rotmat->m9 = -row1_3;
250 // -- switch (row1) {
252 // -- rotmat->m1 = -row2_1;
253 // -- rotmat->m4 = -row2_2;
254 // -- rotmat->m7 = -row2_3;
258 // -- rotmat->m2 = -row2_1;
259 // -- rotmat->m5 = -row2_2;
260 // -- rotmat->m8 = -row2_3;
264 // -- rotmat->m3 = -row2_1;
265 // -- rotmat->m6 = -row2_2;
266 // -- rotmat->m9 = -row2_3;
272 // -- // ------------------------------------------------------------------------------------------------
273 // -- void side_based_matrix(vms_matrix *rotmat,int destside)
275 // -- vms_angvec rotvec;
276 // -- vms_matrix r1,rtemp;
278 // -- switch (destside) {
280 // -- // swap_negate_columns(rotmat,1,2);
281 // -- // swap_negate_rows(rotmat,1,2);
288 // -- // swap_negate_columns(rotmat,1,2);
289 // -- // swap_negate_rows(rotmat,1,2);
305 // ------------------------------------------------------------------------------------------------
306 // Rotate a group about a point.
307 // The segments in the group are indicated (by segment number) in group_seglist. There are group_size segments.
308 // The point about which the groups is rotated is the center of first_seg:first_side.
310 // 0 absolute rotation, destination specified in terms of base_seg:base_side, used in moving or copying a group
311 // 1 relative rotation, destination specified relative to current orientation of first_seg:first_side
312 // Note: The group must exist in the mine, consisting of actual points in the world. If any points in the
313 // segments in the group are shared by segments not in the group, those points will get rotated and the
314 // segments not in the group will have their shapes modified.
317 // 1 unable to rotate group
318 void med_create_group_rotation_matrix(vms_matrix *result_mat, int delta_flag, segment *first_seg, int first_side, segment *base_seg, int base_side, vms_matrix *orient_matrix, int orientation)
320 vms_matrix rotmat2,rotmat,rotmat3,rotmat4;
321 vms_angvec pbh = {0,0,0};
323 // Determine whether this rotation is a delta rotation, meaning to just rotate in place, or an absolute rotation,
324 // which means that the destination rotation is specified, not as a delta, but as an absolute
326 // Create rotation matrix describing rotation.
327 med_extract_matrix_from_segment(first_seg, &rotmat4); // get rotation matrix describing current orientation of first seg
328 set_matrix_based_on_side(&rotmat4, first_side);
329 rotmat3 = *orient_matrix;
330 vm_transpose_matrix(&rotmat3);
331 vm_matrix_x_matrix(&rotmat,&rotmat4,&rotmat3); // this is the desired orientation of the new segment
332 vm_transpose_matrix(&rotmat4);
333 vm_matrix_x_matrix(&rotmat2,&rotmat,&rotmat4); // this is the desired orientation of the new segment
335 // Create rotation matrix describing rotation.
337 med_extract_matrix_from_segment(base_seg, &rotmat); // get rotation matrix describing desired orientation
338 set_matrix_based_on_side(&rotmat, base_side); // modify rotation matrix for desired side
340 // If the new segment is to be attached without rotation, then its orientation is the same as the base_segment
341 vm_matrix_x_matrix(&rotmat4,&rotmat,orient_matrix); // this is the desired orientation of the new segment
343 pbh.b = orientation*16384;
344 vm_angles_2_matrix(&rotmat3,&pbh);
345 vm_matrix_x_matrix(&rotmat, &rotmat4, &rotmat3);
350 med_extract_matrix_from_segment(first_seg, &rotmat3); // get rotation matrix describing current orientation of first seg
352 // It is curious that the following statement has no analogue in the med_attach_segment_rotated code.
353 // Perhaps it is because segments are always attached at their front side. If the back side is the side
354 // passed to the function, then the matrix is not modified, which might suggest that what you need to do below
355 // is use Side_opposite[first_side].
356 set_matrix_based_on_side(&rotmat3, Side_opposite[first_side]); // modify rotation matrix for desired side
358 vm_transpose_matrix(&rotmat3); // get the inverse of the current orientation matrix
359 vm_matrix_x_matrix(&rotmat2,&rotmat,&rotmat3); // now rotmat2 takes the current segment to the desired orientation
360 vm_transpose_matrix(&rotmat2);
363 *result_mat = rotmat2;
367 // -----------------------------------------------------------------------------------------
368 // Rotate all vertices and objects in group.
369 void med_rotate_group(vms_matrix *rotmat, short *group_seglist, int group_size, segment *first_seg, int first_side)
372 sbyte vertex_list[MAX_VERTICES];
373 vms_vector rotate_center;
375 compute_center_point_on_side(&rotate_center, first_seg, first_side);
377 // Create list of points to rotate.
378 for (v=0; v<=Highest_vertex_index; v++)
381 for (s=0; s<group_size; s++) {
382 segment *sp = &Segments[group_seglist[s]];
384 for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
385 vertex_list[sp->verts[v]] = 1;
387 // Rotate center of all objects in group.
388 objnum = sp->objects;
389 while (objnum != -1) {
392 mprintf((0, "%2i ", objnum));
393 vm_vec_sub(&tv1,&Objects[objnum].pos,&rotate_center);
394 vm_vec_rotate(&tv,&tv1,rotmat);
395 vm_vec_add(&Objects[objnum].pos, &tv, &rotate_center);
397 objnum = Objects[objnum].next;
401 // Do the pre-rotation xlate, do the rotation, do the post-rotation xlate
402 for (v=0; v<=Highest_vertex_index; v++)
403 if (vertex_list[v]) {
406 vm_vec_sub(&tv1,&Vertices[v],&rotate_center);
407 vm_vec_rotate(&tv,&tv1,rotmat);
408 vm_vec_add(&Vertices[v],&tv,&rotate_center);
415 // ------------------------------------------------------------------------------------------------
416 void cgl_aux(segment *segp, short *seglistp, int *num_segs, short *ignore_list, int num_ignore_segs)
419 int curseg = segp-Segments;
421 for (i=0; i<num_ignore_segs; i++)
422 if (curseg == ignore_list[i])
425 if ((segp-Segments < 0) || (segp-Segments >= MAX_SEGMENTS)) {
426 mprintf((0,"Warning -- invalid segment index = %i, max = %i\n",segp-Segments,MAX_SEGMENTS));
430 if (!Been_visited[segp-Segments]) {
431 seglistp[(*num_segs)++] = segp-Segments;
432 Been_visited[segp-Segments] = 1;
434 for (side=0; side<MAX_SIDES_PER_SEGMENT; side++)
435 if (IS_CHILD(segp->children[side]))
436 cgl_aux(&Segments[segp->children[side]], seglistp, num_segs, ignore_list, num_ignore_segs);
440 // ------------------------------------------------------------------------------------------------
441 // Sets Been_visited[n] if n is reachable from segp
442 void create_group_list(segment *segp, short *seglistp, int *num_segs, short *ignore_list, int num_ignore_segs)
446 for (i=0; i<MAX_SEGMENTS; i++)
449 cgl_aux(segp, seglistp, num_segs, ignore_list, num_ignore_segs);
453 #define MXS MAX_SEGMENTS
454 #define MXV MAX_VERTICES
456 // ------------------------------------------------------------------------------------------------
457 void duplicate_group(sbyte *vertex_ids, short *segment_ids, int num_segments)
459 int v,s,ss,new_vertex_id,new_segment_id,sidenum;
460 short new_segment_ids[MAX_SEGMENTS];
461 short new_vertex_ids[MAX_VERTICES]; // If new_vertex_ids[v] != -1, then vertex v has been remapped to new_vertex_ids[v]
462 short new_object_ids[MAX_OBJECTS];
464 // duplicate vertices
465 for (v=0; v<MXV; v++)
466 new_vertex_ids[v] = -1;
468 for (v=0; v<MAX_OBJECTS; v++)
469 new_object_ids[v] = -1;
471 // duplicate vertices
472 for (v=0; v<=Highest_vertex_index; v++) {
474 new_vertex_id = med_create_duplicate_vertex(&Vertices[v]);
475 new_vertex_ids[v] = new_vertex_id;
479 // duplicate segments
480 for (s=0; s<num_segments; s++) {
483 new_segment_id = med_create_duplicate_segment(&Segments[segment_ids[s]]);
484 new_segment_ids[s] = new_segment_id;
485 objnum = Segments[new_segment_id].objects;
486 Segments[new_segment_id].objects = -1;
487 while (objnum != -1) {
488 if (Objects[objnum].type != OBJ_PLAYER) {
490 new_obj_id = obj_create_copy(objnum, &Objects[objnum].pos, new_segment_id);
491 mprintf((0, "Object #%i in segment #%i copied to object #%i, segment #%i: new_obj->segnum = %i\n", objnum, Objects[objnum].segnum, new_obj_id, new_segment_id, Objects[new_obj_id].segnum));
493 objnum = Objects[objnum].next;
497 // Now, for each segment in segment_ids, correct its children numbers by translating through new_segment_ids
498 // and correct its vertex numbers by translating through new_vertex_ids
499 for (s=0; s<num_segments; s++) {
500 segment *sp = &Segments[new_segment_ids[s]];
501 for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
502 int seg = sp->children[sidenum];
504 for (ss=0; ss<num_segments; ss++) {
505 if (seg == segment_ids[ss])
506 Segments[new_segment_ids[s]].children[sidenum] = new_segment_ids[ss];
509 } // end for (sidenum=0...
511 // Now fixup vertex ids
512 for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) {
513 if (vertex_ids[sp->verts[v]]) {
514 sp->verts[v] = new_vertex_ids[sp->verts[v]];
519 // Now, copy new_segment_ids into segment_ids
520 for (s=0; s<num_segments; s++) {
521 segment_ids[s] = new_segment_ids[s];
524 // Now, copy new_vertex_ids into vertex_ids
525 for (v=0; v<MXV; v++)
528 for (v=0; v<MXV; v++) {
529 if (new_vertex_ids[v] != -1)
530 vertex_ids[new_vertex_ids[v]] = 1;
536 // ------------------------------------------------------------------------------------------------
537 int in_group(int segnum, int group_num)
541 for (i=0; i<GroupList[group_num].num_segments; i++)
542 if (segnum == GroupList[group_num].segments[i])
548 // ------------------------------------------------------------------------------------------------
549 // Copy a group of segments.
550 // The group is defined as all segments accessible from group_seg.
551 // The group is copied so group_seg:group_side is incident upon base_seg:base_side.
552 // group_seg and its vertices are bashed to coincide with base_seg.
553 // If any vertex of base_seg is contained in a segment that is reachable from group_seg, then errror.
554 int med_copy_group(int delta_flag, segment *base_seg, int base_side, segment *group_seg, int group_side, vms_matrix *orient_matrix)
557 vms_vector srcv,destv;
559 int new_current_group;
563 sbyte in_vertex_list[MAX_VERTICES];
567 if (IS_CHILD(base_seg->children[base_side])) {
568 editor_status("Error -- unable to copy group, base_seg:base_side must be free.");
572 if (num_groups == MAX_GROUPS) {
573 x = MessageBox( -2, -2, 2, "Warning: You have reached the MAXIMUM group number limit. Continue?", "No", "Yes" );
578 if (num_groups < MAX_GROUPS) {
580 new_current_group = num_groups-1;
582 new_current_group = 0;
584 Assert(current_group >= 0);
586 // Find groupsegp index
587 for (s=0;s<GroupList[current_group].num_segments;s++)
588 if (GroupList[current_group].segments[s] == (Groupsegp[current_group]-Segments))
591 GroupList[new_current_group] = GroupList[current_group];
593 // Make a list of all vertices in group.
594 if (group_seg == &New_segment)
595 for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
596 in_vertex_list[group_seg->verts[v]] = 1;
598 for (v=0; v<=Highest_vertex_index; v++)
599 in_vertex_list[v] = 0;
601 for (s=0; s<GroupList[new_current_group].num_segments; s++)
602 for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
603 in_vertex_list[Segments[GroupList[new_current_group].segments[s]].verts[v]] = 1;
606 // Show which objects are in which segments before group copy.
607 // for (s=0; s<=Highest_segment_index; s++) {
608 // int objnum = Segments[s].objects;
610 // mprintf((0, "Before: Segment #%2i contains objects ", s));
612 // while (objnum != -1) {
613 // mprintf((0, "%2i ",objnum));
614 // objnum = Objects[objnum].next;
616 // mprintf((0, "\n"));
619 // Given a list of vertex indices (indicated by !0 in in_vertex_list) and segment indices (in list GroupList[current_group].segments, there
620 // are GroupList[current_group].num_segments segments), copy all segments and vertices
621 // Return updated lists of vertices and segments in in_vertex_list and GroupList[current_group].segments
622 duplicate_group(in_vertex_list, GroupList[new_current_group].segments, GroupList[new_current_group].num_segments);
624 //group_seg = &Segments[GroupList[new_current_group].segments[0]]; // connecting segment in group has been changed, so update group_seg
626 Groupsegp[new_current_group] = group_seg = &Segments[GroupList[new_current_group].segments[gs_index]];
627 Groupside[new_current_group] = Groupside[current_group];
629 for (s=0; s<GroupList[new_current_group].num_segments; s++) {
630 Segments[GroupList[new_current_group].segments[s]].group = new_current_group;
631 Segment2s[GroupList[new_current_group].segments[s]].special = SEGMENT_IS_NOTHING;
632 Segment2s[GroupList[new_current_group].segments[s]].matcen_num = -1;
635 // Breaking connections between segments in the current group and segments not in the group.
636 for (s=0; s<GroupList[new_current_group].num_segments; s++) {
637 mprintf((0, "[%3i %3i] ", GroupList[new_current_group].segments[s], GroupList[current_group].segments[s]));
638 segp = &Segments[GroupList[new_current_group].segments[s]];
639 for (c=0; c<MAX_SIDES_PER_SEGMENT; c++)
640 if (IS_CHILD(segp->children[c])) {
641 if (!in_group(segp->children[c], new_current_group)) {
642 mprintf((0, "2: Breaking connection at seg:side = %i:%i\n", segp-Segments, c));
643 segp->children[c] = -1;
644 validate_segment_side(segp,c); // we have converted a connection to a side so validate the segment
649 copy_uvs_seg_to_seg(&New_segment, Groupsegp[new_current_group]);
652 // First, xlate all vertices so center of group_seg:group_side is at origin
653 compute_center_point_on_side(&srcv,group_seg,group_side);
654 for (v=0; v<=Highest_vertex_index; v++)
655 if (in_vertex_list[v])
656 vm_vec_sub2(&Vertices[v],&srcv);
658 // Now, translate all object positions.
659 for (s=0; s<GroupList[new_current_group].num_segments; s++) {
660 int segnum = GroupList[new_current_group].segments[s];
662 objnum = Segments[segnum].objects;
664 while (objnum != -1) {
665 vm_vec_sub2(&Objects[objnum].pos, &srcv);
666 objnum = Objects[objnum].next;
670 // Now, rotate segments in group so orientation of group_seg is same as base_seg.
671 med_create_group_rotation_matrix(&rotmat, delta_flag, group_seg, group_side, base_seg, base_side, orient_matrix, 0);
672 med_rotate_group(&rotmat, GroupList[new_current_group].segments, GroupList[new_current_group].num_segments, group_seg, group_side);
674 // Now xlate all vertices so group_seg:group_side shares center point with base_seg:base_side
675 compute_center_point_on_side(&destv,base_seg,base_side);
676 for (v=0; v<=Highest_vertex_index; v++)
677 if (in_vertex_list[v])
678 vm_vec_add2(&Vertices[v],&destv);
680 // Now, xlate all object positions.
681 for (s=0; s<GroupList[new_current_group].num_segments; s++) {
682 int segnum = GroupList[new_current_group].segments[s];
683 int objnum = Segments[segnum].objects;
685 while (objnum != -1) {
686 vm_vec_add2(&Objects[objnum].pos, &destv);
687 objnum = Objects[objnum].next;
691 // Now, copy all walls (ie, doors, illusionary, etc.) into the new group.
692 copy_group_walls(current_group, new_current_group);
694 current_group = new_current_group;
696 // Now, form joint on connecting sides.
697 med_form_joint(base_seg,base_side,Groupsegp[current_group],Groupside[new_current_group]);
699 validate_selected_segments();
700 med_combine_duplicate_vertices(in_vertex_list);
706 // ------------------------------------------------------------------------------------------------
707 // Move a group of segments.
708 // The group is defined as all segments accessible from group_seg.
709 // The group is moved so group_seg:group_side is incident upon base_seg:base_side.
710 // group_seg and its vertices are bashed to coincide with base_seg.
711 // If any vertex of base_seg is contained in a segment that is reachable from group_seg, then errror.
712 int med_move_group(int delta_flag, segment *base_seg, int base_side, segment *group_seg, int group_side, vms_matrix *orient_matrix, int orientation)
715 vms_vector srcv,destv;
716 segment *segp, *csegp, *dsegp;
717 sbyte in_vertex_list[MAX_VERTICES], out_vertex_list[MAX_VERTICES];
721 if (IS_CHILD(base_seg->children[base_side]))
722 if (base_seg->children[base_side] != group_seg-Segments) {
723 editor_status("Error -- unable to move group, base_seg:base_side must be free or point to group_seg.");
727 // // See if any vertices in base_seg are contained in any vertex in group_list
728 // for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
729 // for (s=0; s<GroupList[current_group].num_segments; s++)
730 // for (vv=0; vv<MAX_VERTICES_PER_SEGMENT; vv++)
731 // if (Segments[GroupList[current_group].segments[s]].verts[vv] == base_seg->verts[v]) {
732 // editor_status("Error -- unable to move group, it shares a vertex with destination segment.");
736 for (v=0; v<=Highest_vertex_index; v++) {
737 in_vertex_list[v] = 0;
738 out_vertex_list[v] = 0;
741 // Make a list of all vertices in group.
742 for (s=0; s<GroupList[current_group].num_segments; s++)
743 for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
744 in_vertex_list[Segments[GroupList[current_group].segments[s]].verts[v]] = 1;
746 // For all segments which are not in GroupList[current_group].segments, mark all their vertices in the out list.
747 for (s=0; s<=Highest_segment_index; s++) {
748 for (ss=0; ss<GroupList[current_group].num_segments; ss++)
749 if (GroupList[current_group].segments[ss] == s)
751 if (ss == GroupList[current_group].num_segments)
752 for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
753 out_vertex_list[Segments[s].verts[v]] = 1;
756 // Now, for all vertices present in both the in (part of group segment) and out (part of non-group segment)
757 // create an extra copy of the vertex so we can just move the ones in the in list.
758 local_hvi = Highest_vertex_index; // Can't use Highest_vertex_index as loop termination because it gets increased by med_create_duplicate_vertex.
760 for (v=0; v<=local_hvi; v++)
761 if (in_vertex_list[v])
762 if (out_vertex_list[v]) {
765 new_vertex_id = med_create_duplicate_vertex(&Vertices[v]);
766 in_vertex_list[v] = 0;
767 in_vertex_list[new_vertex_id] = 1;
769 // Create a new vertex and assign all occurrences of vertex v in IN list to new vertex number.
770 for (s=0; s<GroupList[current_group].num_segments; s++) {
771 segment *sp = &Segments[GroupList[current_group].segments[s]];
772 for (vv=0; vv<MAX_VERTICES_PER_SEGMENT; vv++)
773 if (sp->verts[vv] == v)
774 sp->verts[vv] = new_vertex_id;
778 for (s=0;s<GroupList[current_group].num_segments;s++)
779 Segments[GroupList[current_group].segments[s]].group = current_group;
781 // Breaking connections between segments in the group and segments not in the group.
782 for (s=0; s<GroupList[current_group].num_segments; s++)
784 segp = &Segments[GroupList[current_group].segments[s]];
785 for (c=0; c<MAX_SIDES_PER_SEGMENT; c++)
786 if (IS_CHILD(segp->children[c]))
788 csegp = &Segments[segp->children[c]];
789 if (csegp->group != current_group)
791 for (d=0; d<MAX_SIDES_PER_SEGMENT; d++)
792 if (IS_CHILD(csegp->children[d]))
794 dsegp = &Segments[csegp->children[d]];
795 if (dsegp->group == current_group)
797 csegp->children[d] = -1;
798 validate_segment_side(csegp,d); // we have converted a connection to a side so validate the segment
801 segp->children[c] = -1;
802 validate_segment_side(segp,c); // we have converted a connection to a side so validate the segment
807 copy_uvs_seg_to_seg(&New_segment, Groupsegp[current_group]);
810 // First, xlate all vertices so center of group_seg:group_side is at origin
811 compute_center_point_on_side(&srcv,group_seg,group_side);
812 for (v=0; v<=Highest_vertex_index; v++)
813 if (in_vertex_list[v])
814 vm_vec_sub2(&Vertices[v],&srcv);
816 // Now, move all object positions.
817 for (s=0; s<GroupList[current_group].num_segments; s++) {
818 int segnum = GroupList[current_group].segments[s];
819 int objnum = Segments[segnum].objects;
821 // mprintf((0, "Translating objects in segment #%2i by [%7.3f %7.3f %7.3f]: ", segnum, f2fl(srcv.x), f2fl(srcv.y), f2fl(srcv.z)));
823 while (objnum != -1) {
824 mprintf((0, "%2i ", objnum));
825 vm_vec_sub2(&Objects[objnum].pos, &srcv);
826 objnum = Objects[objnum].next;
829 // mprintf((0, "\n"));
831 // Now, rotate segments in group so orientation of group_seg is same as base_seg.
832 med_create_group_rotation_matrix(&rotmat, delta_flag, group_seg, group_side, base_seg, base_side, orient_matrix, orientation);
833 med_rotate_group(&rotmat, GroupList[current_group].segments, GroupList[current_group].num_segments, group_seg, group_side);
835 // Now xlate all vertices so group_seg:group_side shares center point with base_seg:base_side
836 compute_center_point_on_side(&destv,base_seg,base_side);
837 for (v=0; v<=Highest_vertex_index; v++)
838 if (in_vertex_list[v])
839 vm_vec_add2(&Vertices[v],&destv);
841 // Now, rotate all object positions.
842 for (s=0; s<GroupList[current_group].num_segments; s++) {
843 int segnum = GroupList[current_group].segments[s];
844 int objnum = Segments[segnum].objects;
846 while (objnum != -1) {
847 vm_vec_add2(&Objects[objnum].pos, &destv);
848 objnum = Objects[objnum].next;
852 // Now, form joint on connecting sides.
853 med_form_joint(base_seg,base_side,group_seg,group_side);
855 validate_selected_segments();
856 med_combine_duplicate_vertices(in_vertex_list);
862 // -----------------------------------------------------------------------------
863 int place_new_segment_in_world(void)
867 segnum = get_free_segment_number();
869 Segments[segnum] = New_segment;
871 for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++)
872 Segments[segnum].verts[v] = med_create_duplicate_vertex(&Vertices[New_segment.verts[v]]);
878 // -----------------------------------------------------------------------------
879 // Attach segment in the new-fangled way, which is by using the CopyGroup code.
880 int AttachSegmentNewAng(vms_angvec *pbh)
883 vms_matrix orient_matrix;
885 GroupList[current_group].num_segments = 1;
886 newseg = place_new_segment_in_world();
887 GroupList[current_group].segments[0] = newseg;
889 if (!med_move_group(1, Cursegp, Curside, &Segments[newseg], AttachSide, vm_angles_2_matrix(&orient_matrix,pbh),0)) {
890 autosave_mine(mine_filename);
892 med_propagate_tmaps_to_segments(Cursegp,&Segments[newseg],0);
893 med_propagate_tmaps_to_back_side(&Segments[newseg], Side_opposite[AttachSide],0);
894 copy_uvs_seg_to_seg(&New_segment,&Segments[newseg]);
896 Cursegp = &Segments[newseg];
897 Curside = Side_opposite[AttachSide];
898 med_create_new_segment_from_cursegp();
900 if (Lock_view_to_cursegp)
901 set_view_target_from_segment(Cursegp);
903 Update_flags |= UF_WORLD_CHANGED;
905 warn_if_concave_segment(Cursegp);
911 int AttachSegmentNew(void)
919 AttachSegmentNewAng(&pbh);
924 // -----------------------------------------------------------------------------
925 void save_selected_segs(int *num, short *segs)
929 for (i=0; i<GroupList[current_group].num_segments; i++)
930 segs[i] = GroupList[current_group].segments[i];
932 *num = GroupList[current_group].num_segments;
935 // -----------------------------------------------------------------------------
936 void restore_selected_segs(int num, short *segs)
940 for (i=0; i<GroupList[current_group].num_segments; i++)
941 GroupList[current_group].segments[i] = segs[i];
943 GroupList[current_group].num_segments = num;
946 // -----------------------------------------------------------------------------
947 void validate_selected_segments(void)
951 for (i=0; i<GroupList[current_group].num_segments; i++)
952 validate_segment(&Segments[GroupList[current_group].segments[i]]);
955 // =====================================================================================
958 // -----------------------------------------------------------------------------
959 void delete_segment_from_group(int segment_num, int group_num)
961 int g, del_seg_index;
964 for (g=0; g<GroupList[group_num].num_segments; g++)
965 if (segment_num == GroupList[group_num].segments[g]) {
970 //mprintf((0, "segment_num=%d delseg_index=%d\n", segment_num, del_seg_index));
972 if (IS_CHILD(del_seg_index)) {
973 for (g=del_seg_index;g<GroupList[group_num].num_segments-1;g++) {
974 GroupList[group_num].segments[g] = GroupList[group_num].segments[g+1];
976 GroupList[group_num].num_segments--;
977 //mprintf((0, "num_segments=%d\n\n", GroupList[group_num].num_segments));
978 Segments[segment_num].group = -1;
982 // =====================================================================================
985 // -----------------------------------------------------------------------------
986 void add_segment_to_group(int segment_num, int group_num)
988 GroupList[group_num].num_segments++;
989 GroupList[group_num].segments[GroupList[group_num].num_segments-1] = segment_num;
991 // =====================================================================================
994 // -----------------------------------------------------------------------------
995 int rotate_segment_new(vms_angvec *pbh)
997 int newseg,baseseg,newseg_side,baseseg_side;
998 vms_matrix orient_matrix,tm1,tm2;
999 int n_selected_segs_save;
1000 short selected_segs_save[MAX_SEGMENTS];
1002 int current_group_save;
1004 if (!IS_CHILD(Cursegp->children[(int) Side_opposite[Curside]])) {
1005 // -- I don't understand this, MK, 01/25/94: if (Cursegp->children[Curside] != group_seg-Segments) {
1006 editor_status("Error -- unable to rotate group, Cursegp:Side_opposite[Curside] cannot be free.");
1010 current_group_save = current_group;
1011 current_group = ROT_GROUP;
1012 Groupsegp[ROT_GROUP] = Cursegp;
1014 save_selected_segs(&n_selected_segs_save, selected_segs_save);
1015 GroupList[ROT_GROUP].num_segments = 0;
1016 newseg = Cursegp - Segments;
1017 newseg_side = Side_opposite[Curside];
1019 // Create list of segments to rotate.
1020 // Sever connection between first seg to rotate and its connection on Side_opposite[Curside].
1021 child_save = Cursegp->children[newseg_side]; // save connection we are about to sever
1022 Cursegp->children[newseg_side] = -1; // sever connection
1023 create_group_list(Cursegp, GroupList[ROT_GROUP].segments, &GroupList[ROT_GROUP].num_segments, Selected_segs, 0); // create list of segments in group
1024 //mprintf((0, "NumSegs = %d\n", GroupList[ROT_GROUP].num_segments));
1025 Cursegp->children[newseg_side] = child_save; // restore severed connection
1026 GroupList[ROT_GROUP].segments[0] = newseg;
1028 baseseg = Segments[newseg].children[newseg_side];
1029 if (!IS_CHILD(baseseg)) {
1030 editor_status("Error -- unable to rotate segment, side opposite curside is not attached.");
1031 restore_selected_segs(n_selected_segs_save,selected_segs_save);
1032 current_group = current_group_save;
1036 baseseg_side = find_connect_side(&Segments[newseg], &Segments[baseseg]);
1038 med_extract_matrix_from_segment(&Segments[newseg],&tm1);
1039 tm1 = vmd_identity_matrix;
1040 vm_angles_2_matrix(&tm2,pbh);
1041 vm_matrix_x_matrix(&orient_matrix,&tm1,&tm2);
1043 Segments[baseseg].children[baseseg_side] = -1;
1044 Segments[newseg].children[newseg_side] = -1;
1046 if (!med_move_group(1, &Segments[baseseg], baseseg_side, &Segments[newseg], newseg_side, &orient_matrix, 0)) {
1047 Cursegp = &Segments[newseg];
1048 med_create_new_segment_from_cursegp();
1049 // validate_selected_segments();
1050 med_propagate_tmaps_to_segments(&Segments[baseseg], &Segments[newseg], 1);
1051 med_propagate_tmaps_to_back_side(&Segments[newseg], Curside, 1);
1054 restore_selected_segs(n_selected_segs_save,selected_segs_save);
1055 current_group = current_group_save;
1060 // -----------------------------------------------------------------------------
1061 // Attach segment in the new-fangled way, which is by using the CopyGroup code.
1062 int RotateSegmentNew(vms_angvec *pbh)
1066 autosave_mine(mine_filename);
1068 rval = rotate_segment_new(pbh);
1070 if (Lock_view_to_cursegp)
1071 set_view_target_from_segment(Cursegp);
1073 Update_flags |= UF_WORLD_CHANGED;
1075 warn_if_concave_segment(Cursegp);
1080 static char current_tmap_list[MAX_TEXTURES][13];
1082 // -----------------------------------------------------------------------------
1084 // 1. Write file info, header info, editor info, vertex data, segment data,
1085 // and new_segment in that order, marking their file offset.
1086 // 2. Go through all the fields and fill in the offset, size, and sizeof
1087 // values in the headers.
1088 int med_save_group( char *filename, short *vertex_ids, short *segment_ids, int num_vertices, int num_segments)
1091 int header_offset, editor_offset, vertex_offset, segment_offset, texture_offset;
1092 char ErrorMessage[100];
1099 SaveFile = fopen( filename, "wb" );
1102 sprintf( ErrorMessage, "ERROR: Unable to open %s\n", filename );
1103 MessageBox( -2, -2, 1, ErrorMessage, "Ok" );
1107 //===================== SAVE FILE INFO ========================
1109 group_fileinfo.fileinfo_version = MINE_VERSION;
1110 group_fileinfo.fileinfo_sizeof = sizeof(group_fileinfo);
1111 group_fileinfo.header_offset = -1;
1112 group_fileinfo.header_size = sizeof(group_header);
1113 group_fileinfo.editor_offset = -1;
1114 group_fileinfo.editor_size = sizeof(group_editor);
1115 group_fileinfo.vertex_offset = -1;
1116 group_fileinfo.vertex_howmany = num_vertices;
1117 group_fileinfo.vertex_sizeof = sizeof(vms_vector);
1118 group_fileinfo.segment_offset = -1;
1119 group_fileinfo.segment_howmany = num_segments;
1120 group_fileinfo.segment_sizeof = sizeof(segment);
1121 group_fileinfo.texture_offset = -1;
1122 group_fileinfo.texture_howmany = 0;
1123 group_fileinfo.texture_sizeof = 13; // num characters in a name
1125 // Write the fileinfo
1126 fwrite( &group_fileinfo, sizeof(group_fileinfo), 1, SaveFile );
1128 //===================== SAVE HEADER INFO ========================
1130 group_header.num_vertices = num_vertices;
1131 group_header.num_segments = num_segments;
1133 // Write the editor info
1134 header_offset = ftell(SaveFile);
1135 fwrite( &group_header, sizeof(group_header), 1, SaveFile );
1137 //===================== SAVE EDITOR INFO ==========================
1138 group_editor.newsegment_offset = -1; // To be written
1139 group_editor.newsegment_size = sizeof(segment);
1140 // Next 3 vars added 10/07 by JAS
1141 if (Groupsegp[current_group]) {
1142 segnum = Groupsegp[current_group]-Segments;
1143 for (i=0;i<num_segments;i++)
1144 if (segnum == segment_ids[i])
1145 group_editor.Groupsegp = i;
1148 group_editor.Groupsegp = 0;
1149 group_editor.Groupside = Groupside[current_group];
1151 editor_offset = ftell(SaveFile);
1152 fwrite( &group_editor, sizeof(group_editor), 1, SaveFile );
1155 //===================== SAVE VERTEX INFO ==========================
1157 vertex_offset = ftell(SaveFile);
1158 for (i=0;i<num_vertices;i++) {
1159 tvert = Vertices[vertex_ids[i]];
1160 fwrite( &tvert, sizeof(tvert), 1, SaveFile );
1163 //===================== SAVE SEGMENT INFO =========================
1166 segment_offset = ftell(SaveFile);
1167 for (i=0;i<num_segments;i++) {
1168 tseg = Segments[segment_ids[i]];
1172 for (k=0;k<num_segments;k++)
1173 if (tseg.children[j] == segment_ids[k]) {
1174 tseg.children[j] = k;
1178 if (found==0) tseg.children[j] = -1;
1182 for (k=0;k<num_vertices;k++)
1183 if (tseg.verts[j] == vertex_ids[k]) {
1188 fwrite( &tseg, sizeof(tseg), 1, SaveFile );
1192 //===================== SAVE TEXTURE INFO ==========================
1194 texture_offset = ftell(SaveFile);
1196 for (i=0;i<NumTextures;i++)
1197 strncpy(current_tmap_list[i], TmapInfo[i].filename, 13);
1199 fwrite( current_tmap_list, 13, NumTextures, SaveFile );
1201 //============= REWRITE FILE INFO, TO SAVE OFFSETS ===============
1203 // Update the offset fields
1204 group_fileinfo.header_offset = header_offset;
1205 group_fileinfo.editor_offset = editor_offset;
1206 group_fileinfo.vertex_offset = vertex_offset;
1207 group_fileinfo.segment_offset = segment_offset;
1208 group_fileinfo.texture_offset = texture_offset;
1210 // Write the fileinfo
1211 fseek( SaveFile, 0, SEEK_SET ); // Move to TOF
1212 fwrite( &group_fileinfo, sizeof(group_fileinfo), 1, SaveFile );
1214 //==================== CLOSE THE FILE =============================
1221 static char old_tmap_list[MAX_TEXTURES][13];
1222 static short tmap_xlate_table[MAX_TEXTURES];
1224 // -----------------------------------------------------------------------------
1226 //int med_load_group(char * filename)
1227 int med_load_group( char *filename, short *vertex_ids, short *segment_ids, int *num_vertices, int *num_segments)
1229 int segnum, vertnum;
1230 char ErrorMessage[200];
1239 LoadFile = cfopen( filename, CF_READ_MODE );
1242 sprintf( ErrorMessage, "ERROR: Unable to open %s\n", filename );
1243 MessageBox( -2, -2, 1, ErrorMessage, "Ok" );
1247 //===================== READ FILE INFO ========================
1249 // These are the default values... version and fileinfo_sizeof
1250 // don't have defaults.
1251 group_fileinfo.header_offset = -1;
1252 group_fileinfo.header_size = sizeof(group_header);
1253 group_fileinfo.editor_offset = -1;
1254 group_fileinfo.editor_size = sizeof(group_editor);
1255 group_fileinfo.vertex_offset = -1;
1256 group_fileinfo.vertex_howmany = 0;
1257 group_fileinfo.vertex_sizeof = sizeof(vms_vector);
1258 group_fileinfo.segment_offset = -1;
1259 group_fileinfo.segment_howmany = 0;
1260 group_fileinfo.segment_sizeof = sizeof(segment);
1261 group_fileinfo.texture_offset = -1;
1262 group_fileinfo.texture_howmany = 0;
1263 group_fileinfo.texture_sizeof = 13; // num characters in a name
1265 // Read in group_top_fileinfo to get size of saved fileinfo.
1267 if (cfseek( LoadFile, 0, SEEK_SET ))
1268 Error( "Error seeking to 0 in group.c" );
1270 if (cfread( &group_top_fileinfo, sizeof(group_top_fileinfo),1,LoadFile )!=1)
1271 Error( "Error reading top_fileinfo in group.c" );
1273 // Check version number
1274 if (group_top_fileinfo.fileinfo_version < COMPATIBLE_VERSION )
1276 sprintf( ErrorMessage, "ErrorMessage: You are trying to load %s\n" \
1277 "a version %d group, which is known to be incompatible\n" \
1278 "with the current expected version %d groups.", \
1279 filename, group_top_fileinfo.fileinfo_version, MINE_VERSION );
1281 if (MessageBox( -2, -2, 2, ErrorMessage, "Forget it", "Try anyway" )==1)
1283 cfclose( LoadFile );
1287 MessageBox( -2, -2, 1, "Good luck!", "I need it" );
1290 // Now, Read in the fileinfo
1292 if (cfseek( LoadFile, 0, SEEK_SET ))
1293 Error( "Error seeking to 0b in group.c" );
1295 if (cfread( &group_fileinfo, group_top_fileinfo.fileinfo_sizeof,1,LoadFile )!=1)
1296 Error( "Error reading group_fileinfo in group.c" );
1298 //===================== READ HEADER INFO ========================
1300 // Set default values.
1301 group_header.num_vertices = 0;
1302 group_header.num_segments = 0;
1304 if (group_fileinfo.header_offset > -1 )
1306 if (cfseek( LoadFile,group_fileinfo.header_offset, SEEK_SET ))
1307 Error( "Error seeking to header_offset in group.c" );
1309 if (cfread( &group_header, group_fileinfo.header_size,1,LoadFile )!=1)
1310 Error( "Error reading group_header in group.c" );
1313 //===================== READ EDITOR INFO ==========================
1315 // Set default values
1316 group_editor.current_seg = 0;
1317 group_editor.newsegment_offset = -1; // To be written
1318 group_editor.newsegment_size = sizeof(segment);
1319 group_editor.Groupsegp = -1;
1320 group_editor.Groupside = 0;
1322 if (group_fileinfo.editor_offset > -1 )
1324 if (cfseek( LoadFile,group_fileinfo.editor_offset, SEEK_SET ))
1325 Error( "Error seeking to editor_offset in group.c" );
1327 if (cfread( &group_editor, group_fileinfo.editor_size,1,LoadFile )!=1)
1328 Error( "Error reading group_editor in group.c" );
1332 //===================== READ VERTEX INFO ==========================
1334 if ( (group_fileinfo.vertex_offset > -1) && (group_fileinfo.vertex_howmany > 0))
1336 if (cfseek( LoadFile,group_fileinfo.vertex_offset, SEEK_SET ))
1337 Error( "Error seeking to vertex_offset in group.c" );
1339 for (i=0;i<group_header.num_vertices;i++) {
1341 if (cfread( &tvert, sizeof(tvert),1,LoadFile )!=1)
1342 Error( "Error reading tvert in group.c" );
1343 vertex_ids[i] = med_create_duplicate_vertex( &tvert );
1344 //mprintf((0, "vertex %d created from original %d\n", vertex_ids[i], i));
1349 //==================== READ SEGMENT INFO ===========================
1351 if ( (group_fileinfo.segment_offset > -1) && (group_fileinfo.segment_howmany > 0))
1353 if (cfseek( LoadFile,group_fileinfo.segment_offset, SEEK_SET ))
1354 Error( "Error seeking to segment_offset in group.c" );
1356 for (i=0;i<group_header.num_segments;i++) {
1357 if (cfread( &tseg, sizeof(segment),1,LoadFile )!=1)
1358 Error( "Error reading tseg in group.c" );
1360 segment_ids[i] = get_free_segment_number();
1361 Segments[segment_ids[i]] = tseg;
1362 Segments[segment_ids[i]].objects = -1;
1364 fuelcen_activate(&Segments[segment_ids[i]], Segment2s[segment_ids[i]].special);
1367 for (i=0;i<group_header.num_segments;i++) {
1369 for (j=0;j<MAX_VERTICES_PER_SEGMENT;j++) {
1370 vertnum = vertex_ids[Segments[segment_ids[i]].verts[j]];
1371 Segments[segment_ids[i]].verts[j] = vertnum;
1374 // Fix children and walls.
1375 for (j=0;j<MAX_SIDES_PER_SEGMENT;j++) {
1376 Segments[segment_ids[i]].sides[j].wall_num = -1;
1377 if (IS_CHILD(Segments[segment_ids[i]].children[j])) {
1378 segnum = segment_ids[Segments[segment_ids[i]].children[j]];
1379 Segments[segment_ids[i]].children[j] = segnum;
1381 //Translate textures.
1382 if (translate == 1) {
1384 tmap_xlate = Segments[segment_ids[i]].sides[j].tmap_num;
1385 Segments[segment_ids[i]].sides[j].tmap_num = tmap_xlate_table[tmap_xlate];
1386 temp = Segments[segment_ids[i]].sides[j].tmap_num2;
1387 tmap_xlate = temp & 0x3fff; // strip off orientation bits
1388 if (tmap_xlate != 0)
1389 Segments[segment_ids[i]].sides[j].tmap_num2 = (temp & (!0x3fff)) | tmap_xlate_table[tmap_xlate]; // mask on original orientation bits
1395 //===================== READ TEXTURE INFO ==========================
1397 if ( (group_fileinfo.texture_offset > -1) && (group_fileinfo.texture_howmany > 0))
1399 if (cfseek( LoadFile, group_fileinfo.texture_offset, SEEK_SET ))
1400 Error( "Error seeking to texture_offset in gamemine.c" );
1402 for (i=0; i< group_fileinfo.texture_howmany; i++ )
1404 if (cfread( &old_tmap_list[i], group_fileinfo.texture_sizeof, 1, LoadFile )!=1)
1405 Error( "Error reading old_tmap_list[i] in gamemine.c" );
1409 //=============== GENERATE TEXTURE TRANSLATION TABLE ===============
1413 Assert (NumTextures < MAX_TEXTURES);
1417 hashtable_init( &ht, NumTextures );
1419 // Remove all the file extensions in the textures list
1421 for (i=0;i<NumTextures;i++) {
1422 temptr = strchr(TmapInfo[i].filename, '.');
1423 if (temptr) *temptr = '\0';
1424 // mprintf( (0, "Texture %d is '%s'\n", i, TmapInfo[i].filename ));
1426 hashtable_insert( &ht, TmapInfo[i].filename, i );
1429 // For every texture, search through the texture list
1430 // to find a matching name.
1431 for (j=0;j<group_fileinfo.texture_howmany;j++) {
1432 // Remove this texture name's extension
1433 temptr = strchr(old_tmap_list[j], '.');
1434 if (temptr) *temptr = '\0';
1436 tmap_xlate_table[j] = hashtable_search( &ht,old_tmap_list[j]);
1437 if (tmap_xlate_table[j] < 0 )
1438 tmap_xlate_table[j] = 0;
1439 if (tmap_xlate_table[j] != j ) translate = 1;
1442 hashtable_free( &ht );
1446 //======================== CLOSE FILE ==============================
1447 cfclose( LoadFile );
1449 //========================= UPDATE VARIABLES ======================
1451 if (group_editor.Groupsegp != -1 )
1452 Groupsegp[current_group] = &Segments[segment_ids[group_editor.Groupsegp]];
1454 Groupsegp[current_group] = NULL;
1456 Groupside[current_group] = group_editor.Groupside;
1458 *num_vertices = group_fileinfo.vertex_howmany;
1459 *num_segments = group_fileinfo.segment_howmany;
1460 warn_if_concave_segments();
1465 char group_filename[PATH_MAX] = "*.GRP";
1467 void checkforgrpext( char * f )
1471 for (i=1; i<strlen(f); i++ )
1473 if (f[i]=='.') return;
1475 if ((f[i]==' '||f[i]==0) )
1497 //short vertex_list[MAX_VERTICES];
1504 char ErrorMessage[200];
1505 sbyte vertex_list[MAX_VERTICES];
1507 if (current_group == -1)
1509 sprintf( ErrorMessage, "ERROR: No current group." );
1510 MessageBox( -2, -2, 1, ErrorMessage, "Ok" );
1514 for (v=0; v<=Highest_vertex_index; v++) {
1518 // Make a list of all vertices in group.
1519 for (s=0; s<GroupList[current_group].num_segments; s++)
1520 for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) {
1521 vertex_list[Segments[GroupList[current_group].segments[s]].verts[v]] = 1;
1525 for (i=0; i<=Highest_vertex_index; i++)
1526 if (vertex_list[i] == 1) {
1527 GroupList[current_group].vertices[v++] = i;
1529 GroupList[current_group].num_vertices = v;
1530 //mprintf((0, "Saving %d vertices, %d segments\n", GroupList[current_group].num_vertices, GroupList[current_group].num_segments));
1531 med_save_group("TEMP.GRP", GroupList[current_group].vertices, GroupList[current_group].segments,
1532 GroupList[current_group].num_vertices, GroupList[current_group].num_segments);
1533 if (ui_get_filename( group_filename, "*.GRP", "SAVE GROUP" ))
1535 checkforgrpext(group_filename);
1536 if (med_save_group(group_filename, GroupList[current_group].vertices, GroupList[current_group].segments,
1537 GroupList[current_group].num_vertices, GroupList[current_group].num_segments))
1550 if (num_groups == MAX_GROUPS)
1552 x = MessageBox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
1556 if (num_groups < MAX_GROUPS)
1559 current_group = num_groups-1;
1561 else current_group = 0;
1563 if (ui_get_filename( group_filename, "*.GRP", "LOAD GROUP" ))
1565 checkforgrpext(group_filename);
1566 med_load_group(group_filename, GroupList[current_group].vertices, GroupList[current_group].segments,
1567 &GroupList[current_group].num_vertices, &GroupList[current_group].num_segments) ;
1568 //mprintf((0, "Loaded %d vertices, %d segments\n", GroupList[current_group].num_vertices, GroupList[current_group].num_segments));
1570 if (!med_move_group(0, Cursegp, Curside, Groupsegp[current_group], Groupside[current_group], &vmd_identity_matrix, 0)) {
1571 autosave_mine(mine_filename);
1572 set_view_target_from_segment(Cursegp);
1573 Update_flags |= UF_WORLD_CHANGED;
1575 diagnostic_message("Group moved.");
1585 int UngroupSegment( void )
1587 if (Cursegp->group == current_group) {
1589 Cursegp->group = -1;
1590 delete_segment_from_group( Cursegp-Segments, current_group );
1592 Update_flags |= UF_WORLD_CHANGED;
1594 diagnostic_message("Segment Ungrouped from Group %d.", current_group);
1601 int GroupSegment( void )
1603 if (Cursegp->group == -1) {
1605 Cursegp->group = current_group;
1606 add_segment_to_group( Cursegp-Segments, current_group );
1608 Update_flags |= UF_WORLD_CHANGED;
1610 diagnostic_message("Segment Added to Group %d.", current_group);
1621 // GroupList[current_group].num_segments = 0;
1622 // Groupsegp[current_group] = 0;
1624 if (num_groups==0) return 0;
1626 for (i=0; i<GroupList[current_group].num_segments; i++)
1627 delete_segment_from_group( GroupList[current_group].segments[i], current_group );
1629 // delete_segment_from_group( &Segments[GroupList[current_group].segments[i]]-Segments, current_group );
1631 for (i=current_group;i<num_groups-1;i++)
1633 GroupList[i] = GroupList[i+1];
1634 Groupsegp[i] = Groupsegp[i+1];
1639 GroupList[num_groups].num_segments = 0;
1640 Groupsegp[num_groups] = 0;
1642 if (current_group > num_groups-1) current_group--;
1644 if (num_groups == 0)
1647 if (Lock_view_to_cursegp)
1648 set_view_target_from_segment(Cursegp);
1649 Update_flags |= UF_WORLD_CHANGED;
1651 diagnostic_message("Group UNgrouped.");
1656 void NextGroup( void )
1662 if (current_group >= num_groups ) current_group = 0;
1664 Update_flags |= UF_ED_STATE_CHANGED;
1667 else editor_status("No Next Group\n");
1670 void PrevGroup( void )
1675 if (current_group < 0 ) current_group = num_groups-1;
1677 Update_flags |= UF_ED_STATE_CHANGED;
1680 else editor_status("No Previous Group\n");
1684 // 0 = successfully selected
1685 // 1 = bad group number
1686 int select_group( int num )
1688 if ((num>=0) && (num<num_groups))
1690 current_group = num;
1697 // -----------------------------------------------------------------------------
1700 if (!Groupsegp[current_group]) {
1701 editor_status("Error -- Cannot move group, no group segment.");
1705 med_compress_mine();
1707 if (!med_move_group(0, Cursegp, Curside, Groupsegp[current_group], Groupside[current_group], &vmd_identity_matrix, 0)) {
1708 autosave_mine(mine_filename);
1709 Update_flags |= UF_WORLD_CHANGED;
1711 diagnostic_message("Group moved.");
1718 // -----------------------------------------------------------------------------
1723 if (!Groupsegp[current_group]) {
1724 editor_status("Error -- Cannot copy group, no group segment.");
1728 // See if the attach side in the group is attached to another segment.
1729 // If so, it must not be in the group for group copy to be legal.
1730 attach_seg = Groupsegp[current_group]->children[Groupside[current_group]];
1731 if (attach_seg != -1) {
1734 for (i=0; i<GroupList[current_group].num_segments; i++)
1735 if (GroupList[current_group].segments[i] == attach_seg)
1738 if (i != GroupList[current_group].num_segments) {
1739 editor_status("Error -- Cannot copy group, attach side has a child (segment %i) attached.", Groupsegp[current_group]->children[Groupside[current_group]]);
1744 med_compress_mine();
1746 if (!med_copy_group(0, Cursegp, Curside, Groupsegp[current_group], Groupside[current_group], &vmd_identity_matrix)) {
1747 autosave_mine(mine_filename);
1748 Update_flags |= UF_WORLD_CHANGED;
1750 diagnostic_message("Group copied.");
1757 // -----------------------------------------------------------------------------
1758 int RotateGroup(void)
1761 if (!Groupsegp[current_group]) {
1762 editor_status("Error -- Cannot rotate group, no group segment.");
1766 Group_orientation[current_group]++;
1767 if ((Group_orientation[current_group] <0) || (Group_orientation[current_group] >4))
1768 Group_orientation[current_group]=0;
1770 med_compress_mine();
1772 if (!med_move_group(0, Cursegp, Curside, Groupsegp[current_group], Groupside[current_group],
1773 &vmd_identity_matrix, Group_orientation[current_group]))
1775 Update_flags |= UF_WORLD_CHANGED;
1777 diagnostic_message("Group rotated.");
1785 // -----------------------------------------------------------------------------
1786 // Creates a group from all segments connected to marked segment.
1787 int SubtractFromGroup(void)
1789 int x, s, original_group;
1794 editor_status("Error -- Cannot create group, no marked segment.");
1798 med_compress_mine();
1799 autosave_mine(mine_filename);
1801 if (num_groups == MAX_GROUPS) {
1802 x = MessageBox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
1806 if (current_group == -1) {
1807 editor_status("Error -- No current group. Cannot subtract.");
1811 original_group = current_group;
1813 current_group = (current_group + 1) % MAX_GROUPS;
1815 // if (num_groups < MAX_GROUPS) {
1816 // current_group = num_groups;
1819 // current_group = 0;
1821 // mprintf((0, "Old group: "));
1822 // for (s=0; s<GroupList[original_group].num_segments; s++)
1823 // mprintf((0, "%3i ", GroupList[original_group].segments[s]));
1824 // mprintf((0, "\n"));
1826 // Create a list of segments to copy.
1827 GroupList[current_group].num_segments = 0;
1828 create_group_list(Markedsegp, GroupList[current_group].segments, &GroupList[current_group].num_segments, Selected_segs, N_selected_segs);
1830 // mprintf((0, "New group: "));
1831 // for (s=0; s<GroupList[current_group].num_segments; s++)
1832 // mprintf((0, "%3i ", GroupList[current_group].segments[s]));
1833 // mprintf((0, "\n"));
1835 // Now, scan the two groups, forming a group which consists of only those segments common to the two groups.
1836 gp = GroupList[current_group].segments;
1837 cur_num_segs = GroupList[current_group].num_segments;
1838 for (s=0; s<cur_num_segs; s++) {
1839 short *gp1 = GroupList[original_group].segments;
1843 for (s1=0; s1<GroupList[original_group].num_segments; s1++)
1845 break; // If break executed, then segment found in both lists.
1847 // If current segment was not found in both lists, remove it by copying the last segment over
1848 // it and decreasing the number of segments.
1849 if (s1 == GroupList[original_group].num_segments) {
1850 gp[s] = gp[cur_num_segs];
1855 // Go through mine and seg group number of all segments which are in group
1856 // All segments which were subtracted from group get group set to -1.
1857 mprintf((0, "In segments: "));
1858 for (s=0; s<cur_num_segs; s++) {
1859 Segments[GroupList[current_group].segments[s]].group = current_group;
1860 mprintf((0, "%2i ", GroupList[current_group].segments[s]));
1863 mprintf((0, "\nRemoved segments: "));
1864 for (s=0; s<=Highest_segment_index; s++) {
1866 if (Segments[s].group == current_group) {
1867 for (t=0; t<cur_num_segs; t++)
1868 if (GroupList[current_group].segments[t] == s)
1870 if (s == cur_num_segs) {
1871 Segments[s].group = -1;
1872 mprintf((0, "%2i ", s));
1877 // mprintf((0, "Combined group: "));
1878 // for (s=0; s<GroupList[current_group].num_segments; s++)
1879 // mprintf((0, "%3i ", GroupList[current_group].segments[s]));
1880 // mprintf((0, "\n\n"));
1882 GroupList[current_group].num_segments = cur_num_segs;
1884 // Replace Marked segment with Group Segment.
1885 Groupsegp[current_group] = Markedsegp;
1886 Groupside[current_group] = Markedside;
1888 for (x=0;x<GroupList[current_group].num_segments;x++)
1889 Segments[GroupList[current_group].segments[x]].group = current_group;
1891 Update_flags |= UF_WORLD_CHANGED;
1893 diagnostic_message("Group created.");
1899 // -----------------------------------------------------------------------------
1900 // Creates a group from all segments already in CurrentGroup which can be reached from marked segment
1901 // without passing through current segment.
1902 int CreateGroup(void)
1907 editor_status("Error -- Cannot create group, no marked segment.");
1911 med_compress_mine();
1912 autosave_mine(mine_filename);
1914 if (num_groups == MAX_GROUPS) {
1915 x = MessageBox( -2, -2, 2, "Warning: You are about to wipe out a group.", "ARGH! NO!", "No problemo." );
1917 return 0; // Aborting at user's request.
1920 if (num_groups < MAX_GROUPS) {
1922 current_group = num_groups-1;
1926 // Create a list of segments to copy.
1927 GroupList[current_group].num_segments = 0;
1928 create_group_list(Markedsegp, GroupList[current_group].segments, &GroupList[current_group].num_segments, Selected_segs, 0);
1930 // Replace Marked segment with Group Segment.
1931 Groupsegp[current_group] = Markedsegp;
1932 Groupside[current_group] = Markedside;
1934 // Markedside = WBACK;
1936 for (x=0;x<GroupList[current_group].num_segments;x++)
1937 Segments[GroupList[current_group].segments[x]].group = current_group;
1939 Update_flags |= UF_WORLD_CHANGED;
1941 diagnostic_message("Group created.");
1947 // -----------------------------------------------------------------------------
1948 // Deletes current group.
1949 int DeleteGroup( void )
1953 autosave_mine(mine_filename);
1955 if (num_groups==0) return 0;
1957 //mprintf((0, "num_segments = %d\n", GroupList[current_group].num_segments));
1959 numsegs = GroupList[current_group].num_segments;
1961 for (i=0; i<numsegs; i++) {
1962 med_delete_segment(&Segments[GroupList[current_group].segments[0]]);
1965 for (i=current_group;i<num_groups-1;i++) {
1966 GroupList[i] = GroupList[i+1];
1967 Groupsegp[i] = Groupsegp[i+1];
1971 GroupList[num_groups].num_segments = 0;
1972 Groupsegp[num_groups] = 0;
1974 if (current_group > num_groups-1) current_group--;
1979 strcpy(undo_status[Autosave_count], "Delete Group UNDONE.");
1980 if (Lock_view_to_cursegp)
1981 set_view_target_from_segment(Cursegp);
1983 Update_flags |= UF_WORLD_CHANGED;
1985 diagnostic_message("Group deleted.");
1986 // warn_if_concave_segments(); // This could be faster -- just check if deleted segment was concave, warn accordingly
1993 int MarkGroupSegment( void )
1995 if ((Cursegp->group != -1) && (Cursegp->group == current_group))
1997 autosave_mine(mine_filename);
1998 Groupsegp[current_group] = Cursegp;
1999 Groupside[current_group] = Curside;
2000 editor_status("Group Segment Marked.");
2001 Update_flags |= UF_ED_STATE_CHANGED;
2002 strcpy(undo_status[Autosave_count], "Mark Group Segment UNDONE.");