public void Place(PlacingTexture p) { if (occupant != null || first != null) throw new InvalidOperationException(); if (p.CorrectedWidth > width || p.CorrectedHeight > height) throw new InvalidOperationException(); if (p.CorrectedWidth == width) { if (p.CorrectedHeight == height) { occupant = p; } else { SplitY(p.CorrectedHeight); first.Place(p); } } else { if (p.CorrectedHeight == height) { SplitX(p.CorrectedWidth); first.Place(p); } else { int diff_w = width - p.CorrectedWidth; int diff_h = height - p.CorrectedHeight; if (diff_w >= diff_h) { SplitX(p.CorrectedWidth); first.Place(p); } else { SplitY(p.CorrectedHeight); first.Place(p); } } } }
private List<PlacingTexture> PlaceAllTextures(out int width, out int height) { // Divides space using a binary tree and places textures in nodes containing enough room. // http://www.blackpawn.com/texts/lightmaps/ List<PlacingTexture> result = new List<PlacingTexture>(m_textures.Count); long pixelcount = 0; int max_width = 0; int max_height = 0; int y = 0; foreach (LoadingTexture l in m_textures) { PlacingTexture p = new PlacingTexture(l); max_width = Math.Max(max_width, p.CorrectedWidth); max_height = Math.Max(max_height, p.CorrectedHeight); pixelcount += p.Area; p.index = y; result.Add(p); y++; } // sort by area descending result.Sort((p, q) => q.Area.CompareTo(p.Area)); int min_width = Math.Max(max_width, MinWidth); int min_height = Math.Max(max_height, MinHeight); int guess_width, guess_height; if (MinWidth == 0 && MinHeight == 0) { guess_width = (int)(Math.Sqrt(pixelcount * 2.0d) + 1); guess_height = Math.Max(min_height, guess_width); guess_width = Math.Max(min_width, guess_width); } else if (MinWidth == 0) { guess_width = Math.Max((int)(pixelcount / min_height), min_width); guess_height = min_height; } else if (MinHeight == 0) { guess_width = min_width; guess_height = Math.Max((int)(pixelcount / MinWidth), min_height); } else { guess_width = min_width; guess_height = min_height; } int real_max_width = MaxWidth; int real_max_height = MaxHeight; if (RequirePot) { guess_width = 1 << (MathHelper.Log2(guess_width)); guess_height = 1 << (MathHelper.Log2(guess_height)); real_max_width = 1 << (MathHelper.Log2(MaxWidth)); real_max_height = 1 << (MathHelper.Log2(MaxHeight)); } if (guess_width == 0) guess_width = guess_height; if (guess_width == 0) { guess_width = 512; guess_height = 512; } PlacingNode root = new PlacingNode(0, 0, guess_width, guess_height); for (int x = 0; x < result.Count; x++) { PlacingTexture p = result[x]; PlacingNode node = root.FindPlacement(p); if (node == null) { if (guess_width <= guess_height && (guess_width < real_max_width || real_max_width == 0)) guess_width = 1 << (MathHelper.Log2(guess_width) + 1); else if (guess_height < real_max_height || real_max_height == 0) guess_height = 1 << (MathHelper.Log2(guess_height) + 1); else if (guess_width < real_max_width || real_max_width == 0) guess_width = 1 << (MathHelper.Log2(guess_width) + 1); else throw new Exception("No room left to place textures."); AssertHelper.Assert(guess_width > root.width || guess_height > root.height); PlacingNode new_root = new PlacingNode(guess_width, guess_height); if (guess_width > root.width) { AssertHelper.Assert(guess_height == root.height); new_root.first = root; new_root.second = new PlacingNode(root.width, 0, guess_width - root.width, root.height); } else { AssertHelper.Assert(guess_width == root.width); new_root.first = root; new_root.second = new PlacingNode(0, root.height, root.width, guess_height - root.height); } root = new_root; x--; continue; } node.Place(p); p.X = node.x; p.Y = node.y; result[x] = p; } width = root.width; height = root.height; result.Sort((p, q) => p.index.CompareTo(q.index)); return result; }
public PlacingNode FindPlacement(PlacingTexture p) { // finds the best spot to put this texture, returns it. // returns null if this texture can't fit. if (occupant != null) return null; if (first != null) { PlacingNode n1 = first.FindPlacement(p); if (n1 == null) return second.FindPlacement(p); return n1; } else { if (width < p.CorrectedWidth || height < p.CorrectedHeight) return null; return this; } }
private void DrawPlacingTexture(System.Drawing.Graphics g, PlacingTexture p) { bool draw_topleft_margin = p.LoadingTexture.PaddingFill != PaddingFill.None && p.LoadingTexture.TexelPosition != TexelPosition.Stretch && p.LoadingTexture.TexelPosition != TexelPosition.TopLeft; bool draw_bottomright_margin = p.LoadingTexture.PaddingFill != PaddingFill.None && p.LoadingTexture.TexelPosition != TexelPosition.Stretch; int offset_topleft = draw_topleft_margin ? 1 : 0; // draw the actual texture g.DrawImage(p.LoadingTexture.Bitmap, p.X + offset_topleft, p.Y + offset_topleft, p.LoadingTexture.Bitmap.Width, p.LoadingTexture.Bitmap.Height); switch (p.LoadingTexture.PaddingFill) { case PaddingFill.Wrap: if (draw_topleft_margin) { // left g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X, p.Y + 1, 1, p.LoadingTexture.Bitmap.Height), new Rectangle(p.LoadingTexture.Bitmap.Width - 1, 0, 1, p.LoadingTexture.Bitmap.Height), GraphicsUnit.Pixel); // top g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X + 1, p.Y, p.LoadingTexture.Bitmap.Width, 1), new Rectangle(0, p.LoadingTexture.Bitmap.Height - 1, p.LoadingTexture.Bitmap.Width, 1), GraphicsUnit.Pixel); // topleft pixel g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X, p.Y, 1, 1), new Rectangle(p.LoadingTexture.Bitmap.Width - 1, p.LoadingTexture.Bitmap.Height - 1, 1, 1), GraphicsUnit.Pixel); AssertHelper.Assert(draw_bottomright_margin); // bottomleft pixel g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X, p.Y + p.LoadingTexture.Bitmap.Height + 1, 1, 1), new Rectangle(p.LoadingTexture.Bitmap.Width - 1, 0, 1, 1), GraphicsUnit.Pixel); // topright pixel g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X + p.LoadingTexture.Bitmap.Width + 1, p.Y, 1, 1), new Rectangle(0, p.LoadingTexture.Bitmap.Height - 1, 1, 1), GraphicsUnit.Pixel); } if (draw_bottomright_margin) { // right g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X + p.LoadingTexture.Bitmap.Width + offset_topleft, p.Y + offset_topleft, 1, p.LoadingTexture.Bitmap.Height), new Rectangle(0, 0, 1, p.LoadingTexture.Bitmap.Height), GraphicsUnit.Pixel); // bottom g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X + offset_topleft, p.Y + p.LoadingTexture.Bitmap.Height + offset_topleft, p.LoadingTexture.Bitmap.Width, 1), new Rectangle(0, 0, p.LoadingTexture.Bitmap.Width, 1), GraphicsUnit.Pixel); // bottomright pixel g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X + p.LoadingTexture.Bitmap.Width + offset_topleft, p.Y + p.LoadingTexture.Bitmap.Height + offset_topleft, 1, 1), new Rectangle(0, 0, 1, 1), GraphicsUnit.Pixel); } break; case PaddingFill.Clamp: if (draw_topleft_margin) { // left g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X, p.Y + 1, 1, p.LoadingTexture.Bitmap.Height), new Rectangle(0, 0, 1, p.LoadingTexture.Bitmap.Height), GraphicsUnit.Pixel); // top g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X + 1, p.Y, p.LoadingTexture.Bitmap.Width, 1), new Rectangle(0, 0, p.LoadingTexture.Bitmap.Width, 1), GraphicsUnit.Pixel); // topleft pixel g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X, p.Y, 1, 1), new Rectangle(0, 0, 1, 1), GraphicsUnit.Pixel); AssertHelper.Assert(draw_bottomright_margin); // bottomleft pixel g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X, p.Y + p.LoadingTexture.Bitmap.Height + 1, 1, 1), new Rectangle(0, p.LoadingTexture.Bitmap.Height - 1, 1, 1), GraphicsUnit.Pixel); // topright pixel g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X + p.LoadingTexture.Bitmap.Width + 1, p.Y, 1, 1), new Rectangle(p.LoadingTexture.Bitmap.Width - 1, 0, 1, 1), GraphicsUnit.Pixel); } if (draw_bottomright_margin) { // right g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X + p.LoadingTexture.Bitmap.Width + offset_topleft, p.Y + offset_topleft, 1, p.LoadingTexture.Bitmap.Height), new Rectangle(p.LoadingTexture.Bitmap.Width - 1, 0, 1, p.LoadingTexture.Bitmap.Height), GraphicsUnit.Pixel); // bottom g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X + offset_topleft, p.Y + p.LoadingTexture.Bitmap.Height + offset_topleft, p.LoadingTexture.Bitmap.Width, 1), new Rectangle(0, p.LoadingTexture.Bitmap.Height - 1, p.LoadingTexture.Bitmap.Width, 1), GraphicsUnit.Pixel); // bottomright pixel g.DrawImage(p.LoadingTexture.Bitmap, new Rectangle(p.X + p.LoadingTexture.Bitmap.Width + offset_topleft, p.Y + p.LoadingTexture.Bitmap.Height + offset_topleft, 1, 1), new Rectangle(p.LoadingTexture.Bitmap.Width - 1, p.LoadingTexture.Bitmap.Height - 1, 1, 1), GraphicsUnit.Pixel); } break; case PaddingFill.Solid: Brush brush = new SolidBrush(p.LoadingTexture.PaddingColour); if (draw_topleft_margin) { // left g.FillRectangle(brush, new Rectangle(p.X, p.Y + 1, 1, p.LoadingTexture.Bitmap.Height)); // top g.FillRectangle(brush, new Rectangle(p.X + 1, p.Y, p.LoadingTexture.Bitmap.Width, 1)); // topleft pixel g.FillRectangle(brush, new Rectangle(p.X, p.Y, 1, 1)); AssertHelper.Assert(draw_bottomright_margin); // bottomleft pixel g.FillRectangle(brush, new Rectangle(p.X, p.Y + p.LoadingTexture.Bitmap.Height + 1, 1, 1)); // topright pixel g.FillRectangle(brush, new Rectangle(p.X + p.LoadingTexture.Bitmap.Width + 1, p.Y, 1, 1)); } if (draw_bottomright_margin) { // right g.FillRectangle(brush, new Rectangle(p.X + p.LoadingTexture.Bitmap.Width + offset_topleft, p.Y + offset_topleft, 1, p.LoadingTexture.Bitmap.Height)); // bottom g.FillRectangle(brush, new Rectangle(p.X + offset_topleft, p.Y + p.LoadingTexture.Bitmap.Height + offset_topleft, p.LoadingTexture.Bitmap.Width, 1)); // bottomright pixel g.FillRectangle(brush, new Rectangle(p.X + p.LoadingTexture.Bitmap.Width + offset_topleft, p.Y + p.LoadingTexture.Bitmap.Height + offset_topleft, 1, 1)); } break; } }