diff --git a/Build/Scripting/ZDoom_DECORATE.cfg b/Build/Scripting/ZDoom_DECORATE.cfg index afc4c442892aac48fe8fde442f634c6a5966fea9..76c793570fddb9d9dc26fb24eec385ee64720fde 100644 --- a/Build/Scripting/ZDoom_DECORATE.cfg +++ b/Build/Scripting/ZDoom_DECORATE.cfg @@ -747,6 +747,12 @@ properties constants { +//rendering + FLATSPRITE; + ROLLSPRITE; + WALLSPRITE; + DONTFLIP; + ROLLCENTER; //pointers AAPTR_DEFAULT; AAPTR_NULL; diff --git a/Source/Core/Config/ThingTypeInfo.cs b/Source/Core/Config/ThingTypeInfo.cs index 6bba25ad2e9c3fad570b5a759017cb5218bb954f..d9ab5a3978f47c0d26e9baaa2580472b535435de 100644 --- a/Source/Core/Config/ThingTypeInfo.cs +++ b/Source/Core/Config/ThingTypeInfo.cs @@ -89,6 +89,7 @@ namespace CodeImp.DoomBuilder.Config //mxd. GZDoom rendering properties private ThingRenderMode rendermode; private bool rollsprite; + private bool rollcenter; private bool dontflip; #endregion @@ -129,6 +130,7 @@ namespace CodeImp.DoomBuilder.Config //mxd. GZDoom rendering properties public ThingRenderMode RenderMode { get { return rendermode; } } public bool RollSprite { get { return rollsprite; } } + public bool RollCenter { get { return rollcenter; } } public bool DontFlip { get { return dontflip; } } #endregion @@ -396,6 +398,7 @@ namespace CodeImp.DoomBuilder.Config this.rendermode = other.rendermode; this.dontflip = other.dontflip; this.rollsprite = other.rollsprite; + this.rollcenter = other.rollcenter; // We have no destructor GC.SuppressFinalize(this); @@ -460,13 +463,13 @@ namespace CodeImp.DoomBuilder.Config // Set sprite StateStructure.FrameInfo info = actor.FindSuitableSprite(); //mxd - if(!locksprite && !string.IsNullOrEmpty(info.Sprite)) //mxd. Added locksprite property + if(!locksprite && info != null) //mxd. Added locksprite property sprite = info.Sprite; else if(string.IsNullOrEmpty(sprite))//mxd sprite = DataManager.INTERNAL_PREFIX + "unknownthing"; //mxd. Store dynamic light name - lightname = info.LightName; + lightname = (info != null ? info.LightName : string.Empty); //mxd. Create sprite frame this.spriteframe = new[] { new SpriteFrameInfo { Sprite = sprite, SpriteLongName = Lump.MakeLongName(sprite, true) } }; @@ -514,7 +517,7 @@ namespace CodeImp.DoomBuilder.Config } //mxd. BRIGHT - this.bright = info.Bright || actor.GetFlagValue("bright", false); + this.bright = (info != null && info.Bright) || actor.GetFlagValue("bright", false); // Safety if(this.radius < 4f || this.fixedsize) this.radius = THING_FIXED_SIZE; @@ -528,7 +531,8 @@ namespace CodeImp.DoomBuilder.Config xybillboard = actor.GetFlagValue("forcexybillboard", false); //mxd //mxd. GZDoom rendering flags - rollsprite = actor.GetFlagValue("rollsprite", false); + rollsprite = actor.GetFlagValue("rollsprite", false); + if(rollsprite) rollcenter = actor.GetFlagValue("rollcenter", false); if(actor.GetFlagValue("wallsprite", false)) rendermode = ThingRenderMode.WALLSPRITE; if(actor.GetFlagValue("flatsprite", false)) { diff --git a/Source/Core/Map/MapSet.cs b/Source/Core/Map/MapSet.cs index f37f997bb78b24f6c74c7c3372e765bd3b493a52..8b7780add0b12110d5e48a796bd83ac045151cb8 100644 --- a/Source/Core/Map/MapSet.cs +++ b/Source/Core/Map/MapSet.cs @@ -3079,40 +3079,34 @@ namespace CodeImp.DoomBuilder.Map foreach(Linedef l in alllines) { // Remove line when it's start, center and end are inside a changed sector and neither side references it - if(l.Start != null && l.End != null) + if(l.Start != null && l.End != null + && (l.Front == null || !changedsectors.Contains(l.Front.Sector)) + && (l.Back == null || !changedsectors.Contains(l.Back.Sector))) { - if(l.Front == null && l.Back == null) - { - l.Dispose(); - } - else if((l.Front == null || !changedsectors.Contains(l.Front.Sector)) && - (l.Back == null || !changedsectors.Contains(l.Back.Sector))) + foreach(Sector s in changedsectors) { - foreach(Sector s in changedsectors) + if(s.Intersect(l.Start.Position) && s.Intersect(l.End.Position) && s.Intersect(l.GetCenterPoint())) { - if(s.Intersect(l.Start.Position) && s.Intersect(l.End.Position) && s.Intersect(l.GetCenterPoint())) - { - Vertex[] tocheck = { l.Start, l.End }; - l.Dispose(); + Vertex[] tocheck = { l.Start, l.End }; + l.Dispose(); - foreach(Vertex v in tocheck) + foreach(Vertex v in tocheck) + { + // If the newly created vertex only has 2 linedefs attached, then merge the linedefs + if(!v.IsDisposed && v.Linedefs.Count == 2 && splitverts.Contains(v)) { - // If the newly created vertex only has 2 linedefs attached, then merge the linedefs - if(!v.IsDisposed && v.Linedefs.Count == 2 && splitverts.Contains(v)) - { - Linedef ld1 = General.GetByIndex(v.Linedefs, 0); - Linedef ld2 = General.GetByIndex(v.Linedefs, 1); - Vertex v2 = (ld2.Start == v) ? ld2.End : ld2.Start; - if(ld1.Start == v) ld1.SetStartVertex(v2); else ld1.SetEndVertex(v2); - ld2.Dispose(); + Linedef ld1 = General.GetByIndex(v.Linedefs, 0); + Linedef ld2 = General.GetByIndex(v.Linedefs, 1); + Vertex v2 = (ld2.Start == v) ? ld2.End : ld2.Start; + if(ld1.Start == v) ld1.SetStartVertex(v2); else ld1.SetEndVertex(v2); + ld2.Dispose(); - // Trash vertex - v.Dispose(); - } + // Trash vertex + v.Dispose(); } - - break; } + + break; } } } diff --git a/Source/Core/VisualModes/VisualThing.cs b/Source/Core/VisualModes/VisualThing.cs index 6c54b6f474f8daa8e7563bb2f2a6e61b0917fd9d..a7f1c045860aca398736ac4a690d4d86f01025e3 100644 --- a/Source/Core/VisualModes/VisualThing.cs +++ b/Source/Core/VisualModes/VisualThing.cs @@ -265,7 +265,7 @@ namespace CodeImp.DoomBuilder.VisualModes } // This sets the vertices for the thing sprite - protected void SetVertices(WorldVertex[][] verts/*, Plane floor, Plane ceiling*/) + protected void SetVertices(WorldVertex[][] verts, Vector2D[] offsets/*, Plane floor, Plane ceiling*/) { // Copy vertices vertices = new WorldVertex[verts.Length][]; @@ -285,8 +285,23 @@ namespace CodeImp.DoomBuilder.VisualModes for(int c = 0; c < vertices.Length; c++) { if(triangles[c] < 2) continue; - float localcenterz = vertices[c][1].z * 0.5f; + Matrix transform, rotation; + float centerx, centerz; + + // ROLLCENTER flag support + if(info.RollSprite && info.RollCenter && thing.Roll != 0) + { + // Rotate around sprite center + centerx = offsets[c].x; + centerz = vertices[c][1].z * 0.5f - offsets[c].y; + } + else + { + // Sprite center is already where it needs to be + centerx = 0f; + centerz = 0f; + } switch(thing.RenderMode) { @@ -296,9 +311,35 @@ namespace CodeImp.DoomBuilder.VisualModes // Actor becomes a flat sprite which can be tilted with the use of the Pitch actor property. case ThingRenderMode.FLATSPRITE: - rotation = (info.RollSprite ? Matrix.RotationY(-thing.RollRad) * Matrix.RotationX(thing.PitchRad) : Matrix.RotationX(thing.PitchRad)) - * Matrix.RotationZ(thing.Angle); - transform = Matrix.Translation(0f, 0f, -localcenterz) * rotation * Matrix.Translation(0f, 0f, localcenterz); + transform = Matrix.Identity; + + // Apply roll + if(info.RollSprite && thing.Roll != 0) + { + if(info.RollCenter) + { + rotation = Matrix.RotationY(-thing.RollRad); + transform = Matrix.Translation(-centerx, -centerx, -centerz) * rotation * Matrix.Translation(centerx, centerx, centerz); + } + else + { + // Sprite center is already where it needs to be + transform = Matrix.RotationY(-thing.RollRad); + } + } + + // Pitch should be performed from the center of the sprite regardless of ROLLCENTER flag... + if(thing.Pitch != 0) + { + float localcenterz = vertices[c][1].z * 0.5f; + rotation = Matrix.RotationX(-thing.PitchRad); + transform *= Matrix.Translation(0f, 0f, -localcenterz) * rotation * Matrix.Translation(0f, 0f, localcenterz); + } + + // Add thing angle into the mix + transform *= Matrix.RotationZ(thing.Angle); + + // Apply transform for(int i = 0; i < vertices[c].Length; i++) { Vector4 transformed = Vector3.Transform(new Vector3(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), transform); @@ -310,8 +351,21 @@ namespace CodeImp.DoomBuilder.VisualModes // Similar to FLATSPRITE but is not affected by pitch. case ThingRenderMode.WALLSPRITE: - rotation = (info.RollSprite ? Matrix.RotationY(-thing.RollRad) * Matrix.RotationZ(thing.Angle) : Matrix.RotationZ(thing.Angle)); - transform = Matrix.Translation(0f, 0f, -localcenterz) * rotation * Matrix.Translation(0f, 0f, localcenterz); + // Apply sprite roll? + if(info.RollSprite && thing.Roll != 0) + { + rotation = Matrix.RotationY(-thing.RollRad) * Matrix.RotationZ(thing.Angle); + if(info.RollCenter) + transform = Matrix.Translation(-centerx, -centerx, -centerz) * rotation * Matrix.Translation(centerx, centerx, centerz); + else + transform = rotation; // Sprite center is already where it needs to be + } + else + { + transform = Matrix.RotationZ(thing.Angle); + } + + // Apply transform for(int i = 0; i < vertices[c].Length; i++) { Vector4 transformed = Vector3.Transform(new Vector3(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), transform); @@ -321,7 +375,7 @@ namespace CodeImp.DoomBuilder.VisualModes } break; - // Some old GLOOME stuff + #region Some old GLOOME FLOOR_SPRITE/CEILING_SPRITE support code /*case Thing.SpriteRenderMode.FLOOR_SPRITE: Matrix floorrotation = Matrix.RotationZ(info.RollSprite ? Thing.RollRad : 0f) * Matrix.RotationY(Thing.Angle) @@ -407,11 +461,18 @@ namespace CodeImp.DoomBuilder.VisualModes } } break;*/ + #endregion case ThingRenderMode.NORMAL: - if(info.RollSprite) + if(info.RollSprite && thing.Roll != 0) { - transform = Matrix.Translation(0f, 0f, -localcenterz) * Matrix.RotationY(-thing.RollRad) * Matrix.Translation(0f, 0f, localcenterz); + rotation = Matrix.RotationY(-thing.RollRad); + if(info.RollCenter) + transform = Matrix.Translation(-centerx, -centerx, -centerz) * rotation * Matrix.Translation(centerx, centerx, centerz); + else + transform = rotation; // Sprite center is already where it needs to be + + // Apply transform for(int i = 0; i < vertices[c].Length; i++) { Vector4 transformed = Vector3.Transform(new Vector3(vertices[c][i].x, vertices[c][i].y, vertices[c][i].z), transform); @@ -515,7 +576,7 @@ namespace CodeImp.DoomBuilder.VisualModes if(Thing.IsDirectional) { Matrix transform = Matrix.Scaling(thing.Size, thing.Size, thing.Size) - * (Matrix.RotationY(-Thing.RollRad) * Matrix.RotationX(Thing.PitchRad) * Matrix.RotationZ(Thing.Angle)) + * (Matrix.RotationY(-Thing.RollRad) * Matrix.RotationX(-Thing.PitchRad) * Matrix.RotationZ(Thing.Angle)) * (sizeless ? position : position * Matrix.Translation(0.0f, 0.0f, thingheight / 2f)); WorldVertex a0 = new WorldVertex(Vector3D.Transform(0.0f, 0.0f, 0.0f, transform)); //start diff --git a/Source/Core/ZDoom/ActorStructure.cs b/Source/Core/ZDoom/ActorStructure.cs index e53f19e0dda8531497fc51070e357d3d19281e62..81e6537b974c568ed13eff78f05cb7c9c4ec3c91 100644 --- a/Source/Core/ZDoom/ActorStructure.cs +++ b/Source/Core/ZDoom/ActorStructure.cs @@ -31,7 +31,7 @@ namespace CodeImp.DoomBuilder.ZDoom { #region ================== Constants - //private readonly string[] SPRITE_POSTFIXES = new[] {"2C8", "2D8", "2A8", "2B8", "1C1", "1D1", "1A1", "1B1", "A2", "A1", "A0", "2", "1", "0" }; + private readonly string[] SPRITE_CHECK_STATES = { "idle", "see", "inactive", "spawn" }; //mxd internal const string ACTOR_CLASS_SPECIAL_TOKENS = ":{}\n;,"; //mxd #endregion @@ -476,7 +476,7 @@ namespace CodeImp.DoomBuilder.ZDoom { //states if(states.Count == 0 && !string.IsNullOrEmpty(ti.Value.Sprite)) - states.Add("spawn", new StateStructure(ti.Value.Sprite.Substring(0, 4))); + states.Add("spawn", new StateStructure(ti.Value.Sprite.Substring(0, 5))); //flags if(ti.Value.Hangs && !flags.ContainsKey("spawnceiling")) @@ -668,8 +668,7 @@ namespace CodeImp.DoomBuilder.ZDoom /// </summary> public StateStructure.FrameInfo FindSuitableSprite() { - StateStructure.FrameInfo result = new StateStructure.FrameInfo(); //mxd - + // Info: actual sprites are resolved in ThingTypeInfo.SetupSpriteFrame() - mxd // Sprite forced? if(HasPropertyWithValue("$sprite")) { @@ -677,65 +676,32 @@ namespace CodeImp.DoomBuilder.ZDoom //mxd. Valid when internal or exists if(sprite.StartsWith(DataManager.INTERNAL_PREFIX, StringComparison.OrdinalIgnoreCase) || General.Map.Data.GetSpriteExists(sprite)) - { - result.Sprite = sprite; - return result; - } + return new StateStructure.FrameInfo { Sprite = sprite }; //mxd. Bitch and moan General.ErrorLogger.Add(ErrorType.Warning, "DECORATE warning in " + classname + ":" + doomednum + ". The sprite \"" + sprite + "\" assigned by the \"$sprite\" property does not exist."); } - // Try the idle state - if(HasState("idle")) - { - StateStructure s = GetState("idle"); - StateStructure.FrameInfo info = s.GetSprite(0); - if(!string.IsNullOrEmpty(info.Sprite)) result = info; - } - - // Try the see state - if(string.IsNullOrEmpty(result.Sprite) && HasState("see")) - { - StateStructure s = GetState("see"); - StateStructure.FrameInfo info = s.GetSprite(0); - if(!string.IsNullOrEmpty(info.Sprite)) result = info; - } - - // Try the inactive state - if(string.IsNullOrEmpty(result.Sprite) && HasState("inactive")) + //mxd. Try to get a suitable sprite from our hardcoded states list + foreach(string state in SPRITE_CHECK_STATES) { - StateStructure s = GetState("inactive"); - StateStructure.FrameInfo info = s.GetSprite(0); - if(!string.IsNullOrEmpty(info.Sprite)) result = info; - } - - // Try the spawn state - if(string.IsNullOrEmpty(result.Sprite) && HasState("spawn")) - { - StateStructure s = GetState("spawn"); + if(!HasState(state)) continue; + + StateStructure s = GetState(state); StateStructure.FrameInfo info = s.GetSprite(0); - if(!string.IsNullOrEmpty(info.Sprite)) result = info; + if(!string.IsNullOrEmpty(info.Sprite)) return info; } // Still no sprite found? then just pick the first we can find - if(string.IsNullOrEmpty(result.Sprite)) + Dictionary<string, StateStructure> list = GetAllStates(); + foreach(StateStructure s in list.Values) { - Dictionary<string, StateStructure> list = GetAllStates(); - foreach(StateStructure s in list.Values) - { - StateStructure.FrameInfo info = s.GetSprite(0); - if(!string.IsNullOrEmpty(info.Sprite)) - { - result = info; - break; - } - } + StateStructure.FrameInfo info = s.GetSprite(0); + if(!string.IsNullOrEmpty(info.Sprite)) return info; } - //mxd. We've found something. Or not... - //Info: actual sprites are resolved in ThingTypeInfo.SetupSpriteFrame() - return result; + //mxd. No dice... + return null; } #endregion diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs index 9217b07fe9d1309f4412313eab1eb2d317c9f257..bc4aed30f53c031be4ee5e7d1360e99440e94d5b 100644 --- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs @@ -275,19 +275,19 @@ namespace CodeImp.DoomBuilder.BuilderModes //mxd. Create verts for all sprite angles WorldVertex[][] allverts = new WorldVertex[info.SpriteFrame.Length][]; + Vector2D[] alloffsets = new Vector2D[info.SpriteFrame.Length]; base.textures = new ImageData[info.SpriteFrame.Length]; isloaded = true; for(int i = 0; i < sprites.Length; i++) { + Vector2D offsets = new Vector2D(); + // Check if the texture is loaded ImageData sprite = sprites[i]; sprite.LoadImage(); if(sprite.IsImageLoaded) { - float offsetx = 0.0f; - float offsety = 0.0f; - base.textures[i] = sprite; // Determine sprite size and offset @@ -296,16 +296,16 @@ namespace CodeImp.DoomBuilder.BuilderModes ISpriteImage spriteimg = sprite as ISpriteImage; if(spriteimg != null) { - offsetx = radius - spriteimg.OffsetX; - offsety = spriteimg.OffsetY - height; + offsets.x = radius - spriteimg.OffsetX; + offsets.y = spriteimg.OffsetY - height; } // Scale by thing type/actor scale // We do this after the offset x/y determination above, because that is entirely in sprite pixels space radius *= info.SpriteScale.Width; height *= info.SpriteScale.Height; - offsetx *= info.SpriteScale.Width; - offsety *= info.SpriteScale.Height; + offsets.x *= info.SpriteScale.Width; + offsets.y *= info.SpriteScale.Height; // Make vertices WorldVertex[] verts = new WorldVertex[6]; @@ -317,21 +317,21 @@ namespace CodeImp.DoomBuilder.BuilderModes if(sizeless) //mxd { float hh = height / 2; - verts[0] = new WorldVertex(-radius + offsetx, 0.0f, offsety - hh, sectorcolor, ul, 1.0f); - verts[1] = new WorldVertex(-radius + offsetx, 0.0f, hh + offsety, sectorcolor, ul, 0.0f); - verts[2] = new WorldVertex(+radius + offsetx, 0.0f, hh + offsety, sectorcolor, ur, 0.0f); + verts[0] = new WorldVertex(-radius + offsets.x, 0.0f, offsets.y - hh, sectorcolor, ul, 1.0f); + verts[1] = new WorldVertex(-radius + offsets.x, 0.0f, hh + offsets.y, sectorcolor, ul, 0.0f); + verts[2] = new WorldVertex(+radius + offsets.x, 0.0f, hh + offsets.y, sectorcolor, ur, 0.0f); verts[3] = verts[0]; verts[4] = verts[2]; - verts[5] = new WorldVertex(+radius + offsetx, 0.0f, offsety - hh, sectorcolor, ur, 1.0f); + verts[5] = new WorldVertex(+radius + offsets.x, 0.0f, offsets.y - hh, sectorcolor, ur, 1.0f); } else { - verts[0] = new WorldVertex(-radius + offsetx, 0.0f, offsety, sectorcolor, ul, 1.0f); - verts[1] = new WorldVertex(-radius + offsetx, 0.0f, height + offsety, sectorcolor, ul, 0.0f); - verts[2] = new WorldVertex(+radius + offsetx, 0.0f, height + offsety, sectorcolor, ur, 0.0f); + verts[0] = new WorldVertex(-radius + offsets.x, 0.0f, offsets.y, sectorcolor, ul, 1.0f); + verts[1] = new WorldVertex(-radius + offsets.x, 0.0f, height + offsets.y, sectorcolor, ul, 0.0f); + verts[2] = new WorldVertex(+radius + offsets.x, 0.0f, height + offsets.y, sectorcolor, ur, 0.0f); verts[3] = verts[0]; verts[4] = verts[2]; - verts[5] = new WorldVertex(+radius + offsetx, 0.0f, offsety, sectorcolor, ur, 1.0f); + verts[5] = new WorldVertex(+radius + offsets.x, 0.0f, offsets.y, sectorcolor, ur, 1.0f); } allverts[i] = verts; } @@ -344,6 +344,10 @@ namespace CodeImp.DoomBuilder.BuilderModes float radius = Math.Min(thingradius, thingheight / 2f); float height = Math.Min(thingradius * 2f, thingheight); + //mxd. Determine sprite offsets + offsets.x = radius; + offsets.y = height / 2; + // Make vertices WorldVertex[] verts = new WorldVertex[6]; verts[0] = new WorldVertex(-radius, 0.0f, 0.0f, sectorcolor, 0.0f, 1.0f); @@ -354,10 +358,13 @@ namespace CodeImp.DoomBuilder.BuilderModes verts[5] = new WorldVertex(+radius, 0.0f, 0.0f, sectorcolor, 1.0f, 1.0f); allverts[i] = verts; } + + //mxd. Store offsets + alloffsets[i] = offsets; } //mxd - SetVertices(allverts/*, floor, ceiling*/); + SetVertices(allverts, alloffsets/*, floor, ceiling*/); // Determine position Vector3D pos = Thing.Position;