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; }