Newer
Older
#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.Generic;
using System.Drawing;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Types;
using CodeImp.DoomBuilder.VisualModes;
using CodeImp.DoomBuilder.GZBuilder.Tools;
using CodeImp.DoomBuilder.Data;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
internal sealed class VisualMiddleDouble : BaseVisualGeometrySidedef
{
#region ================== Constants
#endregion
#region ================== Variables
private bool repeatmidtex;
private Plane topclipplane;
private Plane bottomclipplane;
#endregion
#region ================== Properties
#endregion
#region ================== Constructor / Setup
// Constructor
public VisualMiddleDouble(BaseVisualMode mode, VisualSector vs, Sidedef s) : base(mode, vs, s)
{
geometrytype = VisualGeometryType.WALL_MIDDLE;
partname = "mid";
// Set render pass
this.RenderPass = RenderPass.Mask;
// We have no destructor
GC.SuppressFinalize(this);
}
// This builds the geometry. Returns false when no geometry created.
public override bool Setup()
{
if(Sidedef.LongMiddleTexture == MapSet.EmptyLongName) return false;
Vector2D vl, vr;
codeimp
committed
//mxd. lightfog flag support
MaxED
committed
int lightvalue;
bool lightabsolute;
GetLightValue(out lightvalue, out lightabsolute);
Vector2D tscale = new Vector2D(Sidedef.Fields.GetValue("scalex_mid", 1.0f),
Sidedef.Fields.GetValue("scaley_mid", 1.0f));
Vector2D toffset = new Vector2D(Sidedef.Fields.GetValue("offsetx_mid", 0.0f),
Sidedef.Fields.GetValue("offsety_mid", 0.0f));
MaxED
committed
if(Sidedef.IsFront)
{
vl = new Vector2D(Sidedef.Line.Start.Position.x, Sidedef.Line.Start.Position.y);
vr = new Vector2D(Sidedef.Line.End.Position.x, Sidedef.Line.End.Position.y);
MaxED
committed
}
else
{
vl = new Vector2D(Sidedef.Line.End.Position.x, Sidedef.Line.End.Position.y);
vr = new Vector2D(Sidedef.Line.Start.Position.x, Sidedef.Line.Start.Position.y);
}
// Load sector data
SectorData sd = mode.GetSectorData(Sidedef.Sector);
SectorData osd = mode.GetSectorData(Sidedef.Other.Sector);
if(!osd.Updated) osd.Update();
// Load texture
MaxED
committed
if(Sidedef.LongMiddleTexture != MapSet.EmptyLongName)
{
base.Texture = General.Map.Data.GetTextureImage(Sidedef.LongMiddleTexture);
MaxED
committed
if(base.Texture == null || base.Texture is UnknownImage)
{
base.Texture = General.Map.Data.UnknownTexture3D;
MaxED
committed
}
else if (!base.Texture.IsImageLoaded)
{
MaxED
committed
setuponloadedtexture = Sidedef.LongMiddleTexture;
MaxED
committed
}
else
{
// Use missing texture
base.Texture = General.Map.Data.MissingTexture3D;
setuponloadedtexture = 0;
// Get texture scaled size
Vector2D tsz = new Vector2D(base.Texture.ScaledWidth, base.Texture.ScaledHeight);
tsz = tsz / tscale;
// Get texture offsets
Vector2D tof = new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY);
tof = tof + toffset;
tof = tof / tscale;
if(General.Map.Config.ScaledTextureOffsets && !base.Texture.WorldPanning)
tof = tof * base.Texture.Scale;
// Determine texture coordinates plane as they would be in normal circumstances.
// We can then use this plane to find any texture coordinate we need.
// The logic here is the same as in the original VisualMiddleSingle (except that
// the values are stored in a TexturePlane)
// NOTE: I use a small bias for the floor height, because if the difference in
// height is 0 then the TexturePlane doesn't work!
TexturePlane tp = new TexturePlane();
float floorbias = (Sidedef.Sector.CeilHeight == Sidedef.Sector.FloorHeight) ? 1.0f : 0.0f;
MaxED
committed
float geotop = Math.Min(Sidedef.Sector.CeilHeight, Sidedef.Other.Sector.CeilHeight);
float geobottom = Math.Max(Sidedef.Sector.FloorHeight, Sidedef.Other.Sector.FloorHeight);
MaxED
committed
float zoffset = Sidedef.Sector.CeilHeight - Sidedef.Other.Sector.CeilHeight; //mxd
// When lower unpegged is set, the middle texture is bound to the bottom
if(Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag))
MaxED
committed
tp.tlt.y = tsz.y - (geotop - geobottom);
MaxED
committed
if (zoffset > 0) tp.tlt.y -= zoffset; //mxd
MaxED
committed
tp.trb.y = tp.tlt.y + (Sidedef.Sector.CeilHeight - (Sidedef.Sector.FloorHeight + floorbias));
// Apply texture offset
tp.tlt += tof;
tp.trb += tof;
// Transform pixel coordinates to texture coordinates
tp.tlt /= tsz;
tp.trb /= tsz;
// Left top and right bottom of the geometry that
MaxED
committed
tp.vlt = new Vector3D(vl.x, vl.y, Sidedef.Sector.CeilHeight);
tp.vrb = new Vector3D(vr.x, vr.y, Sidedef.Sector.FloorHeight + floorbias);
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Make the right-top coordinates
tp.trt = new Vector2D(tp.trb.x, tp.tlt.y);
tp.vrt = new Vector3D(tp.vrb.x, tp.vrb.y, tp.vlt.z);
// Keep top and bottom planes for intersection testing
top = sd.Ceiling.plane;
bottom = sd.Floor.plane;
// Create initial polygon, which is just a quad between floor and ceiling
WallPolygon poly = new WallPolygon();
poly.Add(new Vector3D(vl.x, vl.y, sd.Floor.plane.GetZ(vl)));
poly.Add(new Vector3D(vl.x, vl.y, sd.Ceiling.plane.GetZ(vl)));
poly.Add(new Vector3D(vr.x, vr.y, sd.Ceiling.plane.GetZ(vr)));
poly.Add(new Vector3D(vr.x, vr.y, sd.Floor.plane.GetZ(vr)));
// Determine initial color
int lightlevel = lightabsolute ? lightvalue : sd.Ceiling.brightnessbelow + lightvalue;
//mxd
PixelColor wallbrightness = PixelColor.FromInt(mode.CalculateBrightness(lightlevel, Sidedef));
PixelColor wallcolor = PixelColor.Modulate(sd.Ceiling.colorbelow, wallbrightness);
poly.color = wallcolor.WithAlpha(255).ToInt();
// Cut off the part below the other floor and above the other ceiling
CropPoly(ref poly, osd.Ceiling.plane, true);
CropPoly(ref poly, osd.Floor.plane, true);
// Determine if we should repeat the middle texture
MaxED
committed
repeatmidtex = Sidedef.IsFlagSet("wrapmidtex") || Sidedef.Line.IsFlagSet("wrapmidtex"); //mxd
MaxED
committed
if(!repeatmidtex)
{
if(Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag))
textop = geobottom + tof.y + Math.Abs(tsz.y);
textop = geotop + tof.y;
// Calculate bottom portion height
float texbottom = textop - Math.Abs(tsz.y);
// Create crop planes (we also need these for intersection testing)
topclipplane = new Plane(new Vector3D(0, 0, -1), textop);
bottomclipplane = new Plane(new Vector3D(0, 0, 1), -texbottom);
// Crop polygon by these heights
CropPoly(ref poly, topclipplane, true);
CropPoly(ref poly, bottomclipplane, true);
}
MaxED
committed
// Cut out pieces that overlap 3D floors in this sector
List<WallPolygon> polygons = new List<WallPolygon>(1);
polygons.Add(poly);
MaxED
committed
foreach(Effect3DFloor ef in sd.ExtraFloors)
{
MaxED
committed
//mxd. Walls should be clipped by solid 3D floors
MaxED
committed
if(!ef.RenderInside && ef.Alpha == 255)
{
MaxED
committed
int num = polygons.Count;
MaxED
committed
for(int pi = 0; pi < num; pi++)
{
MaxED
committed
// Split by floor plane of 3D floor
WallPolygon p = polygons[pi];
WallPolygon np = SplitPoly(ref p, ef.Ceiling.plane, true);
MaxED
committed
if(np.Count > 0)
{
MaxED
committed
// Split part below floor by the ceiling plane of 3D floor
// and keep only the part below the ceiling (front)
SplitPoly(ref np, ef.Floor.plane, true);
MaxED
committed
if(p.Count == 0)
{
MaxED
committed
polygons[pi] = np;
MaxED
committed
}
else
{
MaxED
committed
polygons[pi] = p;
polygons.Add(np);
}
MaxED
committed
}
else
{
MaxED
committed
polygons[pi] = p;
}
}
}
}
MaxED
committed
if(polygons.Count > 0)
{
// Keep top and bottom planes for intersection testing
top = osd.Ceiling.plane;
bottom = osd.Floor.plane;
// Process the polygon and create vertices
MaxED
committed
List<WorldVertex> verts = CreatePolygonVertices(polygons, tp, sd, lightvalue, lightabsolute);
MaxED
committed
if(verts.Count > 2)
{
// Apply alpha to vertices
byte alpha = SetLinedefRenderstyle(true);
MaxED
committed
if(alpha < 255)
{
for(int i = 0; i < verts.Count; i++)
{
WorldVertex v = verts[i];
PixelColor c = PixelColor.FromInt(v.c);
v.c = c.WithAlpha(alpha).ToInt();
verts[i] = v;
base.SetVertices(null); //mxd
return false;
}
#endregion
#region ================== Methods
// This performs a fast test in object picking
public override bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir)
{
if(!repeatmidtex)
{
MaxED
committed
// When the texture is not repeated, leave when outside crop planes
if((pickintersect.z < bottomclipplane.GetZ(pickintersect)) ||
(pickintersect.z > topclipplane.GetZ(pickintersect)))
return false;
}
return base.PickFastReject(from, to, dir);
}
MaxED
committed
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
//mxd. Alpha based picking
public override bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray)
{
if(!Texture.IsImageLoaded) return base.PickAccurate(from, to, dir, ref u_ray);
float u;
new Line2D(from, to).GetIntersection(Sidedef.Line.Line, out u);
if(Sidedef != Sidedef.Line.Front) u = 1.0f - u;
// Get correct vertical offset to texture space...
float zoffset;
if(repeatmidtex)
{
if(Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag))
zoffset = Sidedef.Sector.FloorHeight;
else
zoffset = Sidedef.Sector.CeilHeight;
}
else
{
zoffset = bottomclipplane.GetZ(pickintersect);
}
// Get offsets in texture space
int ox = (int)Math.Floor((u * Sidedef.Line.Length * UDMFTools.GetFloat(Sidedef.Fields, "scalex_mid", 1.0f) + Sidedef.OffsetX + UDMFTools.GetFloat(Sidedef.Fields, "offsetx_mid")) % Texture.Width);
int oy = (int)Math.Floor(((pickintersect.z - zoffset) * UDMFTools.GetFloat(Sidedef.Fields, "scaley_mid", 1.0f) - Sidedef.OffsetY - UDMFTools.GetFloat(Sidedef.Fields, "offsety_mid")) % Texture.Height);
if(ox < 0) ox = Texture.Width + ox;
if(oy < 0) oy = Texture.Height + oy;
// Check pixel alpha
if(Texture.GetBitmap().GetPixel(General.Clamp(ox, 0, Texture.Width - 1), General.Clamp(Texture.Height - oy, 0, Texture.Height - 1)).A > 0)
{
return base.PickAccurate(from, to, dir, ref u_ray);
}
return false;
}
// Return texture name
public override string GetTextureName()
{
return this.Sidedef.MiddleTexture;
}
// This changes the texture
protected override void SetTexture(string texturename)
{
this.Sidedef.SetTextureMid(texturename);
General.Map.Data.UpdateUsedTextures();
this.Setup();
}
protected override void SetTextureOffsetX(int x)
{
Sidedef.Fields.BeforeFieldsChange();
Sidedef.Fields["offsetx_mid"] = new UniValue(UniversalType.Float, (float)x);
}
protected override void SetTextureOffsetY(int y)
{
Sidedef.Fields.BeforeFieldsChange();
Sidedef.Fields["offsety_mid"] = new UniValue(UniversalType.Float, (float)y);
}
protected override void MoveTextureOffset(Point xy)
{
Sidedef.Fields.BeforeFieldsChange();
float oldx = Sidedef.Fields.GetValue("offsetx_mid", 0.0f);
float oldy = Sidedef.Fields.GetValue("offsety_mid", 0.0f);
float scalex = Sidedef.Fields.GetValue("scalex_mid", 1.0f);
float scaley = Sidedef.Fields.GetValue("scaley_mid", 1.0f);
MaxED
committed
Sidedef.Fields["offsetx_mid"] = new UniValue(UniversalType.Float, GetRoundedTextureOffset(oldx, xy.X, scalex, Texture != null ? Texture.Width : -1)); //mxd
MaxED
committed
//mxd. Don't clamp offsetY of clipped mid textures
bool dontClamp = (Texture == null || (!Sidedef.IsFlagSet("wrapmidtex") && !Sidedef.Line.IsFlagSet("wrapmidtex")));
MaxED
committed
Sidedef.Fields["offsety_mid"] = new UniValue(UniversalType.Float, GetRoundedTextureOffset(oldy, xy.Y, scaley, dontClamp ? -1 : Texture.Height));
}
protected override Point GetTextureOffset()
{
float oldx = Sidedef.Fields.GetValue("offsetx_mid", 0.0f);
float oldy = Sidedef.Fields.GetValue("offsety_mid", 0.0f);
return new Point((int)oldx, (int)oldy);
}
//mxd
MaxED
committed
protected override void ResetTextureScale()
{
Sidedef.Fields.BeforeFieldsChange();
if(Sidedef.Fields.ContainsKey("scalex_mid")) Sidedef.Fields.Remove("scalex_mid");
if(Sidedef.Fields.ContainsKey("scaley_mid")) Sidedef.Fields.Remove("scaley_mid");
}
public override void OnTextureFit(FitTextureOptions options)
MaxED
committed
{
if(!General.Map.UDMF) return;
if(string.IsNullOrEmpty(Sidedef.MiddleTexture) || Sidedef.MiddleTexture == "-" || !Texture.IsImageLoaded) return;
FitTexture(options);
Setup();
}
//mxd
MaxED
committed
public override void SelectNeighbours(bool select, bool withSameTexture, bool withSameHeight)
{
SelectNeighbours(Sidedef.MiddleTexture, select, withSameTexture, withSameHeight);