2010-09-10 13:17:38 +00:00
#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 ;
2016-04-19 20:40:42 +00:00
using System.Globalization ;
2010-09-10 13:17:38 +00:00
using CodeImp.DoomBuilder.Map ;
using CodeImp.DoomBuilder.Geometry ;
using CodeImp.DoomBuilder.Rendering ;
2010-10-03 12:59:49 +00:00
using CodeImp.DoomBuilder.Types ;
2010-09-10 13:17:38 +00:00
using CodeImp.DoomBuilder.VisualModes ;
2014-01-13 08:06:56 +00:00
using CodeImp.DoomBuilder.Data ;
2010-09-10 13:17:38 +00:00
#endregion
2012-11-27 21:12:20 +00:00
namespace CodeImp.DoomBuilder.BuilderModes
2010-09-10 13:17:38 +00:00
{
2016-04-19 20:40:42 +00:00
internal class VisualMiddle3D : BaseVisualGeometrySidedef
2010-09-10 13:17:38 +00:00
{
#region = = = = = = = = = = = = = = = = = = Constants
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
2010-09-11 20:14:36 +00:00
2016-04-19 20:40:42 +00:00
protected Effect3DFloor extrafloor ;
2010-09-10 13:17:38 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Setup
// Constructor
2010-09-14 19:14:44 +00:00
public VisualMiddle3D ( BaseVisualMode mode , VisualSector vs , Sidedef s ) : base ( mode , vs , s )
2010-09-10 13:17:38 +00:00
{
2013-09-11 09:47:53 +00:00
//mxd
2014-12-22 21:36:49 +00:00
geometrytype = VisualGeometryType . WALL_MIDDLE_3D ;
partname = "mid" ;
2013-09-11 09:47:53 +00:00
// We have no destructor
2010-09-10 13:17:38 +00:00
GC . SuppressFinalize ( this ) ;
}
// This builds the geometry. Returns false when no geometry created.
2010-09-14 19:14:44 +00:00
public override bool Setup ( ) { return this . Setup ( this . extrafloor ) ; }
public bool Setup ( Effect3DFloor extrafloor )
2010-09-10 13:17:38 +00:00
{
2010-09-11 20:14:36 +00:00
Sidedef sourceside = extrafloor . Linedef . Front ;
2010-09-14 19:14:44 +00:00
this . extrafloor = extrafloor ;
2010-09-15 20:41:40 +00:00
2015-11-08 20:20:46 +00:00
//mxd. Extrafloor may've become invalid during undo/redo...
2016-11-30 20:12:32 +00:00
if ( sourceside = = null )
{
base . SetVertices ( null ) ;
return false ;
}
2015-11-08 20:20:46 +00:00
Vector2D vl , vr ;
2021-10-21 18:25:56 +00:00
bool useuppertexture = ( sourceside . Line . Args [ 2 ] & ( int ) Effect3DFloor . Flags . UseUpperTexture ) = = ( int ) Effect3DFloor . Flags . UseUpperTexture ;
bool uselowertexture = ( sourceside . Line . Args [ 2 ] & ( int ) Effect3DFloor . Flags . UseLowerTexture ) = = ( int ) Effect3DFloor . Flags . UseLowerTexture ;
2013-09-11 09:47:53 +00:00
//mxd. lightfog flag support
2015-02-06 09:01:33 +00:00
int lightvalue ;
bool lightabsolute ;
GetLightValue ( out lightvalue , out lightabsolute ) ;
2010-09-15 20:41:40 +00:00
2021-10-21 18:25:56 +00:00
Vector2D tscale ;
Vector2D toffset2 = new Vector2D ( 0.0 , 0.0 ) ;
// If the upper or lower textures are used we have to take the scale of those, not of the source
if ( useuppertexture )
tscale = new Vector2D ( Sidedef . Fields . GetValue ( "scalex_top" , 1.0 ) , Sidedef . Fields . GetValue ( "scaley_top" , 1.0 ) ) ;
else if ( uselowertexture )
tscale = new Vector2D ( Sidedef . Fields . GetValue ( "scalex_bottom" , 1.0 ) , Sidedef . Fields . GetValue ( "scaley_bottom" , 1.0 ) ) ;
else
{
tscale = new Vector2D ( sourceside . Fields . GetValue ( "scalex_mid" , 1.0 ) , sourceside . Fields . GetValue ( "scaley_mid" , 1.0 ) ) ;
// The offset of the source side is only taken into account when not using the upper or lower textures
toffset2 = new Vector2D ( sourceside . Fields . GetValue ( "offsetx_mid" , 0.0 ) , sourceside . Fields . GetValue ( "offsety_mid" , 0.0 ) ) ;
}
Vector2D tscaleAbs = new Vector2D ( Math . Abs ( tscale . x ) , Math . Abs ( tscale . y ) ) ;
2020-05-22 19:39:18 +00:00
Vector2D toffset1 = new Vector2D ( Sidedef . Fields . GetValue ( "offsetx_mid" , 0.0 ) ,
Sidedef . Fields . GetValue ( "offsety_mid" , 0.0 ) ) ;
2021-10-21 18:25:56 +00:00
2010-09-10 13:17:38 +00:00
// Left and right vertices for this sidedef
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 ) ;
}
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 ) ;
2013-03-18 13:52:27 +00:00
//mxd. which texture we must use?
2016-04-19 20:40:42 +00:00
long texturelong = 0 ;
2021-10-21 18:25:56 +00:00
if ( useuppertexture )
2014-12-03 23:15:26 +00:00
{
2014-03-05 09:21:28 +00:00
if ( Sidedef . LongHighTexture ! = MapSet . EmptyLongName )
2016-04-19 20:40:42 +00:00
texturelong = Sidedef . LongHighTexture ;
2014-12-03 23:15:26 +00:00
}
2021-10-21 18:25:56 +00:00
else if ( uselowertexture )
2014-12-03 23:15:26 +00:00
{
2014-03-05 09:21:28 +00:00
if ( Sidedef . LongLowTexture ! = MapSet . EmptyLongName )
2016-04-19 20:40:42 +00:00
texturelong = Sidedef . LongLowTexture ;
2014-12-03 23:15:26 +00:00
}
else if ( sourceside . LongMiddleTexture ! = MapSet . EmptyLongName )
{
2016-04-19 20:40:42 +00:00
texturelong = sourceside . LongMiddleTexture ;
2013-03-18 13:52:27 +00:00
}
2010-09-10 13:17:38 +00:00
// Texture given?
2016-04-19 20:40:42 +00:00
if ( texturelong ! = 0 )
2010-09-10 13:17:38 +00:00
{
// Load texture
2016-04-19 20:40:42 +00:00
base . Texture = General . Map . Data . GetTextureImage ( texturelong ) ;
2014-01-13 08:06:56 +00:00
if ( base . Texture = = null | | base . Texture is UnknownImage )
2010-09-10 13:17:38 +00:00
{
2013-07-31 12:38:47 +00:00
base . Texture = General . Map . Data . UnknownTexture3D ;
2016-04-19 20:40:42 +00:00
setuponloadedtexture = texturelong ;
2010-09-10 13:17:38 +00:00
}
2016-04-19 20:40:42 +00:00
else if ( ! base . Texture . IsImageLoaded )
2010-09-10 13:17:38 +00:00
{
2016-04-19 20:40:42 +00:00
setuponloadedtexture = texturelong ;
2010-09-10 13:17:38 +00:00
}
}
else
{
// Use missing texture
base . Texture = General . Map . Data . MissingTexture3D ;
setuponloadedtexture = 0 ;
}
2020-07-11 16:43:49 +00:00
// Get texture scaled size. Round up, because that's apparently what GZDoom does
Vector2D tsz = new Vector2D ( Math . Ceiling ( base . Texture . ScaledWidth / tscale . x ) , Math . Ceiling ( base . Texture . ScaledHeight / tscale . y ) ) ;
2010-09-15 20:41:40 +00:00
// Get texture offsets
2023-05-26 11:26:15 +00:00
//Vector2D tof = new Vector2D(Sidedef.OffsetX, Sidedef.OffsetY) + new Vector2D(sourceside.OffsetX, sourceside.OffsetY);
Vector2D tof = new Vector2D ( Sidedef . OffsetX , 0.0f ) + new Vector2D ( 0.0f , sourceside . OffsetY ) ;
2019-04-14 16:24:37 +00:00
2010-09-15 20:41:40 +00:00
tof = tof + toffset1 + toffset2 ;
2019-04-14 16:24:37 +00:00
// biwa. Also take the ForceWorldPanning MAPINFO entry into account
if ( General . Map . Config . ScaledTextureOffsets & & ( ! base . Texture . WorldPanning & & ! General . Map . Data . MapInfo . ForceWorldPanning ) )
{
tof = tof / tscaleAbs ;
2010-09-15 20:41:40 +00:00
tof = tof * base . Texture . Scale ;
2020-04-02 20:46:40 +00:00
// If the texture gets replaced with a "hires" texture it adds more fuckery
if ( base . Texture is HiResImage )
tof * = tscaleAbs ;
2020-07-11 16:43:49 +00:00
// Round up, since that's apparently what GZDoom does. Not sure if this is the right place or if it also has to be done earlier
tof = new Vector2D ( Math . Ceiling ( tof . x ) , Math . Ceiling ( tof . y ) ) ;
2019-04-14 16:24:37 +00:00
}
2010-09-15 18:17:38 +00:00
// For Vavoom type 3D floors the ceiling is lower than floor and they are reversed.
// We choose here.
2020-05-22 20:30:32 +00:00
double sourcetopheight = extrafloor . VavoomType ? sourceside . Sector . FloorHeight : sourceside . Sector . CeilHeight ;
double sourcebottomheight = extrafloor . VavoomType ? sourceside . Sector . CeilHeight : sourceside . Sector . FloorHeight ;
2023-05-26 11:26:15 +00:00
2010-09-10 13:17:38 +00:00
// 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!
2023-05-26 11:26:15 +00:00
Vector3D vlt , vlb , vrt , vrb ;
Vector2D tlt , tlb , trt , trb ;
bool lowerunpegged = sourceside . Line . IsFlagSet ( General . Map . Config . LowerUnpeggedFlag ) ;
bool slopeskew = sourceside . Line . IsFlagSet ( General . Map . Config . SlopeSkewFlag ) ;
double topheight = slopeskew ? extrafloor . Ceiling . plane . GetZ ( vl ) : sourcetopheight ;
double bottomheight = slopeskew ? extrafloor . Floor . plane . GetZ ( vl ) : sourcebottomheight ;
double topheight2 = slopeskew ? extrafloor . Ceiling . plane . GetZ ( vr ) : sourcetopheight ;
double bottomheight2 = slopeskew ? extrafloor . Floor . plane . GetZ ( vr ) : sourcebottomheight ;
//float floorbias = (topheight == bottomheight) ? 1.0f : 0.0f;
//float floorbias2 = (topheight2 == bottomheight2) ? 1.0f : 0.0f;
tlt . x = tlb . x = 0 ;
trt . x = trb . x = Sidedef . Line . Length ;
// For SRB2, invert Lower Unpegged behavior for non-skewed 3D floors
tlt . y = ! ( lowerunpegged ^ slopeskew ) ? 0 : - ( topheight - bottomheight ) ;
trt . y = ! ( lowerunpegged ^ slopeskew ) ? 0 : - ( topheight2 - bottomheight2 ) ;
tlb . y = ! ( ! lowerunpegged ^ slopeskew ) ? 0 : ( topheight - bottomheight ) ;
trb . y = ! ( ! lowerunpegged ^ slopeskew ) ? 0 : ( topheight2 - bottomheight2 ) ;
2010-09-15 18:17:38 +00:00
2010-09-10 13:17:38 +00:00
// Apply texture offset
2023-05-26 11:26:15 +00:00
tlt + = tof ;
tlb + = tof ;
trb + = tof ;
trt + = tof ;
2010-09-10 13:17:38 +00:00
// Transform pixel coordinates to texture coordinates
2023-05-26 11:26:15 +00:00
tlt / = tsz ;
tlb / = tsz ;
trb / = tsz ;
trt / = tsz ;
// Geometry coordinates
vlt = new Vector3D ( vl . x , vl . y , topheight ) ;
vlb = new Vector3D ( vl . x , vl . y , bottomheight ) ;
vrb = new Vector3D ( vr . x , vr . y , bottomheight2 ) ;
vrt = new Vector3D ( vr . x , vr . y , topheight2 ) ;
TexturePlane tp = new TexturePlane ( ) ;
tp . tlt = lowerunpegged ? tlb : tlt ;
tp . trb = trb ;
tp . trt = trt ;
tp . vlt = lowerunpegged ? vlb : vlt ;
tp . vrb = vrb ;
tp . vrt = vrt ;
2013-03-18 13:52:27 +00:00
//mxd. Get ceiling and floor heights. Use our and neighbour sector's data
2020-05-21 12:20:02 +00:00
SectorData sdo = mode . GetSectorData ( Sidedef . Other . Sector ) ;
double flo = sdo . Floor . plane . GetZ ( vl ) ;
double fro = sdo . Floor . plane . GetZ ( vr ) ;
double clo = sdo . Ceiling . plane . GetZ ( vl ) ;
double cro = sdo . Ceiling . plane . GetZ ( vr ) ;
double fle = sd . Floor . plane . GetZ ( vl ) ;
double fre = sd . Floor . plane . GetZ ( vr ) ;
double cle = sd . Ceiling . plane . GetZ ( vl ) ;
double cre = sd . Ceiling . plane . GetZ ( vr ) ;
double fl = flo > fle ? flo : fle ;
double fr = fro > fre ? fro : fre ;
double cl = clo < cle ? clo : cle ;
double cr = cro < cre ? cro : cre ;
2010-09-10 13:17:38 +00:00
// Anything to see?
if ( ( ( cl - fl ) > 0.01f ) | | ( ( cr - fr ) > 0.01f ) )
{
// Keep top and bottom planes for intersection testing
2010-09-11 20:14:36 +00:00
top = extrafloor . Floor . plane ;
bottom = extrafloor . Ceiling . plane ;
2010-09-10 13:17:38 +00:00
// Create initial polygon, which is just a quad between floor and ceiling
WallPolygon poly = new WallPolygon ( ) ;
poly . Add ( new Vector3D ( vl . x , vl . y , fl ) ) ;
poly . Add ( new Vector3D ( vl . x , vl . y , cl ) ) ;
poly . Add ( new Vector3D ( vr . x , vr . y , cr ) ) ;
poly . Add ( new Vector3D ( vr . x , vr . y , fr ) ) ;
// Determine initial color
2010-09-15 20:41:40 +00:00
int lightlevel = lightabsolute ? lightvalue : sd . Ceiling . brightnessbelow + lightvalue ;
2015-10-02 14:47:34 +00:00
2012-07-05 00:03:40 +00:00
//mxd. This calculates light with doom-style wall shading
2013-09-11 09:47:53 +00:00
PixelColor wallbrightness = PixelColor . FromInt ( mode . CalculateBrightness ( lightlevel , Sidedef ) ) ;
2010-09-10 13:17:38 +00:00
PixelColor wallcolor = PixelColor . Modulate ( sd . Ceiling . colorbelow , wallbrightness ) ;
Fixed, Visual mode: in some cases ceiling glow effect was interfering with Transfer Brightness effect resulting in incorrectly lit sidedef geometry.
Fixed, Visual mode: UDMF sidedef brightness should be ignored when a wall section is affected by Transfer Brightness effect.
Fixed, Visual mode: any custom fog should be rendered regardless of sector brightness.
Fixed, Visual mode: "fogdensity" and "outsidefogdensity" MAPINFO values were processed incorrectly.
Fixed, Visual mode: in some cases Things were rendered twice during a render pass.
Fixed, Visual mode: floor glow effect should affect thing brightness only when applied to floor of the sector thing is in.
Fixed, TEXTURES parser: TEXTURES group was named incorrectly in the Textures Browser window when parsed from a WAD file.
Fixed, MAPINFO, GLDEFS, DECORATE parsers: "//$GZDB_SKIP" special comment was processed incorrectly.
Fixed, MAPINFO parser: "fogdensity" and "outsidefogdensity" properties are now initialized using GZDoom default value (255) instead of 0.
2016-01-25 13:42:53 +00:00
fogfactor = CalculateFogFactor ( lightlevel ) ;
2010-09-10 13:17:38 +00:00
poly . color = wallcolor . WithAlpha ( 255 ) . ToInt ( ) ;
// Cut off the part above the 3D floor and below the 3D ceiling
2010-09-11 20:14:36 +00:00
CropPoly ( ref poly , extrafloor . Floor . plane , false ) ;
CropPoly ( ref poly , extrafloor . Ceiling . plane , false ) ;
2010-09-16 20:51:11 +00:00
// Cut out pieces that overlap 3D floors in this sector
2015-11-20 14:31:54 +00:00
List < WallPolygon > polygons = new List < WallPolygon > { poly } ;
bool translucent = ( extrafloor . RenderAdditive | | extrafloor . Alpha < 255 ) ;
2010-09-16 20:51:11 +00:00
foreach ( Effect3DFloor ef in sd . ExtraFloors )
{
2015-11-20 14:31:54 +00:00
//mxd. Our poly should be clipped when our ond other extrafloors are both solid or both translucent,
// or when only our extrafloor is translucent.
// Our poly should not be clipped when our extrafloor is translucent and the other one isn't and both have renderinside setting.
bool othertranslucent = ( ef . RenderAdditive | | ef . Alpha < 255 ) ;
if ( translucent & & ! othertranslucent & & ! ef . ClipSidedefs ) continue ;
if ( ef . ClipSidedefs = = extrafloor . ClipSidedefs | | ef . ClipSidedefs )
2014-12-03 23:15:26 +00:00
{
Sectors, Linedefs, Things modes: optimized text label rendering.
Fixed, Things mode: in some cases selection labels were not updated after editing a thing.
Fixed, Things mode: selection labels were positioned incorrectly on things with FixedSize setting.
Fixed, Sectors mode: fixed a crash when selecting self-referencing sector when selection labels were enabled.
Fixed, Visual mode: in some cases Auto-align texture actions were not working when "use long texture names" Map Options setting was enabled.
Fixed, MD2/MD3 loader: available animation frames upper bound check was performed incorrectly, which would cause a crash in some very special cases.
Fixed, Game configurations: most Hexen/ZDoom teleport actions use TeleportDests as teleport targets, not MapSpots.
2016-04-05 22:24:36 +00:00
//TODO: find out why ef can be not updated at this point
//TODO: [this crashed on me once when performing auto-align on myriad of textures on BoA C1M0]
if ( ef . Floor = = null | | ef . Ceiling = = null ) ef . Update ( ) ;
2010-09-16 20:51:11 +00:00
int num = polygons . Count ;
for ( int pi = 0 ; pi < num ; pi + + )
{
// Split by floor plane of 3D floor
WallPolygon p = polygons [ pi ] ;
WallPolygon np = SplitPoly ( ref p , ef . Ceiling . plane , true ) ;
if ( np . Count > 0 )
{
// 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 ) ;
if ( p . Count = = 0 )
{
polygons [ pi ] = np ;
}
else
{
polygons [ pi ] = p ;
polygons . Add ( np ) ;
}
}
else
{
polygons [ pi ] = p ;
}
}
}
}
2010-09-10 13:17:38 +00:00
// Process the polygon and create vertices
2015-11-20 14:31:54 +00:00
if ( polygons . Count > 0 )
2010-09-10 13:17:38 +00:00
{
2014-10-28 09:23:21 +00:00
List < WorldVertex > verts = CreatePolygonVertices ( polygons , tp , sd , lightvalue , lightabsolute ) ;
2015-11-20 14:31:54 +00:00
if ( verts . Count > 2 )
2010-09-10 15:59:47 +00:00
{
2017-03-19 16:19:14 +00:00
if ( extrafloor . Sloped3dFloor ) this . RenderPass = RenderPass . Mask ; //mxd
else if ( extrafloor . RenderAdditive ) this . RenderPass = RenderPass . Additive ; //mxd
else if ( ( extrafloor . Alpha < 255 ) | | Texture . IsTranslucent ) this . RenderPass = RenderPass . Alpha ; // [ZZ] translucent texture should trigger Alpha pass
2015-11-20 14:31:54 +00:00
else this . RenderPass = RenderPass . Mask ;
2014-10-28 09:23:21 +00:00
2015-11-20 14:31:54 +00:00
if ( extrafloor . Alpha < 255 )
2010-09-10 15:59:47 +00:00
{
2014-10-28 09:23:21 +00:00
// Apply alpha to vertices
2015-11-20 14:31:54 +00:00
byte alpha = ( byte ) General . Clamp ( extrafloor . Alpha , 0 , 255 ) ;
if ( alpha < 255 )
2010-09-11 20:14:36 +00:00
{
2015-11-20 14:31:54 +00:00
for ( int i = 0 ; i < verts . Count ; i + + )
2014-10-28 09:23:21 +00:00
{
WorldVertex v = verts [ i ] ;
2015-11-20 14:31:54 +00:00
v . c = PixelColor . FromInt ( v . c ) . WithAlpha ( alpha ) . ToInt ( ) ;
2014-10-28 09:23:21 +00:00
verts [ i ] = v ;
}
2010-09-11 20:14:36 +00:00
}
2010-09-10 15:59:47 +00:00
}
2014-10-28 09:23:21 +00:00
base . SetVertices ( verts ) ;
return true ;
2010-09-10 15:59:47 +00:00
}
2010-09-10 13:17:38 +00:00
}
}
2014-10-28 09:23:21 +00:00
base . SetVertices ( null ) ; //mxd
2010-09-10 13:17:38 +00:00
return false ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Methods
2014-09-22 14:33:15 +00:00
// This performs a fast test in object picking (mxd)
public override bool PickFastReject ( Vector3D from , Vector3D to , Vector3D dir )
{
// Top and bottom are swapped in Vavoom-type 3d floors
2015-12-28 15:01:53 +00:00
if ( extrafloor . VavoomType )
2014-09-22 14:33:15 +00:00
return ( pickintersect . z > = top . GetZ ( pickintersect ) ) & & ( pickintersect . z < = bottom . GetZ ( pickintersect ) ) ;
return base . PickFastReject ( from , to , dir ) ;
}
2016-01-26 22:29:12 +00:00
//mxd. Alpha based picking
2020-05-21 12:20:02 +00:00
public override bool PickAccurate ( Vector3D from , Vector3D to , Vector3D dir , ref double u_ray )
2016-01-26 22:29:12 +00:00
{
2016-06-13 23:37:55 +00:00
if ( ! BuilderPlug . Me . AlphaBasedTextureHighlighting | | ! Texture . IsImageLoaded | | ( ! Texture . IsTranslucent & & ! Texture . IsMasked ) ) return base . PickAccurate ( from , to , dir , ref u_ray ) ;
2016-01-26 22:29:12 +00:00
2020-05-21 12:20:02 +00:00
double u ;
2016-01-26 22:29:12 +00:00
Sidedef sourceside = extrafloor . Linedef . Front ;
new Line2D ( from , to ) . GetIntersection ( Sidedef . Line . Line , out u ) ;
if ( Sidedef ! = Sidedef . Line . Front ) u = 1.0f - u ;
2016-03-23 21:26:26 +00:00
// Some textures (e.g. HiResImage) may lie about their size, so use bitmap size instead
2020-01-12 18:37:27 +00:00
int imageWidth = Texture . GetAlphaTestWidth ( ) ;
int imageHeight = Texture . GetAlphaTestHeight ( ) ;
2016-03-23 21:26:26 +00:00
2020-01-12 18:37:27 +00:00
// Determine texture scale...
Vector2D imgscale = new Vector2D ( ( float ) Texture . Width / imageWidth , ( float ) Texture . Height / imageHeight ) ;
Vector2D texscale = ( Texture is HiResImage ) ? imgscale * Texture . Scale : Texture . Scale ;
2017-05-09 03:06:07 +00:00
2020-05-21 12:20:02 +00:00
// Get correct offset to texture space...
double texoffsetx = Sidedef . OffsetX + sourceside . OffsetX + UniFields . GetFloat ( Sidedef . Fields , "offsetx_mid" ) + UniFields . GetFloat ( sourceside . Fields , "offsetx_mid" ) ;
2020-01-12 18:37:27 +00:00
int ox = ( int ) Math . Floor ( ( u * Sidedef . Line . Length * UniFields . GetFloat ( sourceside . Fields , "scalex_mid" , 1.0f ) / texscale . x + ( texoffsetx / imgscale . x ) ) % imageWidth ) ;
2017-05-09 03:06:07 +00:00
2020-05-21 12:20:02 +00:00
double texoffsety = Sidedef . OffsetY + sourceside . OffsetY + UniFields . GetFloat ( Sidedef . Fields , "offsety_mid" ) + UniFields . GetFloat ( sourceside . Fields , "offsety_mid" ) ;
2020-01-12 18:37:27 +00:00
int oy = ( int ) Math . Ceiling ( ( ( pickintersect . z - sourceside . Sector . CeilHeight ) * UniFields . GetFloat ( sourceside . Fields , "scaley_mid" , 1.0f ) / texscale . y - ( texoffsety / imgscale . y ) ) % imageHeight ) ;
2017-05-09 03:06:07 +00:00
2020-01-12 18:37:27 +00:00
// Make sure offsets are inside of texture dimensions...
if ( ox < 0 ) ox + = imageWidth ;
if ( oy < 0 ) oy + = imageHeight ;
2017-05-09 03:06:07 +00:00
2020-01-12 18:37:27 +00:00
// Check pixel alpha
Point pixelpos = new Point ( General . Clamp ( ox , 0 , imageWidth - 1 ) , General . Clamp ( imageHeight - oy , 0 , imageHeight - 1 ) ) ;
return ( Texture . AlphaTestPixel ( pixelpos . X , pixelpos . Y ) & & base . PickAccurate ( @from , to , dir , ref u_ray ) ) ;
2016-01-26 22:29:12 +00:00
}
2010-09-10 13:17:38 +00:00
// Return texture name
public override string GetTextureName ( )
{
2013-03-18 13:52:27 +00:00
//mxd
if ( ( extrafloor . Linedef . Args [ 2 ] & ( int ) Effect3DFloor . Flags . UseUpperTexture ) ! = 0 )
return Sidedef . HighTexture ;
if ( ( extrafloor . Linedef . Args [ 2 ] & ( int ) Effect3DFloor . Flags . UseLowerTexture ) ! = 0 )
return Sidedef . LowTexture ;
2010-09-11 20:14:36 +00:00
return extrafloor . Linedef . Front . MiddleTexture ;
2010-09-10 13:17:38 +00:00
}
// This changes the texture
protected override void SetTexture ( string texturename )
{
2013-03-18 13:52:27 +00:00
//mxd
2015-12-28 15:01:53 +00:00
if ( ( extrafloor . Linedef . Args [ 2 ] & ( int ) Effect3DFloor . Flags . UseUpperTexture ) ! = 0 )
2013-03-18 13:52:27 +00:00
Sidedef . SetTextureHigh ( texturename ) ;
2015-12-28 15:01:53 +00:00
if ( ( extrafloor . Linedef . Args [ 2 ] & ( int ) Effect3DFloor . Flags . UseLowerTexture ) ! = 0 )
2013-03-18 13:52:27 +00:00
Sidedef . SetTextureLow ( texturename ) ;
else
extrafloor . Linedef . Front . SetTextureMid ( texturename ) ;
2010-09-10 13:17:38 +00:00
General . Map . Data . UpdateUsedTextures ( ) ;
2013-03-18 13:52:27 +00:00
2016-04-19 20:40:42 +00:00
//mxd. Update model sector
2014-02-21 14:42:12 +00:00
mode . GetVisualSector ( extrafloor . Linedef . Front . Sector ) . UpdateSectorGeometry ( false ) ;
2010-09-10 13:17:38 +00:00
}
2010-10-03 12:59:49 +00:00
protected override void SetTextureOffsetX ( int x )
{
Sidedef . Fields . BeforeFieldsChange ( ) ;
2020-05-26 16:49:42 +00:00
Sidedef . Fields [ "offsetx_mid" ] = new UniValue ( UniversalType . Float , ( double ) x ) ;
2010-10-03 12:59:49 +00:00
}
protected override void SetTextureOffsetY ( int y )
{
Sidedef . Fields . BeforeFieldsChange ( ) ;
2020-05-26 16:49:42 +00:00
Sidedef . Fields [ "offsety_mid" ] = new UniValue ( UniversalType . Float , ( double ) y ) ;
2010-10-03 12:59:49 +00:00
}
2016-09-06 12:05:47 +00:00
protected override void MoveTextureOffset ( int offsetx , int offsety )
2010-10-03 12:59:49 +00:00
{
Sidedef . Fields . BeforeFieldsChange ( ) ;
2019-04-14 16:24:37 +00:00
bool worldpanning = this . Texture . WorldPanning | | General . Map . Data . MapInfo . ForceWorldPanning ;
2020-05-22 19:39:18 +00:00
double oldx = Sidedef . Fields . GetValue ( "offsetx_mid" , 0.0 ) ;
double oldy = Sidedef . Fields . GetValue ( "offsety_mid" , 0.0 ) ;
double scalex = extrafloor . Linedef . Front . Fields . GetValue ( "scalex_mid" , 1.0 ) ;
double scaley = extrafloor . Linedef . Front . Fields . GetValue ( "scaley_mid" , 1.0 ) ;
2019-04-14 16:24:37 +00:00
bool textureloaded = ( Texture ! = null & & Texture . IsImageLoaded ) ; //mxd
2020-05-22 19:39:18 +00:00
double width = textureloaded ? ( worldpanning ? this . Texture . ScaledWidth / scalex : this . Texture . Width ) : - 1 ; // biwa
double height = textureloaded ? ( worldpanning ? this . Texture . ScaledHeight / scaley : this . Texture . Height ) : - 1 ; // biwa
2019-04-14 16:24:37 +00:00
Sidedef . Fields [ "offsetx_mid" ] = new UniValue ( UniversalType . Float , GetNewTexutreOffset ( oldx , offsetx , width ) ) ; //mxd // biwa
Sidedef . Fields [ "offsety_mid" ] = new UniValue ( UniversalType . Float , GetNewTexutreOffset ( oldy , offsety , height ) ) ; //mxd // biwa
2010-10-03 12:59:49 +00:00
}
protected override Point GetTextureOffset ( )
{
2020-05-22 19:39:18 +00:00
double oldx = Sidedef . Fields . GetValue ( "offsetx_mid" , 0.0 ) ;
double oldy = Sidedef . Fields . GetValue ( "offsety_mid" , 0.0 ) ;
2010-10-03 12:59:49 +00:00
return new Point ( ( int ) oldx , ( int ) oldy ) ;
}
2012-06-28 20:27:48 +00:00
2013-09-11 09:47:53 +00:00
//mxd
2014-09-18 22:06:35 +00:00
public override Linedef GetControlLinedef ( )
{
2013-09-11 09:47:53 +00:00
return extrafloor . Linedef ;
}
2014-09-18 22:06:35 +00:00
2015-03-10 18:49:29 +00:00
//mxd
public override void OnTextureFit ( FitTextureOptions options )
{
if ( ! General . Map . UDMF ) return ;
if ( string . IsNullOrEmpty ( extrafloor . Linedef . Front . MiddleTexture ) | | extrafloor . Linedef . Front . MiddleTexture = = "-" | | ! Texture . IsImageLoaded ) return ;
FitTexture ( options ) ;
2016-04-19 20:40:42 +00:00
// Update the model sector to update all 3d floors
mode . GetVisualSector ( extrafloor . Linedef . Front . Sector ) . UpdateSectorGeometry ( false ) ;
}
//mxd. Only control sidedef scale is used by GZDoom
public override void OnChangeScale ( int incrementX , int incrementY )
{
2016-09-02 19:18:37 +00:00
if ( ! General . Map . UDMF | | Texture = = null | | ! Texture . IsImageLoaded ) return ;
2016-04-19 20:40:42 +00:00
if ( ( General . Map . UndoRedo . NextUndo = = null ) | | ( General . Map . UndoRedo . NextUndo . TicketID ! = undoticket ) )
undoticket = mode . CreateUndo ( "Change wall scale" ) ;
Sidedef target = extrafloor . Linedef . Front ;
if ( target = = null ) return ;
2020-05-22 19:39:18 +00:00
double scaleX = target . Fields . GetValue ( "scalex_mid" , 1.0 ) ;
double scaleY = target . Fields . GetValue ( "scaley_mid" , 1.0 ) ;
2016-04-19 20:40:42 +00:00
target . Fields . BeforeFieldsChange ( ) ;
if ( incrementX ! = 0 )
{
2020-05-22 19:39:18 +00:00
double pix = ( int ) Math . Round ( Texture . Width * scaleX ) - incrementX ;
double newscaleX = Math . Round ( pix / Texture . Width , 3 ) ;
2016-04-19 20:40:42 +00:00
scaleX = ( newscaleX = = 0 ? scaleX * - 1 : newscaleX ) ;
2020-05-22 19:39:18 +00:00
UniFields . SetFloat ( target . Fields , "scalex_mid" , scaleX , 1.0 ) ;
2016-04-19 20:40:42 +00:00
}
if ( incrementY ! = 0 )
{
2020-05-22 19:39:18 +00:00
double pix = ( int ) Math . Round ( Texture . Height * scaleY ) - incrementY ;
double newscaleY = Math . Round ( pix / Texture . Height , 3 ) ;
2016-04-19 20:40:42 +00:00
scaleY = ( newscaleY = = 0 ? scaleY * - 1 : newscaleY ) ;
2020-05-22 19:39:18 +00:00
UniFields . SetFloat ( target . Fields , "scaley_mid" , scaleY , 1.0 ) ;
2016-04-19 20:40:42 +00:00
}
// Update the model sector to update all 3d floors
mode . GetVisualSector ( extrafloor . Linedef . Front . Sector ) . UpdateSectorGeometry ( false ) ;
// Display result
mode . SetActionResult ( "Wall scale changed to " + scaleX . ToString ( "F03" , CultureInfo . InvariantCulture ) + ", " + scaleY . ToString ( "F03" , CultureInfo . InvariantCulture ) + " (" + ( int ) Math . Round ( Texture . Width / scaleX ) + " x " + ( int ) Math . Round ( Texture . Height / scaleY ) + ")." ) ;
}
//mxd
protected override void ResetTextureScale ( )
{
Sidedef target = extrafloor . Linedef . Front ;
target . Fields . BeforeFieldsChange ( ) ;
if ( target . Fields . ContainsKey ( "scalex_mid" ) ) target . Fields . Remove ( "scalex_mid" ) ;
if ( target . Fields . ContainsKey ( "scaley_mid" ) ) target . Fields . Remove ( "scaley_mid" ) ;
}
//mxd
public override void OnResetTextureOffset ( )
{
base . OnResetTextureOffset ( ) ;
// Update the model sector to update all 3d floors
mode . GetVisualSector ( extrafloor . Linedef . Front . Sector ) . UpdateSectorGeometry ( false ) ;
}
//mxd
public override void OnResetLocalTextureOffset ( )
{
2016-09-06 19:14:49 +00:00
if ( ! General . Map . UDMF | | ! General . Map . Config . UseLocalSidedefTextureOffsets )
2016-04-19 20:40:42 +00:00
{
OnResetTextureOffset ( ) ;
return ;
}
base . OnResetLocalTextureOffset ( ) ;
2015-03-10 18:49:29 +00:00
// Update the model sector to update all 3d floors
mode . GetVisualSector ( extrafloor . Linedef . Front . Sector ) . UpdateSectorGeometry ( false ) ;
}
2023-06-10 23:03:17 +00:00
// Texture offset change
public override void OnChangeTextureOffset ( int horizontal , int vertical , bool doSurfaceAngleCorrection )
{
if ( ( General . Map . UndoRedo . NextUndo = = null ) | | ( General . Map . UndoRedo . NextUndo . TicketID ! = undoticket ) )
undoticket = mode . CreateUndo ( "Change texture offsets" ) ;
//mxd
if ( General . Map . UDMF & & General . Map . Config . UseLocalSidedefTextureOffsets )
{
// Apply per-texture offsets
MoveTextureOffset ( - horizontal , - vertical ) ;
Point p = GetTextureOffset ( ) ;
mode . SetActionResult ( "Changed texture offsets to " + p . X + ", " + p . Y + "." ) ;
// Update this part only
this . Setup ( ) ;
}
else
{
//mxd. Apply classic offsets
Sidedef sourceside = extrafloor . Linedef . Front ;
bool textureloaded = ( Texture ! = null & & Texture . IsImageLoaded ) ;
Sidedef . OffsetX = ( Sidedef . OffsetX - horizontal ) ;
if ( textureloaded ) Sidedef . OffsetX % = Texture . Width ;
sourceside . OffsetY = ( sourceside . OffsetY - vertical ) ;
if ( geometrytype ! = VisualGeometryType . WALL_MIDDLE & & textureloaded ) sourceside . OffsetY % = Texture . Height ;
mode . SetActionResult ( "Changed texture offsets to " + Sidedef . OffsetX + ", " + sourceside . OffsetY + "." ) ;
// Update all sidedef geometry
VisualSidedefParts parts = Sector . GetSidedefParts ( Sidedef ) ;
parts . SetupAllParts ( ) ;
}
//mxd. Update linked effects
SectorData sd = mode . GetSectorDataEx ( Sector . Sector ) ;
if ( sd ! = null ) sd . Reset ( true ) ;
}
2010-09-10 13:17:38 +00:00
#endregion
}
}