Visual mode: "Fit Textures" action can now fit textures across multiple selected surfaces. A number of times to repeat a texture can now be specified.

Visual mode: removed "Fit Texture's Width" and "Fit Texture's Height" actions.
Visual mode: "Auto-align texture offsets" actions were incorrectly aligning double-sided middle walls in some cases.
Visual mode: "Auto-align texture offsets" actions now align non-wrapped double-sided middle walls to vertical offset closest to their initial vertical offset.
Visual mode: middle parts of double-sided walls were ignored when Shift-selecting walls.
Nodebuilders/Game configurations: GL nodes definitions were missing from game configurations.
Nodebuilders/Game configurations: "~MAP" wildcard can now be a part of a lump name. 
Nodebuilders: GL nodes were not properly handled by the editor.
Main Window: the window is now moved into the view when stored position is ouside of screen bounds. 
Classic and Visual modes: changing thing pitch was ignored in some cases.
Visual mode: raising and lowering a thing with "+SPAWNCEILING" flag now works the same way as when raising/lowering a regular thing.
Visual mode: using "Raise/Lower Floor/Ceiling to adjacent sector" actions on a thing with "+SPAWNCEILING" flag now works the same way as when using them on a regular thing.  
Rendering: even more fixes to MODELDEF and UDMF properties-related model rendering logic.
Internal, ResourceListEditor: rewritten resource validation check in a more OOP-ish way.
Configurations: fixed an infinite loop crash when a file was trying to include() itself.
UDMF thing flags: added Skill 6-8 to the flags list (because there are thing filters for these).
ZDoom_ACS.cfg: added definitions for SetTeleFog and SwapTeleFog.
ZDoom_DECORATE.cfg: added definitions for A_SetTeleFog and A_SwapTeleFog.
Updated ZDoom ACC.
Updated documentation.
This commit is contained in:
MaxED 2014-12-22 21:36:49 +00:00
parent 59f8bea419
commit deb43343bb
56 changed files with 1452 additions and 650 deletions

View file

@ -329,6 +329,8 @@ special
-83:PickActor(5,8),
-84:IsPointerEqual(2,4),
-85:CanRaiseActor(1),
-86:SetActorTeleFog(3),
-87:SwapActorTeleFog(1),
// Zandronum's
-100:ResetMap(0),

View file

@ -3,10 +3,11 @@ mapformat_doom
// The format interface handles the map data format
formatinterface = "DoomMapSetIO";
maplumpnames
{
include("Doom_misc.cfg", "doommaplumpnames");
}
maplumpnames
{
include("Doom_misc.cfg", "doommaplumpnames");
include("Boom_misc.cfg", "boommaplumpnames");
}
// When this is set to true, sectors with the same tag will light up when a line is highlighted
linetagindicatesectors = true;

View file

