static void UpdateVertexCache(tk2dSpriteCollection gen, Atlas.AtlasData[] packers, tk2dSpriteCollectionData coll, List <SCGE.SpriteLut> spriteLuts)
    {
        float scale     = 2.0f * gen.targetOrthoSize / gen.targetHeight;
        int   padAmount = GetPadAmount(gen);

        for (int i = 0; i < sourceTextures.Length; ++i)
        {
            SCGE.SpriteLut _lut = null;
            for (int j = 0; j < spriteLuts.Count; ++j)
            {
                if (spriteLuts[j].source == i)
                {
                    _lut = spriteLuts[j];
                    break;
                }
            }

            tk2dSpriteCollectionDefinition thisTexParam = gen.textureParams[i];
            Atlas.AtlasData  packer     = null;
            Atlas.AtlasEntry atlasEntry = null;
            int atlasIndex = 0;
            foreach (var p in packers)
            {
                if ((atlasEntry = p.FindEntryWithIndex(_lut.atlasIndex)) != null)
                {
                    packer = p;
                    break;
                }
                ++atlasIndex;
            }
            float fwidth  = packer.width;
            float fheight = packer.height;

            int tx = atlasEntry.x + padAmount, ty = atlasEntry.y + padAmount, tw = atlasEntry.w - padAmount * 2, th = atlasEntry.h - padAmount * 2;
            int sd_y = packer.height - ty - th;

            float uvOffsetX = 0.001f / fwidth;
            float uvOffsetY = 0.001f / fheight;

            Vector2 v0 = new Vector2(tx / fwidth + uvOffsetX, 1.0f - (sd_y + th) / fheight + uvOffsetY);
            Vector2 v1 = new Vector2((tx + tw) / fwidth - uvOffsetX, 1.0f - sd_y / fheight - uvOffsetY);

            Mesh       mesh          = null;
            Transform  meshTransform = null;
            GameObject instantiated  = null;

            if (thisTexParam.overrideMesh)
            {
                // Disabled
                instantiated = GameObject.Instantiate(thisTexParam.overrideMesh) as GameObject;
                MeshFilter meshFilter = instantiated.GetComponentInChildren <MeshFilter>();
                if (meshFilter == null)
                {
                    Debug.LogError("Unable to find mesh");
                    GameObject.DestroyImmediate(instantiated);
                }
                else
                {
                    mesh          = meshFilter.sharedMesh;
                    meshTransform = meshFilter.gameObject.transform;
                }
            }

            if (mesh)
            {
                coll.spriteDefinitions[i].positions = new Vector3[mesh.vertices.Length];
                coll.spriteDefinitions[i].uvs       = new Vector2[mesh.vertices.Length];
                for (int j = 0; j < mesh.vertices.Length; ++j)
                {
                    coll.spriteDefinitions[i].positions[j] = meshTransform.TransformPoint(mesh.vertices[j]);
                    coll.spriteDefinitions[i].uvs[j]       = new Vector2(v0.x + (v1.x - v0.x) * mesh.uv[j].x, v0.y + (v1.y - v0.y) * mesh.uv[j].y);
                }
                coll.spriteDefinitions[i].indices = new int[mesh.triangles.Length];
                for (int j = 0; j < mesh.triangles.Length; ++j)
                {
                    coll.spriteDefinitions[i].indices[j] = mesh.triangles[j];
                }
                coll.spriteDefinitions[i].material = gen.atlasMaterials[atlasIndex];

                GameObject.DestroyImmediate(instantiated);
            }
            else
            {
                Texture2D thisTextureRef = sourceTextures[i];

                float texHeight = thisTextureRef?thisTextureRef.height:2;
                float texWidth  = thisTextureRef?thisTextureRef.width:2;

                float h = thisTextureRef?thisTextureRef.height:64;
                float w = thisTextureRef?thisTextureRef.width:64;
                h *= thisTexParam.scale.x;
                w *= thisTexParam.scale.y;

                float scaleX = w * scale;
                float scaleY = h * scale;

                Vector3 pos0 = new Vector3(-0.5f * scaleX, 0, -0.5f * scaleY);
                switch (thisTexParam.anchor)
                {
                case tk2dSpriteCollectionDefinition.Anchor.LowerLeft: pos0 = new Vector3(0, 0, 0); break;

                case tk2dSpriteCollectionDefinition.Anchor.LowerCenter: pos0 = new Vector3(-0.5f * scaleX, 0, 0); break;

                case tk2dSpriteCollectionDefinition.Anchor.LowerRight: pos0 = new Vector3(-scaleX, 0, 0); break;

                case tk2dSpriteCollectionDefinition.Anchor.MiddleLeft: pos0 = new Vector3(0, 0, -0.5f * scaleY); break;

                case tk2dSpriteCollectionDefinition.Anchor.MiddleCenter: pos0 = new Vector3(-0.5f * scaleX, 0, -0.5f * scaleY); break;

                case tk2dSpriteCollectionDefinition.Anchor.MiddleRight: pos0 = new Vector3(-scaleX, 0, -0.5f * scaleY); break;

                case tk2dSpriteCollectionDefinition.Anchor.UpperLeft: pos0 = new Vector3(0, 0, -scaleY); break;

                case tk2dSpriteCollectionDefinition.Anchor.UpperCenter: pos0 = new Vector3(-0.5f * scaleX, 0, -scaleY); break;

                case tk2dSpriteCollectionDefinition.Anchor.UpperRight: pos0 = new Vector3(-scaleX, 0, -scaleY); break;

                case tk2dSpriteCollectionDefinition.Anchor.Custom:
                {
                    pos0 = new Vector3(-thisTexParam.anchorX * thisTexParam.scale.x * scale, 0, -(h - thisTexParam.anchorY * thisTexParam.scale.y) * scale);
                }
                break;
                }

                Vector3 pos1 = pos0 + new Vector3(scaleX, 0, scaleY);

                List <Vector3> positions = new List <Vector3>();
                List <Vector2> uvs       = new List <Vector2>();

                // build mesh
                if (_lut.isSplit)
                {
                    for (int j = 0; j < spriteLuts.Count; ++j)
                    {
                        if (spriteLuts[j].source == i)
                        {
                            _lut = spriteLuts[j];

                            int thisAtlasIndex = 0;
                            foreach (var p in packers)
                            {
                                if ((atlasEntry = p.FindEntryWithIndex(_lut.atlasIndex)) != null)
                                {
                                    packer = p;
                                    break;
                                }
                                ++thisAtlasIndex;
                            }

                            if (thisAtlasIndex != atlasIndex)
                            {
                                // This is a serious problem, dicing is not supported when multi atlas output is selected
                                Debug.Break();
                            }

                            fwidth  = packer.width;
                            fheight = packer.height;

                            tx = atlasEntry.x + padAmount;
                            ty = atlasEntry.y + padAmount;
                            tw = atlasEntry.w - padAmount * 2;
                            th = atlasEntry.h - padAmount * 2;

                            sd_y = packer.height - ty - th;
                            v0   = new Vector2(tx / fwidth + uvOffsetX, 1.0f - (sd_y + th) / fheight + uvOffsetY);
                            v1   = new Vector2((tx + tw) / fwidth - uvOffsetX, 1.0f - sd_y / fheight - uvOffsetY);

                            float x0 = _lut.rx / texWidth;
                            float y0 = _lut.ry / texHeight;
                            float x1 = (_lut.rx + _lut.rw) / texWidth;
                            float y1 = (_lut.ry + _lut.rh) / texHeight;

                            Vector3 dpos0 = new Vector3(Mathf.Lerp(pos0.x, pos1.x, x0), 0.0f, Mathf.Lerp(pos0.z, pos1.z, y0));
                            Vector3 dpos1 = new Vector3(Mathf.Lerp(pos0.x, pos1.x, x1), 0.0f, Mathf.Lerp(pos0.z, pos1.z, y1));

                            positions.Add(new Vector3(dpos0.x, dpos0.z, 0));
                            positions.Add(new Vector3(dpos1.x, dpos0.z, 0));
                            positions.Add(new Vector3(dpos0.x, dpos1.z, 0));
                            positions.Add(new Vector3(dpos1.x, dpos1.z, 0));

                            if (atlasEntry.flipped)
                            {
                                uvs.Add(new Vector2(v0.x, v0.y));
                                uvs.Add(new Vector2(v0.x, v1.y));
                                uvs.Add(new Vector2(v1.x, v0.y));
                                uvs.Add(new Vector2(v1.x, v1.y));
                            }
                            else
                            {
                                uvs.Add(new Vector2(v0.x, v0.y));
                                uvs.Add(new Vector2(v1.x, v0.y));
                                uvs.Add(new Vector2(v0.x, v1.y));
                                uvs.Add(new Vector2(v1.x, v1.y));
                            }
                        }
                    }
                }
                else
                {
                    float x0 = _lut.rx / texWidth;
                    float y0 = _lut.ry / texHeight;
                    float x1 = (_lut.rx + _lut.rw) / texWidth;
                    float y1 = (_lut.ry + _lut.rh) / texHeight;

                    Vector3 dpos0 = new Vector3(Mathf.Lerp(pos0.x, pos1.x, x0), 0.0f, Mathf.Lerp(pos0.z, pos1.z, y0));
                    Vector3 dpos1 = new Vector3(Mathf.Lerp(pos0.x, pos1.x, x1), 0.0f, Mathf.Lerp(pos0.z, pos1.z, y1));

                    positions.Add(new Vector3(dpos0.x, dpos0.z, 0));
                    positions.Add(new Vector3(dpos1.x, dpos0.z, 0));
                    positions.Add(new Vector3(dpos0.x, dpos1.z, 0));
                    positions.Add(new Vector3(dpos1.x, dpos1.z, 0));

                    if (atlasEntry.flipped)
                    {
                        uvs.Add(new Vector2(v0.x, v0.y));
                        uvs.Add(new Vector2(v0.x, v1.y));
                        uvs.Add(new Vector2(v1.x, v0.y));
                        uvs.Add(new Vector2(v1.x, v1.y));
                    }
                    else
                    {
                        uvs.Add(new Vector2(v0.x, v0.y));
                        uvs.Add(new Vector2(v1.x, v0.y));
                        uvs.Add(new Vector2(v0.x, v1.y));
                        uvs.Add(new Vector2(v1.x, v1.y));
                    }
                }

                // build sprite definition
                coll.spriteDefinitions[i].indices = new int[6 * (positions.Count / 4)];
                for (int j = 0; j < positions.Count / 4; ++j)
                {
                    coll.spriteDefinitions[i].indices[j * 6 + 0] = j * 4 + 0;
                    coll.spriteDefinitions[i].indices[j * 6 + 1] = j * 4 + 3;
                    coll.spriteDefinitions[i].indices[j * 6 + 2] = j * 4 + 1;
                    coll.spriteDefinitions[i].indices[j * 6 + 3] = j * 4 + 2;
                    coll.spriteDefinitions[i].indices[j * 6 + 4] = j * 4 + 3;
                    coll.spriteDefinitions[i].indices[j * 6 + 5] = j * 4 + 0;
                }

                coll.spriteDefinitions[i].positions = new Vector3[positions.Count];
                coll.spriteDefinitions[i].uvs       = new Vector2[uvs.Count];
                for (int j = 0; j < positions.Count; ++j)
                {
                    coll.spriteDefinitions[i].positions[j] = positions[j];
                    coll.spriteDefinitions[i].uvs[j]       = uvs[j];
                }

                coll.spriteDefinitions[i].material = gen.atlasMaterials[atlasIndex];
            }

            Vector3 boundsMin = new Vector3(1.0e32f, 1.0e32f, 1.0e32f);
            Vector3 boundsMax = new Vector3(-1.0e32f, -1.0e32f, -1.0e32f);
            foreach (Vector3 v in coll.spriteDefinitions[i].positions)
            {
                boundsMin = Vector3.Min(boundsMin, v);
                boundsMax = Vector3.Max(boundsMax, v);
            }

            coll.spriteDefinitions[i].boundsData    = new Vector3[2];
            coll.spriteDefinitions[i].boundsData[0] = (boundsMax + boundsMin) / 2.0f;
            coll.spriteDefinitions[i].boundsData[1] = (boundsMax - boundsMin);
            coll.spriteDefinitions[i].name          = gen.textureParams[i].name;
        }
    }
        // Returns number of remaining rects
        public int Build()
        {
            // Initialize
            atlases = new List <AtlasData>();
            remainingRectIndices = new List <int>();
            bool[] usedRect = new bool[sourceRects.Count];

            // Sanity check, can't build with textures larger than the actual max atlas size
            int minSize = Math.Min(atlasWidth, atlasHeight);
            int maxSize = Math.Max(atlasWidth, atlasHeight);

            foreach (RectSize rs in sourceRects)
            {
                int maxDim = Math.Max(rs.width, rs.height);
                int minDim = Math.Min(rs.width, rs.height);

                // largest texture needs to fit in an atlas
                if (maxDim > maxSize || (maxDim <= maxSize && minDim > minSize))
                {
                    remainingRectIndices = new List <int>();
                    for (int i = 0; i < sourceRects.Count; ++i)
                    {
                        remainingRectIndices.Add(i);
                    }
                    return(remainingRectIndices.Count);
                }
            }

            // Start with all source rects, this list will get reduced over time
            List <RectSize> rects   = new List <RectSize>(sourceRects);
            bool            allUsed = false;

            while (allUsed == false && atlases.Count < maxAllowedAtlasCount)
            {
                int  numPasses = 1;
                int  thisCellW = atlasWidth, thisCellH = atlasHeight;
                bool reverted = false;

                while (numPasses > 0)
                {
                    // Create copy to make sure we can scale textures down when necessary
                    List <RectSize> currRects = new List <RectSize>(rects);

//					MaxRectsBinPack binPacker = new MaxRectsBinPack(thisCellW, thisCellH);
//					allUsed = binPacker.Insert(currRects, MaxRectsBinPack.FreeRectChoiceHeuristic.RectBestAreaFit);
                    MaxRectsBinPack binPacker = FindBestBinPacker(thisCellW, thisCellH, ref currRects, ref allUsed);
                    float           occupancy = binPacker.Occupancy();

                    // Consider the atlas resolved when after the first pass, all textures are used, and the occupancy > 0.5f, scaling
                    // down by half to maintain PO2 requirements means this is as good as it gets
                    bool firstPassFull = numPasses == 1 && occupancy > 0.5f;

                    // Reverted copes with the case when halving the atlas size when occupancy < 0.5f, the textures don't fit in the
                    // atlas anymore. At this point, size is reverted to the previous value, and the loop should accept this as the final value
                    if (firstPassFull ||
                        (numPasses > 1 && occupancy > 0.5f && allUsed) ||
                        reverted)
                    {
                        List <AtlasEntry> atlasEntries = new List <AtlasEntry>();

                        foreach (var t in binPacker.GetMapped())
                        {
                            int  matchedId = -1;
                            bool flipped   = false;
                            for (int i = 0; i < sourceRects.Count; ++i)
                            {
                                if (!usedRect[i] && sourceRects[i].width == t.width && sourceRects[i].height == t.height)
                                {
                                    matchedId = i;
                                    break;
                                }
                            }

                            // Not matched anything yet, so look for the same rects rotated
                            if (matchedId == -1)
                            {
                                for (int i = 0; i < sourceRects.Count; ++i)
                                {
                                    if (!usedRect[i] && sourceRects[i].width == t.height && sourceRects[i].height == t.width)
                                    {
                                        matchedId = i;
                                        flipped   = true;
                                        break;
                                    }
                                }
                            }

                            // If this fails its a catastrophic error
                            usedRect[matchedId] = true;
                            AtlasEntry newEntry = new AtlasEntry();
                            newEntry.flipped = flipped;
                            newEntry.x       = t.x;
                            newEntry.y       = t.y;
                            newEntry.w       = t.width;
                            newEntry.h       = t.height;
                            newEntry.index   = matchedId;
                            atlasEntries.Add(newEntry);
                        }

                        AtlasData currAtlas = new AtlasData();
                        currAtlas.width     = thisCellW;
                        currAtlas.height    = thisCellH;
                        currAtlas.occupancy = binPacker.Occupancy();
                        currAtlas.entries   = atlasEntries.ToArray();

                        atlases.Add(currAtlas);

                        rects = currRects;
                        break;                         // done
                    }
                    else
                    {
                        if (!allUsed)
                        {
                            // Can only try another size when it already has been scaled down for the first time
                            if (thisCellW < atlasWidth && thisCellH < atlasHeight)
                            {
                                // Tried to scale down, but the texture doesn't fit, so revert previous change, and
                                // iterate over the data again forcing a pass even though there is wastage
                                if (thisCellW < thisCellH)
                                {
                                    thisCellW *= 2;
                                }
                                else
                                {
                                    thisCellH *= 2;
                                }
                            }


                            reverted = true;
                        }
                        else
                        {
                            // More than half the texture was unused, scale down by one of the dimensions
                            if (thisCellW < thisCellH)
                            {
                                thisCellH /= 2;
                            }
                            else
                            {
                                thisCellW /= 2;
                            }
                        }

                        numPasses++;
                    }
                }
            }

            remainingRectIndices = new List <int>();
            for (int i = 0; i < usedRect.Length; ++i)
            {
                if (!usedRect[i])
                {
                    remainingRectIndices.Add(i);
                }
            }

            return(remainingRectIndices.Count);
        }
        // Returns number of remaining rects
        public int Build()
        {
            // Initialize
            atlases = new List<AtlasData>();
            remainingRectIndices = new List<int>();
            bool[] usedRect = new bool[sourceRects.Count];

            // Sanity check, can't build with textures larger than the actual max atlas size
            int minSize = Math.Min(atlasWidth, atlasHeight);
            int maxSize = Math.Max(atlasWidth, atlasHeight);
            foreach (RectSize rs in sourceRects)
            {
                int maxDim = Math.Max(rs.width, rs.height);
                int minDim = Math.Min(rs.width, rs.height);

                // largest texture needs to fit in an atlas
                if (maxDim > maxSize || (maxDim <= maxSize && minDim > minSize))
                {
                    remainingRectIndices = new List<int>();
                    for (int i = 0; i < sourceRects.Count; ++i)
                        remainingRectIndices.Add(i);
                    return remainingRectIndices.Count;
                }
            }

            // Start with all source rects, this list will get reduced over time
            List<RectSize> rects = new List<RectSize>(sourceRects);
            bool allUsed = false;
            while (allUsed == false && atlases.Count < maxAllowedAtlasCount)
            {
                int numPasses = 1;
                int thisCellW = atlasWidth, thisCellH = atlasHeight;
                bool reverted = false;

                while (numPasses > 0)
                {
                    // Create copy to make sure we can scale textures down when necessary
                    List<RectSize> currRects = new List<RectSize>(rects);

            //					MaxRectsBinPack binPacker = new MaxRectsBinPack(thisCellW, thisCellH);
            //					allUsed = binPacker.Insert(currRects, MaxRectsBinPack.FreeRectChoiceHeuristic.RectBestAreaFit);
                    MaxRectsBinPack binPacker = FindBestBinPacker(thisCellW, thisCellH, ref currRects, ref allUsed);
                    float occupancy = binPacker.Occupancy();

                    // Consider the atlas resolved when after the first pass, all textures are used, and the occupancy > 0.5f, scaling
                    // down by half to maintain PO2 requirements means this is as good as it gets
                    bool firstPassFull = numPasses == 1 && occupancy > 0.5f;

                    // Reverted copes with the case when halving the atlas size when occupancy < 0.5f, the textures don't fit in the
                    // atlas anymore. At this point, size is reverted to the previous value, and the loop should accept this as the final value
                    if ( firstPassFull ||
                        (numPasses > 1 && occupancy > 0.5f && allUsed) ||
                        reverted)
                    {
                        List<AtlasEntry> atlasEntries = new List<AtlasEntry>();

                        foreach (var t in binPacker.GetMapped())
                        {
                            int matchedId = -1;
                            bool flipped = false;
                            for (int i = 0; i < sourceRects.Count; ++i)
                            {
                                if (!usedRect[i] && sourceRects[i].width == t.width && sourceRects[i].height == t.height)
                                {
                                    matchedId = i;
                                    break;
                                }
                            }

                            // Not matched anything yet, so look for the same rects rotated
                            if (matchedId == -1)
                            {
                                for (int i = 0; i < sourceRects.Count; ++i)
                                {
                                    if (!usedRect[i] && sourceRects[i].width == t.height && sourceRects[i].height == t.width)
                                    {
                                        matchedId = i;
                                        flipped = true;
                                        break;
                                    }
                                }
                            }

                            // If this fails its a catastrophic error
                            usedRect[matchedId] = true;
                            AtlasEntry newEntry = new AtlasEntry();
                            newEntry.flipped = flipped;
                            newEntry.x = t.x;
                            newEntry.y = t.y;
                            newEntry.w = t.width;
                            newEntry.h = t.height;
                            newEntry.index = matchedId;
                            atlasEntries.Add(newEntry);
                        }

                        AtlasData currAtlas = new AtlasData();
                        currAtlas.width = thisCellW;
                        currAtlas.height = thisCellH;
                        currAtlas.occupancy = binPacker.Occupancy();
                        currAtlas.entries = atlasEntries.ToArray();

                        atlases.Add(currAtlas);

                        rects = currRects;
                        break; // done
                    }
                    else
                    {
                        if (!allUsed)
                        {
                            // Can only try another size when it already has been scaled down for the first time
                            if (thisCellW < atlasWidth && thisCellH < atlasHeight)
                            {
                                // Tried to scale down, but the texture doesn't fit, so revert previous change, and
                                // iterate over the data again forcing a pass even though there is wastage
                                if (thisCellW < thisCellH) thisCellW *= 2;
                                else thisCellH *= 2;
                            }

                            reverted = true;
                        }
                        else
                        {
                            // More than half the texture was unused, scale down by one of the dimensions
                            if (thisCellW < thisCellH) thisCellH /= 2;
                            else thisCellW /= 2;
                        }

                        numPasses++;
                    }
                }
            }

            remainingRectIndices = new List<int>();
            for (int i = 0; i < usedRect.Length; ++i)
            {
                if (!usedRect[i])
                {
                    remainingRectIndices.Add(i);
                }
            }

            return remainingRectIndices.Count;
        }