public AtlasRegion Pack(string path) { var tex = Load<Texture2D>(path, false); if (tex == null) { Debug.LogWarningFormat("[Packer] {0} not found", path); return null; } if (tex.width > width || tex.height > height) { Debug.LogError("[Packer] A texture size is bigger than the sprite sheet size!"); return null; } var region = PackRegion(new Vector2Int(tex.width, tex.height)); if (region == null) return null; region.name = path; region.page = this; region.degrees = 0; region.rotate = false; region.width = region.originalWidth = tex.width; region.height = region.originalHeight = tex.height; _packedSprites.Add(path, region); CopyTex2RT(tex, region); region.u = (region.x + 1) / (float)width; region.v = (region.y - 1 + tex.height) / (float)height; region.u2 = (region.x - 1 + tex.width) / (float)width; region.v2 = (region.y + 1) / (float)height; region.ReferenceCount++; return region; }
private AtlasRegion PackRegion(Vector2Int v2I) { var v2Ix = v2I.x; var v2Iy = v2I.y; var index = GetFreeAreaIndex(v2Ix, v2Iy); if (index < 0) return null; var freeArea = _mFreeAreas[index]; var target = AllocateRectangle(freeArea.x, freeArea.y, v2Ix, v2Iy); // Generate the new free areas, these are parts of the old ones intersected or touched by the target GenerateNewFreeAreas(target, _mFreeAreas, _mNewFreeAreas); while (_mNewFreeAreas.Count > 0) _mFreeAreas.Add(_mNewFreeAreas.Pop()); _mInsertedRegion.Add(target); if (target.Right > _mPackedWidth) _mPackedWidth = target.Right; if (target.Bottom > _mPackedHeight) _mPackedHeight = target.Bottom; return target; }
private int GetFreeAreaIndex(int spriteWidth, int spriteHeight) { var best = _mOutsideRegion; var index = -1; var paddedWidth = spriteWidth + _padding; var paddedHeight = spriteHeight + _padding; var count = _mFreeAreas.Count; for (var i = count - 1; i >= 0; i--) { var free = _mFreeAreas[i]; if (free.x < _mPackedWidth || free.y < _mPackedHeight) { // Within the packed area, padding required if (free.x >= best.x || !(paddedWidth <= free.width) || !(paddedHeight <= free.height)) continue; index = i; if (paddedWidth == free.width && free.width <= free.height && free.Right < _mWidth || paddedHeight == free.height && free.height <= free.width) break; best = free; } else { // Outside the current packed area, no padding required if (free.x >= best.x || spriteWidth > free.width || spriteHeight > free.height) continue; index = i; if ((spriteWidth == free.width && free.width <= free.height && free.Right < _mWidth) || (spriteHeight == free.height && free.height <= free.width)) break; best = free; } } return index; }
private AtlasRegion AllocateRectangle(int x, int y, int targetWidth, int targetHeight) { if (_mRegionStack.Count <= 0) return new AtlasRegion { x = x, y = y, width = targetWidth, height = targetHeight, Right = x + targetWidth, Bottom = y + targetHeight }; var region = _mRegionStack.Pop(); region.x = x; region.y = y; region.width = targetWidth; region.height = targetHeight; region.Right = x + targetWidth; region.Bottom = y + targetHeight; return region; }
private void GenerateNewFreeAreas(AtlasRegion target, List<AtlasRegion> areas, List<AtlasRegion> results) { float x = target.x; float y = target.y; float right = target.Right + _padding; float bottom = target.Bottom + _padding; AtlasRegion targetWithPadding = null; if (_padding == 0) targetWithPadding = target; for (var i = areas.Count - 1; i >= 0; i--) { var area = areas[i]; if (x >= area.Right || right <= area.x || y >= area.Bottom || bottom <= area.y) continue; if (targetWithPadding == null) targetWithPadding = AllocateRectangle(target.x, target.y, target.width + _padding, target.height + _padding); GenerateDividedAreas(targetWithPadding, area, results); var topOfStack = areas.Pop(); if (i < areas.Count) { // Move the one on the top to the freed position areas[i] = topOfStack; } } if (targetWithPadding != null && targetWithPadding != target) FreeRectangle(targetWithPadding); FilterSelfSubAreas(results); }
private void GenerateDividedAreas(AtlasRegion divider, AtlasRegion area, ICollection<AtlasRegion> results) { var count = 0; var rightDelta = area.Right - divider.Right; if (rightDelta > 0) { results.Add(AllocateRectangle(divider.Right, area.y, rightDelta, area.height)); count++; } var leftDelta = divider.x - area.x; if (leftDelta > 0) { results.Add(AllocateRectangle(area.x, area.y, leftDelta, area.height)); count++; } var bottomDelta = area.Bottom - divider.Bottom; if (bottomDelta > 0) { results.Add(AllocateRectangle(area.x, divider.Bottom, area.width, bottomDelta)); count++; } var topDelta = divider.y - area.y; if (topDelta > 0) { results.Add(AllocateRectangle(area.x, area.y, area.width, topDelta)); count++; } if (count == 0 && (divider.width < area.width || divider.height < area.height)) { // Only touching the area, store the area itself results.Add(area); } else _mRegionStack.Add(region); }
private void CopyTex2RT(Texture tex, AtlasRegion region) { if (_renderTexture == null) { _renderTexture = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32) { name = "PackedSheet[" + name + "]", autoGenerateMips = false, useMipMap = false }; _renderTexture.DiscardContents(); } if (_copyMat == null) { var shader = Shader.Find("Hidden/Copy"); if (shader == null) Debug.LogError("[Packer] shader not found"); _copyMat = new Material(shader); } var x = 1.0f * width / (region.width); var y = 1.0f * height / (region.height); var z = 1.0f * -region.x / (region.width); var w = 1.0f * -region.y / (region.height); _copyMat.SetVector(ShaderUtil.ID_MainTex_ST2, new Vector4(x, y, z,w)); _renderTexture.MarkRestoreExpected(); Graphics.Blit(tex, _renderTexture, _copyMat); _primaryMaterial.mainTexture = _renderTexture; rendererObject = _primaryMaterial; }