// This adds the matching, unmarked sidedefs from a vertex for texture alignment private static void AddSidedefsForAlignment(Stack <SidedefAlignJob> stack, Vertex v, bool forward, float offsetx, long texturelongname) { foreach (Linedef ld in v.Linedefs) { Sidedef side1 = forward ? ld.Front : ld.Back; Sidedef side2 = forward ? ld.Back : ld.Front; if ((ld.Start == v) && (side1 != null) && !side1.Marked) { if (SidedefTextureMatch(side1, texturelongname)) { SidedefAlignJob nj = new SidedefAlignJob(); nj.forward = forward; nj.offsetx = offsetx; nj.sidedef = side1; stack.Push(nj); } } else if ((ld.End == v) && (side2 != null) && !side2.Marked) { if (SidedefTextureMatch(side2, texturelongname)) { SidedefAlignJob nj = new SidedefAlignJob(); nj.forward = forward; nj.offsetx = offsetx; nj.sidedef = side2; stack.Push(nj); } } } }
// This performs texture alignment along all walls that match with the same texture // NOTE: This method uses the sidedefs marking to indicate which sides have been aligned // When resetsidemarks is set to true, all sidedefs will first be marked false (not aligned). // Setting resetsidemarks to false is usefull to align only within a specific selection // (set the marked property to true for the sidedefs outside the selection) public static void AutoAlignTextures(Sidedef start, SidedefPart part, ImageData texture, bool alignx, bool aligny, bool resetsidemarks) { Stack <SidedefAlignJob> todo = new Stack <SidedefAlignJob>(50); float scalex = (General.Map.Config.ScaledTextureOffsets && !texture.WorldPanning) ? texture.Scale.x : 1.0f; float scaley = (General.Map.Config.ScaledTextureOffsets && !texture.WorldPanning) ? texture.Scale.y : 1.0f; // Mark all sidedefs false (they will be marked true when the texture is aligned) if (resetsidemarks) { General.Map.Map.ClearMarkedSidedefs(false); } if (!texture.IsImageLoaded) { return; } // Determine the Y alignment float ystartalign = start.OffsetY; switch (part) { case SidedefPart.Upper: ystartalign += start.Fields.GetValue("offsety_top", 0.0f); break; case SidedefPart.Middle: ystartalign += start.Fields.GetValue("offsety_mid", 0.0f); break; case SidedefPart.Lower: ystartalign += start.Fields.GetValue("offsety_bottom", 0.0f); break; } // Begin with first sidedef SidedefAlignJob first = new SidedefAlignJob(); first.sidedef = start; first.offsetx = start.OffsetX; switch (part) { case SidedefPart.Upper: first.offsetx += start.Fields.GetValue("offsetx_top", 0.0f); break; case SidedefPart.Middle: first.offsetx += start.Fields.GetValue("offsetx_mid", 0.0f); break; case SidedefPart.Lower: first.offsetx += start.Fields.GetValue("offsetx_bottom", 0.0f); break; } first.forward = true; todo.Push(first); // Continue until nothing more to align while (todo.Count > 0) { Vertex v; float forwardoffset; float backwardoffset; float offsetscalex = 1.0f; // Get the align job to do SidedefAlignJob j = todo.Pop(); bool matchtop = ((j.sidedef.LongHighTexture == texture.LongName) && j.sidedef.HighRequired()); bool matchbottom = ((j.sidedef.LongLowTexture == texture.LongName) && j.sidedef.LowRequired()); bool matchmid = ((j.sidedef.LongMiddleTexture == texture.LongName) && (j.sidedef.MiddleRequired() || ((j.sidedef.MiddleTexture.Length > 0) && (j.sidedef.MiddleTexture[0] != '-')))); if (matchtop) { offsetscalex = j.sidedef.Fields.GetValue("scalex_top", 1.0f); } else if (matchbottom) { offsetscalex = j.sidedef.Fields.GetValue("scalex_bottom", 1.0f); } else if (matchmid) { offsetscalex = j.sidedef.Fields.GetValue("scalex_mid", 1.0f); } if (j.forward) { // Apply alignment if (alignx) { //j.sidedef.OffsetX = j.offsetx; float offset = j.offsetx; offset %= (float)texture.Height; offset -= j.sidedef.OffsetX; j.sidedef.Fields.BeforeFieldsChange(); if (matchtop) { j.sidedef.Fields["offsetx_top"] = new UniValue(UniversalType.Float, offset); } if (matchbottom) { j.sidedef.Fields["offsetx_bottom"] = new UniValue(UniversalType.Float, offset); } if (matchmid) { j.sidedef.Fields["offsetx_mid"] = new UniValue(UniversalType.Float, offset); } } if (aligny) { //j.sidedef.OffsetY = (int)Math.Round((start.Sector.CeilHeight - j.sidedef.Sector.CeilHeight) / scaley) + start.OffsetY; float offset = ((float)(start.Sector.CeilHeight - j.sidedef.Sector.CeilHeight) / scaley) + ystartalign; offset %= (float)texture.Height; offset -= j.sidedef.OffsetY; j.sidedef.Fields.BeforeFieldsChange(); if (matchtop) { j.sidedef.Fields["offsety_top"] = new UniValue(UniversalType.Float, offset); } if (matchbottom) { j.sidedef.Fields["offsety_bottom"] = new UniValue(UniversalType.Float, offset); } if (matchmid) { j.sidedef.Fields["offsety_mid"] = new UniValue(UniversalType.Float, offset); } } forwardoffset = j.offsetx + (int)Math.Round(j.sidedef.Line.Length / scalex * offsetscalex); backwardoffset = j.offsetx; // Done this sidedef j.sidedef.Marked = true; // Add sidedefs backward (connected to the left vertex) v = j.sidedef.IsFront ? j.sidedef.Line.Start : j.sidedef.Line.End; AddSidedefsForAlignment(todo, v, false, backwardoffset, texture.LongName); // Add sidedefs forward (connected to the right vertex) v = j.sidedef.IsFront ? j.sidedef.Line.End : j.sidedef.Line.Start; AddSidedefsForAlignment(todo, v, true, forwardoffset, texture.LongName); } else { // Apply alignment if (alignx) { //j.sidedef.OffsetX = j.offsetx - (int)Math.Round(j.sidedef.Line.Length / scalex); float offset = j.offsetx - (int)Math.Round(j.sidedef.Line.Length / scalex); offset %= (float)texture.Height; offset -= j.sidedef.OffsetX; j.sidedef.Fields.BeforeFieldsChange(); if (matchtop) { j.sidedef.Fields["offsetx_top"] = new UniValue(UniversalType.Float, offset); } if (matchbottom) { j.sidedef.Fields["offsetx_bottom"] = new UniValue(UniversalType.Float, offset); } if (matchmid) { j.sidedef.Fields["offsetx_mid"] = new UniValue(UniversalType.Float, offset); } } if (aligny) { //j.sidedef.OffsetY = (int)Math.Round((start.Sector.CeilHeight - j.sidedef.Sector.CeilHeight) / scaley) + start.OffsetY; float offset = ((float)(start.Sector.CeilHeight - j.sidedef.Sector.CeilHeight) / scaley) + ystartalign; offset %= (float)texture.Height; offset -= j.sidedef.OffsetY; j.sidedef.Fields.BeforeFieldsChange(); if (matchtop) { j.sidedef.Fields["offsety_top"] = new UniValue(UniversalType.Float, offset); } if (matchbottom) { j.sidedef.Fields["offsety_bottom"] = new UniValue(UniversalType.Float, offset); } if (matchmid) { j.sidedef.Fields["offsety_mid"] = new UniValue(UniversalType.Float, offset); } } forwardoffset = j.offsetx; backwardoffset = j.offsetx - (int)Math.Round(j.sidedef.Line.Length / scalex * offsetscalex); // Done this sidedef j.sidedef.Marked = true; // Add sidedefs forward (connected to the right vertex) v = j.sidedef.IsFront ? j.sidedef.Line.End : j.sidedef.Line.Start; AddSidedefsForAlignment(todo, v, true, forwardoffset, texture.LongName); // Add sidedefs backward (connected to the left vertex) v = j.sidedef.IsFront ? j.sidedef.Line.Start : j.sidedef.Line.End; AddSidedefsForAlignment(todo, v, false, backwardoffset, texture.LongName); } } }