public void EntityMoveToSide() { Entity e = GetSelectedEntity(); if (e != null && selected_segment > -1 && selected_side > -1) { Side side = GetSelectedSide(); //If moving a door, point the side to the door & set the split plane (if not already split), and clear door's old side if (e.Type == EntityType.DOOR) { //Clear door from old segment side foreach (Side s in this.segment[e.m_segnum].side) { if (s.Door == e.num) { s.Door = -1; break; } } Debug.Assert(side.Door == -1); side.Door = e.num; if (side.chunk_plane_order == -1) { side.chunk_plane_order = 0; } } e.SetPosition(side.FindCenterBetter(), selected_segment); e.FaceDirection(side.FindNormal()); } }
public void UpdateClipNormal(int idx) { Vector3 v; Side s2; switch (clip[idx]) { case DecalClip.ADJACENT: // Average of this normal and the next non-neighbor side (falls back to this segment if no neighbor, which is the same as AVERAGE) s2 = side.FindAdjacentSideOtherSegment(idx); v = s2.FindNormal(); v = v + side.FindNormal(); v.Normalize(); // Will always be valid unless you have an infinitely thin wall (in which case that's bad anyway) clip_normal[idx] = v; break; case DecalClip.AVERAGE: // Average of this normal and the adjacent side normal s2 = side.FindAdjacentSideSameSegment(idx); v = s2.FindNormal(); v = v + side.FindNormal(); v.Normalize(); // Will always be valid because the segment can't have two coincident sides clip_normal[idx] = v; break; case DecalClip.WHOLE: // This could be changed to same as segment if it doesn't work as desired, or could add second WHOLE variant case DecalClip.PERP: // The side normal clip_normal[idx] = side.FindNormal(); break; case DecalClip.SEGMENT: // The edge direction away (in the adjacent side) v = side.FindAdjacentSideEdgeDirAway(idx); clip_normal[idx] = v; break; default: clip_normal[idx] = Vector3.Zero; break; } }
public Vector3 GetSelectedSideNormal() { if (selected_side > -1 && selected_segment > -1) { Side s = segment[selected_segment].side[selected_side]; return(s.FindNormal()); } else { editor.AddOutputText("WARNING: No selected segment side"); return(Vector3.UnitZ); } }
public void GetSelectedSidePosAndNormal(out Vector3 pos, out Vector3 normal) { if (selected_side > -1 && selected_segment > -1) { Side s = segment[selected_segment].side[selected_side]; pos = s.FindCenter(); normal = s.FindNormal(); } else { editor.AddOutputText("WARNING: No selected segment side"); pos = Vector3.Zero; normal = Vector3.Zero; } }
//Returns segnum, or -1 public int InsertSegmentSelectedSide(bool regular = false) { if (selected_side > -1 && selected_segment > -1) { Side s = segment[selected_segment].side[selected_side]; if (s.segment.neighbor[selected_side] < 0) { Vector3 side_normal = -s.FindNormal(); Vector3 side_right = s.FindBestEdgeDir(); // s.FindEdgeDir(0); Vector3 side_up = Vector3.Cross(side_normal, side_right).Normalized(); Vector3 side_center = s.FindCenter(); int idx_new = InsertSegmentBasic(s, side_center, side_normal, side_right, side_up, regular, editor.CurrExtrudeLength); segment[idx_new].CopyTexturesAndDecalsFromSegmentAtBack(segment[selected_segment], selected_side); s.DisableDecals(); // Tag the two segments (for refreshing decals) UnTagAllSegments(); segment[idx_new].m_tag = true; s.segment.m_tag = true; // Select the new cube (depending on the option) if (editor.ShouldInsertAdvance) { selected_segment = idx_new; //selected_side = (int)SideOrder.FRONT; } return(idx_new); } else { // Already has a neighbor } } else { // Nothing selected } return(-1); }
public void FindBaseAttributes(Decal d) { Side s = d.side; m_base_normal = s.FindNormal(); // Center position switch (d.align) { case DecalAlign.TEXTURE: m_base_pos = s.FindUVCenterIn3D(); break; case DecalAlign.CENTER: m_base_pos = s.FindCenter(); break; case DecalAlign.EDGE_RIGHT: case DecalAlign.EDGE_DOWN: case DecalAlign.EDGE_LEFT: case DecalAlign.EDGE_UP: m_base_pos = s.FindEdgeCenter((int)d.align - (int)DecalAlign.EDGE_RIGHT); break; } m_base_rvec = Vector3.Zero; // Up vector switch (d.align) { case DecalAlign.TEXTURE: m_base_uvec = s.FindUVUpVector(); break; case DecalAlign.CENTER: m_base_uvec = s.FindBestEdgeDir(); break; case DecalAlign.EDGE_RIGHT: case DecalAlign.EDGE_DOWN: case DecalAlign.EDGE_LEFT: case DecalAlign.EDGE_UP: m_base_rvec = -s.FindEdgeDir((int)d.align - (int)DecalAlign.EDGE_RIGHT); m_base_uvec = Vector3.Cross(m_base_normal, m_base_rvec); break; } // Find the right vec for cases we haven't already if (m_base_rvec == Vector3.Zero) { m_base_rvec = Vector3.Cross(m_base_uvec, m_base_normal); } // Apply the rotation of the decal properties to the Up and Right Vector Matrix4 vec_rot = Matrix4.CreateFromAxisAngle(m_base_normal, d.RotationAngle()); m_base_uvec = Vector3.TransformNormal(m_base_uvec, vec_rot); m_base_rvec = Vector3.TransformNormal(m_base_rvec, vec_rot); // Base rotation m_base_rot = Matrix4.Transpose(Matrix4.LookAt(Vector3.Zero, m_base_normal, m_base_uvec)); }
public List <Tuple <int, int> > InsertMarkedSidesMulti(int extrude_length, Side s) { Vector3 side_normal = -s.FindNormal(); Vector3 side_right, side_up, side_center; // Find the list of marked sides List <Side> side_list = new List <Side>(); // Tag the segments (for refreshing decals) UnTagAllSegments(); // Get the list of marked sides for (int i = 0; i < MAX_SEGMENTS; i++) { if (!segment[i].Visible) { continue; } for (int j = 0; j < Segment.NUM_SIDES; j++) { if (segment[i].neighbor[j] < 0) { if (segment[i].side[j].marked) { side_list.Add(segment[i].side[j]); segment[i].m_tag = true; } } } } int idx_new; // Create a new segment for each marked side List <Tuple <int, int> > res = new List <Tuple <int, int> >(); for (int i = 0; i < side_list.Count; i++) { side_right = side_list[i].FindBestEdgeDir(); side_up = Vector3.Cross(side_normal, side_right).Normalized(); side_center = side_list[i].FindCenter(); idx_new = InsertSegmentBasic(side_list[i], side_center, side_normal, side_right, side_up, false, extrude_length); segment[idx_new].CopyTexturesAndDecalsFromSegmentAtBack(side_list[i].segment, side_list[i].num); side_list[i].DisableDecals(); segment[idx_new].m_tag = true; // Mark the new side and unmark the old side_list[i].marked = false; segment[idx_new].side[side_list[i].num].marked = true; res.Add(Tuple.Create(idx_new, side_list[i].num)); // Select the segment if it was the selected one if (side_list[i].num == selected_side && side_list[i].segment.num == selected_segment) { selected_segment = idx_new; } } return(res); }
public static void DetermineLevelChunking(Level level) { m_level = level; m_num_chunks = 0; int num_segments = 0; SegmentType[] SegmentTypes = null; if (EnableAutomaticChunking) { SegmentTypes = new SegmentType[Level.MAX_SEGMENTS]; } //Clear out chunk count array m_chunk_size.Init(0); if (!EnableAutomaticChunking) { //No automatic chunking, so put everything in one chunk foreach (int segmentIndex in level.EnumerateAliveSegmentIndices()) { level.segment[segmentIndex].m_chunk_num = 0; num_segments++; } m_chunk_size[0] = num_segments; m_num_chunks = 1; } else { //Loop through segment, assigning each to a chunk foreach (int segmentIndex in level.EnumerateAliveSegmentIndices()) { var segmentData = level.segment[segmentIndex]; num_segments++; //Count the number of portals int num_portals = 0; for (int sideIdx = 0; sideIdx < 6; ++sideIdx) { if (segmentData.neighbor[sideIdx] != -1) { num_portals++; } } //Figure out what kind of segment; default to complex SegmentTypes[segmentIndex] = SegmentType.Complex; if (num_portals == 1) { SegmentTypes[segmentIndex] = SegmentType.Single; } else if (num_portals == 2) { //If two portals, see if they're opposite if (((segmentData.neighbor[0] != -1) && (segmentData.neighbor[2] != -1)) || ((segmentData.neighbor[1] != -1) && (segmentData.neighbor[3] != -1)) || ((segmentData.neighbor[4] != -1) && (segmentData.neighbor[5] != -1))) { //Ok, we've got two opposite sides. SegmentTypes[segmentIndex] = SegmentType.Connector; } } //Now decide what to do with this segment int chunk_num = -1; for (int sideIdx = 0; sideIdx < 6; ++sideIdx) { if (segmentData.neighbor[sideIdx] != -1) { int other = segmentData.neighbor[sideIdx]; //Only look at lower-numbered segments, which will already have type set if (other < segmentIndex) { //Join with the other if it's the same as us, or if we're a signel or it's a single if ((SegmentTypes[other] == SegmentTypes[segmentIndex]) || (SegmentTypes[segmentIndex] == SegmentType.Single) || (SegmentTypes[other] == SegmentType.Single)) { int other_chunk_num = level.segment[other].m_chunk_num; //We should join with the other segment. See if we're already in a chunk if (chunk_num == -1) { //We're not in a chunk, so join with the other chunk_num = other_chunk_num; } else { //We're already in a chunk, so move all segments from other chunk to our chunk for (int t = 0; t < segmentIndex; t++) { if (level.segment[t].Alive) { if (level.segment[t].m_chunk_num == other_chunk_num) { level.segment[t].m_chunk_num = chunk_num; m_chunk_size[other_chunk_num]--; m_chunk_size[chunk_num]++; } } } } } } } } //If we didn't find a chunk to join, start a new one if (chunk_num == -1) { chunk_num = m_num_chunks++; } segmentData.m_chunk_num = chunk_num; m_chunk_size[chunk_num]++; } //Now, go through and split up any overly big connector chunks //Sorry that this code is so ugly and hard to follow for (int chunknum = 0; chunknum < m_num_chunks; chunknum++) { //If more than five, split if (m_chunk_size[chunknum] > MAX_CONNECTOR_CHUNK_SIZE) { //Determine if this is a connector chunk, and if so find terminal segment Segment seg = null; int segnum, sidenum = -1, other_segnum = -1; for (segnum = 0; segnum < Level.MAX_SEGMENTS; segnum++) { seg = level.segment[segnum]; if (seg.Alive && (seg.m_chunk_num == chunknum)) { //If this is a complex chunk, bail if (SegmentTypes[segnum] == SegmentType.Complex) { break; } //It's a connector; see if this one is the terminus if (SegmentTypes[segnum] == SegmentType.Connector) { for (sidenum = 0; sidenum < 6; sidenum++) { other_segnum = seg.neighbor[sidenum]; if (other_segnum != -1) { if (SegmentTypes[other_segnum] != SegmentType.Connector) //Connected to something else, so this is terminus { break; } } } } if (sidenum < 6) { break; } } } if ((segnum < Level.MAX_SEGMENTS) && (SegmentTypes[segnum] == SegmentType.Connector)) //We've found a terminal connector { #if OVERLOAD_LEVEL_EDITOR level.editor.AddOutputText("Spitting chunk " + chunknum + "( " + m_chunk_size[chunknum] + " segments)"); #endif int exit_side = FindExitSide(seg, other_segnum); //Compute size of new chunk(s) int num_sub_chunks = (m_chunk_size[chunknum] + MAX_CONNECTOR_CHUNK_SIZE - 1) / MAX_CONNECTOR_CHUNK_SIZE; int new_chunk_size = (m_chunk_size[chunknum] + num_sub_chunks / 2) / num_sub_chunks; //The first batch of segments stay in the old chunk for (int t = new_chunk_size; t > 0; t--) { exit_side = FindExitSide(seg, other_segnum); if (exit_side != -1) { other_segnum = segnum; segnum = seg.neighbor[exit_side]; seg = level.segment[segnum]; } } //Walk through chain until halfway, moving segments to new chunk int new_chunk_num = m_num_chunks++; int moved_count = 0; while ((exit_side != -1) && ((SegmentTypes[segnum] == SegmentType.Connector) || (SegmentTypes[segnum] == SegmentType.Single))) { seg.m_chunk_num = new_chunk_num; m_chunk_size[chunknum]--; m_chunk_size[new_chunk_num]++; moved_count++; exit_side = FindExitSide(seg, other_segnum); if (exit_side != -1) { other_segnum = segnum; segnum = seg.neighbor[exit_side]; seg = level.segment[segnum]; } if (moved_count == new_chunk_size) { new_chunk_num = m_num_chunks++; moved_count = 0; } } } } } } // if EnableAutomaticChunking //Get all the manually-added separation planes List <SplitPlane> planes = new List <SplitPlane>(); foreach (int segnum in level.EnumerateAliveSegmentIndices()) { Segment seg = level.segment[segnum]; for (int sidenum = 0; sidenum < 6; sidenum++) { Side side = seg.side[sidenum]; if (side.chunk_plane_order != -1) { planes.Add(new SplitPlane(segnum, sidenum, side.chunk_plane_order)); } } } //If we have any planes, sort them and do the splits if (planes.Count > 0) { //Sort planes planes.Sort((p1, p2) => p1.m_order.CompareTo(p2.m_order)); //Process the planes in order foreach (SplitPlane plane in planes) { Segment seg = level.segment[plane.m_segnum]; Side side = seg.side[plane.m_sidenum]; int connected_segnum = seg.neighbor[plane.m_sidenum]; //Ok, found one. Split the chunk this segment is in. int old_chunk_num = seg.m_chunk_num; int new_chunk_num = m_num_chunks++; OpenTK.Vector3 sep_normal = side.FindNormal(); OpenTK.Vector3 sep_vert = level.vertex[side.vert[0]].position; foreach (int checksegnum in level.EnumerateAliveSegmentIndices()) { Segment checkseg = level.segment[checksegnum]; if (checkseg.m_chunk_num != old_chunk_num) { continue; } if (OpenTK.Vector3.Dot(checkseg.FindCenter() - sep_vert, sep_normal) < 0.0f) { checkseg.m_chunk_num = new_chunk_num; m_chunk_size[old_chunk_num]--; m_chunk_size[new_chunk_num]++; } } //Check to see if either of resulting chunks contain non-connected geometry, and if so add them back to the proper chunk if (connected_segnum != -1) { RejoinDisconnectedChunks(plane.m_segnum, new_chunk_num); RejoinDisconnectedChunks(connected_segnum, old_chunk_num); } } } // Compact the chunks array int[] map_old_chunk_to_compact_chunk = new int[m_num_chunks]; int compact_chunk_count = 0; for (int chunknum = 0; chunknum < m_num_chunks; ++chunknum) { if (m_chunk_size[chunknum] == 0) { map_old_chunk_to_compact_chunk[chunknum] = -1; } else { map_old_chunk_to_compact_chunk[chunknum] = compact_chunk_count++; } } foreach (int segnum in level.EnumerateAliveSegmentIndices()) { Segment seg = level.segment[segnum]; System.Diagnostics.Debug.Assert(seg.m_chunk_num >= 0 && seg.m_chunk_num < m_num_chunks); int compact_chunk_idx = map_old_chunk_to_compact_chunk[seg.m_chunk_num]; System.Diagnostics.Debug.Assert(compact_chunk_idx != -1); seg.m_chunk_num = compact_chunk_idx; } level.m_num_chunks = compact_chunk_count; #if OVERLOAD_LEVEL_EDITOR level.editor.AddOutputText("Num segments = " + num_segments + ", num chunks = " + level.m_num_chunks); //Generate colors for chunks for (int c = 0; c < m_num_chunks; c++) { level.ChunkColor[c] = System.Drawing.Color.FromArgb(255, Utility.RandomRange(0, 127), Utility.RandomRange(0, 127), Utility.RandomRange(0, 127)); } #endif //OVERLOAD_LEVEL_EDITOR }