protected static void SaveLibgdxFile(StreamWriter sw, AtlasNode node) { // This node write contents only if it is a leaf node if (node.name != "Unknown") { int index = GetIndex(ref node.name); // image name sw.WriteLine(node.name); // rotate sw.WriteLine(" rotate: false"); // xy sw.WriteLine(" xy: {0}, {1}", node.rc.X, node.rc.Y); // size sw.WriteLine(" size: {0}, {1}", node.rc.Width, node.rc.Height); // orig sw.WriteLine(" orig: {0}, {1}", node.rc.Width, node.rc.Height); // offset sw.WriteLine(" offset: {0}, {1}", 0, 0); // index sw.WriteLine(" index: {0}", index); } if (node.child != null) { if (node.child[0] != null) { SaveLibgdxFile(sw, node.child[0]); } if (node.child[1] != null) { SaveLibgdxFile(sw, node.child[1]); } } }
public AtlasAllocator(int width, int height) { m_Root = new AtlasNode(); m_Root.m_Rect.Set(width, height, 0, 0); m_Width = width; m_Height = height; }
private bool IsUniq(AtlasNode atlasMap) { var uniqTest = GameController.Memory.ReadStringU(GameController.Memory.Read <long>(atlasMap.Address + 0x3c, 0)); return(!string.IsNullOrEmpty(uniqTest) && uniqTest.Contains("Uniq") || Vector2.Distance(atlasMap.Pos, new Vector2(294.979f, 386.641f)) < 5); }
private void load(AtlasNode root) { // heavily based on // http://www.blackpawn.com/texts/lightmaps/default.html // basically it uses a kd-tree to pack the lightmaps // TODO: this shoudln't be hardcoded!! int outputWidth = 512; int outputHeight = 512; _packedTexture = new BitmapSurface(512, 512, null); root.AddToBitmap(_packedTexture); // create the packed rectangles root.UpdateRectList(_packedTextureRects); // now we have the rects, but they need to be converted from pixel coords /* for (int i = 0; i < _packedTextureRects.Length; i++) * { * Rect r = _packedTextureRects[i]; * r.X /= outputWidth; * r.Y /= outputHeight; * r.Width /= outputWidth; * r.Height /= outputHeight; * * _packedTextureRects[i] = r; * }*/ Utils.WriteTga("lightmap.tga", _packedTexture.Pixels, 512, 512); }
public AtlasAllocator(int width, int height, bool potPadding) { m_Root = new AtlasNode(); m_Root.m_Rect.Set(width, height, 0, 0); m_Width = width; m_Height = height; powerOfTwoPadding = potPadding; }
// The insert function traverses the tree looking for a place to insert the texture. // It returns the node of the atlas the texture can go into or null to say it can't fit. // Note we really don't have to store the rectangle for each node. // All we need is a split direction and coordinate like in a kd-tree, but it's more convenient with rects. public AtlasNode Insert(Texture2D image, int index) { if (image == null) // Obviously an error! return null; if (child != null) {// If this node is not a leaf, try inserting into first child. AtlasNode newNode = child[0].Insert(image, index); if (newNode != null) return newNode; // No more room in first child, insert into second child! return child[1].Insert(image, index); } else { // If there is already a lightmap in this node, early out if (hasImage) return null; // If this node is too small for the image, return if (!ImageFits(image, rc)) return null; // If the image is perfect, accept! if (PerfectFit(image, rc)) { hasImage = true; imageRef = image; name = imageRef.name; sortIndex = index; return this; } // If we made it this far, this node must be split. child = new AtlasNode[2]; child[0] = new AtlasNode(); child[1] = new AtlasNode(); // Decide which way to split image float deltaW = rc.width - image.width; float deltaH = rc.height - image.height; if (deltaW > deltaH) { child[0].rc = new Rect(rc.xMin, rc.yMin, image.width, rc.height); child[1].rc = new Rect(rc.xMin + image.width + TEXTURE_PADDING, rc.yMin, rc.width - (image.width + TEXTURE_PADDING), rc.height); } else { child[0].rc = new Rect(rc.xMin, rc.yMin, rc.width, image.height); child[1].rc = new Rect(rc.xMin, rc.yMin + image.height + TEXTURE_PADDING, rc.width, rc.height - (image.height + TEXTURE_PADDING)); } // Lets try inserting into first child, eh? return child[0].Insert(image, index); } }
private void KeyboardMouseEvents_MouseDownExt(object sender, Gma.System.MouseKeyHook.MouseEventExtArgs e) { if (BuyAtlasNode == null) { return; } TradeProcessor.OpenBuyMap(BuyAtlasNode.Area.Name, IsUniq(BuyAtlasNode), GameController.Game.IngameState.ServerData.League); BuyAtlasNode = null; }
public AtlasAllocator(int width, int height, bool potPadding) { m_Root = new AtlasNode(); m_Root.m_Rect.Set(width, height, 0, 0); m_Width = width; m_Height = height; powerOfTwoPadding = potPadding; m_NodePool = new ObjectPool <AtlasNode>(_ => { }, _ => { }); }
private void KeyboardMouseEvents_MouseDownExt(object sender, Gma.System.MouseKeyHook.MouseEventExtArgs e) { if (BuyAtlasNode == null) { return; } TradeProcessor.OpenBuyMap(BuyAtlasNode.Area.Name, IsUniq(BuyAtlasNode)); BuyAtlasNode = null; }
public void Release() { if (m_RightChild != null) { m_RightChild.Release(); m_BottomChild.Release(); } m_RightChild = null; m_BottomChild = null; }
public AtlasNode Insert(Image surface) { if (left != null) { AtlasNode rv; if (right == null) { throw new InvalidOperationException("AtlasNode(): error"); } rv = left.Insert(surface); if (rv == null) { rv = right.Insert(surface); } return(rv); } int img_width = surface.Width + padding * 2; int img_height = surface.Height + padding * 2; if (in_use || img_width > width || img_height > height) { return(null); } if (img_width == width && img_height == height) { in_use = true; tex = surface; return(this); } if (width - img_width > height - img_height) { /* extend to the right */ left = new AtlasNode(x, y, img_width, height, padding); right = new AtlasNode(x + img_width, y, width - img_width, height, padding); } else { /* extend to bottom */ left = new AtlasNode(x, y, width, img_height, padding); right = new AtlasNode(x, y + img_height, width, height - img_height, padding); } return(left.Insert(surface)); }
public AtlasNode(int x, int y, int width, int height, int padding) { left = null; right = null; tex = null; this.x = x; this.y = y; this.width = width; this.height = height; in_use = false; this.padding = padding; }
public TextureAtlas(BitmapSurface[] surfaces) { AtlasNode root = new AtlasNode(); root.Rectangle = new Rect(0, 0, 512, 512); _packedTextureRects = new Rect[surfaces.Length]; for (int i = 0; i < surfaces.Length; i++) { root.Insert(surfaces[i], i); } load(root); }
public void Release(ref ObjectPool <AtlasNode> pool) { if (m_RightChild != null) { m_RightChild.Release(ref pool); m_BottomChild.Release(ref pool); pool.Release(m_RightChild); pool.Release(m_BottomChild); } m_RightChild = null; m_BottomChild = null; m_Rect = Vector4.zero; }
public Vector4 m_Rect = new Vector4(0, 0, 0, 0); // x,y is width and height (scale) z,w offset into atlas (bias) public AtlasNode Allocate(int width, int height) { // not a leaf node, try children if (m_RightChild != null) { AtlasNode node = m_RightChild.Allocate(width, height); if (node == null) { node = m_BottomChild.Allocate(width, height); } return(node); } //leaf node, check for fit if ((width <= m_Rect.x) && (height <= m_Rect.y)) { // perform the split m_RightChild = new AtlasNode(); m_BottomChild = new AtlasNode(); if (width > height) // logic to decide which way to split { // +--------+------+ m_RightChild.m_Rect.z = m_Rect.z + width; // | | | m_RightChild.m_Rect.w = m_Rect.w; // +--------+------+ m_RightChild.m_Rect.x = m_Rect.x - width; // | | m_RightChild.m_Rect.y = height; // | | // +---------------+ m_BottomChild.m_Rect.z = m_Rect.z; m_BottomChild.m_Rect.w = m_Rect.w + height; m_BottomChild.m_Rect.x = m_Rect.x; m_BottomChild.m_Rect.y = m_Rect.y - height; } else { // +---+-----------+ m_RightChild.m_Rect.z = m_Rect.z + width; // | | | m_RightChild.m_Rect.w = m_Rect.w; // | | | m_RightChild.m_Rect.x = m_Rect.x - width; // +---+ + m_RightChild.m_Rect.y = m_Rect.y; // | | | // +---+-----------+ m_BottomChild.m_Rect.z = m_Rect.z; m_BottomChild.m_Rect.w = m_Rect.w + height; m_BottomChild.m_Rect.x = width; m_BottomChild.m_Rect.y = m_Rect.y - height; } m_Rect.x = width; m_Rect.y = height; return(this); } return(null); }
public bool Allocate(ref Vector4 result, int width, int height) { AtlasNode node = m_Root.Allocate(width, height, powerOfTwoPadding); if (node != null) { result = node.m_Rect; return(true); } else { result = Vector4.zero; return(false); } }
public Int16 AtlasNodeCreate(Int16 parent) { Debug.Assert((m_Next < m_Nodes.Length) || (m_FreelistHead != -1), "Error: AtlasNodePool: Out of memory. Please pre-allocate pool to larger capacity"); if (m_FreelistHead != -1) { Int16 freelistHeadNext = m_Nodes[m_FreelistHead].m_FreelistNext; m_Nodes[m_FreelistHead] = new AtlasNode(m_FreelistHead, parent); Int16 res = m_FreelistHead; m_FreelistHead = freelistHeadNext; return(res); } m_Nodes[m_Next] = new AtlasNode(m_Next, parent); return(m_Next++); }
public TextureAtlas(TextureResource[] textures) { // heavily based on // http://www.blackpawn.com/texts/lightmaps/default.html // basically it uses a kd-tree to pack the lightmaps AtlasNode root = new AtlasNode(); root.Rectangle = new Rect(0, 0, 512, 512); _packedTextureRects = new Rect[textures.Length]; for (int i = 0; i < textures.Length; i++) { root.Insert(new BitmapSurface(textures[i]), i); } load(root); }
private void Initialize() { AtlasNode atlasnode = m_fontAtlas.atlas.GetNode(m_character); MaterialDX11 mat = new MaterialDX11("vDefault.cso", "pText.cso"); mat.SetMainColor(1.0f, 0.0f, 0.0f, 1.0f); mat.AddShaderResourceView(m_fontAtlas.atlas.SRV); transform_.Translate( ((float)atlasnode.Width / 2.0f), -(atlasnode.Height / 2.0f) - atlasnode.YOffset, 0.0f); transform_.SetScale( atlasnode.Width, atlasnode.Height, 1.0f); //modelrenderer_ = new MeshRenderer(mat, Quad.GetMesh()); SamplerState state = new SamplerState(ApplicationDX11.Instance.Device, new SamplerStateDescription() { AddressU = TextureAddressMode.Wrap, AddressV = TextureAddressMode.Wrap, AddressW = TextureAddressMode.Wrap, BorderColor = new Color4(0.0f, 1.0f, 0.0f, 1.0f), ComparisonFunction = Comparison.LessEqual, Filter = Filter.MinLinearMagMipPoint, MaximumAnisotropy = 0, MaximumLod = 0, MinimumLod = 0, MipLodBias = 0 }); mat.samplers.Add(state); mat.SetTextureXOffset((float)atlasnode.X / (float)m_fontAtlas.atlas.Width); mat.SetTextureYOffset((float)atlasnode.Y / (float)m_fontAtlas.atlas.Height); mat.SetTextureWidth((float)atlasnode.Width / (float)m_fontAtlas.atlas.Width); mat.SetTextureHeight((float)atlasnode.Height / (float)m_fontAtlas.atlas.Height); }
/// <summary> /// Retrieves the final (exported) texture info, with the final texture /// id and rect /// </summary> /// <param name="textureID"></param> /// <returns></returns> public FinalTexture GetFinalTexture(int textureID) { TextureInfo info = GetTextureInfo(textureID); if (info == null) { return(null); } FinalTexture finalTexture = new FinalTexture(); finalTexture.mRectangle = new RectangleF(0.0f, 0.0f, 1.0f, 1.0f); finalTexture.mFinalTextureID = GetUniqueTextureID(textureID); finalTexture.mTextureInfo = info; foreach (TextureAtlas atlas in TextureAtlasses) { AtlasNode node = atlas.FindNode(info); if (node != null) { int padding = (info.AtlasPadding < 0) ? mAtlasPadding : info.AtlasPadding; float x = (node.mRectangle.X + padding) / (float)atlas.mRoot.mRectangle.Width; float y = (node.mRectangle.Y + padding) / (float)atlas.mRoot.mRectangle.Height; float w = (node.mRectangle.Width - (padding * 2)) / (float)atlas.mRoot.mRectangle.Width; float h = (node.mRectangle.Height - (padding * 2)) / (float)atlas.mRoot.mRectangle.Height; finalTexture.mRectangle = new RectangleF(x, y, w, h); finalTexture.mFinalTextureID = atlas.mID; finalTexture.mTextureAtlas = atlas; return(finalTexture); } } return(finalTexture); }
private void DisposeAtlasNode(AtlasNode node) { if (node == null) { return; } DisposeAtlasNode(node.mLeft); DisposeAtlasNode(node.mRight); FontBuilder.Glyph glyph = node.mUserData as FontBuilder.Glyph; if (glyph != null && glyph.mBitmap != null) { glyph.mBitmap.Dispose(); if (node.mImage != glyph.mBitmap) { node.mImage.Dispose(); } node.mImage = null; glyph.mBitmap = null; } }
/// <summary> /// /// </summary> /// <param name="buildContext"></param> public override void Build ( BuildContext buildContext ) { var fileNames = buildContext.ExpandAndResolveSearchPatterns( Dependencies ); var images = fileNames .Select( fn => Image.LoadTga( fn ) ) .OrderByDescending( img0 => img0.Width * img0.Height ) .ThenByDescending( img1 => img1.Width ) .ThenByDescending( img2 => img2.Height ) .ToList(); if (!images.Any()) { throw new InvalidOperationException("At least one subimage must be added to teh texture atlas"); } // // Pack atlas : // AtlasNode root = new AtlasNode(0,0, Width, Height, Padding ); foreach ( var img in images ) { var n = root.Insert( img ); if (n==null) { throw new InvalidOperationException("No enough room to place image"); } } // // Create image and fill it with atlas elements : // var targetImage = new Image( Width, Height ); targetImage.Fill( FillColor ); root.WriteImages( targetImage ); // // Save and compress : // var tgaOutput = buildContext.GetTempFileName( AssetPath, ".tga" ); var ddsOutput = buildContext.GetTempFileName( AssetPath, ".dds" ); Image.SaveTga( targetImage, tgaOutput ); var compression = UseDXT ? ImageFileTextureAsset.TextureCompression.BC3 : ImageFileTextureAsset.TextureCompression.RGB; ImageFileTextureAsset.RunNVCompress( buildContext, tgaOutput, ddsOutput, NoMips, false, false, true, true, false, compression ); // // Write binary blob (text + dds texture): // using ( var fs = buildContext.OpenTargetStream( this ) ) { var bw = new BinaryWriter( fs ); bw.Write(new[]{'A','T','L','S'}); bw.Write( images.Count ); root.WriteLayout( bw ); bw.Write( (int)(new FileInfo(ddsOutput).Length) ); using ( var dds = File.OpenRead( ddsOutput ) ) { dds.CopyTo( fs ); } } }
public Vector4 m_Rect = new Vector4(0, 0, 0, 0); // x,y is width and height (scale) z,w offset into atlas (offset) public AtlasNode Allocate(int width, int height, bool powerOfTwoPadding) { // not a leaf node, try children if (m_RightChild != null) { AtlasNode node = m_RightChild.Allocate(width, height, powerOfTwoPadding); if (node == null) { node = m_BottomChild.Allocate(width, height, powerOfTwoPadding); } return(node); } int wPadd = 0; int hPadd = 0; if (powerOfTwoPadding) { wPadd = (int)m_Rect.x % width; hPadd = (int)m_Rect.y % height; } //leaf node, check for fit if ((width <= m_Rect.x - wPadd) && (height <= m_Rect.y - hPadd)) { // perform the split m_RightChild = new AtlasNode(); m_BottomChild = new AtlasNode(); m_Rect.z += wPadd; m_Rect.w += hPadd; m_Rect.x -= wPadd; m_Rect.y -= hPadd; if (width > height) // logic to decide which way to split { // +--------+------+ m_RightChild.m_Rect.z = m_Rect.z + width; // | | | m_RightChild.m_Rect.w = m_Rect.w; // +--------+------+ m_RightChild.m_Rect.x = m_Rect.x - width; // | | m_RightChild.m_Rect.y = height; // | | // +---------------+ m_BottomChild.m_Rect.z = m_Rect.z; m_BottomChild.m_Rect.w = m_Rect.w + height; m_BottomChild.m_Rect.x = m_Rect.x; m_BottomChild.m_Rect.y = m_Rect.y - height; } else { // +---+-----------+ m_RightChild.m_Rect.z = m_Rect.z + width; // | | | m_RightChild.m_Rect.w = m_Rect.w; // | | | m_RightChild.m_Rect.x = m_Rect.x - width; // +---+ + m_RightChild.m_Rect.y = m_Rect.y; // | | | // +---+-----------+ m_BottomChild.m_Rect.z = m_Rect.z; m_BottomChild.m_Rect.w = m_Rect.w + height; m_BottomChild.m_Rect.x = width; m_BottomChild.m_Rect.y = m_Rect.y - height; } m_Rect.x = width; m_Rect.y = height; return(this); } return(null); }
public AtlasNode Insert(BitmapSurface surface, int index) { AtlasNode newNode = null; if (Child1 != null && Child2 != null) { // not on a leaf... newNode = Child1.Insert(surface, index); if (newNode != null) { return(newNode); } // no room? try the other child... return(Child2.Insert(surface, index)); } else { // we're on a leaf! if (Surface != null) { return(null); } int fit = testFit(surface); if (fit > 0) { return(null); // too big } if (fit == 0) { Surface = surface; Index = index; return(this); } // guess we need to split this node Child1 = new AtlasNode(); Child2 = new AtlasNode(); int paddedWidth = surface.Width + Padding * 2; int paddedHeight = surface.Height + Padding * 2; float dw = Rectangle.Width - paddedWidth; float dh = Rectangle.Height - paddedHeight; if (dw > dh) { Child1.Rectangle = new Rect(Rectangle.X, Rectangle.Y, paddedWidth, Rectangle.Height); Child2.Rectangle = new Rect(Rectangle.X + paddedWidth + 1, Rectangle.Y, Rectangle.Width - paddedWidth - 1, Rectangle.Height); } else { Child1.Rectangle = new Rect(Rectangle.X, Rectangle.Y, Rectangle.Width, paddedHeight); Child2.Rectangle = new Rect(Rectangle.X, Rectangle.Y + paddedHeight + 1, Rectangle.Width, Rectangle.Height - paddedHeight - 1); } return(Child1.Insert(surface, index)); } }
// The insert function traverses the tree looking for a place to insert the texture. // It returns the node of the atlas the texture can go into or null to say it can't fit. // Note we really don't have to store the rectangle for each node. // All we need is a split direction and coordinate like in a kd-tree, but it's more convenient with rects. public AtlasNode Insert(Texture2D image, int index) { if (image == null) // Obviously an error! { return(null); } if (child != null) {// If this node is not a leaf, try inserting into first child. AtlasNode newNode = child[0].Insert(image, index); if (newNode != null) { return(newNode); } // No more room in first child, insert into second child! return(child[1].Insert(image, index)); } else { // If there is already a lightmap in this node, early out if (hasImage) { return(null); } // If this node is too small for the image, return if (!ImageFits(image, rc)) { return(null); } // If the image is perfect, accept! if (PerfectFit(image, rc)) { hasImage = true; imageRef = image; name = imageRef.name; sortIndex = index; return(this); } // If we made it this far, this node must be split. child = new AtlasNode[2]; child[0] = new AtlasNode(); child[1] = new AtlasNode(); // Decide which way to split image float deltaW = rc.width - image.width; float deltaH = rc.height - image.height; if (deltaW > deltaH) { child[0].rc = new Rect(rc.xMin, rc.yMin, image.width, rc.height); child[1].rc = new Rect(rc.xMin + image.width + TEXTURE_PADDING, rc.yMin, rc.width - (image.width + TEXTURE_PADDING), rc.height); } else { child[0].rc = new Rect(rc.xMin, rc.yMin, rc.width, image.height); child[1].rc = new Rect(rc.xMin, rc.yMin + image.height + TEXTURE_PADDING, rc.width, rc.height - (image.height + TEXTURE_PADDING)); } // Lets try inserting into first child, eh? return(child[0].Insert(image, index)); } }
public void Release() { m_Root.Release(); m_Root = new AtlasNode(); m_Root.m_Rect.Set(m_Width, m_Height, 0, 0); }
/// <summary> /// /// </summary> /// <param name="buildContext"></param> public override void Process(AssetSource assetFile, BuildContext context) { var fileDir = Path.GetDirectoryName(assetFile.FullSourcePath); var fileNames = File.ReadAllLines(assetFile.FullSourcePath) .Select(f1 => f1.Trim()) .Where(f2 => !f2.StartsWith("#") && !string.IsNullOrWhiteSpace(f2)) .Select(f3 => Path.Combine(fileDir, f3)) .ToArray(); var depNames = File.ReadAllLines(assetFile.FullSourcePath) .Select(f1 => f1.Trim()) .Where(f2 => !f2.StartsWith("#") && !string.IsNullOrWhiteSpace(f2)) .Select(f3 => Path.Combine(Path.GetDirectoryName(assetFile.KeyPath), f3)) .ToArray(); var images = fileNames .Select(fn => LoadImage(fn)) .OrderByDescending(img0 => img0.Width * img0.Height) .ThenByDescending(img1 => img1.Width) .ThenByDescending(img2 => img2.Height) .ToList(); if (!images.Any()) { throw new InvalidOperationException("At least one subimage must be added to the texture atlas"); } // // Pack atlas : // AtlasNode root = new AtlasNode(0, 0, Width, Height, Padding); foreach (var img in images) { var n = root.Insert(img); if (n == null) { throw new InvalidOperationException("No enough room to place image"); } } // // Create image and fill it with atlas elements : // var targetImage = new Image(Width, Height); targetImage.Fill(FillColor); root.WriteImages(targetImage); // // Save and compress : // var tgaOutput = context.GetTempFileFullPath(assetFile.KeyPath, ".tga"); var ddsOutput = context.GetTempFileFullPath(assetFile.KeyPath, ".dds"); Image.SaveTga(targetImage, tgaOutput); var compression = UseDXT ? TextureProcessor.TextureCompression.BC3 : TextureProcessor.TextureCompression.RGB; TextureProcessor.RunNVCompress(context, tgaOutput, ddsOutput, NoMips, false, false, true, true, false, compression); // // Write binary blob (text + dds texture): // using (var fs = assetFile.OpenTargetStream(depNames)) { var bw = new BinaryWriter(fs); bw.Write(new[] { 'A', 'T', 'L', 'S' }); bw.Write(images.Count); root.WriteLayout(bw); bw.Write((int)(new FileInfo(ddsOutput).Length)); using (var dds = File.OpenRead(ddsOutput)) { dds.CopyTo(fs); } } }
private void DrawAtlasMaps() { if (!Settings.ShowOnAtlas.Value) { return; } var atlas = GameController.Game.IngameState.IngameUi.AtlasPanel; if (LastVisible != atlas.IsVisible || CompletedMaps == null) { LastVisible = atlas.IsVisible; if (LastVisible) { CompletedMaps = GameController.Game.IngameState.ServerData.CompletedAreas; BonusCompletedMaps = GameController.Game.IngameState.ServerData.BonusCompletedAreas; ShapeUpgradedMaps = GameController.Game.IngameState.ServerData.ShapedMaps; ScanPlayerInventForShapersOrb(); } } if (!atlas.IsVisible) { return; } var root = atlas.GetChildAtIndex(0); var rootPos = new Vector2(root.X, root.Y); var scale = root.Scale; BuyAtlasNode = null; foreach (var atlasMap in GameController.Files.AtlasNodes.EntriesList) { var area = atlasMap.Area; var mapName = area.Name; if (mapName.Contains("Realm")) { continue; } var centerPos = (atlasMap.Pos * 5.69f + rootPos) * scale; var textPos = centerPos; textPos.Y -= 30 * scale; var testSize = (int)Math.Round(Settings.TextSize.Value * scale); var fontFlags = FontDrawFlags.Center | FontDrawFlags.Bottom; byte textTransp; Color textBgColor; bool fill; Color fillColor; if (BonusCompletedMaps.Contains(area)) { textTransp = Settings.BonusCompletedTextTransparency.Value; textBgColor = Settings.BonusCompletedTextBg.Value; fill = Settings.BonusCompletedFilledCircle.Value; fillColor = Settings.BonusCompletedFillColor.Value; } else if (CompletedMaps.Contains(area)) { textTransp = Settings.CompletedTextTransparency.Value; textBgColor = Settings.CompletedTextBg.Value; fill = Settings.CompletedFilledCircle.Value; fillColor = Settings.CompletedFillColor.Value; } else { textTransp = Settings.UnCompletedTextTransparency.Value; textBgColor = Settings.UnCompletedTextBg.Value; fill = Settings.UnCompletedFilledCircle.Value; fillColor = Settings.UnCompletedFillColor.Value; } Color textColor = Settings.WhiteMapColor.Value; if (area.AreaLevel >= 78) { textColor = Settings.RedMapColor.Value; } else if (area.AreaLevel >= 73) { textColor = Settings.YellowMapColor.Value; } textColor.A = textTransp; Graphics.DrawText(mapName, testSize, textPos, textColor, fontFlags); var mapNameSize = Graphics.MeasureText(mapName, testSize, fontFlags); mapNameSize.Width += 5; var nameBoxRect = new RectangleF(textPos.X - mapNameSize.Width / 2, textPos.Y - mapNameSize.Height, mapNameSize.Width, mapNameSize.Height); Graphics.DrawBox(nameBoxRect, textBgColor); if (WinApi.IsKeyDown(Keys.ControlKey)) { var upgraded = ShapeUpgradedMaps.Contains(area); var areaLvlColor = Color.White; var areaLvl = area.AreaLevel; if (upgraded) { areaLvl += 5; areaLvlColor = Color.Orange; } var penalty = LevelXpPenalty(areaLvl); var penaltyTextColor = Color.Lerp(Color.Red, Color.Green, (float)penalty); var labelText = $"{penalty:p0}"; var textSize = Graphics.MeasureText(labelText, testSize, FontDrawFlags.Left | FontDrawFlags.Bottom); textSize.Width += 6; var penaltyRect = new RectangleF(textPos.X + mapNameSize.Width / 2, textPos.Y - textSize.Height, textSize.Width, textSize.Height); Graphics.DrawBox(penaltyRect, Color.Black); Graphics.DrawText(labelText, testSize, penaltyRect.Center, penaltyTextColor, FontDrawFlags.Center | FontDrawFlags.VerticalCenter); labelText = $"{areaLvl}"; textSize = Graphics.MeasureText(labelText, testSize, FontDrawFlags.Right | FontDrawFlags.Bottom); penaltyRect = new RectangleF(textPos.X - mapNameSize.Width / 2 - textSize.Width, textPos.Y - textSize.Height, textSize.Width, textSize.Height); Graphics.DrawBox(penaltyRect, Color.Black); Graphics.DrawText(labelText, testSize, penaltyRect.Center, areaLvlColor, FontDrawFlags.Center | FontDrawFlags.VerticalCenter); if (Settings.ShowBuyButton.Value) { var butTextWidth = 50 * scale; var buyButtonRect = new RectangleF(textPos.X - butTextWidth / 2, textPos.Y - testSize * 2, butTextWidth, testSize); //Graphics.DrawBox(buyButtonRect, Color.Black); //Graphics.DrawPluginImage(System.IO.Path.Combine(PluginDirectory, "images/buy.png"), buyButtonRect, new Color(255, 255, 255, 200)); //Graphics.DrawText("Buy$", testSize, buyButtonRect.Center, Color.Yellow, FontDrawFlags.Center | FontDrawFlags.VerticalCenter); if (buyButtonRect.Contains(MousePos)) { Graphics.DrawPluginImage(System.IO.Path.Combine(PluginDirectory, "images/buy.png"), buyButtonRect, new Color(255, 255, 255, 255)); Graphics.DrawFrame(buyButtonRect, 1, Color.White); BuyAtlasNode = atlasMap; } else { Graphics.DrawPluginImage(System.IO.Path.Combine(PluginDirectory, "images/buy.png"), buyButtonRect, new Color(255, 255, 255, 200)); } } } else { BuyAtlasNode = null; } var imgRectSize = 60 * scale; var imgDrawRect = new RectangleF(centerPos.X - imgRectSize / 2, centerPos.Y - imgRectSize / 2, imgRectSize, imgRectSize); if (InventShapersOrbs.Count > 0) { var areaLvl = area.AreaLevel; if (!ShapeUpgradedMaps.Contains(area)) { var tier = areaLvl - 67; if (InventShapersOrbs.Contains(tier)) { var shapedRect = imgDrawRect; var sizeOffset = 30 * scale; shapedRect.Left -= sizeOffset; shapedRect.Right += sizeOffset; shapedRect.Top -= sizeOffset; shapedRect.Bottom += sizeOffset; Graphics.DrawPluginImage(System.IO.Path.Combine(PluginDirectory, "images/AtlasMapCircle.png"), shapedRect, new Color(155, 0, 255, 255)); } } } if (fill) { Graphics.DrawPluginImage(System.IO.Path.Combine(PluginDirectory, "images/AtlasMapCircleFilled.png"), imgDrawRect, fillColor); } Graphics.DrawPluginImage(System.IO.Path.Combine(PluginDirectory, "images/AtlasMapCircle.png"), imgDrawRect, Color.Black); if (Settings.ShowAmount.Value) { if (IsUniq(atlasMap)) { mapName += ":Uniq"; } if (Settings.MapStashAmount.TryGetValue(mapName, out var amount)) { var mapCountSize = Graphics.MeasureText(amount.ToString(), testSize, fontFlags); mapCountSize.Width += 6; Graphics.DrawBox(new RectangleF(centerPos.X - mapCountSize.Width / 2, centerPos.Y - mapCountSize.Height / 2, mapCountSize.Width, mapCountSize.Height), Color.Black); textColor.A = 255; Graphics.DrawText(amount.ToString(), testSize, centerPos, textColor, FontDrawFlags.Center | FontDrawFlags.VerticalCenter); } } } }
/// <summary> /// /// </summary> /// <param name="buildContext"></param> public override void Build(BuildContext buildContext) { var fileNames = buildContext.ExpandAndResolveSearchPatterns(Dependencies); var images = fileNames .Select(fn => Image.LoadTga(fn)) .OrderByDescending(img0 => img0.Width * img0.Height) .ThenByDescending(img1 => img1.Width) .ThenByDescending(img2 => img2.Height) .ToList(); if (!images.Any()) { throw new InvalidOperationException("At least one subimage must be added to teh texture atlas"); } // // Pack atlas : // AtlasNode root = new AtlasNode(0, 0, Width, Height, Padding); foreach (var img in images) { var n = root.Insert(img); if (n == null) { throw new InvalidOperationException("No enough room to place image"); } } // // Create image and fill it with atlas elements : // var targetImage = new Image(Width, Height); targetImage.Fill(FillColor); root.WriteImages(targetImage); // // Save and compress : // var tgaOutput = buildContext.GetTempFileName(AssetPath, ".tga", true); var ddsOutput = buildContext.GetTempFileName(AssetPath, ".dds", true); Image.SaveTga(targetImage, tgaOutput); var compression = UseDXT ? ImageFileTextureAsset.TextureCompression.BC3 : ImageFileTextureAsset.TextureCompression.RGB; ImageFileTextureAsset.RunNVCompress(buildContext, tgaOutput, ddsOutput, NoMips, false, false, true, true, false, compression); // // Write binary blob (text + dds texture): // using (var fs = buildContext.TargetStream(this)) { var bw = new BinaryWriter(fs); bw.Write(new[] { 'A', 'T', 'L', 'S' }); bw.Write(images.Count); root.WriteLayout(bw); bw.Write((int)(new FileInfo(ddsOutput).Length)); using (var dds = File.OpenRead(ddsOutput)) { dds.CopyTo(fs); } } }
private bool IsUniq(AtlasNode atlasMap) { string uniqTest = Memory.ReadStringU(Memory.ReadLong(atlasMap.Address + 0x3c, 0)); return((!string.IsNullOrEmpty(uniqTest) && uniqTest.Contains("Uniq")) || Vector2.Distance(atlasMap.Pos, new Vector2(294.979f, 386.641f)) < 5); }
/// <summary> /// /// </summary> /// <param name="buildContext"></param> public override void Process ( AssetSource assetFile, BuildContext context ) { var fileDir = Path.GetDirectoryName( assetFile.FullSourcePath ); var fileNames = File.ReadAllLines(assetFile.FullSourcePath) .Select( f1 => f1.Trim() ) .Where( f2 => !f2.StartsWith("#") && !string.IsNullOrWhiteSpace(f2) ) .Select( f3 => Path.Combine( fileDir, f3 ) ) .ToArray(); var depNames = File.ReadAllLines(assetFile.FullSourcePath) .Select( f1 => f1.Trim() ) .Where( f2 => !f2.StartsWith("#") && !string.IsNullOrWhiteSpace(f2) ) .Select( f3 => Path.Combine( Path.GetDirectoryName(assetFile.KeyPath), f3 ) ) .ToArray(); var images = fileNames .Select( fn => LoadImage( fn ) ) .OrderByDescending( img0 => img0.Width * img0.Height ) .ThenByDescending( img1 => img1.Width ) .ThenByDescending( img2 => img2.Height ) .ToList(); if (!images.Any()) { throw new InvalidOperationException("At least one subimage must be added to the texture atlas"); } // // Pack atlas : // AtlasNode root = new AtlasNode(0,0, Width, Height, Padding ); foreach ( var img in images ) { var n = root.Insert( img ); if (n==null) { throw new InvalidOperationException("No enough room to place image"); } } // // Create image and fill it with atlas elements : // var targetImage = new Image( Width, Height ); targetImage.Fill( FillColor ); root.WriteImages( targetImage ); // // Save and compress : // var tgaOutput = context.GetTempFileName( assetFile.KeyPath, ".tga" ); var ddsOutput = context.GetTempFileName( assetFile.KeyPath, ".dds" ); Image.SaveTga( targetImage, tgaOutput ); var compression = UseDXT ? TextureProcessor.TextureCompression.BC3 : TextureProcessor.TextureCompression.RGB; TextureProcessor.RunNVCompress( context, tgaOutput, ddsOutput, NoMips, false, false, true, true, false, compression ); // // Write binary blob (text + dds texture): // using ( var fs = assetFile.OpenTargetStream(depNames) ) { var bw = new BinaryWriter( fs ); bw.Write(new[]{'A','T','L','S'}); bw.Write( images.Count ); root.WriteLayout( bw ); bw.Write( (int)(new FileInfo(ddsOutput).Length) ); using ( var dds = File.OpenRead( ddsOutput ) ) { dds.CopyTo( fs ); } } }
public AtlasNode Insert( Image surface ) { if (left!=null) { AtlasNode rv; if (right==null) { throw new InvalidOperationException("AtlasNode(): error"); } rv = left.Insert(surface); if (rv==null) { rv = right.Insert(surface); } return rv; } int img_width = surface.Width + padding * 2; int img_height = surface.Height + padding * 2; if (in_use || img_width > width || img_height > height) { return null; } if (img_width == width && img_height == height) { in_use = true; tex = surface; return this; } if (width - img_width > height - img_height) { /* extend to the right */ left = new AtlasNode(x, y, img_width, height, padding); right = new AtlasNode(x + img_width, y, width - img_width, height, padding); } else { /* extend to bottom */ left = new AtlasNode(x, y, width, img_height, padding); right = new AtlasNode(x, y + img_height, width, height - img_height, padding); } return left.Insert(surface); }
public AtlasNode (int x, int y, int width, int height, int padding) { left = null; right = null; tex = null; this.x = x; this.y = y; this.width = width; this.height = height; in_use = false; this.padding = padding; }
/// <summary> /// Reloads the font /// </summary> public void ReloadFont() { if (mProject == null) { return; } int borderWidth = 2; string fullPath = mProject.ProjectDirectory + "/" + mFontFile; if (System.IO.File.Exists(fullPath)) { // Ensure that the character list ALWAYS contains at least the ? character. if (!Characters.Contains('?')) { Characters.Add('?'); } // Load the image glyph textures foreach (FontBuilder.ImageGlyph imageGlyph in Images) { imageGlyph.Load(mProject.ProjectDirectory); } if (mFontData != null) { mFontData.Dispose(); } mFontData = new FontBuilder.FontData(Characters, Images); FontBuilder.Glyph[] glyphs = mFontData.GetGlyphs(fullPath, mFontSize); // Clear out our existing textures and texture atlasses foreach (TextureAtlas atlas in mTextureAtlasses) { DisposeAtlasNode(atlas.mRoot); } mTextureAtlasses.Clear(); mTextures.Clear(); mCharInfoList.Clear(); mMaxTop = -99999; Image texture = null; Bitmap textureBitmap = null; if (File.Exists(mProject.ProjectDirectory + "/" + mTextureFile)) { texture = Image.FromFile(mProject.ProjectDirectory + "/" + mTextureFile); textureBitmap = new Bitmap(texture); } TextureAtlas curAtlas = null; foreach (FontBuilder.Glyph glyph in glyphs) { if (glyph == null) { continue; } if (mMaxTop < glyph.mTop) { mMaxTop = glyph.mTop; } Bitmap finalBitmap = null; if (glyph.mBitmap != null) { finalBitmap = (borderWidth == 0) ? new Bitmap(glyph.mBitmap) : Utils.ExpandImageBorder(glyph.mBitmap, borderWidth, true); if (finalBitmap == null) { continue; } Bitmap outlinedBitmap = null; Bitmap texturedBitmap = null; if (OutlineAmount > 0.0f && OutlineColor.A != 0) { float s = Math.Min(1.0f, Math.Max(OutlineSharpness, 0.0f)); outlinedBitmap = BlurBitmap(finalBitmap, OutlineAmount, OutlineSharpness, OutlineColor); finalBitmap.Dispose(); finalBitmap = outlinedBitmap; } BitmapData texturedBitmapData = null; if (textureBitmap != null && glyph.mImageGlyph == 0) { texturedBitmap = ApplyTexture(textureBitmap, glyph.mBitmap, mTextureBaseline, glyph.mTop); texturedBitmapData = texturedBitmap.LockBits(new Rectangle(0, 0, texturedBitmap.Width, texturedBitmap.Height), ImageLockMode.ReadOnly, texturedBitmap.PixelFormat); } // Now we need to use the original bitmap (from the glyph) and reapply it to the new // bitmap (that may or may not have been blurred) with the fill color BitmapData sourceBitmapData = glyph.mBitmap.LockBits(new Rectangle(0, 0, glyph.mBitmap.Width, glyph.mBitmap.Height), ImageLockMode.ReadOnly, glyph.mBitmap.PixelFormat); BitmapData targetBitmapData = finalBitmap.LockBits(new Rectangle((finalBitmap.Width - glyph.mBitmap.Width) / 2, (finalBitmap.Height - glyph.mBitmap.Height) / 2, glyph.mBitmap.Width, glyph.mBitmap.Height), ImageLockMode.WriteOnly, finalBitmap.PixelFormat); try { for (int x = 0; x < sourceBitmapData.Width; x++) { for (int y = 0; y < sourceBitmapData.Height; y++) { IntPtr sourcePixel = (IntPtr)((int)sourceBitmapData.Scan0 + sourceBitmapData.Stride * y + x * 4); IntPtr targetPixel = (IntPtr)((int)targetBitmapData.Scan0 + targetBitmapData.Stride * y + x * 4); IntPtr texturePixel = IntPtr.Zero; if (texturedBitmapData != null) { texturePixel = (IntPtr)((int)texturedBitmapData.Scan0 + texturedBitmapData.Stride * y + x * 4); } Color srcColor = Color.FromArgb(Marshal.ReadInt32(sourcePixel)); Color tgtColor = Color.FromArgb(Marshal.ReadInt32(targetPixel)); Color texColor = (texturePixel != IntPtr.Zero) ? Color.FromArgb(Marshal.ReadInt32(texturePixel)) : Color.White; float alpha = srcColor.A / 255.0f; if (alpha == 0.0f) { continue; } byte r = (byte)(tgtColor.R * (1.0f - alpha) + FillColor.R * (srcColor.R / 255.0f) * (texColor.R / 255.0f) * alpha); byte g = (byte)(tgtColor.G * (1.0f - alpha) + FillColor.G * (srcColor.G / 255.0f) * (texColor.G / 255.0f) * alpha); byte b = (byte)(tgtColor.B * (1.0f - alpha) + FillColor.B * (srcColor.B / 255.0f) * (texColor.B / 255.0f) * alpha); byte a = (byte)(tgtColor.A * (1.0f - alpha) + FillColor.A * (srcColor.A / 255.0f) * (texColor.A / 255.0f) * alpha); Color finalColor = Color.FromArgb(a, r, g, b); Marshal.WriteInt32(targetPixel, finalColor.ToArgb()); } } } catch (Exception ex) { System.Console.WriteLine("Exception : " + ex); } glyph.mBitmap.UnlockBits(sourceBitmapData); finalBitmap.UnlockBits(targetBitmapData); if (texturedBitmapData != null) { texturedBitmap.UnlockBits(texturedBitmapData); } } else { finalBitmap = new Bitmap(4, 4); } if (curAtlas == null || !curAtlas.AddTexture(finalBitmap, glyph)) { curAtlas = new TextureAtlas(0, AtlasSize.Width, AtlasSize.Height, "tmp"); mTextureAtlasses.Add(curAtlas); curAtlas.AddTexture(finalBitmap, glyph); } } if (textureBitmap != null) { textureBitmap.Dispose(); } if (texture != null) { texture.Dispose(); } // Unload the image glyph textures foreach (FontBuilder.ImageGlyph imageGlyph in Images) { imageGlyph.Unload(); } // Then iterate and add to the texture atlas(ses). if (Otter.Interface.Graphics.Instance != null) { foreach (int textureID in mTextures) { Otter.Interface.Graphics.Instance.UnloadTexture(textureID); } foreach (TextureAtlas atlas in mTextureAtlasses) { Bitmap bitmap = atlas.GetBitmap(); // BitmapData BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat); // Get the address of the first line. IntPtr ptr = bitmapData.Scan0; // Declare an array to hold the bytes of the bitmap. int size = Math.Abs(bitmapData.Stride) * bitmap.Height; byte[] bytes = new byte[size]; System.Runtime.InteropServices.Marshal.Copy(ptr, bytes, 0, size); int textureID = Otter.Interface.Graphics.Instance.LoadTexture(bytes, bitmap.Width, bitmap.Height, 32); bitmap.UnlockBits(bitmapData); mTextures.Add(textureID); bitmap.Dispose(); // SLOW foreach (FontBuilder.Glyph glyph in glyphs) { if (glyph == null) { continue; } AtlasNode node = atlas.FindNode(glyph); if (node != null) { NewCharInfo info = new NewCharInfo(); int diffW = node.mRectangle.Width - borderWidth * 2 - glyph.mW; int diffH = node.mRectangle.Height - borderWidth * 2 - glyph.mH; info.mCharCode = glyph.mCharCode; info.mImageGlyph = glyph.mImageGlyph; info.mX = node.mRectangle.X + borderWidth; info.mY = node.mRectangle.Y + borderWidth; info.mW = glyph.mW + diffW; info.mH = glyph.mH + diffH; info.mTop = glyph.mTop + diffH / 2; info.mAdvance = glyph.mAdvance; info.mLeftBearing = glyph.mLeftBearing - diffW / 2; info.mAtlasIndex = mTextureAtlasses.IndexOf(atlas); mCharInfoList.Add(info); } } } MemoryStream stream = new MemoryStream(); Platform platform = new Platform(); platform.Endianness = Endian.Little; platform.ColorFormat = ColorFormat.ARGB; PlatformBinaryWriter bw = new PlatformBinaryWriter(stream, platform); this.Export(bw); mFontID = Otter.Interface.Graphics.Instance.LoadFont(this.Name, stream.GetBuffer(), mTextures); bw.Close(); stream.Close(); } } }