From fcd29375c8f272ced12dec6181aec76c9a5d7c02 Mon Sep 17 00:00:00 2001
From: MaxED <j.maxed@gmail.com>
Date: Thu, 5 Jul 2012 13:48:08 +0000
Subject: [PATCH] Things can now be moved, insterted and deleted in Visual
 modes. "Place thing at cursor position" Action places Things much more
 precisely now.

---
 Help/gz_features.html                         |   2 +-
 Source/Core/Rendering/Renderer3D.cs           |  15 ++-
 Source/Core/VisualModes/VisualGeometry.cs     |   4 +-
 Source/Core/VisualModes/VisualMode.cs         |  34 +++++-
 Source/Core/VisualModes/VisualThing.cs        |   9 +-
 .../VisualModes/BaseVisualMode.cs             | 106 +++++++++++++++---
 .../VisualModes/BaseVisualThing.cs            |   8 +-
 .../VisualModes/BaseVisualMode.cs             |   7 +-
 .../VisualModes/BaseVisualThing.cs            |  13 +--
 9 files changed, 154 insertions(+), 44 deletions(-)

diff --git a/Help/gz_features.html b/Help/gz_features.html
index 09eefa430..c1ed692a1 100644
--- a/Help/gz_features.html
+++ b/Help/gz_features.html
@@ -26,9 +26,9 @@
     <li>Fog rendering (including colored fog in maps in UDMF format).</li>
     <li><a href="gz_gldefs.html">MD2 and MD3 models</a> rendering in 2D and 3D modes.</li>
     <li><a href="gz_mode_drawrect.html">Draw Rectangle</a>, <a href="gz_mode_drawellipse.html">Draw Ellipse</a> and <a href="gz_mode_drawbridge.html">Bridge</a> modes.</li>
+    <li>Ability to <a href="gz_actions.html#movething">move</a>, insert and delete Things in Visual modes.</li>
     <li><a href="gz_actions.html#newtestmap">"Test Map from current position"</a> feature.</li>
     <li><a href="gz_settings.html">"Sync camera position between 2D and 3D modes"</a> feature.</li>
-    <li><a href="gz_actions.html#movething">"Move Things horizontally in Visual Modes"</a> feature.</li>
     <li><a href="gz_actions.html#movethingtocursor">"Place Things at cursor position in Visual Modes"</a> feature.</li>
     <li>PNG image format support.</li>
     <li><a href="gz_plug_colorpicker.html">Color Picker plugin.</a></li>
diff --git a/Source/Core/Rendering/Renderer3D.cs b/Source/Core/Rendering/Renderer3D.cs
index 438e3a68c..24af1e83c 100644
--- a/Source/Core/Rendering/Renderer3D.cs
+++ b/Source/Core/Rendering/Renderer3D.cs
@@ -1186,10 +1186,6 @@ namespace CodeImp.DoomBuilder.Rendering
             //mxd. gather models
             if (General.Settings.GZDrawModels && (!General.Settings.GZDrawSelectedModelsOnly || t.Selected) && t.Thing.IsModel) {
                 ModeldefEntry mde = General.Map.Data.ModeldefEntries[t.Thing.Type];
-
-                //if (!isThingOnScreen(t.BoundingBox))
-                    //return;
-
                 if (!thingsWithModel.ContainsKey(mde)) 
                     thingsWithModel.Add(mde, new List<VisualThing>());
                 thingsWithModel[mde].Add(t);
@@ -1220,13 +1216,13 @@ namespace CodeImp.DoomBuilder.Rendering
             Vector3D thingNormal = D3DDevice.V3D(bbox[0]) - cameraposition; //bbox[0] is always thing center
 
             if (Vector3D.DotProduct(camNormal, thingNormal) < 0) { //behind camera plane
-                //GZBuilder.GZGeneral.TraceLine("Skipped geo. Vector3D.DotProduct(camNormal, thingNormal) < 0");
+                //GZBuilder.GZGeneral.Trace("Skipped geo. Vector3D.DotProduct(camNormal, thingNormal) < 0");
                 return false;
             }
 
             int len = bbox.Length;
             Vector3 screenPos;
-            int behingCount = 0;
+            int behindCount = 0;
             int leftCount = 0;
             int rightCount = 0;
             int topCount = 0;
@@ -1240,7 +1236,7 @@ namespace CodeImp.DoomBuilder.Rendering
                     return true;
 
                 if (screenPos.Z < 0)
-                    behingCount++;
+                    behindCount++;
 
                 if (screenPos.X < 0)
                     leftCount++;
@@ -1252,8 +1248,11 @@ namespace CodeImp.DoomBuilder.Rendering
                     bottomCount++;
             }
 
