UltimateZoneBuilder/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs
MaxED 8f47c316b9 Classic modes, UDMF: floor/ceiling texture offsets can now be locked in place while sector is dragged.
Fixed another bug in Tools.autoAlignTexturesOnSidesUDMF()
Replaced Math.PI-related equations with stuff from Angle2D class.
2013-04-11 09:27:16 +00:00

538 lines
16 KiB
C#

#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.VisualModes;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
internal abstract class BaseVisualGeometrySector : VisualGeometry, IVisualEventReceiver
{
#region ================== Constants
private const float DRAG_ANGLE_TOLERANCE = 0.06f;
#endregion
#region ================== Variables
protected BaseVisualMode mode;
protected long setuponloadedtexture;
// This is only used to see if this object has already received a change
// in a multiselection. The Changed property on the BaseVisualSector is
// used to indicate a rebuild is needed.
protected bool changed;
protected SectorLevel level;
protected Effect3DFloor extrafloor;
// Undo/redo
private int undoticket;
// UV dragging
private float dragstartanglexy;
private float dragstartanglez;
private Vector3D dragorigin;
//private Vector3D deltaxy;
//private Vector3D deltaz;
private int startoffsetx;
private int startoffsety;
protected bool uvdragging;
private int prevoffsetx; // We have to provide delta offsets, but I don't
private int prevoffsety; // want to calculate with delta offsets to prevent
// inaccuracy in the dragging.
#endregion
#region ================== Properties
new public BaseVisualSector Sector { get { return (BaseVisualSector)base.Sector; } }
public bool Changed { get { return changed; } set { changed = value; } }
public SectorLevel Level { get { return level; } }
public Effect3DFloor ExtraFloor { get { return extrafloor; } }
#endregion
#region ================== Constructor / Destructor
// Constructor
protected BaseVisualGeometrySector(BaseVisualMode mode, VisualSector vs) : base(vs)
{
this.mode = mode;
}
#endregion
#region ================== Methods
// This changes the height
protected abstract void ChangeHeight(int amount);
public virtual void SelectNeighbours(bool select, bool withSameTexture, bool withSameHeight) { } //mxd
// This swaps triangles so that the plane faces the other way
protected void SwapTriangleVertices(WorldVertex[] verts)
{
// Swap some vertices to flip all triangles
for(int i = 0; i < verts.Length; i += 3)
{
// Swap
WorldVertex v = verts[i];
verts[i] = verts[i + 1];
verts[i + 1] = v;
}
}
// This is called to update UV dragging
protected virtual void UpdateDragUV()
{
float u_ray = 1.0f;
// Calculate intersection position
this.Level.plane.GetIntersection(General.Map.VisualCamera.Position, General.Map.VisualCamera.Target, ref u_ray);
Vector3D intersect = General.Map.VisualCamera.Position + (General.Map.VisualCamera.Target - General.Map.VisualCamera.Position) * u_ray;
// Calculate offsets
Vector3D dragdelta = intersect - dragorigin;
float offsetx = dragdelta.x;
float offsety = dragdelta.y;
//mxd. Modify offsets based on surface and camera angles
if (General.Map.UDMF) {
float angle = 0;
if(GeometryType == VisualGeometryType.CEILING && level.sector.Fields.ContainsKey("rotationceiling"))
angle = Angle2D.DegToRad((float)level.sector.Fields["rotationceiling"].Value);// * (float)Math.PI / 180f;
else if(GeometryType == VisualGeometryType.FLOOR && level.sector.Fields.ContainsKey("rotationfloor"))
angle = Angle2D.DegToRad((float)level.sector.Fields["rotationfloor"].Value);// *(float)Math.PI / 180f;
Vector2D v = new Vector2D(offsetx, offsety).GetRotated(angle);
Point p = getTranslatedTextureOffset(new Point((int)Math.Round(v.x), (int)Math.Round(v.y)));
offsetx = p.X;
offsety = p.Y;
}
// Apply offsets
int newoffsetx = startoffsetx - (int)Math.Round(offsetx);
int newoffsety = startoffsety + (int)Math.Round(offsety);
mode.ApplyFlatOffsetChange(prevoffsetx - newoffsetx, prevoffsety - newoffsety);
prevoffsetx = newoffsetx;
prevoffsety = newoffsety;
mode.ShowTargetInfo();
}
//mxd
public override Sector GetControlSector() {
return level.sector;
}
//mxd. Modify texture offsets based on camera angle (so "movetextureleft" action always moves texture more or less "left" etc.)
protected Point getTranslatedTextureOffset(Point p) {
Point tp = new Point();
int camAngle = (int)Angle2D.RadToDeg(General.Map.VisualCamera.AngleXY);// * 180f / (float)Math.PI);
if (camAngle > 315 || camAngle < 46) {
tp = p;
} else if (camAngle > 225) {
tp.Y = p.X;
tp.X = -p.Y;
} else if (camAngle > 135) {
tp.X = -p.X;
tp.Y = -p.Y;
}else{
tp.Y = -p.X;
tp.X = p.Y;
}
return tp;
}
//mxd
protected void onTextureChanged() {
if(level.sector == this.Sector.Sector) {
this.Setup();
//check for 3d floors
foreach(Sidedef s in level.sector.Sidedefs) {
if(s.Line.Action == 160 && s.Line.Front != null) {
int sectortag = s.Line.Args[0] + (s.Line.Args[4] << 8);
foreach(Sector sector in General.Map.Map.Sectors) {
if(sector.Tag == sectortag) {
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(sector);
vs.UpdateSectorGeometry(false);
}
}
}
}
} else if(mode.VisualSectorExists(level.sector)) {
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(level.sector);
vs.UpdateSectorGeometry(false);
}
}
//mxd
public virtual bool IsSelected() {
return selected;
}
#endregion
#region ================== Events
// Unused
public virtual void OnEditBegin() { }
public virtual void OnTextureAlign(bool alignx, bool aligny) { }
public virtual void OnToggleUpperUnpegged() { }
public virtual void OnToggleLowerUnpegged() { }
public virtual void OnResetTextureOffset() { }
public virtual void OnCopyTextureOffsets() { }
public virtual void OnPasteTextureOffsets() { }
public virtual void OnInsert() { }
public virtual void OnDelete() { }
protected virtual void SetTexture(string texturename) { }
public virtual void ApplyUpperUnpegged(bool set) { }
public virtual void ApplyLowerUnpegged(bool set) { }
protected abstract void MoveTextureOffset(Point xy);
protected abstract Point GetTextureOffset();
// Setup this plane
public bool Setup() { return this.Setup(this.level, this.extrafloor); }
public virtual bool Setup(SectorLevel level, Effect3DFloor extrafloor)
{
this.level = level;
this.extrafloor = extrafloor;
return false;
}
// Begin select
public virtual void OnSelectBegin()
{
mode.LockTarget();
dragstartanglexy = General.Map.VisualCamera.AngleXY;
dragstartanglez = General.Map.VisualCamera.AngleZ;
dragorigin = pickintersect;
startoffsetx = GetTextureOffset().X;
startoffsety = GetTextureOffset().Y;
prevoffsetx = GetTextureOffset().X;
prevoffsety = GetTextureOffset().Y;
}
// Select or deselect
public virtual void OnSelectEnd()
{
mode.UnlockTarget();
// Was dragging?
if(uvdragging)
{
// Dragging stops now
uvdragging = false;
}
else
{
if(this.selected)
{
this.selected = false;
mode.RemoveSelectedObject(this);
}
else
{
this.selected = true;
mode.AddSelectedObject(this);
}
}
}
// Moving the mouse
public virtual void OnMouseMove(MouseEventArgs e)
{
// Dragging UV?
if(uvdragging)
{
UpdateDragUV();
}
else
{
// Select button pressed?
if(General.Actions.CheckActionActive(General.ThisAssembly, "visualselect"))
{
// Check if tolerance is exceeded to start UV dragging
float deltaxy = General.Map.VisualCamera.AngleXY - dragstartanglexy;
float deltaz = General.Map.VisualCamera.AngleZ - dragstartanglez;
if((Math.Abs(deltaxy) + Math.Abs(deltaz)) > DRAG_ANGLE_TOLERANCE)
{
if(General.Map.UDMF) { //mxd
mode.PreAction(UndoGroup.TextureOffsetChange);
mode.CreateUndo("Change texture offsets");
// Start drag now
uvdragging = true;
mode.Renderer.ShowSelection = false;
mode.Renderer.ShowHighlight = false;
UpdateDragUV();
}
}
}
}
}
// Processing
public virtual void OnProcess(float deltatime)
{
// If the texture was not loaded, but is loaded now, then re-setup geometry
if(setuponloadedtexture != 0)
{
ImageData t = General.Map.Data.GetFlatImage(setuponloadedtexture);
if(t != null)
{
if(t.IsImageLoaded)
{
setuponloadedtexture = 0;
Setup();
}
}
}
}
// Flood-fill textures
public virtual void OnTextureFloodfill()
{
if(BuilderPlug.Me.CopiedFlat != null)
{
string oldtexture = GetTextureName();
long oldtexturelong = Lump.MakeLongName(oldtexture);
string newtexture = BuilderPlug.Me.CopiedFlat;
if(newtexture != oldtexture)
{
// Get the texture
ImageData newtextureimage = General.Map.Data.GetFlatImage(newtexture);
if(newtextureimage != null)
{
bool fillceilings = (this is VisualCeiling);
if(fillceilings)
{
mode.CreateUndo("Flood-fill ceilings with " + newtexture);
mode.SetActionResult("Flood-filled ceilings with " + newtexture + ".");
}
else
{
mode.CreateUndo("Flood-fill floors with " + newtexture);
mode.SetActionResult("Flood-filled floors with " + newtexture + ".");
}
mode.Renderer.SetCrosshairBusy(true);
General.Interface.RedrawDisplay();
if(mode.IsSingleSelection)
{
// Clear all marks, this will align everything it can
General.Map.Map.ClearMarkedSectors(false);
}
else
{
// Limit the alignment to selection only
General.Map.Map.ClearMarkedSectors(true);
List<Sector> sectors = mode.GetSelectedSectors();
foreach(Sector s in sectors) s.Marked = false;
}
// Do the fill
Tools.FloodfillFlats(this.Sector.Sector, fillceilings, oldtexturelong, newtextureimage, false);
// Get the changed sectors
List<Sector> changes = General.Map.Map.GetMarkedSectors(true);
foreach(Sector s in changes)
{
// Update the visual sector
if(mode.VisualSectorExists(s))
{
BaseVisualSector vs = (mode.GetVisualSector(s) as BaseVisualSector);
if(fillceilings)
vs.Ceiling.Setup();
else
vs.Floor.Setup();
}
}
General.Map.Data.UpdateUsedTextures();
mode.Renderer.SetCrosshairBusy(false);
mode.ShowTargetInfo();
}
}
}
}
// Copy properties
public virtual void OnCopyProperties()
{
BuilderPlug.Me.CopiedSectorProps = new SectorProperties(level.sector);
mode.SetActionResult("Copied sector properties.");
}
// Paste properties
public virtual void OnPasteProperties()
{
if(BuilderPlug.Me.CopiedSectorProps != null)
{
mode.CreateUndo("Paste sector properties");
mode.SetActionResult("Pasted sector properties.");
BuilderPlug.Me.CopiedSectorProps.Apply(level.sector);
if(mode.VisualSectorExists(level.sector))
{
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(level.sector);
vs.UpdateSectorGeometry(true);
}
mode.ShowTargetInfo();
}
}
// Select texture
public virtual void OnSelectTexture()
{
if(General.Interface.IsActiveWindow)
{
string oldtexture = GetTextureName();
string newtexture = General.Interface.BrowseFlat(General.Interface, oldtexture);
if(newtexture != oldtexture)
{
mode.ApplySelectTexture(newtexture, true);
}
}
}
// Apply Texture
public virtual void ApplyTexture(string texture)
{
mode.CreateUndo("Change flat " + texture);
SetTexture(texture);
onTextureChanged(); //mxd
}
// Copy texture
public virtual void OnCopyTexture()
{
BuilderPlug.Me.CopiedFlat = GetTextureName();
if(General.Map.Config.MixTexturesFlats) BuilderPlug.Me.CopiedTexture = GetTextureName();
mode.SetActionResult("Copied flat " + GetTextureName() + ".");
}
public virtual void OnPasteTexture() { }
// Return texture name
public virtual string GetTextureName() { return ""; }
// Edit button released
public virtual void OnEditEnd()
{
if(General.Interface.IsActiveWindow)
{
List<Sector> sectors = mode.GetSelectedSectors();
DialogResult result = General.Interface.ShowEditSectors(sectors);
if(result == DialogResult.OK)
{
// Rebuild sector
foreach(Sector s in sectors)
{
if(mode.VisualSectorExists(s))
{
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(s);
vs.UpdateSectorGeometry(true);
}
}
}
}
}
// Sector height change
public virtual void OnChangeTargetHeight(int amount)
{
changed = true;
ChangeHeight(amount);
// Rebuild sector
BaseVisualSector vs;
if(mode.VisualSectorExists(level.sector)) {
vs = (BaseVisualSector)mode.GetVisualSector(level.sector);
} else {//mxd. Need this to apply changes to 3d-floor even if control sector doesn't exist as BaseVisualSector
vs = mode.CreateBaseVisualSector(level.sector);
}
if(vs != null) vs.UpdateSectorGeometry(true);
}
// Sector brightness change
public virtual void OnChangeTargetBrightness(bool up)
{
mode.CreateUndo("Change sector brightness", UndoGroup.SectorBrightnessChange, Sector.Sector.FixedIndex);
if(up)
Sector.Sector.Brightness = General.Map.Config.BrightnessLevels.GetNextHigher(Sector.Sector.Brightness);
else
Sector.Sector.Brightness = General.Map.Config.BrightnessLevels.GetNextLower(Sector.Sector.Brightness);
mode.SetActionResult("Changed sector brightness to " + Sector.Sector.Brightness + ".");
Sector.Sector.UpdateCache();
// Rebuild sector
Sector.UpdateSectorGeometry(false);
}
// Texture offset change
public virtual void OnChangeTextureOffset(int horizontal, int vertical)
{
//mxd
if (General.Map.UDMF) {
if ((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
undoticket = mode.CreateUndo("Change texture offsets");
// Apply offsets
MoveTextureOffset(new Point(-horizontal, -vertical));
mode.SetActionResult("Changed texture offsets by " + -horizontal + ", " + -vertical + ".");
// Update sector geometry
Sector.UpdateSectorGeometry(false);
Sector.Rebuild();
} else {
General.ShowErrorMessage("Floor/ceiling texture offsets cannot be changed in this map format!", MessageBoxButtons.OK);
}
}
#endregion
}
}