public static bool Rebuild(tk2dSpriteCollection gen)
    {
        // avoid "recursive" build being triggered by texture watcher
        if (currentBuild != null)
            return false;

        // Version specific checks. These need to be done before the sprite collection is upgraded.
        if (gen.version < 2)
        {
          	if (!tk2dEditor.SpriteCollectionBuilder.Deprecated.CheckAndFixUpParams(gen))
            {
                // Params failed check
                return false;
            }
            tk2dEditor.SpriteCollectionBuilder.Deprecated.SetUpSpriteSheets(gen);
            tk2dEditor.SpriteCollectionBuilder.Deprecated.TrimTextureList(gen);
        }
        currentBuild = gen;
        gen.Upgrade(); // upgrade if necessary. could be invoked by texture watcher.

        // Get some sensible paths to work with
        string dataDirName = GetOrCreateDataPath(gen) + "/";

        string prefabObjectPath = "";
        if (gen.spriteCollection)
            prefabObjectPath = AssetDatabase.GetAssetPath(gen.spriteCollection);
        else
            prefabObjectPath = dataDirName + gen.name + ".prefab";
        BuildDirectoryToFile(prefabObjectPath);

        // Create prefab object, needed for next stage
        CreateDataObject( gen, prefabObjectPath );

        // Special build for platform specific sprite collection
        if (gen.HasPlatformData)
        {
            // Initialize required sprite collections
            tk2dEditor.SpriteCollectionBuilder.PlatformBuilder.InitializeSpriteCollectionPlatforms(gen, System.IO.Path.GetDirectoryName(prefabObjectPath));

            // The first sprite collection is always THIS
            tk2dAssetPlatform baseAssetPlatform = tk2dSystem.GetAssetPlatform(gen.platforms[0].name);
            float baseScale = (baseAssetPlatform != null)?baseAssetPlatform.scale:1.0f;

            // Building platform specific data, allow recursive builds temporarily
            currentBuild = null;

            // Transfer to platform sprite collections, and build those
            for (int i = 0; i < gen.platforms.Count; ++i)
            {
                tk2dSpriteCollectionPlatform platform = gen.platforms[i];

                tk2dAssetPlatform thisAssetPlatform = tk2dSystem.GetAssetPlatform(gen.platforms[i].name);
                float thisScale = (thisAssetPlatform != null)?thisAssetPlatform.scale:1.0f;

                bool validPlatform = platform.name.Length > 0 && platform.spriteCollection != null;
                bool isRootSpriteCollection = (i == 0);
                if (validPlatform)
                {
                    tk2dSpriteCollection thisPlatformCollection = gen.platforms[i].spriteCollection;

                    // Make sure data directory exists, material overrides will be created in here
                    string dataPath = GetOrCreateDataPath(thisPlatformCollection);

                    tk2dEditor.SpriteCollectionBuilder.PlatformBuilder.UpdatePlatformSpriteCollection(
                        gen, thisPlatformCollection, dataPath, isRootSpriteCollection, thisScale / baseScale, platform.name);
                    Rebuild(thisPlatformCollection);
                }
            }

            // Pull atlas data from default platform
            gen.atlasMaterials = gen.platforms[0].spriteCollection.atlasMaterials;
            gen.altMaterials = gen.platforms[0].spriteCollection.altMaterials;

            // Fill up our sprite collection data
            List<string> platformNames = new List<string>();
            List<string> platformGUIDs = new List<string>();

            for (int i = 0; i < gen.platforms.Count; ++i)
            {
                tk2dSpriteCollectionPlatform platform = gen.platforms[i];
                if (!platform.Valid) continue;

                platformNames.Add(platform.name);
                tk2dSpriteCollectionData data = platform.spriteCollection.spriteCollection;
                string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(data));
                platformGUIDs.Add(guid);

                // Make loadable
                tk2dSystemUtility.MakeLoadableAsset(data, ""); // unnamed loadable object
            }

            // Set up fonts
            for (int j = 0; j < gen.fonts.Length; ++j)
            {
                tk2dSpriteCollectionFont font = gen.fonts[j];
                if (font == null) continue;

                tk2dFontData data = font.data;
                if (!data) continue;
                data.hasPlatformData = true;
                data.material = null;
                data.materialInst = null;
                data.fontPlatforms = platformNames.ToArray();
                data.fontPlatformGUIDs = new string[platformNames.Count];
                for (int i = 0; i < gen.platforms.Count; ++i)
                {
                    tk2dSpriteCollectionPlatform platform = gen.platforms[i];
                    if (!platform.Valid) continue;
                    data.fontPlatformGUIDs[i] = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(gen.platforms[i].spriteCollection.fonts[j].data));
                }

                EditorUtility.SetDirty(data);
            }

            gen.spriteCollection.version = tk2dSpriteCollectionData.CURRENT_VERSION;
            gen.spriteCollection.spriteCollectionName = gen.name;
            gen.spriteCollection.spriteCollectionGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(gen));
            gen.spriteCollection.dataGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(gen.spriteCollection));

            // Clear out data
            gen.spriteCollection.spriteDefinitions = new tk2dSpriteDefinition[0];
            gen.spriteCollection.materials = new Material[0];
            gen.spriteCollection.textures = new Texture2D[0];

            gen.spriteCollection.hasPlatformData = true;
            gen.spriteCollection.spriteCollectionPlatforms = platformNames.ToArray();
            gen.spriteCollection.spriteCollectionPlatformGUIDs = platformGUIDs.ToArray();

            EditorUtility.SetDirty(gen);
            EditorUtility.SetDirty(gen.spriteCollection);

            // Index this properly
            tk2dEditorUtility.GetOrCreateIndex().AddSpriteCollectionData(gen.spriteCollection);
            tk2dEditorUtility.CommitIndex();

            AssetDatabase.SaveAssets();

            ResetCurrentBuild();
            return true;
        }
        else
        {
            // clear platform data
            gen.spriteCollection.ResetPlatformData(); // in case its being used
            gen.spriteCollection.hasPlatformData = false;
            gen.spriteCollection.spriteCollectionPlatforms = new string[0];
            gen.spriteCollection.spriteCollectionPlatformGUIDs = new string[0];

            // Set up fonts
            for (int j = 0; j < gen.fonts.Length; ++j)
            {
                tk2dFontData f = gen.fonts[j].data;
                if (f != null)
                {
                    f.ResetPlatformData();
                    f.hasPlatformData = false;
                    f.fontPlatforms = new string[0];
                    f.fontPlatformGUIDs = new string[0];
                    EditorUtility.SetDirty(f);
                }
            }
        }

        // Make sure all source textures are in the correct format
        SetUpSourceTextureFormats(gen);

        // blank texture used when texture has been deleted
        Texture2D blankTexture = new Texture2D(2, 2);
        blankTexture.SetPixel(0, 0, Color.magenta);
        blankTexture.SetPixel(0, 1, Color.yellow);
        blankTexture.SetPixel(1, 0, Color.cyan);
        blankTexture.SetPixel(1, 1, Color.grey);
        blankTexture.Apply();

        // make local texture sources
        List<Texture2D> allocatedTextures = new List<Texture2D>();
        sourceTextures = new Texture2D[gen.textureParams.Length];
        for (int i = 0; i < gen.textureParams.Length; ++i)
        {
            var param = gen.textureParams[i];
            if (param.extractRegion && param.texture != null)
            {
                Texture2D localTex = new Texture2D(param.regionW, param.regionH);
                for (int y = 0; y < param.regionH; ++y)
                {
                    for (int x = 0; x < param.regionW; ++x)
                    {
                        localTex.SetPixel(x, y, param.texture.GetPixel(param.regionX + x, param.regionY + y));
                    }
                }
                localTex.name = param.texture.name + "/" + param.regionId.ToString();
                localTex.Apply();
                allocatedTextures.Add(localTex);
                sourceTextures[i] = localTex;
            }
            else
            {
                sourceTextures[i] = gen.textureParams[i].texture;
            }
        }

        // catalog all textures to atlas
        int numTexturesToAtlas = 0;
        List<SpriteLut> spriteLuts = new List<SpriteLut>();
        for (int i = 0; i < gen.textureParams.Length; ++i)
        {
            Texture2D currentTexture = sourceTextures[i];

            if (sourceTextures[i] == null)
            {
                gen.textureParams[i].dice = false;
                gen.textureParams[i].anchor = tk2dSpriteCollectionDefinition.Anchor.MiddleCenter;
                gen.textureParams[i].name = "";
                gen.textureParams[i].extractRegion = false;
                gen.textureParams[i].fromSpriteSheet = false;

                currentTexture = blankTexture;
            }
            else
            {
                if (gen.textureParams[i].name == null || gen.textureParams[i].name == "")
                {
                    if (gen.textureParams[i].texture != currentTexture && !gen.textureParams[i].fromSpriteSheet)
                    {
                        gen.textureParams[i].name = currentTexture.name;
                    }
                }
            }

            if (gen.textureParams[i].dice)
            {
                // prepare to dice this up
                int diceUnitX = gen.textureParams[i].diceUnitX;
                int diceUnitY = gen.textureParams[i].diceUnitY;
                if (diceUnitX <= 0) diceUnitX = 128; // something sensible, please
                if (diceUnitY <= 0) diceUnitY = diceUnitX; // make square if not set

                Texture2D srcTex = currentTexture;
                for (int sx = 0; sx < srcTex.width; sx += diceUnitX)
                {
                    for (int sy = 0; sy < srcTex.height; sy += diceUnitY)
                    {
                        int tw = Mathf.Min(diceUnitX, srcTex.width - sx);
                        int th = Mathf.Min(diceUnitY, srcTex.height - sy);

                        SpriteLut diceLut = new SpriteLut();
                        diceLut.source = i;
                        diceLut.isSplit = true;
                        diceLut.sourceTex = srcTex;
                        diceLut.isDuplicate = false; // duplicate diced textures can be chopped up differently, so don't detect dupes here

                        Texture2D dest = ProcessTexture(gen, gen.textureParams[i].additive, true, gen.textureParams[i].disableTrimming, false, srcTex, sx, sy, tw, th, ref diceLut, GetPadAmount(gen, i));
                        if (dest)
                        {
                            diceLut.atlasIndex = numTexturesToAtlas++;
                            spriteLuts.Add(diceLut);
                        }
                    }
                }
            }
            else
            {
                SpriteLut lut = new SpriteLut();
                lut.sourceTex = currentTexture;
                lut.source = i;

                lut.isSplit = false;
                lut.isDuplicate = false;
                for (int j = 0; j < spriteLuts.Count; ++j)
                {
                    if (spriteLuts[j].sourceTex == lut.sourceTex)
                    {
                        lut.isDuplicate = true;
                        lut.atlasIndex = spriteLuts[j].atlasIndex;
                        lut.tex = spriteLuts[j].tex; // get old processed tex

                        lut.rx = spriteLuts[j].rx; lut.ry = spriteLuts[j].ry;
                        lut.rw = spriteLuts[j].rw; lut.rh = spriteLuts[j].rh;

                        break;
                    }
                }

                if (!lut.isDuplicate)
                {
                    lut.atlasIndex = numTexturesToAtlas++;
                    bool stretchPad = false;
                    if (gen.textureParams[i].pad == tk2dSpriteCollectionDefinition.Pad.Extend) stretchPad = true;

                    Texture2D dest = ProcessTexture(gen, gen.textureParams[i].additive, stretchPad, gen.textureParams[i].disableTrimming, false, currentTexture, 0, 0, currentTexture.width, currentTexture.height, ref lut, GetPadAmount(gen, i));
                    if (dest == null)
                    {
                        // fall back to a tiny blank texture
                        lut.tex = new Texture2D(1, 1);
                        lut.tex.SetPixel(0, 0, new Color( 0, 0, 0, 0 ));
                        PadTexture(lut.tex, GetPadAmount(gen, i), stretchPad);
                        lut.tex.Apply();

                        lut.rx = currentTexture.width / 2; lut.ry = currentTexture.height / 2;
                        lut.rw = 1; lut.rh = 1;
                    }
                }

                spriteLuts.Add(lut);
            }
        }

        // Font
        Dictionary<tk2dSpriteCollectionFont, tk2dEditor.Font.Info> fontInfoDict = new Dictionary<tk2dSpriteCollectionFont, tk2dEditor.Font.Info>();
        if (gen.fonts != null)
        {
            for (int i = 0; i < gen.fonts.Length; ++i)
            {
                var font = gen.fonts[i];
                if (!font.InUse) continue;

                var fontInfo = tk2dEditor.Font.Builder.ParseBMFont( AssetDatabase.GetAssetPath(font.bmFont) );
                fontInfoDict[font] = fontInfo;
                foreach (var c in fontInfo.chars)
                {
                    // skip empty textures
                    if (c.width <= 0 || c.height <= 0)
                        continue;

                    SpriteLut lut = new SpriteLut();

                    int cy = font.flipTextureY?c.y:(fontInfo.scaleH - c.y - c.height);
                    Texture2D dest = ProcessTexture(gen, false, false, false, true,
                        font.texture, c.x, cy, c.width, c.height,
                        ref lut, GetPadAmount(gen, -1));
                    if (dest == null)
                    {
                        // probably fully transparent
                        continue;
                    }

                    lut.sourceTex = dest;
                    lut.tex = dest;
                    lut.source = -1;
                    lut.isFont = true;
                    lut.isDuplicate = false;
                    lut.fontId = i;
                    lut.charId = c.id;
                    lut.rx = lut.rx - c.x;
                    lut.ry = lut.ry - cy;

                    lut.atlasIndex = numTexturesToAtlas++;

                    spriteLuts.Add(lut);
                }

                // Add one blank char for fallbacks
                {
                    int dims = 5;
                    SpriteLut lut = new SpriteLut();
                    lut.tex = new Texture2D(dims, dims);
                    for (int y = 0; y < dims; ++y)
                        for (int x = 0; x < dims; ++x)
                            lut.tex.SetPixel(x, y, Color.clear);
                    lut.tex.Apply();
                    lut.sourceTex = lut.tex;
                    lut.isFont = true;
                    lut.isDuplicate = false;
                    lut.fontId = i;
                    lut.charId = -1;
                    lut.atlasIndex = numTexturesToAtlas++;

                    spriteLuts.Add(lut);
                }
            }
        }

        // Create texture
        Texture2D[] textureList = new Texture2D[numTexturesToAtlas];
        int titer = 0;
        for (int i = 0; i < spriteLuts.Count; ++i)
        {
            SpriteLut _lut = spriteLuts[i];
            if (!_lut.isDuplicate)
            {
                textureList[titer++] = _lut.tex;
            }
        }

        // Build atlas
        bool forceAtlasSize = gen.forceTextureSize;
        int atlasWidth = forceAtlasSize?gen.forcedTextureWidth:gen.maxTextureSize;
        int atlasHeight = forceAtlasSize?gen.forcedTextureHeight:gen.maxTextureSize;
        bool forceSquareAtlas = forceAtlasSize?false:gen.forceSquareAtlas;
        bool allowFindingOptimalSize = !forceAtlasSize;
        tk2dEditor.Atlas.Builder atlasBuilder = new tk2dEditor.Atlas.Builder(atlasWidth, atlasHeight, gen.allowMultipleAtlases?64:1, allowFindingOptimalSize, forceSquareAtlas);
        if (textureList.Length > 0)
        {
            foreach (Texture2D currTexture in textureList)
            {
                atlasBuilder.AddRect(currTexture.width, currTexture.height);
            }
            if (atlasBuilder.Build() != 0)
            {
                if (atlasBuilder.HasOversizeTextures())
                {
                    EditorUtility.DisplayDialog("Unable to fit in atlas",
                                                "You have a texture which exceeds the atlas size. " +
                                                "Consider putting it in a separate atlas, enabling dicing, or " +
                                                "reducing the texture size",
                                                "Ok");
                }
                else
                {
                    EditorUtility.DisplayDialog("Unable to fit textures in requested atlas area",
                                                "There are too many textures in this collection for the requested " +
                                                "atlas size.",
                                                "Ok");
                }
                return false;
            }
        }

        // Fill atlas textures
        tk2dEditor.Atlas.Data[] atlasData = atlasBuilder.GetAtlasData();
        System.Array.Resize(ref gen.atlasTextures, atlasData.Length);
        System.Array.Resize(ref gen.atlasMaterials, atlasData.Length);
        if (atlasData.Length > 1)
        {
            // wipe out alt materials when atlas spanning is on
            gen.altMaterials = new Material[0];
        }
        for (int atlasIndex = 0; atlasIndex < atlasData.Length; ++atlasIndex)
        {
            Texture2D tex = new Texture2D(64, 64, TextureFormat.ARGB32, false);

            gen.atlasWastage = (1.0f - atlasData[0].occupancy) * 100.0f;
            gen.atlasWidth = atlasData[0].width;
            gen.atlasHeight = atlasData[0].height;

            tex.Resize(atlasData[atlasIndex].width, atlasData[atlasIndex].height);

            // Clear texture, unsure if this is REALLY necessary
            // Turns out it is
            for (int yy = 0; yy < tex.height; ++yy)
            {
                for (int xx = 0; xx < tex.width; ++xx)
                {
                    tex.SetPixel(xx, yy, Color.clear);
                }
            }

            for (int i = 0; i < atlasData[atlasIndex].entries.Length; ++i)
            {
                var entry = atlasData[atlasIndex].entries[i];
                Texture2D source = textureList[entry.index];

                if (!entry.flipped)
                {
                    for (int y = 0; y < source.height; ++y)
                    {
                        for (int x = 0; x < source.width; ++x)
                        {
                            tex.SetPixel(entry.x + x, entry.y + y, source.GetPixel(x, y));
                        }
                    }
                }
                else
                {
                    for (int y = 0; y < source.height; ++y)
                    {
                        for (int x = 0; x < source.width; ++x)
                        {
                            tex.SetPixel(entry.x + y, entry.y + x, source.GetPixel(x, y));
                        }
                    }
                }
            }
            tex.Apply();

            string texturePath = gen.atlasTextures[atlasIndex]?AssetDatabase.GetAssetPath(gen.atlasTextures[atlasIndex]):(dataDirName + "atlas" + atlasIndex + ".png");
            BuildDirectoryToFile(texturePath);

            // Write filled atlas to disk
            byte[] bytes = tex.EncodeToPNG();
            System.IO.FileStream fs = System.IO.File.Create(texturePath);
            fs.Write(bytes, 0, bytes.Length);
            fs.Close();

            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            Object.DestroyImmediate(tex);

            tex = AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D)) as Texture2D;
            gen.atlasTextures[atlasIndex] = tex;

            // Make sure texture is set up with the correct max size and compression type
            SetUpTargetTexture(gen, tex);

            // Create material if necessary
            if (gen.atlasMaterials[atlasIndex] == null)
            {
                Material mat;
                if (gen.premultipliedAlpha)
                    mat = new Material(Shader.Find("tk2d/PremulVertexColor"));
                else
                    mat = new Material(Shader.Find("tk2d/BlendVertexColor"));

                mat.mainTexture = tex;

                string materialPath = gen.atlasMaterials[atlasIndex]?AssetDatabase.GetAssetPath(gen.atlasMaterials[atlasIndex]):(dataDirName + "atlas" + atlasIndex + " material.mat");
                BuildDirectoryToFile(materialPath);

                AssetDatabase.CreateAsset(mat, materialPath);
                AssetDatabase.SaveAssets();

                gen.atlasMaterials[atlasIndex] = AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material)) as Material;
            }

            // gen.altMaterials must either have length 0, or contain at least the material used in the game
            if (!gen.allowMultipleAtlases && (gen.altMaterials == null || gen.altMaterials.Length == 0))
                gen.altMaterials = new Material[1] { gen.atlasMaterials[0] };
        }

        tk2dSpriteCollectionData coll = gen.spriteCollection;
        coll.textures = new Texture[gen.atlasTextures.Length];
        for (int i = 0; i < gen.atlasTextures.Length; ++i)
        {
            coll.textures[i] = gen.atlasTextures[i];
        }

        if (gen.atlasMaterials.Length == 1)
        {
            coll.materials = new Material[gen.altMaterials.Length];
            for (int i = 0; i < gen.altMaterials.Length; ++i)
                coll.materials[i] = gen.altMaterials[i];
        }
        else
        {
            coll.materials = new Material[gen.atlasMaterials.Length];
            for (int i = 0; i < gen.atlasMaterials.Length; ++i)
                coll.materials[i] = gen.atlasMaterials[i];
        }

        // Wipe out legacy data
        coll.material = null;

        coll.premultipliedAlpha = gen.premultipliedAlpha;
        coll.spriteDefinitions = new tk2dSpriteDefinition[gen.textureParams.Length];
        coll.version = tk2dSpriteCollectionData.CURRENT_VERSION;
        coll.materialIdsValid = true;
        coll.spriteCollectionGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(gen));
        coll.spriteCollectionName = gen.name;
        coll.invOrthoSize = 1.0f / gen.targetOrthoSize;
        coll.halfTargetHeight = gen.targetHeight * 0.5f;

        int buildKey = Random.Range(0, int.MaxValue);
        while (buildKey == coll.buildKey)
        {
            buildKey = Random.Range(0, int.MaxValue);
        }
        coll.buildKey = buildKey; // a random build number so we can identify changed collections quickly

        for (int i = 0; i < coll.spriteDefinitions.Length; ++i)
        {
            coll.spriteDefinitions[i] = new tk2dSpriteDefinition();
            if (gen.textureParams[i].texture)
            {
                string assetPath = AssetDatabase.GetAssetPath(gen.textureParams[i].texture);
                string guid = AssetDatabase.AssetPathToGUID(assetPath);
                coll.spriteDefinitions[i].sourceTextureGUID = guid;
            }
            else
            {
                coll.spriteDefinitions[i].sourceTextureGUID = "";
            }

            coll.spriteDefinitions[i].extractRegion = gen.textureParams[i].extractRegion;
            coll.spriteDefinitions[i].regionX = gen.textureParams[i].regionX;
            coll.spriteDefinitions[i].regionY = gen.textureParams[i].regionY;
            coll.spriteDefinitions[i].regionW = gen.textureParams[i].regionW;
            coll.spriteDefinitions[i].regionH = gen.textureParams[i].regionH;
        }
        coll.allowMultipleAtlases = gen.allowMultipleAtlases;
        coll.dataGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(coll));

        float scale = 1.0f;
        if (gen.useTk2dCamera)
        {
            scale = 1.0f * gen.globalScale;
        }
        else
        {
            scale = (2.0f * gen.targetOrthoSize / gen.targetHeight) * gen.globalScale;
        }

        // Build fonts
        foreach (var font in fontInfoDict.Keys)
        {
            var fontInfo = fontInfoDict[font];
            List<SpriteLut> fontSpriteLut = new List<SpriteLut>();
            int fontId = 0;
            for (fontId = 0; fontId < gen.fonts.Length; ++fontId)
                if (gen.fonts[fontId] == font) break;
            foreach (var v in spriteLuts)
            {
                if (v.isFont && v.fontId == fontId)
                    fontSpriteLut.Add(v);
            }

            fontInfo.scaleW = coll.textures[0].width;
            fontInfo.scaleH = coll.textures[0].height;

            // Set material
            if (font.useGradient && font.gradientTexture != null && font.gradientCount > 0)
            {
                font.editorData.gradientCount = font.gradientCount;
                font.editorData.gradientTexture = font.gradientTexture;
            }
            else
            {
                font.editorData.gradientCount = 1;
                font.editorData.gradientTexture = null;
            }

            // Build a local sprite lut only relevant to this font
            UpdateFontData(gen, scale, atlasData, fontSpriteLut, font, fontInfo);

            if (font.useGradient && font.gradientTexture != null)
            {
                font.data.gradientCount = font.editorData.gradientCount;
                font.data.gradientTexture = font.editorData.gradientTexture;
                font.data.textureGradients = true;
            }
            else
            {
                font.data.gradientCount = 1;
                font.data.gradientTexture = null;
                font.data.textureGradients = false;
            }

            font.data.spriteCollection = gen.spriteCollection;
            font.data.material = coll.materials[font.materialId];
            font.editorData.material = coll.materials[font.materialId];

            font.data.invOrthoSize = coll.invOrthoSize;
            font.data.halfTargetHeight = coll.halfTargetHeight;
            font.data.texelSize = new Vector3(scale, scale, 0.0f);

            // Managed?
            font.data.managedFont = gen.managedSpriteCollection;
            font.data.needMaterialInstance = gen.managedSpriteCollection;

            // Mark to save
            EditorUtility.SetDirty(font.editorData);
            EditorUtility.SetDirty(font.data);

            // Update font
            tk2dEditorUtility.GetOrCreateIndex().AddOrUpdateFont(font.editorData);
            tk2dEditorUtility.CommitIndex();

            // Loadable?
            if (!tk2dSystemUtility.IsLoadableAsset(font.data))
                tk2dSystemUtility.MakeLoadableAsset(font.data, "");
        }

        // Build textures
        UpdateVertexCache(gen, scale, atlasData, coll, spriteLuts);

        // Free tmp textures
        foreach (var sprite in spriteLuts)
        {
            if (!sprite.isDuplicate)
                Object.DestroyImmediate(sprite.tex);
        }
        foreach (var tex in allocatedTextures)
            Object.DestroyImmediate(tex);

        // refresh existing
        RefreshExistingAssets(gen.spriteCollection);

        // save changes
        gen.spriteCollection.assetName = gen.assetName;
        gen.spriteCollection.managedSpriteCollection = gen.managedSpriteCollection;
        gen.spriteCollection.needMaterialInstance = gen.managedSpriteCollection;

        var index = tk2dEditorUtility.GetOrCreateIndex();
        index.AddSpriteCollectionData(gen.spriteCollection);
        EditorUtility.SetDirty(gen.spriteCollection);
        EditorUtility.SetDirty(gen);

        sourceTextures = null; // need to clear, its static
        currentBuild = null;

        tk2dEditorUtility.GetOrCreateIndex().AddSpriteCollectionData(gen.spriteCollection);
        tk2dEditorUtility.CommitIndex();

        Object.DestroyImmediate(blankTexture);

        // update resource system
        if (tk2dSystemUtility.IsLoadableAsset(gen.spriteCollection))
        {
            tk2dSystemUtility.UpdateAssetName(gen.spriteCollection, gen.assetName);
        }

        return true;
    }
    public static bool Rebuild(tk2dSpriteCollection gen)
    {
        // avoid "recursive" build being triggered by texture watcher
        if (currentBuild != null)
            return false;

        currentBuild = gen;

        List<Texture2D> allocatedTextures = new List<Texture2D>();
        string path = AssetDatabase.GetAssetPath(gen);
        string subDirName = Path.GetDirectoryName( path.Substring(7) );
        if (subDirName.Length > 0) subDirName += "/";

        string dataDirFullPath = Application.dataPath + "/" + subDirName + Path.GetFileNameWithoutExtension(path) + "_Data";
        string dataDirName = "Assets/" + dataDirFullPath.Substring( Application.dataPath.Length + 1 ) + "/";

        if (gen.atlasTextures == null || gen.atlasTextures.Length == 0 ||
            gen.atlasMaterials == null || gen.atlasMaterials.Length == 0 ||
            gen.spriteCollection == null)
        {
            if (!Directory.Exists(dataDirFullPath)) Directory.CreateDirectory(dataDirFullPath);
            AssetDatabase.Refresh();
        }

        string prefabObjectPath = gen.spriteCollection?AssetDatabase.GetAssetPath(gen.spriteCollection):(dataDirName + "data.prefab");

          	if (!CheckAndFixUpParams(gen))
        {
            // Params failed check
            return false;
        }

        SetUpSourceTextureFormats(gen);
        SetUpSpriteSheets(gen);
        TrimTextureList(gen);

        // blank texture used when texture has been deleted
        Texture2D blankTexture = new Texture2D(2, 2);
        blankTexture.SetPixel(0, 0, Color.magenta);
        blankTexture.SetPixel(0, 1, Color.yellow);
        blankTexture.SetPixel(1, 0, Color.cyan);
        blankTexture.SetPixel(1, 1, Color.grey);
        blankTexture.Apply();

        // make local texture sources
        sourceTextures = new Texture2D[gen.textureRefs.Length];
        for (int i = 0; i < gen.textureParams.Length; ++i)
        {
            var param = gen.textureParams[i];
            if (param.extractRegion && gen.textureRefs[i] != null)
            {
                Texture2D localTex = new Texture2D(param.regionW, param.regionH);
                for (int y = 0; y < param.regionH; ++y)
                {
                    for (int x = 0; x < param.regionW; ++x)
                    {
                        localTex.SetPixel(x, y, gen.textureRefs[i].GetPixel(param.regionX + x, param.regionY + y));
                    }
                }
                localTex.name = gen.textureRefs[i].name + "/" + param.regionId.ToString();
                localTex.Apply();
                allocatedTextures.Add(localTex);
                sourceTextures[i] = localTex;
            }
            else
            {
                sourceTextures[i] = gen.textureRefs[i];
            }
        }

        // catalog all textures to atlas
        int numTexturesToAtlas = 0;
        List<SpriteLut> spriteLuts = new List<SpriteLut>();
        for (int i = 0; i < gen.textureParams.Length; ++i)
        {
            Texture2D currentTexture = sourceTextures[i];

            if (sourceTextures[i] == null)
            {
                gen.textureParams[i].dice = false;
                gen.textureParams[i].anchor = tk2dSpriteCollectionDefinition.Anchor.MiddleCenter;
                gen.textureParams[i].name = "";
                gen.textureParams[i].extractRegion = false;
                gen.textureParams[i].fromSpriteSheet = false;

                currentTexture = blankTexture;
            }
            else
            {
                if (gen.textureParams[i].name == null || gen.textureParams[i].name == "")
                {
                    if (gen.textureParams[i].texture != currentTexture && !gen.textureParams[i].fromSpriteSheet)
                    {
                        gen.textureParams[i].name = currentTexture.name;
                    }
                }
            }

            gen.textureParams[i].texture = currentTexture;

            if (gen.textureParams[i].dice)
            {
                // prepare to dice this up
                int diceUnitX = gen.textureParams[i].diceUnitX;
                int diceUnitY = gen.textureParams[i].diceUnitY;
                if (diceUnitX <= 0) diceUnitX = 128; // something sensible, please
                if (diceUnitY <= 0) diceUnitY = diceUnitX; // make square if not set

                Texture2D srcTex = currentTexture;
                for (int sx = 0; sx < srcTex.width; sx += diceUnitX)
                {
                    for (int sy = 0; sy < srcTex.height; sy += diceUnitY)
                    {
                        int tw = Mathf.Min(diceUnitX, srcTex.width - sx);
                        int th = Mathf.Min(diceUnitY, srcTex.height - sy);

                        SpriteLut diceLut = new SpriteLut();
                        diceLut.source = i;
                        diceLut.isSplit = true;
                        diceLut.sourceTex = srcTex;
                        diceLut.isDuplicate = false; // duplicate diced textures can be chopped up differently, so don't detect dupes here

                        Texture2D dest = ProcessTexture(gen, gen.textureParams[i].additive, true, srcTex, sx, sy, tw, th, ref diceLut, GetPadAmount(gen, i));
                        if (dest)
                        {
                            diceLut.atlasIndex = numTexturesToAtlas++;
                            spriteLuts.Add(diceLut);
                        }
                    }
                }
            }
            else
            {
                SpriteLut lut = new SpriteLut();
                lut.sourceTex = currentTexture;
                lut.source = i;

                lut.isSplit = false;
                lut.isDuplicate = false;
                for (int j = 0; j < spriteLuts.Count; ++j)
                {
                    if (spriteLuts[j].sourceTex == lut.sourceTex)
                    {
                        lut.isDuplicate = true;
                        lut.atlasIndex = spriteLuts[j].atlasIndex;
                        lut.tex = spriteLuts[j].tex; // get old processed tex

                        lut.rx = spriteLuts[j].rx; lut.ry = spriteLuts[j].ry;
                        lut.rw = spriteLuts[j].rw; lut.rh = spriteLuts[j].rh;

                        break;
                    }
                }

                if (!lut.isDuplicate)
                {
                    lut.atlasIndex = numTexturesToAtlas++;
                    bool stretchPad = false;
                    if (gen.textureParams[i].pad == tk2dSpriteCollectionDefinition.Pad.Extend) stretchPad = true;

                    Texture2D dest = ProcessTexture(gen, gen.textureParams[i].additive, stretchPad, currentTexture, 0, 0, currentTexture.width, currentTexture.height, ref lut, GetPadAmount(gen, i));
                    if (dest == null)
                    {
                        // fall back to a tiny blank texture
                        lut.tex = new Texture2D(1, 1);
                        lut.tex.SetPixel(0, 0, new Color( 0, 0, 0, 0 ));
                        PadTexture(lut.tex, GetPadAmount(gen, i), stretchPad);
                        lut.tex.Apply();

                        lut.rx = currentTexture.width / 2; lut.ry = currentTexture.height / 2;
                        lut.rw = 1; lut.rh = 1;
                    }
                }

                spriteLuts.Add(lut);
            }
        }

        // Font
        Dictionary<tk2dSpriteCollectionFont, tk2dEditor.Font.Info> fontInfoDict = new Dictionary<tk2dSpriteCollectionFont, tk2dEditor.Font.Info>();
        if (gen.fonts != null)
        {
            for (int i = 0; i < gen.fonts.Length; ++i)
            {
                var font = gen.fonts[i];
                if (!font.InUse) continue;

                var fontInfo = tk2dEditor.Font.Builder.ParseBMFont( AssetDatabase.GetAssetPath(font.bmFont) );
                fontInfoDict[font] = fontInfo;
                foreach (var c in fontInfo.chars)
                {
                    // skip empty textures
                    if (c.width <= 0 || c.height <= 0)
                        continue;

                    SpriteLut lut = new SpriteLut();

                    int cy = font.flipTextureY?c.y:(fontInfo.scaleH - c.y - c.height);
                    Texture2D dest = ProcessTexture(gen, false, false,
                        font.texture, c.x, cy, c.width, c.height,
                        ref lut, GetPadAmount(gen, -1));
                    if (dest == null)
                    {
                        // probably fully transparent
                        continue;
                    }

                    lut.sourceTex = dest;
                    lut.tex = dest;
                    lut.source = -1;
                    lut.isFont = true;
                    lut.isDuplicate = false;
                    lut.fontId = i;
                    lut.charId = c.id;
                    lut.rx = lut.rx - c.x;
                    lut.ry = lut.ry - cy;

                    lut.atlasIndex = numTexturesToAtlas++;

                    spriteLuts.Add(lut);
                }

                // Add one blank char for fallbacks
                {
                    int dims = 5;
                    SpriteLut lut = new SpriteLut();
                    lut.tex = new Texture2D(dims, dims);
                    for (int y = 0; y < dims; ++y)
                        for (int x = 0; x < dims; ++x)
                            lut.tex.SetPixel(x, y, Color.clear);
                    lut.tex.Apply();
                    lut.sourceTex = lut.tex;
                    lut.isFont = true;
                    lut.isDuplicate = false;
                    lut.fontId = i;
                    lut.charId = -1;
                    lut.atlasIndex = numTexturesToAtlas++;

                    spriteLuts.Add(lut);
                }
            }
        }

        // Create texture
        Texture2D[] textureList = new Texture2D[numTexturesToAtlas];
        int titer = 0;
        for (int i = 0; i < spriteLuts.Count; ++i)
        {
            SpriteLut _lut = spriteLuts[i];
            if (!_lut.isDuplicate)
            {
                textureList[titer++] = _lut.tex;
            }
        }

        // Build atlas
        tk2dEditor.Atlas.Builder atlasBuilder = new tk2dEditor.Atlas.Builder(gen.maxTextureSize, gen.maxTextureSize, gen.allowMultipleAtlases?64:1, gen.forceSquareAtlas);
        if (textureList.Length > 0)
        {
            foreach (Texture2D currTexture in textureList)
            {
                atlasBuilder.AddRect(currTexture.width, currTexture.height);
            }
            if (atlasBuilder.Build() != 0)
            {
                if (atlasBuilder.HasOversizeTextures())
                {
                    EditorUtility.DisplayDialog("Unable to fit in atlas",
                                                "You have a texture which exceeds the atlas size." +
                                                "Consider putting it in a separate atlas, enabling dicing, or" +
                                                "reducing the texture size",
                                                "Ok");
                }
                else
                {
                    EditorUtility.DisplayDialog("Unable to fit textures in requested atlas area",
                                                "There are too many textures in this collection for the requested " +
                                                "atlas size.",
                                                "Ok");
                }
                return false;
            }
        }

        // Fill atlas textures
        tk2dEditor.Atlas.Data[] atlasData = atlasBuilder.GetAtlasData();
        System.Array.Resize(ref gen.atlasTextures, atlasData.Length);
        System.Array.Resize(ref gen.atlasMaterials, atlasData.Length);
        for (int atlasIndex = 0; atlasIndex < atlasData.Length; ++atlasIndex)
        {
            Texture2D tex = new Texture2D(64, 64, TextureFormat.ARGB32, false);

            gen.atlasWastage = (1.0f - atlasData[0].occupancy) * 100.0f;
            gen.atlasWidth = atlasData[0].width;
            gen.atlasHeight = atlasData[0].height;

            tex.Resize(atlasData[atlasIndex].width, atlasData[atlasIndex].height);

            // Clear texture, unsure if this is REALLY necessary
            // Turns out it is
            for (int yy = 0; yy < tex.height; ++yy)
            {
                for (int xx = 0; xx < tex.width; ++xx)
                {
                    tex.SetPixel(xx, yy, Color.clear);
                }
            }

            for (int i = 0; i < atlasData[atlasIndex].entries.Length; ++i)
            {
                var entry = atlasData[atlasIndex].entries[i];
                Texture2D source = textureList[entry.index];

                if (!entry.flipped)
                {
                    for (int y = 0; y < source.height; ++y)
                    {
                        for (int x = 0; x < source.width; ++x)
                        {
                            tex.SetPixel(entry.x + x, entry.y + y, source.GetPixel(x, y));
                        }
                    }
                }
                else
                {
                    for (int y = 0; y < source.height; ++y)
                    {
                        for (int x = 0; x < source.width; ++x)
                        {
                            tex.SetPixel(entry.x + y, entry.y + x, source.GetPixel(x, y));
                        }
                    }
                }
            }
            tex.Apply();

            string texturePath = gen.atlasTextures[atlasIndex]?AssetDatabase.GetAssetPath(gen.atlasTextures[atlasIndex]):(dataDirName + "atlas" + atlasIndex + ".png");

            // Write filled atlas to disk
            byte[] bytes = tex.EncodeToPNG();
            System.IO.FileStream fs = System.IO.File.Create(texturePath);
            fs.Write(bytes, 0, bytes.Length);
            fs.Close();

            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            Object.DestroyImmediate(tex);

            tex = AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D)) as Texture2D;
            gen.atlasTextures[atlasIndex] = tex;

            // Make sure texture is set up with the correct max size and compression type
            SetUpTargetTexture(gen, tex);

            // Create material if necessary
            if (gen.atlasMaterials[atlasIndex] == null)
            {
                Material mat;
                if (gen.premultipliedAlpha)
                    mat = new Material(Shader.Find("tk2d/PremulVertexColor"));
                else
                    mat = new Material(Shader.Find("tk2d/BlendVertexColor"));

                mat.mainTexture = tex;

                string materialPath = gen.atlasMaterials[atlasIndex]?AssetDatabase.GetAssetPath(gen.atlasMaterials[atlasIndex]):(dataDirName + "atlas" + atlasIndex + "_material.mat");
                AssetDatabase.CreateAsset(mat, materialPath);
                AssetDatabase.SaveAssets();

                gen.atlasMaterials[atlasIndex] = AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material)) as Material;
            }
        }

        // Create prefab
        if (gen.spriteCollection == null)
        {
            GameObject go = new GameObject();
            go.AddComponent<tk2dSpriteCollectionData>();
        #if (UNITY_3_0 || UNITY_3_1 || UNITY_3_2 || UNITY_3_3 || UNITY_3_4 || UNITY_3_4)
            Object p = EditorUtility.CreateEmptyPrefab(prefabObjectPath);
            EditorUtility.ReplacePrefab(go, p);
        #else
            Object p = PrefabUtility.CreateEmptyPrefab(prefabObjectPath);
            PrefabUtility.ReplacePrefab(go, p);
        #endif
            GameObject.DestroyImmediate(go);
            AssetDatabase.SaveAssets();

            gen.spriteCollection = AssetDatabase.LoadAssetAtPath(prefabObjectPath, typeof(tk2dSpriteCollectionData)) as tk2dSpriteCollectionData;
        }

        tk2dSpriteCollectionData coll = gen.spriteCollection;
        coll.textures = new Texture[gen.atlasTextures.Length];
        coll.materials = new Material[gen.atlasMaterials.Length];
        for (int i = 0; i < gen.atlasTextures.Length; ++i)
        {
            coll.textures[i] = gen.atlasTextures[i];
        }
        for (int i = 0; i < gen.atlasMaterials.Length; ++i)
        {
            coll.materials[i] = gen.atlasMaterials[i];
        }

        // Wipe out legacy data
        coll.material = null;

        coll.premultipliedAlpha = gen.premultipliedAlpha;
        coll.spriteDefinitions = new tk2dSpriteDefinition[gen.textureParams.Length];
        coll.version = tk2dSpriteCollectionData.CURRENT_VERSION;
        coll.spriteCollectionGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(gen));
        coll.spriteCollectionName = gen.name;
        coll.invOrthoSize = 1.0f / gen.targetOrthoSize;
        coll.halfTargetHeight = gen.targetHeight * 0.5f;

        int buildKey = Random.Range(0, int.MaxValue);
        while (buildKey == coll.buildKey)
        {
            buildKey = Random.Range(0, int.MaxValue);
        }
        coll.buildKey = buildKey; // a random build number so we can identify changed collections quickly

        for (int i = 0; i < coll.spriteDefinitions.Length; ++i)
        {
            coll.spriteDefinitions[i] = new tk2dSpriteDefinition();
            if (gen.textureRefs[i])
            {
                string assetPath = AssetDatabase.GetAssetPath(gen.textureRefs[i]);
                string guid = AssetDatabase.AssetPathToGUID(assetPath);
                coll.spriteDefinitions[i].sourceTextureGUID = guid;
            }
            else
            {
                coll.spriteDefinitions[i].sourceTextureGUID = "";
            }

            coll.spriteDefinitions[i].extractRegion = gen.textureParams[i].extractRegion;
            coll.spriteDefinitions[i].regionX = gen.textureParams[i].regionX;
            coll.spriteDefinitions[i].regionY = gen.textureParams[i].regionY;
            coll.spriteDefinitions[i].regionW = gen.textureParams[i].regionW;
            coll.spriteDefinitions[i].regionH = gen.textureParams[i].regionH;
        }
        coll.allowMultipleAtlases = gen.allowMultipleAtlases;
        coll.dataGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(coll));

        float scale = 1.0f;
        if (gen.useTk2dCamera)
        {
            scale = 1.0f;
        }
        else
        {
            scale = 2.0f * gen.targetOrthoSize / gen.targetHeight;
        }

        // Build fonts
        foreach (var font in fontInfoDict.Keys)
        {
            var fontInfo = fontInfoDict[font];
            List<SpriteLut> fontSpriteLut = new List<SpriteLut>();
            int fontId = 0;
            for (fontId = 0; fontId < gen.fonts.Length; ++fontId)
                if (gen.fonts[fontId] == font) break;
            foreach (var v in spriteLuts)
            {
                if (v.isFont && v.fontId == fontId)
                    fontSpriteLut.Add(v);
            }

            fontInfo.scaleW = coll.textures[0].width;
            fontInfo.scaleH = coll.textures[0].height;

            // Build a local sprite lut only relevant to this font
            UpdateFontData(gen, scale, atlasData, fontSpriteLut, font, fontInfo);

            // Set material
            font.data.material = coll.materials[0];
            font.editorData.material = coll.materials[0];

            // Mark to save
            EditorUtility.SetDirty(font.data);
        }

        // Build textures
        UpdateVertexCache(gen, scale, atlasData, coll, spriteLuts);

        // Free tmp textures
        foreach (var sprite in spriteLuts)
        {
            if (!sprite.isDuplicate)
                Object.DestroyImmediate(sprite.tex);
        }
        foreach (var tex in allocatedTextures)
            Object.DestroyImmediate(tex);

        // refresh existing
        RefreshExistingAssets(gen.spriteCollection);

        // save changes
        var index = tk2dEditorUtility.GetOrCreateIndex();
        index.AddSpriteCollectionData(gen.spriteCollection);
        EditorUtility.SetDirty(gen.spriteCollection);
        EditorUtility.SetDirty(gen);

        sourceTextures = null; // need to clear, its static
        currentBuild = null;

        tk2dEditorUtility.GetOrCreateIndex().AddSpriteCollectionData(gen.spriteCollection);
        tk2dEditorUtility.CommitIndex();

        Object.DestroyImmediate(blankTexture);

        return true;
    }