-            if (behingCount == len || leftCount == len || rightCount == len || topCount == len || bottomCount == len)
+            if (behindCount == len || leftCount == len || rightCount == len || topCount == len || bottomCount == len) {
+                //dbg
+                //GZBuilder.GZGeneral.Trace("Skipped geo. Not on screen");
                 return false; //Not on screen
+            }
             return true;
         }
 
diff --git a/Source/Core/VisualModes/VisualGeometry.cs b/Source/Core/VisualModes/VisualGeometry.cs
index 66f83301e..74c4b179a 100644
--- a/Source/Core/VisualModes/VisualGeometry.cs
+++ b/Source/Core/VisualModes/VisualGeometry.cs
@@ -91,14 +91,14 @@ namespace CodeImp.DoomBuilder.VisualModes
 		#region ================== Properties
 		
 		// Internal properties
-		internal WorldVertex[] Vertices { get { return vertices; } }
+		public WorldVertex[] Vertices { get { return vertices; } } //mxd
 		internal int VertexOffset { get { return vertexoffset; } set { vertexoffset = value; } }
 		internal int Triangles { get { return triangles; } }
 		internal int RenderPassInt { get { return renderpass; } }
 		internal Color4 ModColor4 { get { return modcolor4; } }
 
         //mxd
-        internal Vector3[] BoundingBox { get { return boundingBox; } }
+        public Vector3[] BoundingBox { get { return boundingBox; } }
         public VisualGeometryType GeometryType { get { return geoType; } }
 
 		/// <summary>
diff --git a/Source/Core/VisualModes/VisualMode.cs b/Source/Core/VisualModes/VisualMode.cs
index b440bccc6..ef0c2530e 100644
--- a/Source/Core/VisualModes/VisualMode.cs
+++ b/Source/Core/VisualModes/VisualMode.cs
@@ -425,17 +425,43 @@ namespace CodeImp.DoomBuilder.VisualModes
 
         [BeginAction("placethingatcursor", BaseAction = true)]
         protected void placeThingAtCursor() {
+            Vector2D hitCoords = getHitPosition();
+            if (!hitCoords.IsFinite()) {
+                General.Interface.DisplayStatus(StatusType.Warning, "Cannot place Thing here");
+                return;
+            }
+
+            moveSelectedThings(new Vector2D((float)Math.Round(hitCoords.x), (float)Math.Round(hitCoords.y)), true);
+        }
+
+        //mxd. 
+        protected Vector2D getHitPosition() {
             Vector3D start = General.Map.VisualCamera.Position;
             Vector3D delta = General.Map.VisualCamera.Target - General.Map.VisualCamera.Position;
             delta = delta.GetFixedLength(General.Settings.ViewDistance * 0.98f);
             VisualPickResult target = PickObject(start, start + delta);
 
-            if (target.picked == null) {
-                General.Interface.DisplayStatus(StatusType.Warning, "Cannot place Thing here");
-                return;
+            if (target.picked == null) return new Vector2D(float.NaN, float.NaN);
+
+            //now find where exactly did we hit
+            Vector2D hitCoords = new Vector2D();
+            if (target.picked is VisualGeometry) {
+                VisualGeometry vg = target.picked as VisualGeometry;
+                hitCoords = getIntersection(start, start + delta, new Vector3D(vg.BoundingBox[0].X, vg.BoundingBox[0].Y, vg.BoundingBox[0].Z), new Vector3D(vg.Vertices[0].nx, vg.Vertices[0].ny, vg.Vertices[0].nz));
+            } else if (target.picked is VisualThing) {
+                VisualThing vt = target.picked as VisualThing;
+                hitCoords = getIntersection(start, start + delta, new Vector3D(vt.BoundingBox[0].X, vt.BoundingBox[0].Y, vt.BoundingBox[0].Z), D3DDevice.V3D(vt.Center - vt.PositionV3));
+            } else {
+                return new Vector2D(float.NaN, float.NaN);
             }
 
-            moveSelectedThings(new Vector2D((float)Math.Round(target.hitpos.x), (float)Math.Round(target.hitpos.y)), true);
+            return hitCoords;
+        }
+
+        //mxd. this checks intersection between line and plane 
+        protected Vector2D getIntersection(Vector3D start, Vector3D end, Vector3D planeCenter, Vector3D planeNormal) {
+            Vector3D delta = new Vector3D(planeCenter.x - start.x, planeCenter.y - start.y, planeCenter.z - start.z);
+            return start + Vector3D.DotProduct(planeNormal, delta) / Vector3D.DotProduct(planeNormal, end - start) * (end - start);
         }
 
         //should move selected things in specified direction
diff --git a/Source/Core/VisualModes/VisualThing.cs b/Source/Core/VisualModes/VisualThing.cs
index 635039401..c2191038e 100644
--- a/Source/Core/VisualModes/VisualThing.cs
+++ b/Source/Core/VisualModes/VisualThing.cs
@@ -499,7 +499,14 @@ namespace CodeImp.DoomBuilder.VisualModes
 
         //mxd. update bounding box
         public void UpdateBoundingBox() {
-            updateBoundingBox(lightRadius, lightRadius * 2f);
+            //updateBoundingBox(lightRadius, lightRadius * 2f);
+            if (thing.IsModel) {
+                updateBoundingBoxForModel();
+            } else if (lightType != -1 && lightRadius > thing.Size) {
+                updateBoundingBox(lightRadius, lightRadius * 2);
+            } else {
+                updateBoundingBox((int)thing.Size, thingHeight);
+            }
         }
 
         private void updateBoundingBox(float width, float height) {
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
index 8bbcab408..b00dc81f8 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
@@ -959,6 +959,40 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				return new NullVisualEventReceiver();
 			}
 		}