@ -35,25 +35,29 @@ thingflagstranslation
// How thing flags should be compared (for the stuck thing error check)
thingflagscompare
{
skills {
skills
{
1;
2;
4;
}
gamemodes {
16 {
//invert = true;
gamemodes
{
16
{
ignoredgroup = "skills";
ingnorethisgroupwhenunset = true;
}
32 {
32
{
invert = true;
requiredflag = "16";
}
64 {
64
{
invert = true;
requiredflag = "16";
requiredgroup = "skills";
@ -101,85 +105,8 @@ allowempty = The nodebuilder is allowed to leave this lump empty.
script = This lump is a text-based script. Specify the filename of the script configuration to use.
*/
doommaplumpnames
boommaplumpnames
{
~MAP
{
required = true;
blindcopy = true;
nodebuild = false;
}
THINGS
{
required = true;
nodebuild = true;
allowempty = true;
}
LINEDEFS
{
required = true;
nodebuild = true;
allowempty = false;
}
SIDEDEFS
{
required = true;
nodebuild = true;
allowempty = false;
}
VERTEXES
{
required = true;
nodebuild = true;
allowempty = false;
}
SEGS
{
required = false;
nodebuild = true;
allowempty = false;
}
SSECTORS
{
required = false;
nodebuild = true;
allowempty = false;
}
NODES
{
required = false;
nodebuild = true;
allowempty = false;
}
SECTORS
{
required = true;
nodebuild = true;
allowempty = false;
}
REJECT
{
required = false;
nodebuild = true;
allowempty = false;
}
BLOCKMAP
{
required = false;
nodebuild = true;
allowempty = false;
}
DEHACKED
{
required = false;

View file

@ -9,6 +9,9 @@ thingflags
skill3 = "Skill 3";
skill4 = "Skill 4";
skill5 = "Skill 5";
skill6 = "Skill 6";
skill7 = "Skill 7";
skill8 = "Skill 8";
ambush = "Deaf";
single = "Singleplayer";
dm = "Deathmatch";
@ -30,6 +33,9 @@ defaultthingflags
skill3;
skill4;
skill5;
skill6;
skill7;
skill8;
single;
coop;
dm;
@ -39,27 +45,36 @@ defaultthingflags
// How thing flags should be compared (for the stuck thing error check)
thingflagscompare
{
skills {
skills
{
skill1;
skill2;
skill3;
skill4;
skill5;
skill6;
skill7;
skill8;
}
gamemodes {
single {
gamemodes
{
single
{
requiredgroup = "skills";
}
coop {
coop
{
requiredgroup = "skills";
}
dm {
dm
{
ignoredgroup = "skills";
}
}
classes {
classes
{
class1;
class2;
class3;

View file

@ -90,6 +90,7 @@ mapformat_doom
{
include("Doom_misc.cfg", "doommaplumpnames");
include("ZDoom_misc.cfg", "doommaplumpnames");
include("ZDoom_misc.cfg", "glmaplumpnames");
}
// When this is set to true, sectors with the same tag will light up when a line is highlighted
@ -203,6 +204,7 @@ mapformat_hexen
{
include("Doom_misc.cfg", "hexenmaplumpnames");
include("ZDoom_misc.cfg", "hexenmaplumpnames");
include("ZDoom_misc.cfg", "glmaplumpnames");
}
// When this is set to true, sectors with the same tag will light up when a line is highlighted

View file

@ -110,6 +110,9 @@ defaultthingflags_udmf
skill3;
skill4;
skill5;
skill6;
skill7;
skill8;
single;
coop;
dm;
@ -123,10 +126,8 @@ defaultthingflags_udmf
// How thing flags should be compared (for the stuck thing error check)
thingflagscompare_udmf
{
skills {
skill6;
skill7;
skill8;
skills
{
skill9;
skill10;
skill11;
@ -137,7 +138,8 @@ thingflagscompare_udmf
skill16;
}
classes {
classes
{
class4;
class5;
class6;
@ -403,6 +405,52 @@ scriptbuild = This lump is a text-based script, which should be compiled using c
script = This lump is a text-based script. Specify the filename of the script configuration to use.
*/
// GL nodebuilders generate this stuff
glmaplumpnames
{
GL_~MAP
{
required = false;
nodebuild = true;
allowempty = true;
}
GL_VERT
{
required = false;
nodebuild = true;
allowempty = false;
}
GL_SEGS
{
required = false;
nodebuild = true;
allowempty = false;
}
GL_SSECT
{
required = false;
nodebuild = true;
allowempty = false;
}
GL_NODES
{
required = false;
nodebuild = true;
allowempty = false;
}
GL_PVS
{
required = false;
nodebuild = true;
allowempty = true;
}
}
doommaplumpnames
{
REJECT

View file

@ -397,6 +397,7 @@ keywords
SetPointer = "bool SetPointer(int assign_slot, int tid[, int pointer_selector[, int flags]])\nSet the value of one of the caller's stored pointers.";
SetResultValue = "void SetResultValue(int value)";
SetSkyScrollSpeed = "void SetSkyScrollSpeed(int sky, fixed skyspeed)\nChanges the scrolling speed of a sky.\nThis is useful in conjunction with ChangeSky.\nsky: either 1 or 2.\nskyspeed: the desired scrolling speed.";
SetTeleFog = "void SetTeleFog(int tid, str telefogsrcclass, str telefogdestclass";
SetThingSpecial = "void SetThingSpecial(int tid, int special [, int arg0 [, int arg1 [, int arg2 [, int arg3 [, int arg4]]]]])\nSets the special for any things with the same TID.\nThis is similar to Thing_SetSpecial, except it can only be used from ACS,\nand it can set all of a thing's special arguments.\nIf tid is 0, then the activator is used.";
SetUserArray = "void SetUserArray(int tid, str name, int pos, int value)\nSets one of the affected actor's user array-bound variables.";
SetUserCVar = "bool SetUserCVar(int playernumber, str cvar, int value)\nSets the console variable of a particular player.\nOnly mod-defined console variables through CVARINFO can be changed by using this function.\nReturns FALSE if cvar is invalid, it is not writable, or the player doesn't exist.";
@ -437,6 +438,7 @@ keywords
StrParam = "int StrParam(type:expression)\nStrParam will create a new string formatted based upon the same method for Print or Log.\nThe return value is the string table index of the new string.";
StrRight = "str StrRight(str string, int length)\nCreates a new string containing the length last characters of string.\nIf string does not exist, an empty string is returned.\nIf string is shorter than length characters, the entire string is returned.";
Suspend = "Suspend";
SwapTeleFog = "int SwapTeleFog(int tid)";
Switch = "Switch(expression)";
TagWait = "void TagWait(int tag)";
TakeActorInventory = "void TakeActorInventory(int tid, str inventory_item, int amount)\nThis function will take the amount of items from the specified actor.\nTakeActorInventory can remove items that are flagged as undroppable.";

View file

@ -55,6 +55,8 @@ keywords
A_RemoveTracer = "A_RemoveTracer[(int flags)]\nflags: RMVF flags.";
A_Remove = "A_Remove(int pointer, int flags)\nflags: RMVF flags.";
A_SentinelBob = "A_SentinelBob";
A_SetTeleFog = "A_SetTeleFog(string telefogsourceclass, string telefogdestclass)";
A_SwapTeleFog = "A_SwapTeleFog";
A_TurretLook = "A_TurretLook";
A_Teleport = "A_Teleport[(string teleportstate = \"Teleport\"[, string targettype = \"BossSpot\"[, string fogtype = \"TeleportFog\"[, int flags = 0[, float mindist = 0[, float maxdist = 0]]]]])]";
A_VileChase = "A_VileChase";

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -23,12 +23,16 @@
<b>Action category:</b> Tools.<br />
<b>Default key:</b> none.</p>
<p><strong>Usage:</strong><br />
Create a terrain model in your favorite 3d modeling app (<a href="http://www.blender.org/">Blender</a>, <a href="http://www.earthsculptor.com/">Earth Sculptor</a> or any other, which can save a model to Wavefront .obj format)<br />
Use <strong>File -&gt; Import -&gt; Wavefront .obj as Terrain</strong> action to import it as sectors. Each polygon in your model will be transformed into sector.</p>
Create a terrain model in your favorite 3d modeling app (<a href="http://www.blender.org/">Blender</a>, <a href="http://www.earthsculptor.com/">Earth Sculptor</a> or any other, which can save or export a model to Wavefront .obj format)<br />
Use <strong>File -&gt; Import -&gt; Wavefront .obj as Terrain</strong> action to import it as sectors. Each polygon in your model will be transformed into a sector.</p>
<strong>Settings:</strong><img style="float:right" src="import_terrain_settings.jpg"/><br />
<em>Scale:</em> self explanatory. The default is 1.0.<br />
<em>Up axis:</em> should be the same as vertical axis in the 3d modeling app used to create a terrain model.<br />
<em>Sloped terrain:</em> when enabled, the mode will generate sloped terrain using floor vertex height offsets (&quot;ZFloor&quot;) in UDMF or Vertex slope floor (1504) things. Regardless of this setting, the mode will also set each new sector's floor height to the averaged height of a polygon it was created from.<br />
<br />
<strong>Limitations:</strong><br />
The model should be triangulated.<br />
Polygons should not overlap each other when viewed from the top.<br />
<strong>UDMF only</strong>, because the mode uses vertex height offsets to create sloped floors (&quot;zfloor&quot; vertex field).
<p><img src="terrain1.jpg" alt="" width="842" height="722" /></p>
<p><img src="terrain2.jpg" alt="" width="842" height="632" /></p>
</div>

View file

@ -10,20 +10,23 @@
<param name="keyword" value="Template">
</object>
<div id="gz_title">
<h1>&quot;Fit Texture&quot; actions</h1>
<h1>&quot;Fit Textures&quot; action</h1>
</div>
<div id="contents">
<p><strong>Action names:</strong> Fit Texture, Fit Texture's Width, Fit Texture's Height<br />
<p><strong>Action name:</strong> Fit Textures.<br />
<b>Action category:</b> Visual Modes.<br />
<b>Default keys:</b> Ctrl-Alt-A, Alt-A, Alt-Shift-A.</p>
<p><strong>Example:</strong><br />
Initial scaling:<br />
<img src="texturefit1.jpg"/></p>
<p>After &quot;Fit Texture's Width&quot; action:<br />
<b>Default key:</b> Ctrl-Alt-A.</p>
<p>This action allows you to align textures to selected surfaces in Visual mode. &quot;<em>Fit across connected surfaces</em>&quot; setting controls whether adjacent surfaces sharing the same texture are threated as a continuous surface.</p>
<p><strong>Examples:</strong><br /><br />
Initial setup:<br />
<img src="texturefit1.jpg"/></p><br />
<p>Examples of various settings in action:<br />
<img src="texturefit2.jpg"/></p>
<p>After &quot;Fit Texture's Height&quot; action:<br />
<img src="texturefit3.jpg"/></p>
<p>After &quot;Fit Texture&quot; action:<br />
<img src="texturefit4.jpg"/></p>
<br />
<img src="texturefit3.jpg"/>
<br />
<img src="texturefit4.jpg"/>
<br />
<img src="texturefit5.jpg"/>
</div>
</body>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

View file

@ -113,8 +113,8 @@ namespace CodeImp.DoomBuilder.Compilers
}
//mxd
string outErr = process.StandardError.ReadToEnd();
string outMsg = process.StandardOutput.ReadToEnd();
string outErr = process.StandardError.ReadToEnd().Trim().Replace("\b", "");
string outMsg = process.StandardOutput.ReadToEnd().Trim().Replace("\b", "");
// Wait for compiler to complete
process.WaitForExit();

View file

@ -145,7 +145,7 @@ namespace CodeImp.DoomBuilder.Controls
resourceitems.Items[0].ForeColor = SystemColors.GrayText;
// Validate path (mxd)
resourceitems.Items[0].BackColor = (LocationValid(list[i]) ? resourceitems.BackColor : Color.MistyRose);
resourceitems.Items[0].BackColor = (list[i].IsValid() ? resourceitems.BackColor : Color.MistyRose);
}
// Done
@ -211,7 +211,7 @@ namespace CodeImp.DoomBuilder.Controls
resourceitems.Items[index].ForeColor = SystemColors.WindowText;
// Validate path (mxd)
resourceitems.Items[index].BackColor = (LocationValid(rl) ? resourceitems.BackColor : Color.MistyRose);
resourceitems.Items[index].BackColor = (rl.IsValid() ? resourceitems.BackColor : Color.MistyRose);
// Done
resourceitems.EndUpdate();
@ -249,23 +249,6 @@ namespace CodeImp.DoomBuilder.Controls
}
}
}
//mxd
internal static bool LocationValid(DataLocation location)
{
switch(location.type)
{
case DataLocation.RESOURCE_DIRECTORY:
return Directory.Exists(location.location);
case DataLocation.RESOURCE_WAD:
case DataLocation.RESOURCE_PK3:
return File.Exists(location.location);
default:
throw new NotImplementedException("ResourceListEditor.FixedResourceLocationList: got unknown location type: " + location.type);
}
}
// This fixes the column header in the list
private void ResizeColumnHeader()
@ -413,7 +396,7 @@ namespace CodeImp.DoomBuilder.Controls
{
foreach(ListViewItem item in resourceitems.Items)
{
if (!LocationValid((DataLocation) item.Tag)) return false;
if (!((DataLocation)item.Tag).IsValid()) return false;
}
return true;
}

View file

@ -17,6 +17,7 @@
#region ================== Namespaces
using System;
using System.IO;
#endregion
@ -71,5 +72,26 @@ namespace CodeImp.DoomBuilder.Data
{
return (this.CompareTo(other) == 0);
}
//mxd
public bool IsValid()
{
switch(type)
{
case RESOURCE_DIRECTORY:
if(!Directory.Exists(location)) return false;
break;
case RESOURCE_WAD:
case RESOURCE_PK3:
if(!File.Exists(location)) return false;
break;
default:
throw new NotImplementedException("ResourceListEditor.FixedResourceLocationList: got unknown location type: " + type);
}
return true;
}
}
}

View file

@ -16,9 +16,11 @@
#region ================== Namespaces
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using CodeImp.DoomBuilder.IO;
using System.Collections.Specialized;
@ -107,6 +109,13 @@ namespace CodeImp.DoomBuilder.Data
// Write to config
cfg.WriteSetting(path, resinfo);
}
//mxd
public bool IsValid()
{
foreach (DataLocation location in this) if (!location.IsValid()) return false;
return true;
}
#endregion
}

View file

@ -1,8 +1,8 @@
#region ================== Namespaces
using System.Collections.Generic;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.GZBuilder.MD3;
using CodeImp.DoomBuilder.Rendering;
using SlimDX;
using SlimDX.Direct3D9;
@ -15,6 +15,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data
#region ================== Variables
private ModelLoadState loadstate;
private Vector3 scale;
private Matrix transform;
private Matrix transformstretched;
#endregion
@ -25,14 +28,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data
internal GZModel Model;
internal Matrix Scale;
internal Vector2D OffsetXY;
internal float OffsetZ;
internal float AngleOffset; //in radians
internal float PitchOffset; //in radians
internal float RollOffset; //in radians
internal bool OverridePalette; //used for voxel models only
internal Vector3 Scale { get { return scale; } }
internal Matrix Transform { get { return (General.Settings.GZStretchView ? transformstretched : transform); } }
internal bool OverridePalette; //used for voxel models only
internal bool InheritActorPitch;
internal bool InheritActorRoll;
@ -48,8 +46,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data
{
ModelNames = new List<string>();
TextureNames = new List<string>();
Scale = Matrix.Identity;
OffsetXY = new Vector2D();
transform = Matrix.Identity;
transformstretched = Matrix.Identity;
}
internal void Dispose()
@ -62,6 +60,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data
}
}
internal void SetTransform(Matrix rotation, Matrix offset, Vector3 scale)
{
this.scale = scale;
this.transform = Matrix.Scaling(scale) * rotation * offset;
this.transformstretched = Matrix.Scaling(scale.X, scale.Y, scale.Z * Renderer3D.GZDOOM_INVERTED_VERTICAL_VIEW_STRETCH) * rotation * offset;
}
#endregion
}
}

View file

@ -409,14 +409,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
//classname is set in ModeldefParser
ModelData mde = new ModelData();
mde.Scale = Matrix.Scaling(scale);
mde.OffsetXY = new Vector2D(offset.Y, -offset.X); // Things are complicated in GZDoom...
mde.OffsetZ = offset.Z;
mde.AngleOffset = Angle2D.DegToRad(angleOffset);
mde.RollOffset = Angle2D.DegToRad(rollOffset);
mde.PitchOffset = Angle2D.DegToRad(pitchOffset);
mde.InheritActorPitch = inheritactorpitch;
mde.InheritActorRoll = inheritactorroll;
Matrix moffset = Matrix.Translation(offset.Y, -offset.X, offset.Z); // Things are complicated in GZDoom...
Matrix mrotation = Matrix.RotationY(inheritactorroll ? -Angle2D.DegToRad(rollOffset) : 0)
* Matrix.RotationX(inheritactorpitch ? -Angle2D.DegToRad(pitchOffset) : 0)
* Matrix.RotationZ(Angle2D.DegToRad(angleOffset));
mde.SetTransform(mrotation, moffset, scale);
for(int i = 0; i < modelNames.Length; i++)
{

View file

@ -734,12 +734,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
//create bounding box
BoundingBoxSizes bbs = new BoundingBoxSizes();
bbs.MinX = (short)((xsize / 2 - pivot.x) * mde.Scale.M11); //That should be scale x
bbs.MaxX = (short)((xsize / 2 + pivot.x) * mde.Scale.M11);
bbs.MinZ = (short)((zsize / 2 - pivot.z) * mde.Scale.M11);
bbs.MaxZ = (short)((zsize / 2 + pivot.z) * mde.Scale.M11);
bbs.MinY = (short)((ysize / 2 - pivot.y) * mde.Scale.M11);
bbs.MaxY = (short)((ysize / 2 + pivot.y) * mde.Scale.M11);
bbs.MinX = (short)((xsize / 2f - pivot.x) * mde.Scale.X);
bbs.MaxX = (short)((xsize / 2f + pivot.x) * mde.Scale.X);
bbs.MinZ = (short)((zsize / 2f - pivot.z) * mde.Scale.Z);
bbs.MaxZ = (short)((zsize / 2f + pivot.z) * mde.Scale.Z);
bbs.MinY = (short)((ysize / 2f - pivot.y) * mde.Scale.Y);
bbs.MaxY = (short)((ysize / 2f + pivot.y) * mde.Scale.Y);
mde.Model.BoundingBox = BoundingBoxTools.CalculateBoundingBox(bbs);

View file

@ -686,6 +686,7 @@ namespace CodeImp.DoomBuilder {
// Determine original map name
origmapname = (options.PreviousName != "" && purpose != SavePurpose.IntoFile) ? options.PreviousName : options.CurrentName;
string origwadfile = string.Empty; //mxd
try
{
@ -736,7 +737,7 @@ namespace CodeImp.DoomBuilder {
if (File.Exists(newfilepathname))
{
// Move the target file aside
string origwadfile = newfilepathname + ".temp";
origwadfile = newfilepathname + ".temp";
File.Move(newfilepathname, origwadfile);
// Open original file
@ -772,6 +773,7 @@ namespace CodeImp.DoomBuilder {
catch (IOException)
{
General.ShowErrorMessage("IO Error while writing target file: " + newfilepathname + ". Please make sure the location is accessible and not in use by another program.", MessageBoxButtons.OK);
if(!string.IsNullOrEmpty(origwadfile) && File.Exists(origwadfile)) File.Delete(origwadfile); //mxd. Clean-up
data.Resume();
General.WriteLogLine("Map saving failed");
return false;
@ -779,6 +781,7 @@ namespace CodeImp.DoomBuilder {
catch (UnauthorizedAccessException)
{
General.ShowErrorMessage("Error while accessing target file: " + newfilepathname + ". Please make sure the location is accessible and not in use by another program.", MessageBoxButtons.OK);
if(!string.IsNullOrEmpty(origwadfile) && File.Exists(origwadfile)) File.Delete(origwadfile); //mxd. Clean-up
data.Resume();
General.WriteLogLine("Map saving failed");
return false;
@ -923,6 +926,9 @@ namespace CodeImp.DoomBuilder {
// Determine source file
string sourcefile = (filepathname.Length > 0 ? filepathname : tempwad.Filename);
//mxd.
RemoveUnneededLumps(tempwad, TEMP_MAP_HEADER, true);
// Copy lumps to buildwad
General.WriteLogLine("Copying map lumps to temporary build file...");
CopyLumpsByType(tempwad, TEMP_MAP_HEADER, buildwad, BUILD_MAP_HEADER, true, false, false, true);
@ -976,7 +982,7 @@ namespace CodeImp.DoomBuilder {
//mxd. collect errors
string compilererrors = "";
foreach (CompilerError e in compiler.Errors)
compilererrors += "Error: " + Environment.NewLine + e.description;
compilererrors += Environment.NewLine + e.description;
// Nodebuilder did not build the lumps!
if (failaswarning)
@ -993,7 +999,7 @@ namespace CodeImp.DoomBuilder {
//collect errors
string compilererrors = "";
foreach (CompilerError e in compiler.Errors)
compilererrors += "Error: " + Environment.NewLine + e.description;
compilererrors += Environment.NewLine + e.description;
// Nodebuilder did not build the lumps!
General.ShowErrorMessage("Unable to build the nodes: The nodebuilder failed to build the expected data structures" + (compiler.Errors.Length > 0 ? ":" + Environment.NewLine + compilererrors : "."), MessageBoxButtons.OK);
@ -1023,10 +1029,14 @@ namespace CodeImp.DoomBuilder {
foreach (KeyValuePair<string, MapLumpInfo> group in config.MapLumps)
{
// Check if this lump should exist
if (group.Value.NodeBuild && !group.Value.AllowEmpty)
if(group.Value.NodeBuild && !group.Value.AllowEmpty && group.Value.Required)
{
//mxd
string lumpname = group.Key;
if (lumpname.Contains(CONFIG_MAP_HEADER)) lumpname = lumpname.Replace(CONFIG_MAP_HEADER, mapheader);
// Find the lump in the source
if(wad.FindLump(group.Key, srcindex, srcindex + config.MapLumps.Count + 2) == null)
if(wad.FindLump(lumpname, srcindex, srcindex + config.MapLumps.Count + 2) == null)
{
// Missing a lump!
lumpscomplete = false;
@ -1103,7 +1113,7 @@ namespace CodeImp.DoomBuilder {
if(group.Value.Required)
{
// Get the lump name
string lumpname = (group.Key == CONFIG_MAP_HEADER ? mapname : group.Key);
string lumpname = (group.Key.Contains(CONFIG_MAP_HEADER) ? group.Key.Replace(CONFIG_MAP_HEADER, mapname) : group.Key); //mxd
// Check if the lump is missing at the target
int targetindex = FindSpecificLump(target, lumpname, headerindex, mapname, config.MapLumps);
@ -1127,15 +1137,16 @@ namespace CodeImp.DoomBuilder {
}
//mxd. This is called on tempwad, which should only have the current map inside it.
private void RemoveUnneededLumps(WAD target, string mapname)
private void RemoveUnneededLumps(WAD target, string mapname, bool glnodesonly)
{
//Get the list of lumps required by current map format
List<string> requiredLumps = new List<string>();
foreach(KeyValuePair<string, MapLumpInfo> group in config.MapLumps)
{
if(group.Value.NodeBuild) continue; //this lump well be recreated by a nodebuilder when saving the map
//(or it won't be if the new map format doesn't require this lump,
//so it will just stay there, possibly messing things up)
//this lump well be recreated by a nodebuilder when saving the map
//(or it won't be if the new map format or nodebuilder doesn't require / build this lump,
//so it will just stay there, possibly messing things up)
if(group.Value.NodeBuild && (!glnodesonly || group.Key.ToUpperInvariant().StartsWith("GL_"))) continue;
string lumpname = group.Key;
if(lumpname == CONFIG_MAP_HEADER) lumpname = mapname;
@ -1158,10 +1169,17 @@ namespace CodeImp.DoomBuilder {
foreach (Lump srclump in source.Lumps)
{
// Check if we should stop skipping lumps here
if (skipping && !mapconfig.MapLumps.ContainsKey(srclump.Name))
if (skipping)
{
// Stop skipping
skipping = false;
//mxd
string srclumpname = srclump.Name;
if (srclumpname.Contains(sourcemapname)) srclumpname = srclumpname.Replace(sourcemapname, CONFIG_MAP_HEADER);
if (!mapconfig.MapLumps.ContainsKey(srclumpname))
{
// Stop skipping
skipping = false;
}
}
// Check if we should start skipping lumps here
@ -1211,8 +1229,8 @@ namespace CodeImp.DoomBuilder {
(group.Value.NodeBuild && copynodebuild) || ((group.Value.Script != null || group.Value.ScriptBuild) && copyscript))
{
// Get the lump name
string srclumpname = (group.Key == CONFIG_MAP_HEADER ? sourcemapname : group.Key);
string tgtlumpname = (group.Key == CONFIG_MAP_HEADER ? targetmapname : group.Key);
string srclumpname = (group.Key.Contains(CONFIG_MAP_HEADER) ? group.Key.Replace(CONFIG_MAP_HEADER, sourcemapname) : group.Key); //mxd
string tgtlumpname = (group.Key.Contains(CONFIG_MAP_HEADER) ? group.Key.Replace(CONFIG_MAP_HEADER, targetmapname) : group.Key); //mxd
// Find the lump in the source
int sourceindex = FindSpecificLump(source, srclumpname, srcheaderindex, sourcemapname, config.MapLumps);
@ -1249,7 +1267,7 @@ namespace CodeImp.DoomBuilder {
// This finds a lump within the range of known lump names
// Returns -1 when the lump cannot be found
internal static int FindSpecificLump(WAD source, string lumpname, int mapheaderindex, string mapheadername, Dictionary<string, MapLumpInfo> maplumps)
private static int FindSpecificLump(WAD source, string lumpname, int mapheaderindex, string mapheadername, Dictionary<string, MapLumpInfo> maplumps)
{
// Use the configured map lump names to find the specific lump within range,
// because when an unknown lump is met, this search must stop.
@ -1261,8 +1279,10 @@ namespace CodeImp.DoomBuilder {
if ((mapheaderindex + i) < source.Lumps.Count)
{
// Check if this is a known lump name
if (maplumps.ContainsKey(source.Lumps[mapheaderindex + i].Name) ||
(maplumps.ContainsKey(CONFIG_MAP_HEADER) && (source.Lumps[mapheaderindex + i].Name == mapheadername)))
string srclumpname = source.Lumps[mapheaderindex + i].Name; //mxd
if (srclumpname.Contains(mapheadername)) srclumpname = srclumpname.Replace(mapheadername, CONFIG_MAP_HEADER);
if (maplumps.ContainsKey(srclumpname)) //mxd
{
// Is this the lump we are looking for?
if (source.Lumps[mapheaderindex + i].Name == lumpname)
@ -1897,7 +1917,7 @@ namespace CodeImp.DoomBuilder {
//mxd. Some lumps may've become unneeded during map format conversion.
if(oldFormatInterface != config.FormatInterface)
RemoveUnneededLumps(tempwad, TEMP_MAP_HEADER);
RemoveUnneededLumps(tempwad, TEMP_MAP_HEADER, false);
// Create required lumps if they don't exist yet
CreateRequiredLumps(tempwad, TEMP_MAP_HEADER);

View file

@ -27,6 +27,7 @@ using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Types;
using System.Windows.Forms;
using CodeImp.DoomBuilder.GZBuilder.Tools;
using CodeImp.DoomBuilder.VisualModes;
#endregion
@ -1799,6 +1800,26 @@ namespace CodeImp.DoomBuilder.Geometry
((sd.LongMiddleTexture == texturelongname) && (sd.MiddleRequired() || sd.LongMiddleTexture != MapSet.EmptyLongName)) ;
}
//mxd. This converts offsetY from/to "normalized" offset for given wall part
public static float GetSidedefOffsetY(Sidedef side, VisualGeometryType part, float offset, float scaleY, bool fromNormalized)
{
switch (part)
{
case VisualGeometryType.WALL_UPPER:
return GetSidedefTopOffsetY(side, offset, scaleY, fromNormalized);
case VisualGeometryType.WALL_MIDDLE:
case VisualGeometryType.WALL_MIDDLE_3D:
return GetSidedefMiddleOffsetY(side, offset, scaleY, fromNormalized);
case VisualGeometryType.WALL_LOWER:
return GetSidedefBottomOffsetY(side, offset, scaleY, fromNormalized);
default:
throw new NotSupportedException("Tools.GetSidedefOffsetY: '" + part + "' geometry type is not supported!");
}
}
//mxd. This converts offsetY from/to "normalized" offset for given upper wall
public static float GetSidedefTopOffsetY(Sidedef side, float offset, float scaleY, bool fromNormalized)
{
@ -1806,7 +1827,7 @@ namespace CodeImp.DoomBuilder.Geometry
return offset;
//if we don't have UpperUnpegged flag, normalize offset
float surfaceHeight = (side.Sector.CeilHeight - side.Other.Sector.CeilHeight) * scaleY;
float surfaceHeight = side.GetHighHeight() * scaleY;
if(fromNormalized) return (float)Math.Round(offset + surfaceHeight);
return (float)Math.Round(offset - surfaceHeight);
@ -1815,13 +1836,38 @@ namespace CodeImp.DoomBuilder.Geometry
//mxd. This converts offsetY from/to "normalized" offset for given middle wall
public static float GetSidedefMiddleOffsetY(Sidedef side, float offset, float scaleY, bool fromNormalized)
{
if(!side.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag) || side.Sector == null)
return offset;
if(side.Sector == null) return offset;
// If we have LowerUnpegged flag, normalize offset
// Absolute value is used because ceiling height of vavoom-type 3d floors
// is lower than floor height
float surfaceHeight = (Math.Abs(side.Sector.CeilHeight - side.Sector.FloorHeight)) * scaleY;
// Normalize offset
float surfaceHeight;
if(side.Other != null && side.Other.Sector != null)
{
if(side.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag))
{
// Double-sided with LowerUnpeggedFlag set
surfaceHeight = (side.Sector.CeilHeight - Math.Max(side.Sector.FloorHeight, side.Other.Sector.FloorHeight)) * scaleY;
}
else
{
// Double-sided without LowerUnpeggedFlag
surfaceHeight = Math.Abs(side.Sector.CeilHeight - side.Other.Sector.CeilHeight) * scaleY;
}
}
else
{
if(side.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag))
{
// Single-sided with LowerUnpeggedFlag set
// Absolute value is used because ceiling height of vavoom-type 3d floors
// is lower than floor height
surfaceHeight = (Math.Abs(side.Sector.CeilHeight - side.Sector.FloorHeight)) * scaleY;
}
else
{
// Single-sided without LowerUnpeggedFlag
return offset;
}
}
if(fromNormalized) return (float)Math.Round(offset + surfaceHeight);
return (float)Math.Round(offset - surfaceHeight);

View file

@ -839,6 +839,7 @@ namespace CodeImp.DoomBuilder.IO
if(args.Count < 1) RaiseError(file, line, ERROR_INVALID_ARGS);
if(!(args[0] is string)) RaiseError(file, line, ERROR_INVALID_ARGS + " Expected a string for argument 1.");
if((args.Count > 1) && !(args[1] is string)) RaiseError(file, line, ERROR_INVALID_ARGS + " Expected a string for argument 2.");
if(args[0].ToString().ToUpperInvariant() == Path.GetFileName(file).ToUpperInvariant()) RaiseError(file, line, " A file cannot call include() on itself."); //mxd
if(cpErrorResult) return;
// Determine the full path of the file to include

View file

@ -78,7 +78,7 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Properties
public MapSet Map { get { return map; } }
public int Type { get { return type; } set { BeforePropsChange(); type = value; /*UpdateCache();*/ } } //mxd
public int Type { get { return type; } set { BeforePropsChange(); type = value; } } //mxd
public Vector3D Position { get { return pos; } }
public float ScaleX { get { return scaleX; } } //mxd. This is UDMF property, not actual scale!
public float ScaleY { get { return scaleY; } } //mxd. This is UDMF property, not actual scale!
@ -431,7 +431,7 @@ namespace CodeImp.DoomBuilder.Map
BeforePropsChange();
pitch = p;
pitchrad = ((isModel && General.Map.Data.ModeldefEntries[type].InheritActorRoll) ? Angle2D.DegToRad(pitch) : 0);
pitchrad = ((isModel && General.Map.Data.ModeldefEntries[type].InheritActorPitch) ? Angle2D.DegToRad(pitch) : 0);
if (type != General.Map.Config.Start3DModeThingType)
General.Map.IsChanged = true;
@ -444,7 +444,6 @@ namespace CodeImp.DoomBuilder.Map
roll = r;
rollrad = ((isModel && General.Map.Data.ModeldefEntries[type].InheritActorRoll) ? Angle2D.DegToRad(roll) : 0);
//rotation = Matrix.RotationYawPitchRoll(rollrad, pitchrad, anglerad); //mxd
if (type != General.Map.Config.Start3DModeThingType)
General.Map.IsChanged = true;

View file

@ -1339,7 +1339,7 @@ namespace CodeImp.DoomBuilder.Rendering
foreach(Thing t in group.Value)
{
if(General.Settings.GZDrawModelsMode == ModelRenderMode.SELECTION && !t.Selected) continue;
Vector2D screenpos = ((Vector2D)t.Position + General.Map.Data.ModeldefEntries[t.Type].OffsetXY.GetScaled(t.ScaleX).GetRotated(t.Angle)).GetTransformed(translatex, translatey, scale, -scale);
Vector2D screenpos = ((Vector2D)t.Position).GetTransformed(translatex, translatey, scale, -scale);
float modelScale = scale * t.ActorScale.Width * t.ScaleX;
//should we render this model?
@ -1355,12 +1355,9 @@ namespace CodeImp.DoomBuilder.Rendering
float sy = t.ScaleY * t.ActorScale.Height;
Matrix modelscale = Matrix.Scaling(sx, sx, sy);
Matrix mdescale = General.Map.Data.ModeldefEntries[t.Type].Scale;
Matrix rotation = Matrix.RotationY(-(t.RollRad + General.Map.Data.ModeldefEntries[t.Type].RollOffset))
* Matrix.RotationX(-(t.PitchRad + General.Map.Data.ModeldefEntries[t.Type].PitchOffset))
* Matrix.RotationZ(t.Angle + General.Map.Data.ModeldefEntries[t.Type].AngleOffset);
Matrix rotation = Matrix.RotationY(-t.RollRad) * Matrix.RotationX(-t.PitchRad) * Matrix.RotationZ(t.Angle);
Matrix position = Matrix.Translation(screenpos.x, screenpos.y, 0.0f);
Matrix world = mdescale * rotation * modelscale * viewscale * position;
Matrix world = General.Map.Data.ModeldefEntries[t.Type].Transform * rotation * modelscale * viewscale * position;
graphics.Shaders.Things2D.SetTransformSettings(world);
graphics.Shaders.Things2D.ApplySettings();

View file

@ -42,7 +42,8 @@ namespace CodeImp.DoomBuilder.Rendering
private const float PROJ_NEAR_PLANE = 1f;
private const float CROSSHAIR_SCALE = 0.06f;
private const float FOG_RANGE = 0.9f;
private const float INVERTED_VERTICAL_STRETCH = 1.0f / 1.2f;
internal const float GZDOOM_VERTICAL_VIEW_STRETCH = 1.2f;
internal const float GZDOOM_INVERTED_VERTICAL_VIEW_STRETCH = 1.0f / GZDOOM_VERTICAL_VIEW_STRETCH;
#endregion
@ -55,7 +56,6 @@ namespace CodeImp.DoomBuilder.Rendering
private Matrix worldviewproj;
private Matrix view2d;
private Matrix world;
private float viewstretch; //mxd
private Vector3D cameraposition;
private int shaderpass;
@ -257,7 +257,7 @@ namespace CodeImp.DoomBuilder.Rendering
internal void CreateProjection()
{
// Calculate aspect
float screenheight = General.Map.Graphics.RenderTarget.ClientSize.Height * (General.Settings.GZStretchView ? INVERTED_VERTICAL_STRETCH : 1.0f); //mxd
float screenheight = General.Map.Graphics.RenderTarget.ClientSize.Height * (General.Settings.GZStretchView ? GZDOOM_INVERTED_VERTICAL_VIEW_STRETCH : 1.0f); //mxd
float aspect = General.Map.Graphics.RenderTarget.ClientSize.Width / screenheight;
// The DirectX PerspectiveFovRH matrix method calculates the scaling in X and Y as follows:
@ -273,9 +273,6 @@ namespace CodeImp.DoomBuilder.Rendering
// Make the projection matrix
projection = Matrix.PerspectiveFovRH(fovy, aspect, PROJ_NEAR_PLANE, General.Settings.ViewDistance);
// Update the view stretch scaler (mxd)
viewstretch = (General.Settings.GZStretchView ? INVERTED_VERTICAL_STRETCH : 1.0f);
// Apply matrices
ApplyMatrices3D();
}
@ -1121,19 +1118,12 @@ namespace CodeImp.DoomBuilder.Rendering
// Create the matrix for positioning / rotation
float sx = t.Thing.ScaleX * t.Thing.ActorScale.Width;
float sy = t.Thing.ScaleY * t.Thing.ActorScale.Height * viewstretch;
float sy = t.Thing.ScaleY * t.Thing.ActorScale.Height;
Matrix modelscale = Matrix.Scaling(sx, sx, sy);
Matrix mdescale = General.Map.Data.ModeldefEntries[t.Thing.Type].Scale;
Matrix modelrotation = Matrix.RotationY(-t.Thing.RollRad) * Matrix.RotationX(-t.Thing.PitchRad) * Matrix.RotationZ(t.Thing.Angle);
Matrix rotation = Matrix.RotationY(-(t.Thing.RollRad + General.Map.Data.ModeldefEntries[t.Thing.Type].RollOffset))
* Matrix.RotationX(-(t.Thing.PitchRad + General.Map.Data.ModeldefEntries[t.Thing.Type].PitchOffset))
* Matrix.RotationZ(t.Thing.Angle + General.Map.Data.ModeldefEntries[t.Thing.Type].AngleOffset);
Vector2D offset2d = General.Map.Data.ModeldefEntries[t.Thing.Type].OffsetXY.GetScaled(t.Thing.ScaleX).GetRotated(t.Thing.Angle);
Matrix position = Matrix.Translation(offset2d.x, offset2d.y, General.Map.Data.ModeldefEntries[t.Thing.Type].OffsetZ * t.Thing.ScaleY) * t.Position;
world = mdescale * rotation * modelscale * position;
world = General.Map.Data.ModeldefEntries[t.Thing.Type].Transform * modelrotation * modelscale * t.Position;
ApplyMatrices3D();
//mxd. set variables for fog rendering

View file

@ -69,7 +69,8 @@ namespace CodeImp.DoomBuilder.VisualModes
//mxd
private Vector3[] boundingBox;
protected VisualGeometryType geoType;
protected VisualGeometryType geometrytype;
protected string partname; //UDMF part name
#endregion
@ -83,7 +84,7 @@ namespace CodeImp.DoomBuilder.VisualModes
//mxd
public Vector3[] BoundingBox { get { return boundingBox; } }
public VisualGeometryType GeometryType { get { return geoType; } }
public VisualGeometryType GeometryType { get { return geometrytype; } }
/// <summary>
/// Render pass in which this geometry must be rendered. Default is Solid.
@ -126,8 +127,7 @@ namespace CodeImp.DoomBuilder.VisualModes
{
this.sector = vs;
this.ModulateColor = new PixelColor(255, 255, 255, 255);
//mxd
geoType = VisualGeometryType.UNKNOWN;
this.geometrytype = VisualGeometryType.UNKNOWN; //mxd
}
/// <summary>
@ -139,8 +139,7 @@ namespace CodeImp.DoomBuilder.VisualModes
this.sector = vs;
this.sidedef = sd;
this.ModulateColor = new PixelColor(255, 255, 255, 255);
//mxd
geoType = VisualGeometryType.UNKNOWN;
this.geometrytype = VisualGeometryType.UNKNOWN; //mxd
}
#endregion

View file

@ -360,16 +360,13 @@ namespace CodeImp.DoomBuilder.Windows
{
// Get configuration item
ci = listconfigs.Items[i].Tag as ConfigurationInfo;
foreach (DataLocation location in ci.Resources)
if(!ci.Resources.IsValid())
{
if (!ResourceListEditor.LocationValid(location))
{
MessageBox.Show(this, "At least one resource doesn't exist in '" + ci.Name + "' game configuration!", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
tabs.SelectedTab = tabresources;
listconfigs.Focus();
listconfigs.Items[i].Selected = true;
return;
}
MessageBox.Show(this, "At least one resource doesn't exist in '" + ci.Name + "' game configuration!", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
tabs.SelectedTab = tabresources;
listconfigs.Focus();
listconfigs.Items[i].Selected = true;
return;
}
}

View file

@ -508,10 +508,14 @@ namespace CodeImp.DoomBuilder.Windows
{
// Position window from configuration settings
this.SuspendLayout();
this.Location = new Point(General.Settings.ReadSetting("mainwindow.positionx", this.Location.X),
General.Settings.ReadSetting("mainwindow.positiony", this.Location.Y));
this.Size = new Size(General.Settings.ReadSetting("mainwindow.sizewidth", this.Size.Width),
General.Settings.ReadSetting("mainwindow.sizeheight", this.Size.Height));
this.Location = new Point(General.Clamp(General.Settings.ReadSetting("mainwindow.positionx", this.Location.X),
SystemInformation.VirtualScreen.Left - this.Size.Width + 128,
SystemInformation.VirtualScreen.Right - 128),
General.Clamp(General.Settings.ReadSetting("mainwindow.positiony", this.Location.Y),
SystemInformation.VirtualScreen.Top,
SystemInformation.VirtualScreen.Bottom - 128));
this.WindowState = (FormWindowState)General.Settings.ReadSetting("mainwindow.windowstate", (int)FormWindowState.Maximized);
this.ResumeLayout(true);

View file

@ -64,8 +64,10 @@ namespace CodeImp.DoomBuilder.ZDoom
}
else if(token == "{") //read the settings
{
ModelData data = new ModelData();
data.IsVoxel = true;
ModelData mde = new ModelData();
mde.IsVoxel = true;
float scale = 1.0f;
float angleoffset = 0;
while(SkipWhitespace(true))
{
@ -79,17 +81,18 @@ namespace CodeImp.DoomBuilder.ZDoom
{
if(!string.IsNullOrEmpty(modelName) && spriteNames.Count > 0)
{
data.ModelNames.Add(modelName);
mde.ModelNames.Add(modelName);
mde.SetTransform(Matrix.RotationZ(Angle2D.DegToRad(angleoffset)), Matrix.Identity, new Vector3(scale));
foreach(string s in spriteNames)
{
if(entries.ContainsKey(s)) //TODO: is this a proper behaviour?
{
entries[s] = data;
entries[s] = mde;
}
else
{
entries.Add(s, data);
entries.Add(s, mde);
}
}
@ -103,7 +106,7 @@ namespace CodeImp.DoomBuilder.ZDoom
}
else if(token == "overridepalette")
{
data.OverridePalette = true;
mde.OverridePalette = true;
}
else if(token == "angleoffset")
{
@ -116,15 +119,12 @@ namespace CodeImp.DoomBuilder.ZDoom
break;
}
float angleOffset = 0; //90?
token = StripTokenQuotes(ReadToken());
if(!ReadSignedFloat(token, ref angleOffset))
if(!ReadSignedFloat(token, ref angleoffset))
{
// Not numeric!
General.ErrorLogger.Add(ErrorType.Error, "Error in " + sourcefilename + " at line " + GetCurrentLineNumber() + ": expected AngleOffset value, but got '" + token + "'");
}
data.AngleOffset = Angle2D.DegToRad(angleOffset);
}
else if(token == "scale")
{
@ -137,15 +137,12 @@ namespace CodeImp.DoomBuilder.ZDoom
break;
}
float scale = 1.0f;
token = StripTokenQuotes(ReadToken());
if(!ReadSignedFloat(token, ref scale))
{
// Not numeric!
General.ErrorLogger.Add(ErrorType.Error, "Error in " + sourcefilename + " at line " + GetCurrentLineNumber() + ": expected Scale value, but got '" + token + "'");
}
data.Scale = Matrix.Scaling(scale, scale, scale);
}
prevToken = token.ToUpperInvariant();
}

View file

@ -326,6 +326,12 @@
<Compile Include="Interface\FilterSelectedThingsForm.Designer.cs">
<DependentUpon>FilterSelectedThingsForm.cs</DependentUpon>
</Compile>
<Compile Include="Interface\FitTexturesForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Interface\FitTexturesForm.Designer.cs">
<DependentUpon>FitTexturesForm.cs</DependentUpon>
</Compile>
<Compile Include="Interface\PastePropertiesOptionsForm.cs">
<SubType>Form</SubType>
</Compile>
@ -504,6 +510,9 @@
<EmbeddedResource Include="Interface\FilterSelectedThingsForm.resx">
<DependentUpon>FilterSelectedThingsForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Interface\FitTexturesForm.resx">
<DependentUpon>FitTexturesForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Interface\PastePropertiesOptionsForm.resx">
<DependentUpon>PastePropertiesOptionsForm.cs</DependentUpon>
</EmbeddedResource>

View file

@ -1,7 +1,10 @@
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Drawing;
using CodeImp.DoomBuilder.GZBuilder.Tools;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.VisualModes;
@ -9,68 +12,157 @@ using CodeImp.DoomBuilder.VisualModes;
namespace CodeImp.DoomBuilder.BuilderModes
{
public static class BuilderModesTools
// A struct, which contains information about visual sides connected to start and end of given visual side
internal class SortedVisualSide
{
internal readonly BaseVisualGeometrySidedef Side;
internal readonly Vector2D Start;
internal readonly Vector2D End;
internal Rectangle Bounds;
internal readonly Dictionary<SortedVisualSide, bool> NextSides;
internal readonly Dictionary<SortedVisualSide, bool> PreviousSides;
internal readonly int Index;
private static int index;
//Initial texture coordinates
private readonly float OffsetX;
private readonly float OffsetY;
private readonly float ScaleX;
private readonly float ScaleY;
internal SortedVisualSide(BaseVisualGeometrySidedef side)
{
Side = side;
Bounds = BuilderModesTools.GetSidedefPartSize(side);
Index = index++;
if (side.Sidedef.Line.Front == side.Sidedef)
{
Start = side.Sidedef.Line.Start.Position;
End = side.Sidedef.Line.End.Position;
}
else
{
Start = side.Sidedef.Line.End.Position;
End = side.Sidedef.Line.Start.Position;
}
switch (side.GeometryType)
{
case VisualGeometryType.WALL_UPPER:
OffsetX = UDMFTools.GetFloat(side.Sidedef.Fields, "offsetx_top");
OffsetY = UDMFTools.GetFloat(side.Sidedef.Fields, "offsety_top");
ScaleX = UDMFTools.GetFloat(side.Sidedef.Fields, "scalex_top", 1.0f);
ScaleY = UDMFTools.GetFloat(side.Sidedef.Fields, "scaley_top", 1.0f);
break;
case VisualGeometryType.WALL_MIDDLE:
OffsetX = UDMFTools.GetFloat(side.Sidedef.Fields, "offsetx_mid");
OffsetY = UDMFTools.GetFloat(side.Sidedef.Fields, "offsety_mid");
ScaleX = UDMFTools.GetFloat(side.Sidedef.Fields, "scalex_mid", 1.0f);
ScaleY = UDMFTools.GetFloat(side.Sidedef.Fields, "scaley_mid", 1.0f);
break;
case VisualGeometryType.WALL_MIDDLE_3D:
Sidedef cs = side.GetControlLinedef().Front;
OffsetX = UDMFTools.GetFloat(cs.Fields, "offsetx_mid");
OffsetY = UDMFTools.GetFloat(cs.Fields, "offsety_mid");
ScaleX = UDMFTools.GetFloat(cs.Fields, "scalex_mid", 1.0f);
ScaleY = UDMFTools.GetFloat(cs.Fields, "scaley_mid", 1.0f);
break;
case VisualGeometryType.WALL_LOWER:
OffsetX = UDMFTools.GetFloat(side.Sidedef.Fields, "offsetx_bottom");
OffsetY = UDMFTools.GetFloat(side.Sidedef.Fields, "offsety_bottom");
ScaleX = UDMFTools.GetFloat(side.Sidedef.Fields, "scalex_bottom", 1.0f);
ScaleY = UDMFTools.GetFloat(side.Sidedef.Fields, "scaley_bottom", 1.0f);
break;
}
NextSides = new Dictionary<SortedVisualSide, bool>();
PreviousSides = new Dictionary<SortedVisualSide, bool>();
}
internal void OnTextureFit(FitTextureOptions options)
{
options.Bounds = Bounds;
options.InitialOffsetX = OffsetX;
options.InitialOffsetY = OffsetY;
options.InitialScaleX = ScaleX;
options.InitialScaleY = ScaleY;
Side.OnTextureFit(options);
}
}
internal static class BuilderModesTools
{
#region ================== Sidedef
internal static Rectangle GetSidedefPartSize(BaseVisualGeometrySidedef side, VisualGeometryType type)
internal static Rectangle GetSidedefPartSize(BaseVisualGeometrySidedef side)
{
if(type == VisualGeometryType.WALL_MIDDLE_3D)
if(side.GeometryType == VisualGeometryType.WALL_MIDDLE_3D)
{
Rectangle rect = new Rectangle(0, 0, 1, 0);
Rectangle rect = new Rectangle(0, 0, Math.Max(1, (int)Math.Round(side.Sidedef.Line.Length)), 0);
Linedef cl = side.GetControlLinedef();
if(cl.Front != null && cl.Front.Sector != null)
{
// Use ceiling height for vavoom-type 3d floors. Also, FloorHeight is > CeilHeight for these...
// Use floor height for vavoom-type 3d floors, because FloorHeight should be > CeilHeight for this type of 3d floor.
if (cl.Args[1] == 0)
{
rect.Y = cl.Front.Sector.CeilHeight;
rect.Y = -cl.Front.Sector.FloorHeight;
rect.Height = cl.Front.Sector.FloorHeight - cl.Front.Sector.CeilHeight;
}
else
{
rect.Y = cl.Front.Sector.FloorHeight;
rect.Y = -cl.Front.Sector.CeilHeight;
rect.Height = cl.Front.GetMiddleHeight();
}
}
else
{
rect.Y = side.Sidedef.Sector.FloorHeight;
rect.Y = -side.Sidedef.Sector.CeilHeight;
rect.Height = side.Sidedef.GetMiddleHeight();
}
return rect;
}
return GetSidedefPartSize(side.Sidedef, type);
return GetSidedefPartSize(side.Sidedef, side.GeometryType);
}
public static Rectangle GetSidedefPartSize(Sidedef side, VisualGeometryType type)
{
Rectangle rect = new Rectangle(0, 0, 1, 0);
Rectangle rect = new Rectangle(0, 0, Math.Max(1, (int)Math.Round(side.Line.Length)), 0);
switch(type)
{
case VisualGeometryType.WALL_LOWER:
rect.Y = side.Sector.FloorHeight;
rect.Height = side.GetLowHeight();
break;
case VisualGeometryType.WALL_UPPER:
if(side.Other != null && side.Other.Sector != null)
if (side.LowRequired())
{
rect.Y = side.Other.Sector.CeilHeight;
rect.Height = side.GetHighHeight();
}
else
{
rect.Height = 0;
rect.Y = -side.Other.Sector.FloorHeight;
rect.Height = side.GetLowHeight();
}
break;
case VisualGeometryType.WALL_UPPER:
if(side.HighRequired())
{
rect.Y = -side.Sector.CeilHeight;
rect.Height = side.GetHighHeight();
}
break;
case VisualGeometryType.WALL_MIDDLE:
rect.Y = side.Sector.FloorHeight;
if(side.MiddleRequired())
{
rect.Y = -side.Sector.CeilHeight;
}
else if (side.Other.Sector != null) // Double-sided
{
rect.Y = -Math.Min(side.Sector.CeilHeight, side.Other.Sector.CeilHeight);
}
rect.Height = side.GetMiddleHeight();
break;
@ -81,6 +173,124 @@ namespace CodeImp.DoomBuilder.BuilderModes
return rect;
}
public static List<SortedVisualSide> SortVisualSides(IEnumerable<BaseVisualGeometrySidedef> tosort)
{
List<SortedVisualSide> result = new List<SortedVisualSide>();
// Sort by texture
Dictionary<long, List<BaseVisualGeometrySidedef>> sidesbytexture = new Dictionary<long, List<BaseVisualGeometrySidedef>>();
foreach (BaseVisualGeometrySidedef side in tosort)
{
long texturelong;
if (side is VisualLower) texturelong = side.Sidedef.LongLowTexture;
else if (side is VisualUpper) texturelong = side.Sidedef.LongHighTexture;
else texturelong = side.Sidedef.LongMiddleTexture;
if(texturelong == MapSet.EmptyLongName) continue; //not interested...
if(!sidesbytexture.ContainsKey(texturelong)) sidesbytexture.Add(texturelong, new List<BaseVisualGeometrySidedef>());
sidesbytexture[texturelong].Add(side);
}
// Connect sides
foreach (KeyValuePair<long, List<BaseVisualGeometrySidedef>> pair in sidesbytexture)
{
IEnumerable<SortedVisualSide> group = ConnectSides(pair.Value);
result.AddRange(group);
}
return result;
}
// Connect sides, left to right
// NextSides - sides connected to the right (Start) vertex,
// PreviousSides - sides connected to the left (End) vertex
private static IEnumerable<SortedVisualSide> ConnectSides(List<BaseVisualGeometrySidedef> allsides)
{
List<SortedVisualSide> result = new List<SortedVisualSide>();
List<SortedVisualSide> sides = new List<SortedVisualSide>(allsides.Count);
foreach (BaseVisualGeometrySidedef side in allsides)
{
sides.Add(new SortedVisualSide(side));
}
foreach(SortedVisualSide curside in sides)
{
// Find sides connected to the end of curside
foreach(SortedVisualSide nextside in sides)
{
if(curside.Index == nextside.Index) continue;
if(nextside.Start == curside.End && nextside.End != curside.Start)
{
// Add both ways
if(!nextside.PreviousSides.ContainsKey(curside)) nextside.PreviousSides.Add(curside, false);
if(!curside.NextSides.ContainsKey(nextside)) curside.NextSides.Add(nextside, false);
}
}
// Find sides connected to the start of curside
foreach(SortedVisualSide prevside in sides)
{
if(curside.Index == prevside.Index) continue;
if(prevside.End == curside.Start && prevside.Start != curside.End)
{
// Add both ways
if(!prevside.NextSides.ContainsKey(curside)) prevside.NextSides.Add(curside, false);
if(!curside.PreviousSides.ContainsKey(prevside)) curside.PreviousSides.Add(prevside, false);
}
}
result.Add(curside);
}
// Try to find the left-most side
SortedVisualSide start = result[0];
foreach (SortedVisualSide side in result)
{
if (side.PreviousSides.Count == 0)
{
start = side;
break;
}
}
// Set horizontal offsets...
ApplyHorizontalOffset(start, null, true, new Dictionary<int, bool>());
return result;
}
private static void ApplyHorizontalOffset(SortedVisualSide side, SortedVisualSide prevside, bool forward, Dictionary<int, bool> processed)
{
// Set offset
if (!processed.ContainsKey(side.Index))
{
if (prevside != null)
{
if (forward)
side.Bounds.X = prevside.Bounds.X + (int)Math.Round(prevside.Side.Sidedef.Line.Length);
else
side.Bounds.X = prevside.Bounds.X - (int)Math.Round(side.Side.Sidedef.Line.Length);
}
processed.Add(side.Index, false);
}
// Repeat for NextSides
foreach (KeyValuePair<SortedVisualSide, bool> pair in side.NextSides)
{
if (!processed.ContainsKey(pair.Key.Index))
ApplyHorizontalOffset(pair.Key, side, true, processed);
}
// Repeat for PreviousSides
foreach(KeyValuePair<SortedVisualSide, bool> pair in side.PreviousSides)
{
if(!processed.ContainsKey(pair.Key.Index))
ApplyHorizontalOffset(pair.Key, side, false, processed);
}
}
#endregion
}
}

View file

@ -0,0 +1,257 @@
namespace CodeImp.DoomBuilder.BuilderModes
{
partial class FitTexturesForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.labelhorizrepeat = new System.Windows.Forms.Label();
this.repeatgroup = new System.Windows.Forms.GroupBox();
this.labelvertrepeat = new System.Windows.Forms.Label();
this.horizrepeat = new System.Windows.Forms.NumericUpDown();
this.vertrepeat = new System.Windows.Forms.NumericUpDown();
this.resethoriz = new System.Windows.Forms.Button();
this.resetvert = new System.Windows.Forms.Button();
this.cbfitwidth = new System.Windows.Forms.CheckBox();
this.accept = new System.Windows.Forms.Button();
this.cancel = new System.Windows.Forms.Button();
this.cbfitheight = new System.Windows.Forms.CheckBox();
this.cbfitconnected = new System.Windows.Forms.CheckBox();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.repeatgroup.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.horizrepeat)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.vertrepeat)).BeginInit();
this.groupBox2.SuspendLayout();
this.SuspendLayout();
//
// labelhorizrepeat
//
this.labelhorizrepeat.AutoSize = true;
this.labelhorizrepeat.Location = new System.Drawing.Point(23, 26);
this.labelhorizrepeat.Name = "labelhorizrepeat";
this.labelhorizrepeat.Size = new System.Drawing.Size(57, 13);
this.labelhorizrepeat.TabIndex = 0;
this.labelhorizrepeat.Text = "Horizontal:";
//
// repeatgroup
//
this.repeatgroup.Controls.Add(this.resetvert);
this.repeatgroup.Controls.Add(this.resethoriz);
this.repeatgroup.Controls.Add(this.vertrepeat);
this.repeatgroup.Controls.Add(this.horizrepeat);
this.repeatgroup.Controls.Add(this.labelvertrepeat);
this.repeatgroup.Controls.Add(this.labelhorizrepeat);
this.repeatgroup.Location = new System.Drawing.Point(12, 107);
this.repeatgroup.Name = "repeatgroup";
this.repeatgroup.Size = new System.Drawing.Size(183, 80);
this.repeatgroup.TabIndex = 1;
this.repeatgroup.TabStop = false;
this.repeatgroup.Text = " Texture Repeating ";
//
// labelvertrepeat
//
this.labelvertrepeat.AutoSize = true;
this.labelvertrepeat.Location = new System.Drawing.Point(33, 52);
this.labelvertrepeat.Name = "labelvertrepeat";
this.labelvertrepeat.Size = new System.Drawing.Size(45, 13);
this.labelvertrepeat.TabIndex = 1;
this.labelvertrepeat.Text = "Vertical:";
//
// horizrepeat
//
this.horizrepeat.Location = new System.Drawing.Point(84, 22);
this.horizrepeat.Maximum = new decimal(new int[] {
512,
0,
0,
0});
this.horizrepeat.Minimum = new decimal(new int[] {
1,
0,
0,
0});
this.horizrepeat.Name = "horizrepeat";
this.horizrepeat.Size = new System.Drawing.Size(60, 20);
this.horizrepeat.TabIndex = 2;
this.horizrepeat.Value = new decimal(new int[] {
1,
0,
0,
0});
this.horizrepeat.ValueChanged += new System.EventHandler(this.repeat_ValueChanged);
//
// vertrepeat
//
this.vertrepeat.Location = new System.Drawing.Point(84, 48);
this.vertrepeat.Maximum = new decimal(new int[] {
512,
0,
0,
0});
this.vertrepeat.Minimum = new decimal(new int[] {
1,
0,
0,
0});
this.vertrepeat.Name = "vertrepeat";
this.vertrepeat.Size = new System.Drawing.Size(60, 20);
this.vertrepeat.TabIndex = 3;
this.vertrepeat.Value = new decimal(new int[] {
1,
0,
0,
0});
this.vertrepeat.ValueChanged += new System.EventHandler(this.repeat_ValueChanged);
//
// resethoriz
//
this.resethoriz.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.Reset;
this.resethoriz.Location = new System.Drawing.Point(150, 20);
this.resethoriz.Name = "resethoriz";
this.resethoriz.Size = new System.Drawing.Size(24, 24);
this.resethoriz.TabIndex = 4;
this.resethoriz.UseVisualStyleBackColor = true;
this.resethoriz.Click += new System.EventHandler(this.resethoriz_Click);
//
// resetvert
//
this.resetvert.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.Reset;
this.resetvert.Location = new System.Drawing.Point(150, 46);
this.resetvert.Name = "resetvert";
this.resetvert.Size = new System.Drawing.Size(24, 24);
this.resetvert.TabIndex = 5;
this.resetvert.UseVisualStyleBackColor = true;
this.resetvert.Click += new System.EventHandler(this.resetvert_Click);
//
// cbfitwidth
//
this.cbfitwidth.AutoSize = true;
this.cbfitwidth.Location = new System.Drawing.Point(10, 19);
this.cbfitwidth.Name = "cbfitwidth";
this.cbfitwidth.Size = new System.Drawing.Size(65, 17);
this.cbfitwidth.TabIndex = 2;
this.cbfitwidth.Text = "Fit width";
this.cbfitwidth.UseVisualStyleBackColor = true;
this.cbfitwidth.CheckedChanged += new System.EventHandler(this.cbfitwidth_CheckedChanged);
//
// accept
//
this.accept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.accept.Location = new System.Drawing.Point(106, 194);
this.accept.Name = "accept";
this.accept.Size = new System.Drawing.Size(88, 24);
this.accept.TabIndex = 6;
this.accept.Text = "Apply";
this.accept.UseVisualStyleBackColor = true;
this.accept.Click += new System.EventHandler(this.accept_Click);
//
// cancel
//
this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.cancel.Location = new System.Drawing.Point(12, 194);
this.cancel.Name = "cancel";
this.cancel.Size = new System.Drawing.Size(88, 24);
this.cancel.TabIndex = 7;
this.cancel.Text = "Cancel";
this.cancel.UseVisualStyleBackColor = true;
this.cancel.Click += new System.EventHandler(this.cancel_Click);
//
// cbfitheight
//
this.cbfitheight.AutoSize = true;
this.cbfitheight.Location = new System.Drawing.Point(10, 42);
this.cbfitheight.Name = "cbfitheight";
this.cbfitheight.Size = new System.Drawing.Size(69, 17);
this.cbfitheight.TabIndex = 8;
this.cbfitheight.Text = "Fit height";
this.cbfitheight.UseVisualStyleBackColor = true;
this.cbfitheight.CheckedChanged += new System.EventHandler(this.cbfitheight_CheckedChanged);
//
// cbfitconnected
//
this.cbfitconnected.AutoSize = true;
this.cbfitconnected.Location = new System.Drawing.Point(10, 65);
this.cbfitconnected.Name = "cbfitconnected";
this.cbfitconnected.Size = new System.Drawing.Size(168, 17);
this.cbfitconnected.TabIndex = 9;
this.cbfitconnected.Text = "Fit across connected surfaces";
this.cbfitconnected.UseVisualStyleBackColor = true;
this.cbfitconnected.CheckedChanged += new System.EventHandler(this.repeat_ValueChanged);
//
// groupBox2
//
this.groupBox2.Controls.Add(this.cbfitwidth);
this.groupBox2.Controls.Add(this.cbfitconnected);
this.groupBox2.Controls.Add(this.cbfitheight);
this.groupBox2.Location = new System.Drawing.Point(12, 12);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(183, 89);
this.groupBox2.TabIndex = 10;
this.groupBox2.TabStop = false;
this.groupBox2.Text = " Options ";
//
// FitTexturesForm
//
this.AcceptButton = this.accept;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(206, 223);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.cancel);
this.Controls.Add(this.accept);
this.Controls.Add(this.repeatgroup);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "FitTexturesForm";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Fit Textures";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FitTexturesForm_FormClosing);
this.repeatgroup.ResumeLayout(false);
this.repeatgroup.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.horizrepeat)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.vertrepeat)).EndInit();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Label labelhorizrepeat;
private System.Windows.Forms.GroupBox repeatgroup;
private System.Windows.Forms.Button resethoriz;
private System.Windows.Forms.NumericUpDown vertrepeat;
private System.Windows.Forms.NumericUpDown horizrepeat;
private System.Windows.Forms.Label labelvertrepeat;
private System.Windows.Forms.Button resetvert;
private System.Windows.Forms.CheckBox cbfitwidth;
private System.Windows.Forms.Button accept;
private System.Windows.Forms.Button cancel;
private System.Windows.Forms.CheckBox cbfitheight;
private System.Windows.Forms.CheckBox cbfitconnected;
private System.Windows.Forms.GroupBox groupBox2;
}
}

View file

@ -0,0 +1,271 @@
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Windows;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
internal struct FitTextureOptions
{
public int HorizontalRepeat;
public int VerticalRepeat;
public bool FitWidth;
public bool FitHeight;
public bool FitAcrossSurfaces;
public Rectangle GlobalBounds;
public Rectangle Bounds;
//Initial texture coordinats
public float InitialOffsetX;
public float InitialOffsetY;
public float InitialScaleX;
public float InitialScaleY;
}
internal partial class FitTexturesForm : DelayedForm
{
#region ================== Event handlers
#endregion
#region ================== Variables
private static Point location = Point.Empty;
private bool blockupdate;
// Settings
private static int horizontalrepeat = 1;
private static int verticalrepeat = 1;
private static bool fitacrosssurfaces = true;
private static bool fitwidth = true;
private static bool fitheight = true;
//Surface stuff
private List<SortedVisualSide> strips;
private Rectangle bounds;
#endregion
#region ================== Constructor
public FitTexturesForm()
{
InitializeComponent();
if (!location.IsEmpty)
{
this.StartPosition = FormStartPosition.Manual;
this.Location = location;
}
}
#endregion
#region ================== Methods
public void Setup(IEnumerable<BaseVisualGeometrySidedef> sides)
{
// Get shapes
strips = BuilderModesTools.SortVisualSides(sides);
// No dice...
if(strips.Count == 0)
{
General.Interface.DisplayStatus(StatusType.Warning, "Failed to setup sidedef chains...");
this.DialogResult = DialogResult.Cancel;
this.Close();
return;
}
// Create bounds
int minx = int.MaxValue;
int maxx = int.MinValue;
int miny = int.MaxValue;
int maxy = int.MinValue;
foreach(SortedVisualSide side in strips)
{
if(side.Bounds.X < minx) minx = side.Bounds.X;
if(side.Bounds.X + side.Bounds.Width > maxx) maxx = side.Bounds.X + side.Bounds.Width;
if(side.Bounds.Y < miny) miny = side.Bounds.Y;
if(side.Bounds.Y + side.Bounds.Height > maxy) maxy = side.Bounds.Y + side.Bounds.Height;
}
bounds = new Rectangle(minx, miny, maxx-minx, maxy-miny);
// Normalize Y-offset
foreach (SortedVisualSide side in strips) side.Bounds.Y -= bounds.Y;
bounds.Y = 0;
#if DEBUG
//debug
DrawDebugUV();
#endif
// Restore settings
blockupdate = true;
horizrepeat.Value = horizontalrepeat;
vertrepeat.Value = verticalrepeat;
cbfitconnected.Checked = fitacrosssurfaces;
cbfitwidth.Checked = fitwidth;
cbfitheight.Checked = fitheight;
UpdateRepeatGroup();
blockupdate = false;
//trigger update
UpdateChanges();
}
private void UpdateChanges()
{
// Apply changes
FitTextureOptions options = new FitTextureOptions
{
GlobalBounds = bounds,
FitAcrossSurfaces = cbfitconnected.Checked,
FitWidth = cbfitwidth.Checked,
FitHeight = cbfitheight.Checked,
HorizontalRepeat = (int)horizrepeat.Value,
VerticalRepeat = (int)vertrepeat.Value
};
foreach(SortedVisualSide side in strips) side.OnTextureFit(options);
}
private void UpdateRepeatGroup()
{
// Disable whole group?
repeatgroup.Enabled = cbfitwidth.Checked || cbfitheight.Checked;
if(!repeatgroup.Enabled) return;
// Update control status
labelhorizrepeat.Enabled = cbfitwidth.Checked;
horizrepeat.Enabled = cbfitwidth.Checked;
resethoriz.Enabled = cbfitwidth.Checked;
labelvertrepeat.Enabled = cbfitheight.Checked;
vertrepeat.Enabled = cbfitheight.Checked;
resetvert.Enabled = cbfitheight.Checked;
}
#endregion
#region ================== Events
private void FitTexturesForm_FormClosing(object sender, FormClosingEventArgs e)
{
location = this.Location;
// Store settings
if (this.DialogResult == DialogResult.OK)
{
horizontalrepeat = (int)horizrepeat.Value;
verticalrepeat = (int)vertrepeat.Value;
fitacrosssurfaces = cbfitwidth.Checked;
fitwidth = cbfitwidth.Checked;
fitheight = cbfitheight.Checked;
}
}
private void resethoriz_Click(object sender, EventArgs e)
{
horizrepeat.Value = 1;
}
private void resetvert_Click(object sender, EventArgs e)
{
vertrepeat.Value = 1;
}
private void repeat_ValueChanged(object sender, EventArgs e)
{
if(blockupdate) return;
UpdateChanges();
}
private void accept_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}
private void cancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
private void cbfitwidth_CheckedChanged(object sender, EventArgs e)
{
if(blockupdate) return;
UpdateRepeatGroup();
UpdateChanges();
}
private void cbfitheight_CheckedChanged(object sender, EventArgs e)
{
if(blockupdate) return;
UpdateRepeatGroup();
UpdateChanges();
}
#endregion
#region ================== Debug
#if DEBUG
private void DrawDebugUV()
{
const int margin = 20;
//find bounds
int minx = int.MaxValue;
int maxx = int.MinValue;
int miny = int.MaxValue;
int maxy = int.MinValue;
foreach(SortedVisualSide side in strips)
{
if(side.Bounds.X < minx) minx = side.Bounds.X;
if(side.Bounds.X + side.Bounds.Width > maxx) maxx = side.Bounds.X + side.Bounds.Width;
if(side.Bounds.Y < miny) miny = side.Bounds.Y;
if(side.Bounds.Y + side.Bounds.Height > maxy) maxy = side.Bounds.Y + side.Bounds.Height;
}
Bitmap bmp = new Bitmap(maxx - minx + margin * 2, maxy - miny + margin * 2);
using(Graphics g = Graphics.FromImage(bmp))
{
int i = 0;
foreach(SortedVisualSide side in strips)
{
Color c = General.Colors.BrightColors[General.Random(0, General.Colors.BrightColors.Length - 1)].ToColor();
Pen p = new Pen(c);
Brush b = new SolidBrush(c);
int x = side.Bounds.X - minx + margin;
int y = side.Bounds.Y - miny + margin;
g.DrawRectangle(p, x, y, side.Bounds.Width, side.Bounds.Height);
g.DrawString(i++ + ": line " + side.Side.Sidedef.Line.Index + "; x:" + side.Bounds.X + " y:" + side.Bounds.Y, this.Font, b, x + 2, y + 2);
}
}
bmp.Save("testuv.png", ImageFormat.Png);
General.Interface.DisplayStatus(StatusType.Info, "Saved test image!");
}
#endif
#endregion
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -908,39 +908,15 @@ visualautoaligntoselectiony
//mxd
visualfittextures
{
title = "Fit Texture";
title = "Fit Textures";
category = "visual";
description = "Scales texture to match size of selected surface.";
description = "Scales textures to match the size of selected surfaces.";
allowkeys = true;
allowmouse = true;
allowscroll = true;
default = 393281; //Ctrl-Alt-A
}
//mxd
visualfittexturesx
{
title = "Fit Texture's Width";
category = "visual";
description = "Scales width of a texture to match width of selected surface.";
allowkeys = true;
allowmouse = true;
allowscroll = true;
default = 262209; //Alt-A
}
//mxd
visualfittexturesy
{
title = "Fit Texture's Height";
category = "visual";
description = "Scales height of a texture to match height of selected surface.";
allowkeys = true;
allowmouse = true;
allowscroll = true;
default = 327745; //Alt-Shift-A
}
toggleupperunpegged
{
title = "Toggle Upper Unpegged";
@ -986,7 +962,7 @@ resettexture
{
title = "Reset Texture Offsets";
category = "visual";
description = "Resets the texture offsets on the targeted or selected sidedef.";
description = "Resets the texture offsets on targeted or selected sidedefs.";
allowkeys = true;
allowmouse = true;
allowscroll = true;
@ -997,7 +973,7 @@ resettextureudmf
{
title = "Reset Local Texture Offsets (UDMF)";
category = "visual";
description = "Resets upper/middle/lower texture offsets and scale on the targeted or selected sidedef. Resets texture offsets, rotation and scale on targeted or selected floors/ceilings.";
description = "Resets upper/middle/lower texture offsets and scale on targeted or selected sidedefs. Resets texture offsets, rotation and scale on targeted or selected floors/ceilings.";
allowkeys = true;
allowmouse = true;
allowscroll = true;

View file

@ -129,7 +129,7 @@ group sidedefs
"Use <k>buildermodes_texturecopy</k>, <k>buildermodes_texturepaste</k> and <k>buildermodes_floodfilltextures</k> to copy, paste and flood-fill highlighted texture"
"Use <k>buildermodes_texturecopyoffsets</k> and <k>buildermodes_texturepasteoffsets</k> to copy and paste texture offsets"
"Use <k>buildermodes_visualautoalignx</k>, <k>buildermodes_visualautoalign</k> or <k>buildermodes_visualautoalign</k> to auto align textures on X, Y and XY axis"
"Use <k>buildermodes_visualfittexturesx</k>, <k>buildermodes_visualfittexturesy</k> or <k>buildermodes_visualfittextures</k> to fit texture's width, height or both into it's surface (UDMF only)"
"Use <k>buildermodes_visualfittextures</k> to fit texture's width or height to selected surfaces (UDMF only)"
"Use <k>buildermodes_resettexture</k> to reset global texture offsets"
"Use <k>buildermodes_resettextureudmf</k> to reset local texture offsets and scale (UDMF only)"
"Use <k>buildermodes_toggleupperunpegged</k> or <k>buildermodes_togglelowerunpegged</k> to toggle Upper or Lower Unpegged setting"

View file

@ -99,7 +99,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
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)
protected static void SwapTriangleVertices(WorldVertex[] verts)
{
// Swap some vertices to flip all triangles
for(int i = 0; i < verts.Length; i += 3)
@ -260,7 +260,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
//do we need to align this? (and also grab texture scale while we are at it)
float scaleX, scaleY;
bool isFloor = (geoType == VisualGeometryType.FLOOR);
bool isFloor = (geometrytype == VisualGeometryType.FLOOR);
if(mode.HighlightedTarget is VisualFloor)
{
@ -333,7 +333,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
//mxd
protected void AlignTextureToSlopeLine(Linedef slopeSource, float slopeAngle, bool isFront, bool alignx, bool aligny)
{
bool isFloor = (geoType == VisualGeometryType.FLOOR);
bool isFloor = (geometrytype == VisualGeometryType.FLOOR);
Sector.Sector.Fields.BeforeFieldsChange();
float sourceAngle = (float)Math.Round(General.ClampAngle(isFront ? -Angle2D.RadToDeg(slopeSource.Angle) + 90 : -Angle2D.RadToDeg(slopeSource.Angle) - 90), 1);
@ -426,7 +426,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Unused
public virtual void OnEditBegin() { }
public virtual void OnTextureFit(bool fitWidth, bool fitHeight) { } //mxd
public virtual void OnTextureFit(FitTextureOptions options) { } //mxd
public virtual void OnToggleUpperUnpegged() { }
public virtual void OnToggleLowerUnpegged() { }
public virtual void OnResetTextureOffset() { }

View file

@ -18,16 +18,16 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.GZBuilder.Tools;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Types;
using CodeImp.DoomBuilder.VisualModes;
using System.Drawing;
using CodeImp.DoomBuilder.GZBuilder.Tools;
#endregion
@ -43,7 +43,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
#region ================== Variables
protected BaseVisualMode mode;
protected readonly BaseVisualMode mode;
protected Plane top;
protected Plane bottom;
@ -236,7 +236,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// This splits a polygon with a plane and returns the other part as a new polygon
// The polygon is expected to be convex and clockwise
protected WallPolygon SplitPoly(ref WallPolygon poly, Plane p, bool keepfront)
protected static WallPolygon SplitPoly(ref WallPolygon poly, Plane p, bool keepfront)
{
const float NEAR_ZERO = 0.01f;
WallPolygon front = new WallPolygon(poly.Count);
@ -312,7 +312,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// This crops a polygon with a plane and keeps only a certain part of the polygon
protected void CropPoly(ref WallPolygon poly, Plane p, bool keepfront)
protected static void CropPoly(ref WallPolygon poly, Plane p, bool keepfront)
{
const float NEAR_ZERO = 0.01f;
float sideswitch = keepfront ? 1 : -1;
@ -372,7 +372,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
//mxd
protected float GetRoundedTextureOffset(float oldValue, float offset, float scale, float textureSize)
protected static float GetRoundedTextureOffset(float oldValue, float offset, float scale, float textureSize)
{
if(offset == 0f) return oldValue;
float scaledOffset = offset * scale;
@ -383,7 +383,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
//mxd
protected void OnTextureChanged()
private void OnTextureChanged()
{
//check for 3d floors
if(Sidedef.Line.Action == 160)
@ -400,7 +400,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
}
}
}
//mxd
@ -408,7 +407,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
if(Sidedef.Sector == null || (!withSameTexture && !withSameHeight)) return;
Rectangle rect = BuilderModesTools.GetSidedefPartSize(this, geoType);
Rectangle rect = BuilderModesTools.GetSidedefPartSize(this);
if(rect.Height == 0) return;
if(select && !selected)
@ -477,8 +476,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
&& line.Front.HighRequired()
&& BuilderModesTools.GetSidedefPartSize(line.Front, VisualGeometryType.WALL_UPPER).IntersectsWith(rect));
addFrontMiddle = (line.Front.MiddleTexture == texture
&& line.Front.MiddleRequired()
addFrontMiddle = (line.Front.MiddleTexture == texture
&& (line.Front.MiddleRequired() || line.Back != null)
&& line.Front.GetMiddleHeight() > 0
&& BuilderModesTools.GetSidedefPartSize(line.Front, VisualGeometryType.WALL_MIDDLE).IntersectsWith(rect));
@ -495,7 +494,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
&& BuilderModesTools.GetSidedefPartSize(line.Back, VisualGeometryType.WALL_UPPER).IntersectsWith(rect));
addBackMiddle = (line.Back.MiddleTexture == texture
&& line.Back.MiddleRequired()
&& (line.Back.MiddleRequired() || line.Front != null)
&& line.Back.GetMiddleHeight() > 0
&& BuilderModesTools.GetSidedefPartSize(line.Back, VisualGeometryType.WALL_MIDDLE).IntersectsWith(rect));
@ -600,6 +599,96 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
return selected;
}
//mxd
protected void FitTexture(FitTextureOptions options)
{
// Create undo name
string s;
if(options.FitWidth && options.FitHeight) s = "width and height";
else if(options.FitWidth) s = "width";
else s = "height";
//create undo
mode.CreateUndo("Fit texture (" + s + ")", UndoGroup.TextureOffsetChange, Sector.Sector.FixedIndex);
Sidedef.Fields.BeforeFieldsChange();
// Fit width
if(options.FitWidth)
{
float scalex, offsetx;
if(options.FitAcrossSurfaces)
{
scalex = Texture.ScaledWidth / (Sidedef.Line.Length * (options.GlobalBounds.Width / Sidedef.Line.Length)) * options.HorizontalRepeat;
offsetx = (float)Math.Round((options.Bounds.X * scalex - Sidedef.OffsetX) % Texture.Width, General.Map.FormatInterface.VertexDecimals);
}
else
{
scalex = Texture.ScaledWidth / Sidedef.Line.Length * options.HorizontalRepeat;
offsetx = -Sidedef.OffsetX;
}
UDMFTools.SetFloat(Sidedef.Fields, "scalex_" + partname, (float)Math.Round(scalex, General.Map.FormatInterface.VertexDecimals), 1.0f);
UDMFTools.SetFloat(Sidedef.Fields, "offsetx_" + partname, offsetx, 0.0f);
}
else
{
// Restore initial offsets
UDMFTools.SetFloat(Sidedef.Fields, "scalex_" + partname, options.InitialScaleX, 1.0f);
UDMFTools.SetFloat(Sidedef.Fields, "offsetx_" + partname, options.InitialOffsetX, 0.0f);
}
// Fit height
if(options.FitHeight)
{
if(Sidedef.Sector != null)
{
float scaley, offsety;
if(options.FitAcrossSurfaces)
{
scaley = Texture.ScaledHeight / (options.Bounds.Height * ((float)options.GlobalBounds.Height / options.Bounds.Height)) * options.VerticalRepeat;
if(this is VisualLower) // Special cases, special cases...
{
offsety = Tools.GetSidedefOffsetY(Sidedef, geometrytype, -Sidedef.OffsetY, scaley, true) % Texture.Height;
}
else if(this is VisualMiddleDouble)
{
if (Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag))
{
//offsety = Tools.GetSidedefOffsetY(Sidedef, geometrytype, options.Bounds.Y * scaley - Sidedef.OffsetY, scaley, true) - (options.Bounds.Height + Sidedef.GetHighHeight()) * scaley;
offsety = (options.Bounds.Y - /*options.Bounds.Height -*/ Sidedef.GetHighHeight()) * scaley - Sidedef.OffsetY;
//offsety = Tools.GetSidedefOffsetY(Sidedef, geometrytype, offsety, scaley, true);
}
else
{
offsety = options.Bounds.Y * scaley - Sidedef.OffsetY;
}
}
else
{
offsety = Tools.GetSidedefOffsetY(Sidedef, geometrytype, options.Bounds.Y * scaley - Sidedef.OffsetY, scaley, true) % Texture.Height;
}
}
else
{
scaley = Texture.ScaledHeight / options.Bounds.Height * options.VerticalRepeat;
offsety = Tools.GetSidedefOffsetY(Sidedef, geometrytype, -Sidedef.OffsetY, scaley, true) % Texture.Height;
}
UDMFTools.SetFloat(Sidedef.Fields, "scaley_" + partname, (float)Math.Round(scaley, General.Map.FormatInterface.VertexDecimals), 1.0f);
UDMFTools.SetFloat(Sidedef.Fields, "offsety_" + partname, (float)Math.Round(offsety, General.Map.FormatInterface.VertexDecimals), 0.0f);
}
}
else
{
// Restore initial offsets
UDMFTools.SetFloat(Sidedef.Fields, "scaley_" + partname, options.InitialScaleY, 1.0f);
UDMFTools.SetFloat(Sidedef.Fields, "offsety_" + partname, options.InitialOffsetY, 0.0f);
}
}
#endregion
@ -615,7 +704,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
protected abstract void MoveTextureOffset(Point xy);
protected abstract Point GetTextureOffset();
public virtual void SelectNeighbours(bool select, bool withSameTexture, bool withSameHeight) { } //mxd
public virtual void OnTextureFit(bool fitWidth, bool fitHeight) { } //mxd
public virtual void OnTextureFit(FitTextureOptions options) { } //mxd
// Insert middle texture
public virtual void OnInsert()
@ -1178,6 +1267,15 @@ namespace CodeImp.DoomBuilder.BuilderModes
float offsety = dragdeltaz.GetLength();
if((Math.Sign(dragdeltaxy.x) < 0) || (Math.Sign(dragdeltaxy.y) < 0) || (Math.Sign(dragdeltaxy.z) < 0)) offsetx = -offsetx;
if((Math.Sign(dragdeltaz.x) < 0) || (Math.Sign(dragdeltaz.y) < 0) || (Math.Sign(dragdeltaz.z) < 0)) offsety = -offsety;
//mxd. Modify by surface scale?
if (General.Map.UDMF)
{
float sx = UDMFTools.GetFloat(Sidedef.Fields, "scalex_" + partname, 1.0f);
float sy = UDMFTools.GetFloat(Sidedef.Fields, "scaley_" + partname, 1.0f);
if (Math.Abs(sx) < 1) offsetx *= sx;
if (Math.Abs(sy) < 1) offsety *= sy;
}
// Apply offsets
if(General.Interface.CtrlState && General.Interface.ShiftState)
@ -1226,7 +1324,33 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Sector brightness change
public virtual void OnChangeTargetBrightness(bool up)
{
if(!Sector.Changed)
//mxd. Change UDMF wall light?
if (General.Map.UDMF)
{
int light = Sidedef.Fields.GetValue("light", 0);
bool absolute = Sidedef.Fields.GetValue("lightabsolute", false);
int newLight;
if(up)
newLight = General.Map.Config.BrightnessLevels.GetNextHigher(light, absolute);
else
newLight = General.Map.Config.BrightnessLevels.GetNextLower(light, absolute);
if(newLight == light) return;
//create undo
mode.CreateUndo("Change wall brightness", UndoGroup.SurfaceBrightnessChange, Sector.Sector.FixedIndex);
Sidedef.Fields.BeforeFieldsChange();
//apply changes
Sidedef.Fields["light"] = new UniValue(UniversalType.Integer, newLight);
mode.SetActionResult("Changed wall brightness to " + newLight + ".");
Sector.Sector.UpdateCache();
//rebuild sector
Sector.UpdateSectorGeometry(false);
}
else if(!Sector.Changed)
{
// Change brightness
mode.CreateUndo("Change sector brightness", UndoGroup.SectorBrightnessChange, Sector.Sector.FixedIndex);
@ -1279,7 +1403,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
Sidedef.OffsetX = (Sidedef.OffsetX - horizontal);
if (Texture != null) Sidedef.OffsetX %= Texture.Width;
Sidedef.OffsetY = (Sidedef.OffsetY - vertical);
if(geoType != VisualGeometryType.WALL_MIDDLE && Texture != null) Sidedef.OffsetY %= Texture.Height;
if(geometrytype != VisualGeometryType.WALL_MIDDLE && Texture != null) Sidedef.OffsetY %= Texture.Height;
mode.SetActionResult("Changed texture offsets to " + Sidedef.OffsetX + ", " + Sidedef.OffsetY + ".");
}

View file

@ -294,8 +294,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
selectionchanged = false;
if(singleselection)
ClearSelection();
if(singleselection) ClearSelection();
UpdateChangedObjects();
ShowTargetInfo();
@ -2115,11 +2114,15 @@ namespace CodeImp.DoomBuilder.BuilderModes
foreach(BaseVisualThing vt in things)
{
if(vt.Thing.Sector == null) continue;
ThingTypeInfo ti = General.Map.Data.GetThingInfo(vt.Thing.Type);
int zvalue = (int)(vt.Thing.Sector.FloorHeight + vt.Thing.Position.z);
if(zvalue != vt.Thing.Sector.CeilHeight - ti.Height)
vt.OnChangeTargetHeight((int)(vt.Thing.Sector.CeilHeight - ti.Height) - zvalue);
if (vt.Info.Hangs)
{
vt.OnMove(new Vector3D(vt.Thing.Position, 0));
}
else
{
vt.OnMove(new Vector3D(vt.Thing.Position, vt.Thing.Sector.CeilHeight - vt.Info.Height));
}
}
}
@ -2327,8 +2330,14 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
if(vt.Thing.Sector == null) continue;
if(vt.Thing.Position.z != 0)
vt.OnChangeTargetHeight((int)-vt.Thing.Position.z);
if (vt.Info.Hangs)
{
vt.OnMove(new Vector3D(vt.Thing.Position, vt.Thing.Sector.CeilHeight - vt.Thing.Sector.FloorHeight - vt.Info.Height));
}
else
{
vt.OnMove(new Vector3D(vt.Thing.Position, 0));
}
}
}
@ -2770,31 +2779,32 @@ namespace CodeImp.DoomBuilder.BuilderModes
//mxd
[BeginAction("visualfittextures")]
public void TextureFit()
private void FitTextures()
{
PreAction(UndoGroup.None);
// Get selection
List<IVisualEventReceiver> objs = GetSelectedObjects(false, true, false, false);
foreach(IVisualEventReceiver i in objs) i.OnTextureFit(true, true);
PostAction();
}
List<BaseVisualGeometrySidedef> sides = new List<BaseVisualGeometrySidedef>();
foreach(IVisualEventReceiver side in objs)
{
if(side is BaseVisualGeometrySidedef) sides.Add(side as BaseVisualGeometrySidedef);
}
//mxd
[BeginAction("visualfittexturesx")]
public void TextureFitX()
{
PreAction(UndoGroup.None);
List<IVisualEventReceiver> objs = GetSelectedObjects(false, true, false, false);
foreach(IVisualEventReceiver i in objs) i.OnTextureFit(true, false);
PostAction();
}
if(sides.Count == 0)
{
General.Interface.DisplayStatus(StatusType.Warning, "Fit Textures action requires selected sidedefs.");
return;
}
// Show form
FitTexturesForm form = new FitTexturesForm();
form.Setup(sides);
// Undo changes?
if(form.ShowDialog((Form)General.Interface) == DialogResult.Cancel)
General.Map.UndoRedo.WithdrawUndo();
//mxd
[BeginAction("visualfittexturesy")]
public void TextureFitY()
{
PreAction(UndoGroup.None);
List<IVisualEventReceiver> objs = GetSelectedObjects(false, true, false, false);
foreach(IVisualEventReceiver i in objs) i.OnTextureFit(false, true);
PostAction();
}
@ -3698,15 +3708,26 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
else
{
offset = Tools.GetSidedefMiddleOffsetY(j.sidedef, offset, j.scaleY, true);
offset = Tools.GetSidedefMiddleOffsetY(j.sidedef, offset, j.scaleY, true) % texture.Height;
//mxd. Clamp offset if this part is middle single or wrapped middle double
if(j.sidedef.Other == null || j.sidedef.IsFlagSet("wrapmidtex") || j.sidedef.Line.IsFlagSet("wrapmidtex"))
if(j.sidedef.Other != null && !j.sidedef.IsFlagSet("wrapmidtex") && !j.sidedef.Line.IsFlagSet("wrapmidtex"))
{
offset %= texture.Height;
//mxd. This should be doublesided non-wrapped line. Find the nearset aligned position
float curoffset = UDMFTools.GetFloat(j.sidedef.Fields, "offsety_mid");
offset += texture.Height * (int)Math.Round((curoffset - offset) / texture.Height);
// Make sure the surface stays between floor and ceiling
if (offset < -texture.Height)
{
offset += texture.Height;
}
else if(offset + texture.Height > j.sidedef.GetMiddleHeight())
{
offset -= texture.Height;
}
}
j.sidedef.Fields["offsety_mid"] = new UniValue(UniversalType.Float, offset);//mxd
j.sidedef.Fields["offsety_mid"] = new UniValue(UniversalType.Float, offset); //mxd
}
}
}

View file

@ -38,7 +38,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
#region ================== Variables
protected BaseVisualMode mode;
private readonly BaseVisualMode mode;
private ThingTypeInfo info;
private bool isloaded;
@ -60,7 +60,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
#region ================== Properties
public bool Changed { get { return changed; } set { changed |= value; } }
public ThingTypeInfo Info { get { return info; } } //mxd
#endregion
#region ================== Constructor / Setup
@ -473,7 +474,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public virtual void OnProcess(float deltatime) { }
public virtual void OnTextureFloodfill() { }
public virtual void OnInsert() { }
public virtual void OnTextureFit(bool fitWidth, bool fitHeight) { } //mxd
public virtual void OnTextureFit(FitTextureOptions options) { } //mxd
public virtual void ApplyTexture(string texture) { }
public virtual void ApplyUpperUnpegged(bool set) { }
public virtual void ApplyLowerUnpegged(bool set) { }
@ -572,7 +573,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
undoticket = mode.CreateUndo("Change thing height");
Thing.Move(Thing.Position + new Vector3D(0.0f, 0.0f, amount));
Thing.Move(Thing.Position + new Vector3D(0.0f, 0.0f, (info.Hangs ? -amount : amount)));
mode.SetActionResult("Changed thing height to " + Thing.Position.z + ".");

View file

@ -238,7 +238,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public virtual void OnCopyTextureOffsets() { }
public virtual void OnPasteTextureOffsets() { }
public virtual void OnTextureAlign(bool alignx, bool aligny) { }
public virtual void OnTextureFit(bool fitWidth, bool fitHeight) { } //mxd
public virtual void OnTextureFit(FitTextureOptions options) { } //mxd
public virtual void OnToggleUpperUnpegged() { }
public virtual void OnToggleLowerUnpegged() { }
public virtual void OnResetTextureOffset() { }

View file

@ -44,7 +44,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
void OnCopyProperties();
void OnPasteProperties();
void OnTextureAlign(bool alignx, bool aligny);
void OnTextureFit(bool fitWidth, bool fitHeight); //mxd
void OnTextureFit(FitTextureOptions options); //mxd
void OnTextureFloodfill();
void OnToggleUpperUnpegged();
void OnToggleLowerUnpegged();

View file

@ -45,7 +45,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void OnCopyProperties() { }
public void OnPasteProperties() { }
public void OnTextureAlign(bool alignx, bool aligny) { }
public void OnTextureFit(bool fitWidth, bool fitHeight) { } //mxd
public void OnTextureFit(FitTextureOptions options) { } //mxd
public void OnTextureFloodfill() { }
public void OnToggleUpperUnpegged() { }
public void OnToggleLowerUnpegged() { }

View file

@ -54,7 +54,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
public VisualCeiling(BaseVisualMode mode, VisualSector vs) : base(mode, vs)
{
//mxd
geoType = VisualGeometryType.CEILING;
geometrytype = VisualGeometryType.CEILING;
partname = "ceiling";
//mxd
if(mode.UseSelectionFromClassicMode && vs != null && vs.Sector.Selected && (General.Map.ViewMode == ViewMode.CeilingTextures || General.Map.ViewMode == ViewMode.Normal))

View file

@ -54,7 +54,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
public VisualFloor(BaseVisualMode mode, VisualSector vs) : base(mode, vs)
{
//mxd
geoType = VisualGeometryType.FLOOR;
geometrytype = VisualGeometryType.FLOOR;
partname = "floor";
//mxd
if(mode.UseSelectionFromClassicMode && vs != null && vs.Sector.Selected
@ -243,7 +244,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
mode.SetActionResult("Texture offsets reset.");
Sector.Sector.Fields.BeforeFieldsChange();
string[] keys = new string[] { "xpanningfloor", "ypanningfloor", "xscalefloor", "yscalefloor", "rotationfloor" };
string[] keys = new[] { "xpanningfloor", "ypanningfloor", "xscalefloor", "yscalefloor", "rotationfloor" };
foreach(string key in keys)
{

View file

@ -51,7 +51,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
public VisualLower(BaseVisualMode mode, VisualSector vs, Sidedef s) : base(mode, vs, s)
{
//mxd
geoType = VisualGeometryType.WALL_LOWER;
geometrytype = VisualGeometryType.WALL_LOWER;
partname = "bottom";
// We have no destructor
GC.SuppressFinalize(this);
@ -302,69 +303,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
//mxd
public override void OnChangeTargetBrightness(bool up)
{
if(!General.Map.UDMF)
{
base.OnChangeTargetBrightness(up);
return;
}
int light = Sidedef.Fields.GetValue("light", 0);
bool absolute = Sidedef.Fields.GetValue("lightabsolute", false);
int newLight;
if(up)
newLight = General.Map.Config.BrightnessLevels.GetNextHigher(light, absolute);
else
newLight = General.Map.Config.BrightnessLevels.GetNextLower(light, absolute);
if(newLight == light) return;
//create undo
mode.CreateUndo("Change lower wall brightness", UndoGroup.SurfaceBrightnessChange, Sector.Sector.FixedIndex);
Sidedef.Fields.BeforeFieldsChange();
//apply changes
Sidedef.Fields["light"] = new UniValue(UniversalType.Integer, newLight);
mode.SetActionResult("Changed lower wall brightness to " + newLight + ".");
Sector.Sector.UpdateCache();
//rebuild sector
Sector.UpdateSectorGeometry(false);
}
//mxd
public override void OnTextureFit(bool fitWidth, bool fitHeight)
public override void OnTextureFit(FitTextureOptions options)
{
if(!General.Map.UDMF) return;
if(!Sidedef.LowRequired() || string.IsNullOrEmpty(Sidedef.LowTexture) || Sidedef.LowTexture == "-" || !Texture.IsImageLoaded) return;
string s;
if(fitWidth && fitHeight) s = "width and height";
else if(fitWidth) s = "width";
else s = "height";
//create undo
mode.CreateUndo("Fit texture (" + s + ")", UndoGroup.TextureOffsetChange, Sector.Sector.FixedIndex);
Sidedef.Fields.BeforeFieldsChange();
if(fitWidth)
{
float scaleX = Texture.ScaledWidth / Sidedef.Line.Length;
UDMFTools.SetFloat(Sidedef.Fields, "scalex_bottom", scaleX, 1.0f);
UDMFTools.SetFloat(Sidedef.Fields, "offsetx_bottom", -Sidedef.OffsetX, 0.0f);
}
if(fitHeight && Sidedef.Sector != null && Sidedef.Other.Sector != null)
{
float scaleY = (float)Texture.Height / (Sidedef.Other.Sector.FloorHeight - Sidedef.Sector.FloorHeight);
UDMFTools.SetFloat(Sidedef.Fields, "scaley_bottom", scaleY, 1.0f);
float offsetY = Tools.GetSidedefBottomOffsetY(Sidedef, -Sidedef.OffsetY, scaleY, true) % Texture.Height;
UDMFTools.SetFloat(Sidedef.Fields, "offsety_bottom", offsetY, 0.0f);
}
FitTexture(options);
Setup();
}

View file

@ -52,7 +52,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
public VisualMiddle3D(BaseVisualMode mode, VisualSector vs, Sidedef s) : base(mode, vs, s)
{
//mxd
geoType = VisualGeometryType.WALL_MIDDLE_3D;
geometrytype = VisualGeometryType.WALL_MIDDLE_3D;
partname = "mid";
// We have no destructor
GC.SuppressFinalize(this);

View file

@ -29,7 +29,8 @@ namespace CodeImp.DoomBuilder.BuilderModes {
: base(mode, vs, s)
{
//mxd
geoType = VisualGeometryType.WALL_MIDDLE;
geometrytype = VisualGeometryType.WALL_MIDDLE;
partname = "mid";
// We have no destructor
GC.SuppressFinalize(this);
@ -345,39 +346,6 @@ namespace CodeImp.DoomBuilder.BuilderModes {
return new Point((int)oldx, (int)oldy);
}
//mxd
public override void OnChangeTargetBrightness(bool up)
{
if(!General.Map.UDMF)
{
base.OnChangeTargetBrightness(up);
return;
}
int light = Sidedef.Fields.GetValue("light", 0);
bool absolute = Sidedef.Fields.GetValue("lightabsolute", false);
int newLight;
if(up)
newLight = General.Map.Config.BrightnessLevels.GetNextHigher(light, absolute);
else
newLight = General.Map.Config.BrightnessLevels.GetNextLower(light, absolute);
if(newLight == light) return;
//create undo
mode.CreateUndo("Change middle wall brightness", UndoGroup.SurfaceBrightnessChange, Sector.Sector.FixedIndex);
Sidedef.Fields.BeforeFieldsChange();
//apply changes
Sidedef.Fields["light"] = new UniValue(UniversalType.Integer, newLight);
mode.SetActionResult("Changed middle wall brightness to " + newLight + ".");
Sector.Sector.UpdateCache();
//rebuild sector
Sector.UpdateSectorGeometry(false);
}
//mxd
public override Linedef GetControlLinedef()
{

View file

@ -55,7 +55,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
public VisualMiddleDouble(BaseVisualMode mode, VisualSector vs, Sidedef s) : base(mode, vs, s)
{
//mxd
geoType = VisualGeometryType.WALL_MIDDLE;
geometrytype = VisualGeometryType.WALL_MIDDLE;
partname = "mid";
// Set render pass
this.RenderPass = RenderPass.Mask;
@ -360,69 +361,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
//mxd
public override void OnChangeTargetBrightness(bool up)
{
if(!General.Map.UDMF)
{
base.OnChangeTargetBrightness(up);
return;
}
int light = Sidedef.Fields.GetValue("light", 0);
bool absolute = Sidedef.Fields.GetValue("lightabsolute", false);
int newLight;
if(up)
newLight = General.Map.Config.BrightnessLevels.GetNextHigher(light, absolute);
else
newLight = General.Map.Config.BrightnessLevels.GetNextLower(light, absolute);
if(newLight == light) return;
//create undo
mode.CreateUndo("Change middle wall brightness", UndoGroup.SurfaceBrightnessChange, Sector.Sector.FixedIndex);
Sidedef.Fields.BeforeFieldsChange();
//apply changes
Sidedef.Fields["light"] = new UniValue(UniversalType.Integer, newLight);
mode.SetActionResult("Changed middle wall brightness to " + newLight + ".");
Sector.Sector.UpdateCache();
//rebuild sector
Sector.UpdateSectorGeometry(false);
}
//mxd
public override void OnTextureFit(bool fitWidth, bool fitHeight)
public override void OnTextureFit(FitTextureOptions options)
{
if(!General.Map.UDMF) return;
if(string.IsNullOrEmpty(Sidedef.MiddleTexture) || Sidedef.MiddleTexture == "-" || !Texture.IsImageLoaded) return;
string s;
if(fitWidth && fitHeight) s = "width and height";
else if(fitWidth) s = "width";
else s = "height";
//create undo
mode.CreateUndo("Fit texture (" + s + ")", UndoGroup.TextureOffsetChange, Sector.Sector.FixedIndex);
Sidedef.Fields.BeforeFieldsChange();
if(fitWidth)
{
float scaleX = Texture.ScaledWidth / Sidedef.Line.Length;
UDMFTools.SetFloat(Sidedef.Fields, "scalex_mid", scaleX, 1.0f);
UDMFTools.SetFloat(Sidedef.Fields, "offsetx_mid", -Sidedef.OffsetX, 0.0f);
}
if(fitHeight && Sidedef.Sector != null)
{
float scaleY = Texture.ScaledHeight / (Sidedef.Sector.CeilHeight - Sidedef.Sector.FloorHeight);
UDMFTools.SetFloat(Sidedef.Fields, "scaley_mid", scaleY, 1.0f);
float offsetY = Tools.GetSidedefMiddleOffsetY(Sidedef, -Sidedef.OffsetY, scaleY, true) % Texture.Height;
UDMFTools.SetFloat(Sidedef.Fields, "offsety_mid", offsetY, 0.0f);
}
FitTexture(options);
Setup();
}

View file

@ -51,7 +51,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
public VisualMiddleSingle(BaseVisualMode mode, VisualSector vs, Sidedef s) : base(mode, vs, s)
{
//mxd
geoType = VisualGeometryType.WALL_MIDDLE;
geometrytype = VisualGeometryType.WALL_MIDDLE;
partname = "mid";
// We have no destructor
GC.SuppressFinalize(this);
@ -293,69 +294,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
//mxd
public override void OnChangeTargetBrightness(bool up)
{
if(!General.Map.UDMF)
{
base.OnChangeTargetBrightness(up);
return;
}
int light = Sidedef.Fields.GetValue("light", 0);
bool absolute = Sidedef.Fields.GetValue("lightabsolute", false);
int newLight;
if(up)
newLight = General.Map.Config.BrightnessLevels.GetNextHigher(light, absolute);
else
newLight = General.Map.Config.BrightnessLevels.GetNextLower(light, absolute);
if(newLight == light) return;
//create undo
mode.CreateUndo("Change middle wall brightness", UndoGroup.SurfaceBrightnessChange, Sector.Sector.FixedIndex);
Sidedef.Fields.BeforeFieldsChange();
//apply changes
Sidedef.Fields["light"] = new UniValue(UniversalType.Integer, newLight);
mode.SetActionResult("Changed middle wall brightness to " + newLight + ".");
Sector.Sector.UpdateCache();
//rebuild sector
Sector.UpdateSectorGeometry(false);
}
//mxd
public override void OnTextureFit(bool fitWidth, bool fitHeight)
public override void OnTextureFit(FitTextureOptions options)
{
if(!General.Map.UDMF) return;
if(string.IsNullOrEmpty(Sidedef.MiddleTexture) || Sidedef.MiddleTexture == "-" || !Texture.IsImageLoaded) return;
string s;
if(fitWidth && fitHeight) s = "width and height";
else if(fitWidth) s = "width";
else s = "height";
//create undo
mode.CreateUndo("Fit texture (" + s + ")", UndoGroup.TextureOffsetChange, Sector.Sector.FixedIndex);
Sidedef.Fields.BeforeFieldsChange();
if(fitWidth)
{
float scaleX = Texture.ScaledWidth / Sidedef.Line.Length;
UDMFTools.SetFloat(Sidedef.Fields, "scalex_mid", scaleX, 1.0f);
UDMFTools.SetFloat(Sidedef.Fields, "offsetx_mid", -Sidedef.OffsetX, 0.0f);
}
if(fitHeight && Sidedef.Sector != null)
{
float scaleY = Texture.ScaledHeight / (Sidedef.Sector.CeilHeight - Sidedef.Sector.FloorHeight);
UDMFTools.SetFloat(Sidedef.Fields, "scaley_mid", scaleY, 1.0f);
float offsetY = Tools.GetSidedefMiddleOffsetY(Sidedef, -Sidedef.OffsetY, scaleY, true) % Texture.Height;
UDMFTools.SetFloat(Sidedef.Fields, "offsety_mid", offsetY, 0.0f);
}
FitTexture(options);
Setup();
}

View file

@ -25,12 +25,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
internal struct VisualSidedefParts
{
// Members
public VisualUpper upper;
public VisualLower lower;
public VisualMiddleDouble middledouble;
public VisualMiddleSingle middlesingle;
public List<VisualMiddle3D> middle3d;
public List<VisualMiddleBack> middleback;//mxd
public readonly VisualUpper upper;
public readonly VisualLower lower;
public readonly VisualMiddleDouble middledouble;
public readonly VisualMiddleSingle middlesingle;
public readonly List<VisualMiddle3D> middle3d;
public readonly List<VisualMiddleBack> middleback;//mxd
// Constructor
public VisualSidedefParts(VisualUpper u, VisualLower l, VisualMiddleDouble m, List<VisualMiddle3D> e, List<VisualMiddleBack> eb)
@ -40,7 +40,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
this.middledouble = m;
this.middlesingle = null;
this.middle3d = e;
this.middleback = eb;//mxd
this.middleback = eb; //mxd
}
// Constructor
@ -63,13 +63,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
if(upper != null) upper.Setup();
if(middle3d != null)
{
foreach(VisualMiddle3D m in middle3d)
m.Setup();
foreach(VisualMiddle3D m in middle3d) m.Setup();
}
if(middleback != null)
{
foreach(VisualMiddleBack m in middleback)
m.Setup();
foreach(VisualMiddleBack m in middleback) m.Setup();
}
}
@ -82,13 +80,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
if(upper != null) upper.Selected = false;
if(middle3d != null)
{
foreach(VisualMiddle3D m in middle3d)
m.Selected = false;
foreach(VisualMiddle3D m in middle3d) m.Selected = false;
}
if(middleback != null)
{
foreach(VisualMiddleBack m in middleback)
m.Selected = false;
foreach(VisualMiddleBack m in middleback) m.Selected = false;
}
}
}

View file

@ -51,7 +51,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
public VisualUpper(BaseVisualMode mode, VisualSector vs, Sidedef s) : base(mode, vs, s)
{
//mxd
geoType = VisualGeometryType.WALL_UPPER;
geometrytype = VisualGeometryType.WALL_UPPER;
partname = "top";
// We have no destructor
GC.SuppressFinalize(this);
@ -293,69 +294,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
//mxd
public override void OnChangeTargetBrightness(bool up)
{
if(!General.Map.UDMF)
{
base.OnChangeTargetBrightness(up);
return;
}
int light = Sidedef.Fields.GetValue("light", 0);
bool absolute = Sidedef.Fields.GetValue("lightabsolute", false);
int newLight;
if(up)
newLight = General.Map.Config.BrightnessLevels.GetNextHigher(light, absolute);
else
newLight = General.Map.Config.BrightnessLevels.GetNextLower(light, absolute);
if(newLight == light) return;
//create undo
mode.CreateUndo("Change upper wall brightness", UndoGroup.SurfaceBrightnessChange, Sector.Sector.FixedIndex);
Sidedef.Fields.BeforeFieldsChange();
//apply changes
Sidedef.Fields["light"] = new UniValue(UniversalType.Integer, newLight);
mode.SetActionResult("Changed upper wall brightness to " + newLight + ".");
Sector.Sector.UpdateCache();
//rebuild sector
Sector.UpdateSectorGeometry(false);
}
//mxd
public override void OnTextureFit(bool fitWidth, bool fitHeight)
public override void OnTextureFit(FitTextureOptions options)
{
if(!General.Map.UDMF) return;
if(!Sidedef.HighRequired() || string.IsNullOrEmpty(Sidedef.HighTexture) || Sidedef.HighTexture == "-" || !Texture.IsImageLoaded) return;
string s;
if(fitWidth && fitHeight) s = "width and height";
else if(fitWidth) s = "width";
else s = "height";
//create undo
mode.CreateUndo("Fit texture (" + s + ")", UndoGroup.TextureOffsetChange, Sector.Sector.FixedIndex);
Sidedef.Fields.BeforeFieldsChange();
if(fitWidth)
{
float scaleX = Texture.ScaledWidth / Sidedef.Line.Length;
UDMFTools.SetFloat(Sidedef.Fields, "scalex_top", scaleX, 1.0f);
UDMFTools.SetFloat(Sidedef.Fields, "offsetx_top", -Sidedef.OffsetX, 0.0f);
}
if(fitHeight && Sidedef.Sector != null && Sidedef.Other.Sector != null)
{
float scaleY = (float)Texture.Height / (Sidedef.Sector.CeilHeight - Sidedef.Other.Sector.CeilHeight);
UDMFTools.SetFloat(Sidedef.Fields, "scaley_top", scaleY, 1.0f);
float offsetY = Tools.GetSidedefTopOffsetY(Sidedef, -Sidedef.OffsetY, scaleY, true) % Texture.Height;
UDMFTools.SetFloat(Sidedef.Fields, "offsety_top", offsetY, 0.0f);
}
FitTexture(options);
Setup();
}