2013-04-11 11:26:57 +00:00
|
|
|
|
using System.Collections.Generic;
|
2013-03-18 13:52:27 +00:00
|
|
|
|
using CodeImp.DoomBuilder.VisualModes;
|
|
|
|
|
using CodeImp.DoomBuilder.Map;
|
|
|
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
|
|
|
using System.Windows.Forms;
|
2013-07-08 13:13:28 +00:00
|
|
|
|
using CodeImp.DoomBuilder.Windows;
|
2013-03-18 13:52:27 +00:00
|
|
|
|
|
|
|
|
|
namespace CodeImp.DoomBuilder.BuilderModes
|
|
|
|
|
{
|
|
|
|
|
internal class BaseVisualVertex : VisualVertex, IVisualEventReceiver
|
|
|
|
|
{
|
|
|
|
|
#region ================== Variables
|
|
|
|
|
|
|
|
|
|
protected BaseVisualMode mode;
|
|
|
|
|
private float cageradius2;
|
|
|
|
|
private Vector3D boxp1;
|
|
|
|
|
private Vector3D boxp2;
|
|
|
|
|
|
|
|
|
|
// Undo/redo
|
|
|
|
|
private int undoticket;
|
|
|
|
|
|
2013-07-10 08:59:17 +00:00
|
|
|
|
//updating
|
|
|
|
|
private static Dictionary<BaseVisualSector, bool> updateList;
|
|
|
|
|
|
2013-03-18 13:52:27 +00:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
// Constructor
|
|
|
|
|
public BaseVisualVertex(BaseVisualMode mode, Vertex v, bool ceilingVertex)
|
|
|
|
|
: base(v, ceilingVertex) {
|
|
|
|
|
|
|
|
|
|
this.mode = mode;
|
|
|
|
|
cageradius2 = General.Settings.GZVisualVertexSize * Angle2D.SQRT2;
|
|
|
|
|
cageradius2 = cageradius2 * cageradius2;
|
|
|
|
|
|
|
|
|
|
changed = true;
|
|
|
|
|
Update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//this updates the handle itself
|
|
|
|
|
public override void Update() {
|
|
|
|
|
if(!changed) return;
|
2013-07-08 13:13:28 +00:00
|
|
|
|
float z = ceilingVertex ? vertex.ZCeiling : vertex.ZFloor;
|
2013-03-18 13:52:27 +00:00
|
|
|
|
|
|
|
|
|
int height = getSectorHeight();
|
2013-07-08 13:13:28 +00:00
|
|
|
|
if(!float.IsNaN(z)) {
|
|
|
|
|
haveOffset = (z != height || !neighboursHaveSameHeight(height));
|
2013-03-18 13:52:27 +00:00
|
|
|
|
} else {
|
|
|
|
|
z = height;
|
2013-05-02 11:03:16 +00:00
|
|
|
|
haveOffset = false;
|
2013-03-18 13:52:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector3D pos = new Vector3D(vertex.Position.x, vertex.Position.y, z);
|
|
|
|
|
SetPosition(pos);
|
|
|
|
|
|
|
|
|
|
int radius = General.Settings.GZVisualVertexSize;
|
|
|
|
|
boxp1 = new Vector3D(pos.x - radius, pos.y - radius, pos.z - radius);
|
|
|
|
|
boxp2 = new Vector3D(pos.x + radius, pos.y + radius, pos.z + radius);
|
|
|
|
|
|
|
|
|
|
changed = false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-02 07:47:22 +00:00
|
|
|
|
private bool neighboursHaveSameHeight(int height) {
|
|
|
|
|
if(ceilingVertex) {
|
|
|
|
|
foreach(Linedef line in vertex.Linedefs) {
|
|
|
|
|
if(line.Front != null && line.Front.Sector != null && line.Front.Sector.Sidedefs.Count == 3 && line.Front.Sector.CeilHeight != height) return false;
|
|
|
|
|
if(line.Back != null && line.Back.Sector != null && line.Back.Sector.Sidedefs.Count == 3 && line.Back.Sector.CeilHeight != height) return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
foreach(Linedef line in vertex.Linedefs) {
|
|
|
|
|
if(line.Front != null && line.Front.Sector != null && line.Front.Sector.Sidedefs.Count == 3 && line.Front.Sector.FloorHeight != height) return false;
|
|
|
|
|
if(line.Back != null && line.Back.Sector != null && line.Back.Sector.Sidedefs.Count == 3 && line.Back.Sector.FloorHeight != height) return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-18 13:52:27 +00:00
|
|
|
|
private void updateGeometry(Vertex v) {
|
|
|
|
|
VertexData vd = mode.GetVertexData(v);
|
|
|
|
|
foreach(KeyValuePair<Sector, bool> s in vd.UpdateAlso) {
|
|
|
|
|
if(mode.VisualSectorExists(s.Key)) {
|
|
|
|
|
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(s.Key);
|
|
|
|
|
vs.UpdateSectorGeometry(s.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-08 13:13:28 +00:00
|
|
|
|
//get the most appropriate height from sectors
|
2013-03-18 13:52:27 +00:00
|
|
|
|
private int getSectorHeight() {
|
|
|
|
|
int height = 0;
|
|
|
|
|
|
|
|
|
|
VertexData vd = mode.GetVertexData(vertex);
|
|
|
|
|
Sector[] sectors = new Sector[vd.UpdateAlso.Keys.Count];
|
|
|
|
|
vd.UpdateAlso.Keys.CopyTo(sectors, 0);
|
|
|
|
|
|
|
|
|
|
if(ceilingVertex) {
|
|
|
|
|
height = sectors[0].CeilHeight;
|
|
|
|
|
for(int i = 1; i < sectors.Length; i++) {
|
2013-05-02 11:03:16 +00:00
|
|
|
|
if(sectors[i].CeilHeight < height)
|
2013-03-18 13:52:27 +00:00
|
|
|
|
height = sectors[i].CeilHeight;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
height = sectors[0].FloorHeight;
|
|
|
|
|
for(int i = 1; i < sectors.Length; i++) {
|
2013-05-02 11:03:16 +00:00
|
|
|
|
if(sectors[i].FloorHeight > height)
|
2013-03-18 13:52:27 +00:00
|
|
|
|
height = sectors[i].FloorHeight;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return height;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-01 11:06:01 +00:00
|
|
|
|
//mxd
|
|
|
|
|
public virtual bool IsSelected() {
|
|
|
|
|
return selected;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-18 13:52:27 +00:00
|
|
|
|
#region ================== Object picking
|
|
|
|
|
|
|
|
|
|
// This performs a fast test in object picking
|
|
|
|
|
public override bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir) {
|
|
|
|
|
float distance2 = Line2D.GetDistanceToLineSq(from, to, vertex.Position, false);
|
|
|
|
|
return (distance2 <= cageradius2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This performs an accurate test for object picking
|
|
|
|
|
public override bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray) {
|
|
|
|
|
Vector3D delta = to - from;
|
|
|
|
|
float tfar = float.MaxValue;
|
|
|
|
|
float tnear = float.MinValue;
|
|
|
|
|
|
|
|
|
|
// Ray-Box intersection code
|
|
|
|
|
// See http://www.masm32.com/board/index.php?topic=9941.0
|
|
|
|
|
|
|
|
|
|
// Check X slab
|
|
|
|
|
if(delta.x == 0.0f) {
|
|
|
|
|
if(from.x > boxp2.x || from.x < boxp1.x) {
|
|
|
|
|
// Ray is parallel to the planes & outside slab
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
float tmp = 1.0f / delta.x;
|
|
|
|
|
float t1 = (boxp1.x - from.x) * tmp;
|
|
|
|
|
float t2 = (boxp2.x - from.x) * tmp;
|
|
|
|
|
if(t1 > t2)
|
|
|
|
|
General.Swap(ref t1, ref t2);
|
|
|
|
|
if(t1 > tnear)
|
|
|
|
|
tnear = t1;
|
|
|
|
|
if(t2 < tfar)
|
|
|
|
|
tfar = t2;
|
|
|
|
|
if(tnear > tfar || tfar < 0.0f) {
|
|
|
|
|
// Ray missed box or box is behind ray
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check Y slab
|
|
|
|
|
if(delta.y == 0.0f) {
|
|
|
|
|
if(from.y > boxp2.y || from.y < boxp1.y) {
|
|
|
|
|
// Ray is parallel to the planes & outside slab
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
float tmp = 1.0f / delta.y;
|
|
|
|
|
float t1 = (boxp1.y - from.y) * tmp;
|
|
|
|
|
float t2 = (boxp2.y - from.y) * tmp;
|
|
|
|
|
if(t1 > t2)
|
|
|
|
|
General.Swap(ref t1, ref t2);
|
|
|
|
|
if(t1 > tnear)
|
|
|
|
|
tnear = t1;
|
|
|
|
|
if(t2 < tfar)
|
|
|
|
|
tfar = t2;
|
|
|
|
|
if(tnear > tfar || tfar < 0.0f) {
|
|
|
|
|
// Ray missed box or box is behind ray
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check Z slab
|
|
|
|
|
if(delta.z == 0.0f) {
|
|
|
|
|
if(from.z > boxp2.z || from.z < boxp1.z) {
|
|
|
|
|
// Ray is parallel to the planes & outside slab
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
float tmp = 1.0f / delta.z;
|
|
|
|
|
float t1 = (boxp1.z - from.z) * tmp;
|
|
|
|
|
float t2 = (boxp2.z - from.z) * tmp;
|
|
|
|
|
if(t1 > t2)
|
|
|
|
|
General.Swap(ref t1, ref t2);
|
|
|
|
|
if(t1 > tnear)
|
|
|
|
|
tnear = t1;
|
|
|
|
|
if(t2 < tfar)
|
|
|
|
|
tfar = t2;
|
|
|
|
|
if(tnear > tfar || tfar < 0.0f) {
|
|
|
|
|
// Ray missed box or box is behind ray
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set interpolation point
|
|
|
|
|
u_ray = (tnear > 0.0f) ? tnear : tfar;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Unused events
|
|
|
|
|
|
|
|
|
|
// Unused
|
|
|
|
|
public virtual void OnSelectBegin() { }
|
|
|
|
|
public virtual void OnEditBegin() { }
|
|
|
|
|
public virtual void OnMouseMove(MouseEventArgs e) { }
|
|
|
|
|
public virtual void OnChangeTargetBrightness(bool up) { }
|
2013-04-26 12:32:51 +00:00
|
|
|
|
public virtual void OnChangeTextureOffset(int horizontal, int vertical, bool doSurfaceAngleCorrection) { }
|
Visual mode, UDMF: added "Scale Texture Up (X)", "Scale Texture Down (X)", "Scale Texture Up (Y)", "Scale Texture Down (Y)" actions. Default keys are Num6, Num4, Num8, Num5.
Visual mode, UDMF: renamed "Rotate Thing Clockwise" and "Rotate Thing Counterclockwise" actions to "Rotate Clockwise" and "Rotate Counterclockwise". These actions can now be used to change rotation of floor/ceiling textures.
Visual mode, UDMF: "Reset Texture Offsets" action now also resets sidedef's scale and floor/ceiling's scale and rotation.
Visual mode, UDMF: control line's OffsetX and OffsetY were not taken into account when calculating texture offsets of 3d floors' sides.
Visual mode, UDMF: fixed a ton of bugs in Auto align functions.
Visual mode, UDMF: when using "Move Texture Left/Right/Up/Down by 1" actions texture offsets were not updated properly when texture's scale was < 1.0.
Visual mode, UDMF: OffsetX and OffsetY were not taken into account in "Fit Texture Width/Height" actions.
Dockers Panel: added Pin/Unpin button, which acts the same as "Preferences -> Interface -> Side panels -> Auto hide" checkbox.
Texture size labels can now be disabled by unchecking "Preferences -> Interface -> Show texture and flat sizes in browsers" checkbox.
Texture size labels now are not shown for unknown textures.
Most of texture size labels had incorrect bg color.
ZDoom_linedefs.cfg: action specials 223 and 224 had incorrect Arg0.
2013-06-24 14:21:13 +00:00
|
|
|
|
public virtual void OnChangeTextureScale(float incrementX, float incrementY) { }
|
2013-03-18 13:52:27 +00:00
|
|
|
|
public virtual void OnSelectTexture() { }
|
|
|
|
|
public virtual void OnCopyTexture() { }
|
|
|
|
|
public virtual void OnPasteTexture() { }
|
|
|
|
|
public virtual void OnCopyTextureOffsets() { }
|
|
|
|
|
public virtual void OnPasteTextureOffsets() { }
|
|
|
|
|
public virtual void OnTextureAlign(bool alignx, bool aligny) { }
|
2013-05-02 07:47:22 +00:00
|
|
|
|
public virtual void OnTextureFit(bool fitWidth, bool fitHeight) { } //mxd
|
2013-03-18 13:52:27 +00:00
|
|
|
|
public virtual void OnToggleUpperUnpegged() { }
|
|
|
|
|
public virtual void OnToggleLowerUnpegged() { }
|
|
|
|
|
public virtual void OnResetTextureOffset() { }
|
|
|
|
|
public virtual void OnProcess(float deltatime) { }
|
|
|
|
|
public virtual void OnTextureFloodfill() { }
|
|
|
|
|
public virtual void OnInsert() { }
|
|
|
|
|
public virtual void ApplyTexture(string texture) { }
|
|
|
|
|
public virtual void ApplyUpperUnpegged(bool set) { }
|
|
|
|
|
public virtual void ApplyLowerUnpegged(bool set) { }
|
|
|
|
|
public virtual string GetTextureName() { return ""; }
|
2013-04-01 11:06:01 +00:00
|
|
|
|
public virtual void SelectNeighbours(bool select, bool withSameTexture, bool withSameHeight) { } //mxd
|
2013-03-18 13:52:27 +00:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Events
|
|
|
|
|
|
|
|
|
|
// Select or deselect
|
|
|
|
|
public virtual void OnSelectEnd() {
|
|
|
|
|
if(this.selected) {
|
|
|
|
|
this.selected = false;
|
|
|
|
|
mode.RemoveSelectedObject(this);
|
|
|
|
|
} else {
|
|
|
|
|
this.selected = true;
|
|
|
|
|
mode.AddSelectedObject(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy properties
|
|
|
|
|
public virtual void OnCopyProperties() {
|
|
|
|
|
BuilderPlug.Me.CopiedVertexProps = new VertexProperties(vertex);
|
|
|
|
|
mode.SetActionResult("Copied vertex properties.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Paste properties
|
|
|
|
|
public virtual void OnPasteProperties() {
|
|
|
|
|
if(BuilderPlug.Me.CopiedVertexProps != null) {
|
|
|
|
|
mode.CreateUndo("Paste vertex properties");
|
|
|
|
|
mode.SetActionResult("Pasted vertex properties.");
|
|
|
|
|
BuilderPlug.Me.CopiedVertexProps.Apply(vertex);
|
|
|
|
|
|
|
|
|
|
//update affected sectors
|
|
|
|
|
updateGeometry(vertex);
|
|
|
|
|
changed = true;
|
|
|
|
|
mode.ShowTargetInfo();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-02 07:47:22 +00:00
|
|
|
|
//Delete key pressed - remove zoffset field
|
|
|
|
|
public virtual void OnDelete() {
|
2013-07-08 13:13:28 +00:00
|
|
|
|
if(ceilingVertex) {
|
|
|
|
|
if(float.IsNaN(vertex.ZCeiling)) return;
|
|
|
|
|
vertex.ZCeiling = float.NaN;
|
|
|
|
|
|
|
|
|
|
//update affected sectors
|
|
|
|
|
updateGeometry(vertex);
|
|
|
|
|
changed = true;
|
|
|
|
|
mode.ShowTargetInfo();
|
|
|
|
|
} else {
|
|
|
|
|
if(float.IsNaN(vertex.ZFloor)) return;
|
|
|
|
|
vertex.ZFloor = float.NaN;
|
2013-05-02 07:47:22 +00:00
|
|
|
|
|
|
|
|
|
//update affected sectors
|
|
|
|
|
updateGeometry(vertex);
|
|
|
|
|
changed = true;
|
|
|
|
|
mode.ShowTargetInfo();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-18 13:52:27 +00:00
|
|
|
|
// Edit button released
|
|
|
|
|
public virtual void OnEditEnd() {
|
|
|
|
|
if(General.Interface.IsActiveWindow) {
|
|
|
|
|
List<Vertex> verts = mode.GetSelectedVertices();
|
2013-07-10 08:59:17 +00:00
|
|
|
|
updateList = new Dictionary<BaseVisualSector, bool>();
|
|
|
|
|
|
|
|
|
|
foreach(Vertex v in verts){
|
|
|
|
|
VertexData vd = mode.GetVertexData(v);
|
|
|
|
|
foreach(KeyValuePair<Sector, bool> s in vd.UpdateAlso) {
|
|
|
|
|
if(mode.VisualSectorExists(s.Key)) {
|
|
|
|
|
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(s.Key);
|
|
|
|
|
updateList.Add(vs, s.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-08 13:13:28 +00:00
|
|
|
|
General.Interface.OnEditFormValuesChanged += new System.EventHandler(Interface_OnEditFormValuesChanged);
|
2013-07-19 15:30:58 +00:00
|
|
|
|
mode.StartRealtimeInterfaceUpdate(SelectionType.Vertices);
|
2013-07-08 13:13:28 +00:00
|
|
|
|
General.Interface.ShowEditVertices(verts, false);
|
2013-07-19 15:30:58 +00:00
|
|
|
|
mode.StopRealtimeInterfaceUpdate(SelectionType.Vertices);
|
2013-07-10 09:57:30 +00:00
|
|
|
|
General.Interface.OnEditFormValuesChanged -= Interface_OnEditFormValuesChanged;
|
2013-07-10 08:59:17 +00:00
|
|
|
|
|
|
|
|
|
updateList.Clear();
|
|
|
|
|
updateList = null;
|
2013-03-18 13:52:27 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-08 13:13:28 +00:00
|
|
|
|
//mxd
|
|
|
|
|
private void Interface_OnEditFormValuesChanged(object sender, System.EventArgs e) {
|
2013-07-10 08:59:17 +00:00
|
|
|
|
foreach(KeyValuePair<BaseVisualSector, bool> group in updateList)
|
|
|
|
|
group.Key.UpdateSectorGeometry(group.Value);
|
2013-07-08 13:13:28 +00:00
|
|
|
|
|
|
|
|
|
changed = true;
|
|
|
|
|
Update();
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-18 13:52:27 +00:00
|
|
|
|
// Raise/lower thing
|
|
|
|
|
public virtual void OnChangeTargetHeight(int amount) {
|
|
|
|
|
if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
|
|
|
|
|
undoticket = mode.CreateUndo("Change vertex height");
|
|
|
|
|
|
2013-07-08 13:13:28 +00:00
|
|
|
|
if(ceilingVertex) {
|
|
|
|
|
vertex.ZCeiling = (float.IsNaN(vertex.ZCeiling) ? getSectorHeight() + amount : vertex.ZCeiling + amount);
|
|
|
|
|
|
|
|
|
|
if(vertex.ZCeiling == getSectorHeight()) {
|
|
|
|
|
vertex.ZCeiling = float.NaN;
|
|
|
|
|
mode.SetActionResult("Cleared vertex height.");
|
|
|
|
|
} else {
|
|
|
|
|
mode.SetActionResult("Changed vertex height to " + vertex.ZCeiling + ".");
|
|
|
|
|
}
|
2013-03-18 13:52:27 +00:00
|
|
|
|
} else {
|
2013-07-08 13:13:28 +00:00
|
|
|
|
vertex.ZFloor = (float.IsNaN(vertex.ZFloor) ? getSectorHeight() + amount : vertex.ZFloor + amount);
|
|
|
|
|
|
|
|
|
|
if(vertex.ZFloor == getSectorHeight()) {
|
|
|
|
|
vertex.ZFloor = float.NaN;
|
|
|
|
|
mode.SetActionResult("Cleared vertex height.");
|
|
|
|
|
} else {
|
|
|
|
|
mode.SetActionResult("Changed vertex height to " + vertex.ZFloor + ".");
|
|
|
|
|
}
|
2013-03-18 13:52:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update what must be updated
|
|
|
|
|
updateGeometry(vertex);
|
2013-05-02 07:47:22 +00:00
|
|
|
|
changed = true;
|
2013-03-18 13:52:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|