+
+        //mxd. Copied from BuilderModes.ThingsMode
+        // This creates a new thing
+        private Thing InsertThing(Vector2D pos) {
+            if (pos.x < General.Map.Config.LeftBoundary || pos.x > General.Map.Config.RightBoundary ||
+                pos.y > General.Map.Config.TopBoundary || pos.y < General.Map.Config.BottomBoundary) {
+                General.Interface.DisplayStatus(StatusType.Warning, "Failed to insert thing: outside of map boundaries.");
+                return null;
+            }
+
+            // Create thing
+            Thing t = General.Map.Map.CreateThing();
+            if (t != null) {
+                General.Settings.ApplyDefaultThingSettings(t);
+                t.Move(pos);
+                t.UpdateConfiguration();
+                General.Map.IsChanged = true;
+
+                // Update things filter so that it includes this thing
+                General.Map.ThingsFilter.Update();
+
+                // Snap to grid enabled?
+                if (General.Interface.SnapToGrid) {
+                    // Snap to grid
+                    t.SnapToGrid();
+                }
+                else {
+                    // Snap to map format accuracy
+                    t.SnapToAccuracy();
+                }
+            }
+
+            return t;
+        }
 		
 		#endregion
 
@@ -1308,24 +1342,62 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			foreach(IVisualEventReceiver i in objs) i.OnPasteProperties();
 			PostAction();
 		}
-		
-		[BeginAction("insertitem", BaseAction = true)]
-		public void Insert()
-		{
-			PreAction(UndoGroup.None);
-			List<IVisualEventReceiver> objs = GetSelectedObjects(true, true, true);
-			foreach(IVisualEventReceiver i in objs) i.OnInsert();
-			PostAction();
-		}
 
