/* * Portions of this file are copyright Rebirth contributors and licensed as * described in COPYING.txt. * Portions of this file are copyright Parallax Software and licensed * according to the Parallax license below. * See COPYING.txt for license details. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ /* * * Interrogation functions for segment data structure. * */ #include #include #include #include #include "key.h" #include "gr.h" #include "inferno.h" #include "segment.h" #include "editor.h" #include "editor/esegment.h" #include "dxxerror.h" #include "object.h" #include "gameseg.h" #include "render.h" #include "game.h" #include "wall.h" #include "switch.h" #include "fuelcen.h" #include "cntrlcen.h" #include "seguvs.h" #include "gameseq.h" #include "kdefs.h" #include "medwall.h" #include "hostage.h" #include "compiler-range_for.h" #include "partial_range.h" #include "segiter.h" int Do_duplicate_vertex_check = 0; // Gets set to 1 in med_create_duplicate_vertex, means to check for duplicate vertices in compress_mine #define BOTTOM_STUFF 0 // Remap all vertices in polygons in a segment through translation table xlate_verts. #if BOTTOM_STUFF void remap_vertices(segment *segp, int *xlate_verts) { int sidenum, facenum, polynum, v; for (sidenum=0; sidenumsides[sidenum].num_faces; facenum++) for (polynum=0; polynumsides[sidenum].faces[facenum].num_polys; polynum++) { poly *pp = &segp->sides[sidenum].faces[facenum].polys[polynum]; for (v=0; vnum_vertices; v++) pp->verts[v] = xlate_verts[pp->verts[v]]; } } // Copy everything from sourceside to destside except sourceside->faces[xx].polys[xx].verts void copy_side_except_vertex_ids(side *destside, side *sourceside) { int facenum, polynum, v; destside->num_faces = sourceside->num_faces; destside->tri_edge = sourceside->tri_edge; destside->wall_num = sourceside->wall_num; for (facenum=0; facenumnum_faces; facenum++) { face *destface = &destside->faces[facenum]; face *sourceface = &sourceside->faces[facenum]; destface->num_polys = sourceface->num_polys; destface->normal = sourceface->normal; for (polynum=0; polynumnum_polys; polynum++) { poly *destpoly = &destface->polys[polynum]; poly *sourcepoly = &sourceface->polys[polynum]; destpoly->num_vertices = sourcepoly->num_vertices; destpoly->face_type = sourcepoly->face_type; destpoly->tmap_num = sourcepoly->tmap_num; destpoly->tmap_num2 = sourcepoly->tmap_num2; for (v=0; vnum_vertices; v++) destpoly->uvls[v] = sourcepoly->uvls[v]; } } } // [side] [index] [cur:next] // To remap the vertices on a side after a forward rotation sbyte xlate_previous[6][4][2] = { { {7, 3}, {3, 2}, {2, 6}, {6, 7} }, // remapping left to left { {5, 4}, {4, 0}, {7, 3}, {6, 7} }, // remapping back to top { {5, 4}, {1, 5}, {0, 1}, {4, 0} }, // remapping right to right { {0, 1}, {1, 5}, {2, 6}, {3, 2} }, // remapping front to bottom { {1, 5}, {5, 4}, {6, 7}, {2, 6} }, // remapping bottom to back { {4, 0}, {0, 1}, {3, 2}, {7, 3} }, // remapping top to front }; void remap_vertices_previous(segment *segp, int sidenum) { int v, w, facenum, polynum; for (facenum=0; facenumsides[sidenum].num_faces; facenum++) { for (polynum=0; polynumsides[sidenum].faces[facenum].num_polys; polynum++) { poly *pp = &segp->sides[sidenum].faces[facenum].polys[polynum]; for (v=0; vnum_vertices; v++) { for (w=0; w<4; w++) { if (pp->verts[v] == xlate_previous[sidenum][w][0]) { pp->verts[v] = xlate_previous[sidenum][w][1]; break; } } Assert(w<4); // If w == 4, then didn't find current vertex in list, which means xlate_previous table is bogus } } } } sbyte xlate_previous_right[6][4][2] = { { {5, 6}, {6, 7}, {2, 3}, {1, 2} }, // bottom to left { {6, 7}, {7, 4}, {3, 0}, {2, 3} }, // left to top { {7, 4}, {4, 5}, {0, 1}, {3, 0} }, // top to right { {4, 5}, {5, 6}, {1, 2}, {0, 1} }, // right to bottom { {6, 7}, {5, 6}, {4, 5}, {7, 4} }, // back to back { {3, 2}, {0, 3}, {1, 0}, {2, 1} }, // front to front }; void remap_vertices_previous_right(segment *segp, int sidenum) { int v, w, facenum, polynum; for (facenum=0; facenumsides[sidenum].num_faces; facenum++) { for (polynum=0; polynumsides[sidenum].faces[facenum].num_polys; polynum++) { poly *pp = &segp->sides[sidenum].faces[facenum].polys[polynum]; for (v=0; vnum_vertices; v++) { for (w=0; w<4; w++) { if (pp->verts[v] == xlate_previous_right[sidenum][w][0]) { pp->verts[v] = xlate_previous_right[sidenum][w][1]; break; } } Assert(w<4); // If w == 4, then didn't find current vertex in list, which means xlate_previous table is bogus } } } } // ----------------------------------------------------------------------------------- // Takes top to front void med_rotate_segment_forward(segment *segp) { segment seg_copy; int i; seg_copy = *segp; seg_copy.verts[0] = segp->verts[4]; seg_copy.verts[1] = segp->verts[0]; seg_copy.verts[2] = segp->verts[3]; seg_copy.verts[3] = segp->verts[7]; seg_copy.verts[4] = segp->verts[5]; seg_copy.verts[5] = segp->verts[1]; seg_copy.verts[6] = segp->verts[2]; seg_copy.verts[7] = segp->verts[6]; seg_copy.children[WFRONT] = segp->children[WTOP]; seg_copy.children[WTOP] = segp->children[WBACK]; seg_copy.children[WBACK] = segp->children[WBOTTOM]; seg_copy.children[WBOTTOM] = segp->children[WFRONT]; seg_copy.sides[WFRONT] = segp->sides[WTOP]; seg_copy.sides[WTOP] = segp->sides[WBACK]; seg_copy.sides[WBACK] = segp->sides[WBOTTOM]; seg_copy.sides[WBOTTOM] = segp->sides[WFRONT]; for (i=0; i<6; i++) remap_vertices_previous(&seg_copy, i); *segp = seg_copy; } // ----------------------------------------------------------------------------------- // Takes top to right void med_rotate_segment_right(segment *segp) { segment seg_copy; int i; seg_copy = *segp; seg_copy.verts[4] = segp->verts[7]; seg_copy.verts[5] = segp->verts[4]; seg_copy.verts[1] = segp->verts[0]; seg_copy.verts[0] = segp->verts[3]; seg_copy.verts[3] = segp->verts[2]; seg_copy.verts[2] = segp->verts[1]; seg_copy.verts[6] = segp->verts[5]; seg_copy.verts[7] = segp->verts[6]; seg_copy.children[WRIGHT] = segp->children[WTOP]; seg_copy.children[WBOTTOM] = segp->children[WRIGHT]; seg_copy.children[WLEFT] = segp->children[WBOTTOM]; seg_copy.children[WTOP] = segp->children[WLEFT]; seg_copy.sides[WRIGHT] = segp->sides[WTOP]; seg_copy.sides[WBOTTOM] = segp->sides[WRIGHT]; seg_copy.sides[WLEFT] = segp->sides[WBOTTOM]; seg_copy.sides[WTOP] = segp->sides[WLEFT]; for (i=0; i<6; i++) remap_vertices_previous_right(&seg_copy, i); *segp = seg_copy; } void make_curside_bottom_side(void) { switch (Curside) { case WRIGHT: med_rotate_segment_right(Cursegp); break; case WTOP: med_rotate_segment_right(Cursegp); med_rotate_segment_right(Cursegp); break; case WLEFT: med_rotate_segment_right(Cursegp); med_rotate_segment_right(Cursegp); med_rotate_segment_right(Cursegp); break; case WBOTTOM: break; case WFRONT: med_rotate_segment_forward(Cursegp); break; case WBACK: med_rotate_segment_forward(Cursegp); med_rotate_segment_forward(Cursegp); med_rotate_segment_forward(Cursegp); break; } Update_flags = UF_WORLD_CHANGED; } #endif int ToggleBottom(void) { Render_only_bottom = !Render_only_bottom; Update_flags = UF_WORLD_CHANGED; return 0; } // ------------------------------------------------------------------------------- // Return number of times vertex vi appears in all segments. // This function can be used to determine whether a vertex is used exactly once in // all segments, in which case it can be freely moved because it is not connected // to any other segment. static int med_vertex_count(int vi) { int s,v; segment *sp; int count; count = 0; for (s=0; ssegnum != segment_none) for (v=0; vverts[v] == vi) count++; } return count; } // ------------------------------------------------------------------------------- int is_free_vertex(int vi) { return med_vertex_count(vi) == 1; } // ------------------------------------------------------------------------------- // Return true if one fixed point number is very close to another, else return false. static int fnear(fix f1, fix f2) { return (abs(f1 - f2) <= FIX_EPSILON); } // ------------------------------------------------------------------------------- static int vnear(vms_vector *vp1, vms_vector *vp2) { return fnear(vp1->x, vp2->x) && fnear(vp1->y, vp2->y) && fnear(vp1->z, vp2->z); } // ------------------------------------------------------------------------------- // Add the vertex *vp to the global list of vertices, return its index. // Search until a matching vertex is found (has nearly the same coordinates) or until Num_vertices // vertices have been looked at without a match. If no match, add a new vertex. int med_add_vertex(vms_vector *vp) { int v,free_index; int count; // number of used vertices found, for loops exits when count == Num_vertices // set_vertex_counts(); Assert(Num_vertices < MAX_SEGMENT_VERTICES); count = 0; free_index = -1; for (v=0; (v < MAX_SEGMENT_VERTICES) && (count < Num_vertices); v++) if (Vertex_active[v]) { count++; if (vnear(vp,&Vertices[v])) { return v; } } else if (free_index == -1) free_index = v; // we want free_index to be the first free slot to add a vertex if (free_index == -1) free_index = Num_vertices; while (Vertex_active[free_index] && (free_index < MAX_VERTICES)) free_index++; Assert(free_index < MAX_VERTICES); Vertices[free_index] = *vp; Vertex_active[free_index] = 1; Num_vertices++; if (free_index > Highest_vertex_index) Highest_vertex_index = free_index; return free_index; } // ------------------------------------------------------------------------------------------ // Returns the index of a free segment. // Scans the Segments array. segnum_t get_free_segment_number(void) { for (segnum_t segnum=0; segnum Highest_segment_index) Highest_segment_index = segnum; return segnum; } Assert(0); return 0; } // ------------------------------------------------------------------------------- // Create a new segment, duplicating exactly, including vertex ids and children, the passed segment. segnum_t med_create_duplicate_segment(segment *sp) { segnum_t segnum; segnum = get_free_segment_number(); Segments[segnum] = *sp; return segnum; } // ------------------------------------------------------------------------------- // Add the vertex *vp to the global list of vertices, return its index. // This is the same as med_add_vertex, except that it does not search for the presence of the vertex. int med_create_duplicate_vertex(vms_vector *vp) { int free_index; Assert(Num_vertices < MAX_SEGMENT_VERTICES); Do_duplicate_vertex_check = 1; free_index = Num_vertices; while (Vertex_active[free_index] && (free_index < MAX_VERTICES)) free_index++; Assert(free_index < MAX_VERTICES); Vertices[free_index] = *vp; Vertex_active[free_index] = 1; Num_vertices++; if (free_index > Highest_vertex_index) Highest_vertex_index = free_index; return free_index; } // ------------------------------------------------------------------------------- // Set the vertex *vp at index vnum in the global list of vertices, return its index (just for compatibility). int med_set_vertex(int vnum,vms_vector *vp) { Assert(vnum < MAX_VERTICES); Vertices[vnum] = *vp; // Just in case this vertex wasn't active, mark it as active. if (!Vertex_active[vnum]) { Vertex_active[vnum] = 1; Num_vertices++; if ((vnum > Highest_vertex_index) && (vnum < NEW_SEGMENT_VERTICES)) { Highest_vertex_index = vnum; } } return vnum; } // ------------------------------------------------------------------------------- void create_removable_wall(segment *sp, int sidenum, int tmap_num) { create_walls_on_side(sp, sidenum); sp->sides[sidenum].tmap_num = tmap_num; assign_default_uvs_to_side(sp, sidenum); assign_light_to_side(sp, sidenum); } #if 0 // --------------------------------------------------------------------------------------------- // Orthogonalize matrix smat, returning result in rmat. // Does not modify smat. // Uses Gram-Schmidt process. // See page 172 of Strang, Gilbert, Linear Algebra and its Applications // Matt -- This routine should be moved to the vector matrix library. // It IS legal for smat == rmat. // We should also have the functions: // mat_a = mat_b * scalar; // we now have mat_a = mat_a * scalar; // mat_a = mat_b + mat_c * scalar; // or maybe not, maybe this is not primitive void make_orthogonal(vms_matrix *rmat,vms_matrix *smat) { vms_matrix tmat; vms_vector tvec1,tvec2; float dot; // Copy source matrix to work area. tmat = *smat; // Normalize the three rows of the matrix tmat. vm_vec_normalize(&tmat.xrow); vm_vec_normalize(&tmat.yrow); vm_vec_normalize(&tmat.zrow); // Now, compute the first vector. // This is very easy -- just copy the (normalized) source vector. rmat->zrow = tmat.zrow; // Now, compute the second vector. // From page 172 of Strang, we use the equation: // b' = b - [transpose(q1) * b] * q1 // where: b = the second row of tmat // q1 = the first row of rmat // b' = the second row of rmat // Compute: transpose(q1) * b dot = vm_vec_dotprod(&rmat->zrow,&tmat.yrow); // Compute: b - dot * q1 rmat->yrow.x = tmat.yrow.x - fixmul(dot,rmat->zrow.x); rmat->yrow.y = tmat.yrow.y - fixmul(dot,rmat->zrow.y); rmat->yrow.z = tmat.yrow.z - fixmul(dot,rmat->zrow.z); // Now, compute the third vector. // From page 173 of Strang, we use the equation: // c' = c - (q1*c)*q1 - (q2*c)*q2 // where: c = the third row of tmat // q1 = the first row of rmat // q2 = the second row of rmat // c' = the third row of rmat // Compute: q1*c dot = vm_vec_dotprod(&rmat->zrow,&tmat.xrow); tvec1.x = fixmul(dot,rmat->zrow.x); tvec1.y = fixmul(dot,rmat->zrow.y); tvec1.z = fixmul(dot,rmat->zrow.z); // Compute: q2*c dot = vm_vec_dotprod(&rmat->yrow,&tmat.xrow); tvec2.x = fixmul(dot,rmat->yrow.x); tvec2.y = fixmul(dot,rmat->yrow.y); tvec2.z = fixmul(dot,rmat->yrow.z); vm_vec_sub(&rmat->xrow,vm_vec_sub(&rmat->xrow,&tmat.xrow,&tvec1),&tvec2); } #endif // ------------------------------------------------------------------------------------------ // Given a segment, extract the rotation matrix which defines it. // Do this by extracting the forward, right, up vectors and then making them orthogonal. // In the process of making the vectors orthogonal, favor them in the order forward, up, right. // This means that the forward vector will remain unchanged. void med_extract_matrix_from_segment(segment *sp,vms_matrix *rotmat) { vms_vector forwardvec,upvec; extract_forward_vector_from_segment(sp,&forwardvec); extract_up_vector_from_segment(sp,&upvec); if (((forwardvec.x == 0) && (forwardvec.y == 0) && (forwardvec.z == 0)) || ((upvec.x == 0) && (upvec.y == 0) && (upvec.z == 0))) { *rotmat = vmd_identity_matrix; return; } vm_vector_2_matrix(rotmat,&forwardvec,&upvec,NULL); #if 0 vms_matrix rm; extract_forward_vector_from_segment(sp,&rm.zrow); extract_right_vector_from_segment(sp,&rm.xrow); extract_up_vector_from_segment(sp,&rm.yrow); vm_vec_normalize(&rm.xrow); vm_vec_normalize(&rm.yrow); vm_vec_normalize(&rm.zrow); make_orthogonal(rotmat,&rm); vm_vec_normalize(&rotmat->xrow); vm_vec_normalize(&rotmat->yrow); vm_vec_normalize(&rotmat->zrow); // *rotmat = rm; // include this line (and remove the call to make_orthogonal) if you don't want the matrix orthogonalized #endif } // ------------------------------------------------------------------------------------------ // Given a rotation matrix *rotmat which describes the orientation of a segment // and a side destside, return the rotation matrix which describes the orientation for the side. void set_matrix_based_on_side(vms_matrix *rotmat,int destside) { vms_angvec rotvec; vms_matrix r1; switch (destside) { case WLEFT: vm_angvec_make(&rotvec,0,0,-16384); vm_angles_2_matrix(&r1,&rotvec); *rotmat = vm_matrix_x_matrix(*rotmat, r1); break; case WTOP: vm_angvec_make(&rotvec,-16384,0,0); vm_angles_2_matrix(&r1,&rotvec); *rotmat = vm_matrix_x_matrix(*rotmat, r1); break; case WRIGHT: vm_angvec_make(&rotvec,0,0,16384); vm_angles_2_matrix(&r1,&rotvec); *rotmat = vm_matrix_x_matrix(*rotmat, r1); break; case WBOTTOM: vm_angvec_make(&rotvec,+16384,-32768,0); // bank was -32768, but I think that was an erroneous compensation vm_angles_2_matrix(&r1,&rotvec); *rotmat = vm_matrix_x_matrix(*rotmat, r1); break; case WFRONT: vm_angvec_make(&rotvec,0,0,-32768); vm_angles_2_matrix(&r1,&rotvec); *rotmat = vm_matrix_x_matrix(*rotmat, r1); break; case WBACK: break; } } // ------------------------------------------------------------------------------------- static void change_vertex_occurrences(int dest, int src) { int g,v; // Fix vertices in groups for (g=0;ghole) && (!Vertex_active[vert]); vert--) ; if (vert > hole) { // Ok, hole is the index of a hole, vert is the index of a vertex which follows it. // Copy vert into hole, update pointers to it. Vertices[hole] = Vertices[vert]; change_vertex_occurrences(hole, vert); vert--; } } Highest_vertex_index = Num_vertices-1; } // -------------------------------------------------------------------------------------------------- static void compress_segments(void) { if (Highest_segment_index == Num_segments - 1) return; segnum_t hole,seg; seg = Highest_segment_index; for (hole=0; hole < seg; hole++) if (Segments[hole].segnum == segment_none) { // found an unused segment which is a hole if a used segment follows (not necessarily immediately) it. for ( ; (seg>hole) && (Segments[seg].segnum == segment_none); seg--) ; if (seg > hole) { int f,g,l,s,t; segment *sp; // Ok, hole is the index of a hole, seg is the index of a segment which follows it. // Copy seg into hole, update pointers to it, update Cursegp, Markedsegp if necessary. Segments[hole] = Segments[seg]; Segments[seg].segnum = segment_none; if (Cursegp == &Segments[seg]) Cursegp = &Segments[hole]; if (Markedsegp == &Segments[seg]) Markedsegp = &Segments[hole]; // Fix segments in groups for (g=0;gchildren[s])) { segment *csegp; csegp = &Segments[sp->children[s]]; // Find out on what side the segment connection to the former seg is on in *csegp. for (t=0; tchildren[t] == seg) { csegp->children[t] = hole; // It used to be connected to seg, so make it connected to hole } } // end for t } // end if } // end for s //Update object segment pointers range_for (auto objp, objects_in(*sp)) { Assert(objp->segnum == seg); objp->segnum = hole; } seg--; } // end if (seg > hole) } // end if Highest_segment_index = Num_segments-1; med_create_new_segment_from_cursegp(); } // ------------------------------------------------------------------------------- // Combine duplicate vertices. // If two vertices have the same coordinates, within some small tolerance, then assign // the same vertex number to the two vertices, freeing up one of the vertices. void med_combine_duplicate_vertices(sbyte *vlp) { int v,w; for (v=0; vsides[s].tmap_num = sseg->sides[s].tmap_num; dseg->sides[s].tmap_num2 = 0; } } // ------------------------------------------------------------------------------------------ // Attach a segment with a rotated orientation. // Return value: // 0 = successful attach // 1 = No room in Segments[]. // 2 = No room in Vertices[]. // 3 = newside != WFRONT -- for now, the new segment must be attached at its (own) front side // 4 = already a face attached on destseg:destside static int med_attach_segment_rotated(segment *destseg, segment *newseg, int destside, int newside,vms_matrix *attmat) { const sbyte *dvp; segment *nsp; int side,v; vms_matrix rotmat,rotmat1,rotmat2,rotmat3,rotmat4; vms_vector vr,vc,tvs[4],xlate_vec; segnum_t segnum; vms_vector forvec,upvec; // Return if already a face attached on this side. if (IS_CHILD(destseg->children[destside])) return 4; segnum = get_free_segment_number(); forvec = attmat->fvec; upvec = attmat->uvec; // We are pretty confident we can add the segment. nsp = &Segments[segnum]; nsp->segnum = segnum; nsp->objects = object_none; nsp->matcen_num = -1; // Copy group value. nsp->group = destseg->group; // Add segment to proper group list. if (nsp->group > -1) add_segment_to_group(nsp-Segments, nsp->group); // Copy the texture map ids. copy_tmap_ids(nsp,newseg); // clear all connections for (side=0; sidechildren[side] = segment_none; nsp->sides[side].wall_num = -1; } // Form the connection destseg->children[destside] = segnum; // destseg->sides[destside].render_flag = 0; nsp->children[newside] = destseg-Segments; // Copy vertex indices of the four vertices forming the joint dvp = Side_to_verts[destside]; // Set the vertex indices for the four vertices forming the front of the new side for (v=0; v<4; v++) nsp->verts[v] = destseg->verts[(int) dvp[v]]; // The other 4 vertices must be created. // Their coordinates are determined by the 4 welded vertices and the vector from front // to back of the original *newseg. // Do lots of hideous matrix stuff, about 3/4 of which could probably be simplified out. med_extract_matrix_from_segment(destseg,&rotmat); // get orientation matrix for destseg (orthogonal rotation matrix) set_matrix_based_on_side(&rotmat,destside); vm_vector_2_matrix(&rotmat1,&forvec,&upvec,NULL); vm_matrix_x_matrix(&rotmat4,&rotmat,&rotmat1); // this is the desired orientation of the new segment med_extract_matrix_from_segment(newseg,&rotmat3); // this is the current orientation of the new segment vm_transpose_matrix(&rotmat3); // get the inverse of the current orientation matrix vm_matrix_x_matrix(&rotmat2,&rotmat4,&rotmat3); // now rotmat2 takes the current segment to the desired orientation // Warning -- look at this line! vm_transpose_matrix(&rotmat2); // added 12:33 pm, 10/01/93 // Compute and rotate the center point of the attaching face. compute_center_point_on_side(&vc,newseg,newside); vm_vec_rotate(&vr,&vc,&rotmat2); // Now rotate the free vertices in the segment for (v=0; v<4; v++) vm_vec_rotate(&tvs[v],&Vertices[newseg->verts[v+4]],&rotmat2); // Now translate the new segment so that the center point of the attaching faces are the same. compute_center_point_on_side(&vc,destseg,destside); vm_vec_sub(&xlate_vec,&vc,&vr); // Create and add the 4 new vertices. for (v=0; v<4; v++) { vm_vec_add2(&tvs[v],&xlate_vec); nsp->verts[v+4] = med_add_vertex(&tvs[v]); } set_vertex_counts(); // Now all the vertices are in place. Create the faces. validate_segment(nsp); // Say to not render at the joint. // destseg->sides[destside].render_flag = 0; // nsp->sides[newside].render_flag = 0; Cursegp = nsp; return 0; } // ------------------------------------------------------------------------------------------ // Attach side newside of newseg to side destside of destseg. // Copies *newseg into global array Segments, increments Num_segments. // Forms a weld between the two segments by making the new segment fit to the old segment. // Updates number of faces per side if necessitated by new vertex coordinates. // Updates Cursegp. // Return value: // 0 = successful attach // 1 = No room in Segments[]. // 2 = No room in Vertices[]. // 3 = newside != WFRONT -- for now, the new segment must be attached at its (own) front side // 4 = already a face attached on side newside int med_attach_segment(segment *destseg, segment *newseg, int destside, int newside) { int rval; segment *ocursegp = Cursegp; vms_angvec tang = {0,0,0}; vms_matrix rotmat; vm_angles_2_matrix(&rotmat,&tang); rval = med_attach_segment_rotated(destseg,newseg,destside,newside,&rotmat); med_propagate_tmaps_to_segments(ocursegp,Cursegp,0); med_propagate_tmaps_to_back_side(Cursegp, Side_opposite[newside],0); copy_uvs_seg_to_seg(&New_segment,Cursegp); return rval; } // ------------------------------------------------------------------------------- // Delete a vertex, sort of. // Decrement the vertex count. If the count goes to 0, then the vertex is free (has been deleted). static void delete_vertex(int v) { Assert(v < MAX_VERTICES); // abort if vertex is not in array Vertices Assert(Vertex_active[v] >= 1); // abort if trying to delete a non-existent vertex Vertex_active[v]--; } // ------------------------------------------------------------------------------- // Update Num_vertices. // This routine should be called by anyone who calls delete_vertex. It could be called in delete_vertex, // but then it would be called much more often than necessary, and it is a slow routine. static void update_num_vertices(void) { int v; // Now count the number of vertices. Num_vertices = 0; for (v=0; v<=Highest_vertex_index; v++) if (Vertex_active[v]) Num_vertices++; } // ------------------------------------------------------------------------------- // Set Vertex_active to number of occurrences of each vertex. // Set Num_vertices. void set_vertex_counts(void) { int v; Num_vertices = 0; for (v=0; v<=Highest_vertex_index; v++) Vertex_active[v] = 0; // Count number of occurrences of each vertex. for (segnum_t s=0; s<=Highest_segment_index; s++) if (Segments[s].segnum != segment_none) for (v=0; vverts[v]); update_num_vertices(); } // ------------------------------------------------------------------------------- // Delete segment *sp in Segments array. // Return value: // 0 successfully deleted. // 1 unable to delete. int med_delete_segment(segment *sp) { int side; segnum_t s,segnum = sp-Segments; // Cannot delete segment if only segment. if (Num_segments == 1) return 1; // Don't try to delete if segment doesn't exist. if (sp->segnum == segment_none) { return 1; } // Delete its refueling center if it has one fuelcen_delete(sp); delete_vertices_in_segment(sp); Num_segments--; // If deleted segment has walls on any side, wipe out the wall. for (side=0; side < MAX_SIDES_PER_SEGMENT; side++) if (sp->sides[side].wall_num != -1) wall_remove_side(sp, side); // Find out what this segment was connected to and break those connections at the other end. for (side=0; side < MAX_SIDES_PER_SEGMENT; side++) if (IS_CHILD(sp->children[side])) { segment *csp; // the connecting segment csp = &Segments[sp->children[side]]; for (int s=0; schildren[s] == segnum) { csp->children[s] = segment_none; // this is the side of connection, break it validate_segment_side(csp,s); // we have converted a connection to a side so validate the segment med_propagate_tmaps_to_back_side(csp,s,0); } Cursegp = csp; med_create_new_segment_from_cursegp(); copy_uvs_seg_to_seg(&New_segment,Cursegp); } sp->segnum = segment_none; // Mark segment as inactive. // If deleted segment = marked segment, then say there is no marked segment if (sp == Markedsegp) Markedsegp = 0; // If deleted segment = a Group segment ptr, then wipe it out. for (s=0;sgroup > -1) delete_segment_from_group(sp-Segments, sp->group); // If we deleted something which was not connected to anything, must now select a new current segment. if (Cursegp == sp) for (segnum_t s=0; spos,Cursegp); obj_relink(objnum,Cursegp-Segments); } else obj_delete(objnum); } // Make sure everything deleted ok... Assert( sp->objects==object_none ); // If we are leaving many holes in Segments or Vertices, then compress mine, because it is inefficient to be that way // if ((Highest_segment_index > Num_segments+4) || (Highest_vertex_index > Num_vertices+4*8)) // med_compress_mine(); return 0; } // ------------------------------------------------------------------------------------------ // Copy texture maps from sseg to dseg static void copy_tmaps_to_segment(segment *dseg, segment *sseg) { int s; for (s=0; ssides[s].type = sseg->sides[s].type; dseg->sides[s].tmap_num = sseg->sides[s].tmap_num; dseg->sides[s].tmap_num2 = sseg->sides[s].tmap_num2; } } // ------------------------------------------------------------------------------------------ // Rotate the segment *seg by the pitch, bank, heading defined by *rot, destructively // modifying its four free vertices in the global array Vertices. // It is illegal to rotate a segment which has connectivity != 1. // Pitch, bank, heading are about the point which is the average of the four points // forming the side of connection. // Return value: // 0 = successful rotation // 1 = Connectivity makes rotation illegal (connected to 0 or 2+ segments) // 2 = Rotation causes degeneracy, such as self-intersecting segment. // 3 = Unable to rotate because not connected to exactly 1 segment. int med_rotate_segment(segment *seg, vms_matrix *rotmat) { segment *destseg; int newside=0,destside,s; int count; int back_side,side_tmaps[MAX_SIDES_PER_SEGMENT]; // Find side of attachment count = 0; for (s=0; schildren[s])) { count++; newside = s; } // Return if passed in segment is connected to other than 1 segment. if (count != 1) return 3; destseg = &Segments[seg->children[newside]]; destside = 0; while ((destseg->children[destside] != seg-Segments) && (destside < MAX_SIDES_PER_SEGMENT)) destside++; // Before deleting the segment, copy its texture maps to New_segment copy_tmaps_to_segment(&New_segment,seg); if (Curside == WFRONT) Curside = WBACK; med_attach_segment_rotated(destseg,&New_segment,destside,AttachSide,rotmat); // Save tmap_num on each side to restore after call to med_propagate_tmaps_to_segments and _back_side // which will change the tmap nums. for (s=0; ssides[s].tmap_num; back_side = Side_opposite[find_connect_side(destseg, seg)]; med_propagate_tmaps_to_segments(destseg, seg,0); med_propagate_tmaps_to_back_side(seg, back_side,0); for (s=0; ssides[s].tmap_num = side_tmaps[s]; return 0; } // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // ---------------------------------------------------------------------------- // Compute the sum of the distances between the four pairs of points. // The connections are: // firstv1 : 0 (firstv1+1)%4 : 1 (firstv1+2)%4 : 2 (firstv1+3)%4 : 3 static fix seg_seg_vertex_distsum(segment *seg1, int side1, segment *seg2, int side2, int firstv1) { fix distsum; int secondv; distsum = 0; for (secondv=0; secondv<4; secondv++) { int firstv; firstv = (4-secondv + (3 - firstv1)) % 4; distsum += vm_vec_dist(&Vertices[seg1->verts[Side_to_verts[side1][firstv]]],&Vertices[seg2->verts[Side_to_verts[side2][secondv]]]); } return distsum; } // ---------------------------------------------------------------------------- // Determine how to connect two segments together with the least amount of twisting. // Returns vertex index in 0..3 on first segment. Assumed ordering of vertices // on second segment is 0,1,2,3. // So, if return value is 2, connect 2:0 3:1 0:2 1:3. // Theory: // We select an ordering of vertices for connection. For the first pair of vertices to be connected, // compute the vector. For the three remaining pairs of vertices, compute the vectors from one vertex // to the other. Compute the dot products of these vectors with the original vector. Add them up. // The close we are to 3, the better fit we have. Reason: The largest value for the dot product is // 1.0, and this occurs for a parallel set of vectors. static int get_index_of_best_fit(segment *seg1, int side1, segment *seg2, int side2) { int firstv; fix min_distance; int best_index=0; min_distance = F1_0*30000; for (firstv=0; firstv<4; firstv++) { fix t; t = seg_seg_vertex_distsum(seg1, side1, seg2, side2, firstv); if (t <= min_distance) { min_distance = t; best_index = firstv; } } return best_index; } #define MAX_VALIDATIONS 50 // ---------------------------------------------------------------------------- // Remap uv coordinates in all sides in segment *sp which have a vertex in vp[4]. // vp contains absolute vertex indices. static void remap_side_uvs(segment *sp,int *vp) { int s,i,v; for (s=0; schildren[side1]) || IS_CHILD(seg2->children[side2])) return 2; // Make sure there is no wall there if ((seg1->sides[side1].wall_num != -1) || (seg2->sides[side2].wall_num != -1)) return 2; // We can form the joint. Find the best orientation of vertices. bfi = get_index_of_best_fit(seg1, side1, seg2, side2); vp1 = Side_to_verts[side1]; vp2 = Side_to_verts[side2]; // Make a copy of the list of vertices in seg2 which will be deleted and set the // associated vertex number, so that all occurrences of the vertices can be replaced. for (v=0; v<4; v++) lost_vertices[v] = seg2->verts[(int) vp2[v]]; // Now, for each vertex in lost_vertices, determine which vertex it maps to. for (v=0; v<4; v++) remap_vertices[3 - ((v + bfi) % 4)] = seg1->verts[(int) vp1[v]]; // Now, in all segments, replace all occurrences of vertices in lost_vertices with remap_vertices // Put the one segment we know are being modified into the validation list. // Note: seg1 does not require a full validation, only a validation of the affected side. Its vertices do not move. nv = 1; validation_list[0] = seg2 - Segments; for (v=0; v<4; v++) for (s=0; s<=Highest_segment_index; s++) if (Segments[s].segnum != segment_none) for (sv=0; svchildren[side1] = seg2 - Segments; seg2->children[side2] = seg1 - Segments; // validate all segments validate_segment_side(seg1,side1); for (s=0; schildren[side] != -1) // then it is not legal to form the brider. // Return: // 0 bridge segment formed // 1 unable to form bridge because one (or both) of the sides is not open. // Note that no new vertices are created by this process. int med_form_bridge_segment(segment *seg1, int side1, segment *seg2, int side2) { segment *bs; const sbyte *sv; int v,bfi,i; if (IS_CHILD(seg1->children[side1]) || IS_CHILD(seg2->children[side2])) return 1; bs = &Segments[get_free_segment_number()]; bs->segnum = bs-Segments; bs->objects = object_none; // Copy vertices from seg2 into last 4 vertices of bridge segment. sv = Side_to_verts[side2]; for (v=0; v<4; v++) bs->verts[(3-v)+4] = seg2->verts[(int) sv[v]]; // Copy vertices from seg1 into first 4 vertices of bridge segment. bfi = get_index_of_best_fit(seg1, side1, seg2, side2); sv = Side_to_verts[side1]; for (v=0; v<4; v++) bs->verts[(v + bfi) % 4] = seg1->verts[(int) sv[v]]; // Form connections to children, first initialize all to unconnected. for (i=0; ichildren[i] = segment_none; bs->sides[i].wall_num = -1; } // Now form connections between segments. bs->children[AttachSide] = seg1 - Segments; bs->children[(int) Side_opposite[AttachSide]] = seg2 - Segments; seg1->children[side1] = bs-Segments; //seg2 - Segments; seg2->children[side2] = bs-Segments; //seg1 - Segments; // Validate bridge segment, and if degenerate, clean up mess. Degenerate_segment_found = 0; validate_segment(bs); if (Degenerate_segment_found) { seg1->children[side1] = segment_none; seg2->children[side2] = segment_none; bs->children[AttachSide] = segment_none; bs->children[(int) Side_opposite[AttachSide]] = segment_none; if (med_delete_segment(bs)) { Int3(); } editor_status("Bridge segment would be degenerate, not created.\n"); return 1; } else { validate_segment(seg1); // used to only validate side, but segment does more error checking: ,side1); validate_segment(seg2); // ,side2); med_propagate_tmaps_to_segments(seg1,bs,0); editor_status("Bridge segment formed."); warn_if_concave_segment(bs); return 0; } } // ------------------------------------------------------------------------------- // Create a segment given center, dimensions, rotation matrix. // Note that the created segment will always have planar sides and rectangular cross sections. // It will be created with walls on all sides, ie not connected to anything. void med_create_segment(segment *sp,fix cx, fix cy, fix cz, fix length, fix width, fix height, vms_matrix *mp) { int i,f; vms_vector v0,v1,cv; Num_segments++; sp->segnum = 1; // What to put here? I don't know. // Form connections to children, of which it has none. for (i=0; ichildren[i] = segment_none; // sp->sides[i].render_flag = 0; sp->sides[i].wall_num = -1; } sp->group = -1; sp->matcen_num = -1; // Create relative-to-center vertices, which are the rotated points on the box defined by length, width, height sp->verts[0] = med_add_vertex(vm_vec_rotate(&v1,vm_vec_make(&v0,+width/2,+height/2,-length/2),mp)); sp->verts[1] = med_add_vertex(vm_vec_rotate(&v1,vm_vec_make(&v0,+width/2,-height/2,-length/2),mp)); sp->verts[2] = med_add_vertex(vm_vec_rotate(&v1,vm_vec_make(&v0,-width/2,-height/2,-length/2),mp)); sp->verts[3] = med_add_vertex(vm_vec_rotate(&v1,vm_vec_make(&v0,-width/2,+height/2,-length/2),mp)); sp->verts[4] = med_add_vertex(vm_vec_rotate(&v1,vm_vec_make(&v0,+width/2,+height/2,+length/2),mp)); sp->verts[5] = med_add_vertex(vm_vec_rotate(&v1,vm_vec_make(&v0,+width/2,-height/2,+length/2),mp)); sp->verts[6] = med_add_vertex(vm_vec_rotate(&v1,vm_vec_make(&v0,-width/2,-height/2,+length/2),mp)); sp->verts[7] = med_add_vertex(vm_vec_rotate(&v1,vm_vec_make(&v0,-width/2,+height/2,+length/2),mp)); // Now create the vector which is the center of the segment and add that to all vertices. while (!vm_vec_make(&cv,cx,cy,cz)); // Now, add the center to all vertices, placing the segment in 3 space. for (i=0; iverts[i]],&Vertices[sp->verts[i]],&cv); // Set scale vector. // sp->scale.x = width; // sp->scale.y = height; // sp->scale.z = length; // Add faces to all sides. for (f=0; fobjects = object_none; //no objects in this segment // Assume nothing special about this segment sp->special = 0; sp->value = 0; sp->static_light = 0; sp->matcen_num = -1; copy_tmaps_to_segment(sp, &New_segment); assign_default_uvs_to_segment(sp); } // ---------------------------------------------------------------------------------------------- // Create New_segment using a specified scale factor. void med_create_new_segment(vms_vector *scale) { int s,t; vms_vector v0; segment *sp = &New_segment; fix length,width,height; length = scale->z; width = scale->x; height = scale->y; sp->segnum = 1; // What to put here? I don't know. // Create relative-to-center vertices, which are the points on the box defined by length, width, height t = Num_vertices; sp->verts[0] = med_set_vertex(NEW_SEGMENT_VERTICES+0,vm_vec_make(&v0,+width/2,+height/2,-length/2)); sp->verts[1] = med_set_vertex(NEW_SEGMENT_VERTICES+1,vm_vec_make(&v0,+width/2,-height/2,-length/2)); sp->verts[2] = med_set_vertex(NEW_SEGMENT_VERTICES+2,vm_vec_make(&v0,-width/2,-height/2,-length/2)); sp->verts[3] = med_set_vertex(NEW_SEGMENT_VERTICES+3,vm_vec_make(&v0,-width/2,+height/2,-length/2)); sp->verts[4] = med_set_vertex(NEW_SEGMENT_VERTICES+4,vm_vec_make(&v0,+width/2,+height/2,+length/2)); sp->verts[5] = med_set_vertex(NEW_SEGMENT_VERTICES+5,vm_vec_make(&v0,+width/2,-height/2,+length/2)); sp->verts[6] = med_set_vertex(NEW_SEGMENT_VERTICES+6,vm_vec_make(&v0,-width/2,-height/2,+length/2)); sp->verts[7] = med_set_vertex(NEW_SEGMENT_VERTICES+7,vm_vec_make(&v0,-width/2,+height/2,+length/2)); Num_vertices = t; // sp->scale = *scale; // Form connections to children, of which it has none, init faces and tmaps. for (s=0; schildren[s] = segment_none; // sp->sides[s].render_flag = 0; sp->sides[s].wall_num = -1; create_walls_on_side(sp,s); sp->sides[s].tmap_num = s; // assign some stupid old tmap to this side. sp->sides[s].tmap_num2 = 0; } Seg_orientation = {}; sp->objects = object_none; //no objects in this segment assign_default_uvs_to_segment(sp); // Assume nothing special about this segment sp->special = 0; sp->value = 0; sp->static_light = 0; sp->matcen_num = -1; } // ------------------------------------------------------------------------------- void med_create_new_segment_from_cursegp(void) { vms_vector scalevec; vms_vector uvec, rvec, fvec; med_extract_up_vector_from_segment_side(Cursegp, Curside, &uvec); med_extract_right_vector_from_segment_side(Cursegp, Curside, &rvec); extract_forward_vector_from_segment(Cursegp, &fvec); scalevec.x = vm_vec_mag(&rvec); scalevec.y = vm_vec_mag(&uvec); scalevec.z = vm_vec_mag(&fvec); med_create_new_segment(&scalevec); } // ------------------------------------------------------------------------------- // Initialize all vertices to inactive status. void init_all_vertices(void) { for (unsigned v=0; v &vertnums) { vms_matrix rotmat; vms_vector t; med_extract_matrix_from_segment(sp,&rotmat); compute_segment_center(&Vertices[vertnums[0]],sp); t = rotmat.rvec; vm_vec_scale(&t,i2f(32)); vm_vec_add(&Vertices[vertnums[1]],&Vertices[vertnums[0]],&t); t = rotmat.uvec; vm_vec_scale(&t,i2f(32)); vm_vec_add(&Vertices[vertnums[2]],&Vertices[vertnums[0]],&t); t = rotmat.fvec; vm_vec_scale(&t,i2f(32)); vm_vec_add(&Vertices[vertnums[3]],&Vertices[vertnums[0]],&t); } // ----------------------------------------------------------------------------- // Determine if a segment is concave. Returns true if concave int check_seg_concavity(segment *s) { int sn,vn; vms_vector n0,n1; for (sn=0;snverts[Side_to_verts[sn][vn%4]]], &Vertices[s->verts[Side_to_verts[sn][(vn+1)%4]]], &Vertices[s->verts[Side_to_verts[sn][(vn+2)%4]]]); //vm_vec_normalize(&n1); if (vn>0) if (vm_vec_dotprod(&n0,&n1) < f0_5) return 1; n0 = n1; } return 0; } // ----------------------------------------------------------------------------- // Find all concave segments and add to list void find_concave_segs() { int i; segment *s; Warning_segs.clear(); for (s=&Segments[0],i=Highest_segment_index;i>=0;s++,i--) if (s->segnum != segment_none) if (check_seg_concavity(s)) Warning_segs.emplace_back(SEG_PTR_2_NUM(s)); } // ----------------------------------------------------------------------------- void warn_if_concave_segments(void) { find_concave_segs(); if (Warning_segs.count()) { editor_status_fmt("*** WARNING *** %d concave segments in mine! *** WARNING ***",Warning_segs.count()); } } // ----------------------------------------------------------------------------- // Check segment s, if concave, warn void warn_if_concave_segment(segment *s) { int result; result = check_seg_concavity(s); if (result) { Warning_segs.emplace_back(s-Segments); editor_status("*** WARNING *** New segment is concave! *** WARNING ***"); } //else //editor_status(""); } // ------------------------------------------------------------------------------- // Find segment adjacent to sp:side. // Adjacent means a segment which shares all four vertices. // Return true if segment found and fill in segment in adj_sp and side in adj_side. // Return false if unable to find, in which case adj_sp and adj_side are undefined. int med_find_adjacent_segment_side(segment *sp, int side, segment **adj_sp, int *adj_side) { int seg,s,v,vv; int abs_verts[4]; // Stuff abs_verts[4] array with absolute vertex indices for (v=0; v<4; v++) abs_verts[v] = sp->verts[Side_to_verts[side][v]]; // Scan all segments, looking for a segment which contains the four abs_verts for (seg=0; seg<=Highest_segment_index; seg++) { if (seg != sp-Segments) { for (v=0; v<4; v++) { // do for each vertex in abs_verts for (vv=0; vvchildren[side])) return 0; compute_center_point_on_side(&vsc, sp, side); closest_seg_dist = JOINT_THRESHOLD; // Scan all segments, looking for a segment which contains the four abs_verts for (seg=0; seg<=Highest_segment_index; seg++) if (seg != sp-Segments) for (s=0;s