-		[BeginAction("deleteitem", BaseAction = true)]
-		public void Delete()
-		{
-			PreAction(UndoGroup.None);
-			List<IVisualEventReceiver> objs = GetSelectedObjects(true, true, true);
-			foreach(IVisualEventReceiver i in objs) i.OnDelete();
-			PostAction();
-		}
+        //mxd. now we can actually insert things in Visual modes
+        [BeginAction("insertitem", BaseAction = true)] 
+        public void Insert() {
+            Vector2D hitpos = getHitPosition();
+
+            if (!hitpos.IsFinite()) {
+                General.Interface.DisplayStatus(StatusType.Warning, "Cannot insert item here!");
+                return;
+            }
+
+            ClearSelection();
+            PreActionNoChange();
+
+            General.Map.UndoRedo.CreateUndo("Insert thing");
+
+            Thing t = InsertThing(new Vector2D(hitpos.x, hitpos.y));
+
+            if (t == null) {
+                General.Map.UndoRedo.WithdrawUndo();
+                return;
+            }
+
+            // Edit the thing?
+            if (BuilderPlug.Me.EditNewThing)
+                General.Interface.ShowEditThings(new List<Thing> { t });
+
+            //add thing to blockmap
+            blockmap.AddThing(t);
+
+            General.Interface.DisplayStatus(StatusType.Action, "Inserted a new thing.");
+            General.Map.IsChanged = true;
+            General.Map.ThingsFilter.Update();
+            PostAction();
+        }
+
+        [BeginAction("deleteitem", BaseAction = true)] //mxd. now we can actually delete things in Visual modes
+        public void Delete() {
+            List<IVisualEventReceiver> objs = GetSelectedObjects(false, false, true);
+            if (objs.Count == 0) return;
+
+            string rest = objs.Count + " thing" + (objs.Count > 1 ? "s." : ".");
+
+            //make undo
+            General.Map.UndoRedo.CreateUndo("Delete " + rest);
+            General.Interface.DisplayStatus(StatusType.Info, "Deleted " + rest);
+
+            PreActionNoChange();
+            foreach (IVisualEventReceiver i in objs) i.OnDelete(); //are they deleted from BlockMap automatically?..
+
+            // Update cache values
+            General.Map.IsChanged = true;
+            General.Map.ThingsFilter.Update();
+
+            PostAction();
+        }
 		
 		#endregion
 	}
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs
index cf6124f40..1554fd3d3 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs
@@ -388,7 +388,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		public virtual void OnProcess(double deltatime) { }
 		public virtual void OnTextureFloodfill() { }
 		public virtual void OnInsert() { }
-		public virtual void OnDelete() { }
+		//public virtual void OnDelete() { }
 		public virtual void ApplyTexture(string texture) { }
 		public virtual void ApplyUpperUnpegged(bool set) { }
 		public virtual void ApplyLowerUnpegged(bool set) { }
@@ -410,6 +410,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				mode.AddSelectedObject(this);
 			}
 		}
+
+        //mxd. Delete thing
+        public virtual void OnDelete() {
+            this.Thing.Dispose();
+            this.Dispose();
+        }
 		
 		// Copy properties
 		public virtual void OnCopyProperties()
diff --git a/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualMode.cs b/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualMode.cs
index f7cfff7d4..d79a0b776 100644
--- a/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualMode.cs
+++ b/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualMode.cs
@@ -1591,9 +1591,9 @@ namespace CodeImp.DoomBuilder.GZDoomEditing
         [BeginAction("insertitem", BaseAction = true)] //mxd. now we can actually insert things in Visual modes
 		public void Insert()
 		{
-            PickTarget();
+            Vector2D hitpos = getHitPosition();
 
-            if (target.picked == null) {
+            if (!hitpos.IsFinite()) {
                 General.Interface.DisplayStatus(StatusType.Warning, "Cannot insert item here!");
                 return;
             }
@@ -1602,7 +1602,8 @@ namespace CodeImp.DoomBuilder.GZDoomEditing
             PreActionNoChange();
 
             General.Map.UndoRedo.CreateUndo("Insert thing");
-            Thing t = InsertThing(new Vector2D(target.hitpos.x, target.hitpos.y));
+
+            Thing t = InsertThing(new Vector2D(hitpos.x, hitpos.y));
 
             if (t == null) {
                 General.Map.UndoRedo.WithdrawUndo();
diff --git a/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualThing.cs b/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualThing.cs
index ecce5255a..cb0365bc2 100644
--- a/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualThing.cs
+++ b/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualThing.cs
@@ -428,13 +428,6 @@ namespace CodeImp.DoomBuilder.GZDoomEditing
 		public virtual void ApplyTexture(string texture) { }
 		public virtual void ApplyUpperUnpegged(bool set) { }
 		public virtual void ApplyLowerUnpegged(bool set) { }
-
-        //mxd. Delete thing
-        public virtual void OnDelete() {
-            this.Thing.Dispose();
-            this.Dispose();
-        }
-
 		
 		// Return texture name
 		public virtual string GetTextureName() { return ""; }
@@ -453,6 +446,12 @@ namespace CodeImp.DoomBuilder.GZDoomEditing
 				mode.AddSelectedObject(this);
 			}
 		}
+
+        //mxd. Delete thing
+        public virtual void OnDelete() {
+            this.Thing.Dispose();
+            this.Dispose();
+        }
 		
 		// Copy properties
 		public virtual void OnCopyProperties()
-- 
GitLab