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; } }
static Texture2D ProcessTexture(bool premultipliedAlpha, bool additive, bool stretchPad, Texture2D srcTex, int sx, int sy, int tw, int th, ref SCGE.SpriteLut spriteLut, int padAmount) { // Can't have additive without premultiplied alpha if (!premultipliedAlpha) { additive = false; } int[] ww = new int[tw]; int[] hh = new int[th]; for (int x = 0; x < tw; ++x) { ww[x] = 0; } for (int y = 0; y < th; ++y) { hh[y] = 0; } int numNotTransparent = 0; for (int x = 0; x < tw; ++x) { for (int y = 0; y < th; ++y) { Color col = srcTex.GetPixel(sx + x, sy + y); if (col.a > 0) { ww[x] = 1; hh[y] = 1; numNotTransparent++; } } } if (numNotTransparent > 0) { int x0 = 0, x1 = 0, y0 = 0, y1 = 0; for (int x = 0; x < tw; ++x) { if (ww[x] == 1) { x0 = x; break; } } for (int x = tw - 1; x >= 0; --x) { if (ww[x] == 1) { x1 = x; break; } } for (int y = 0; y < th; ++y) { if (hh[y] == 1) { y0 = y; break; } } for (int y = th - 1; y >= 0; --y) { if (hh[y] == 1) { y1 = y; break; } } int w1 = x1 - x0 + 1; int h1 = y1 - y0 + 1; Texture2D dtex = new Texture2D(w1 + padAmount * 2, h1 + padAmount * 2); for (int x = 0; x < w1; ++x) { for (int y = 0; y < h1; ++y) { Color col = srcTex.GetPixel(sx + x0 + x, sy + y0 + y); dtex.SetPixel(x + padAmount, y + padAmount, col); } } PadTexture(dtex, padAmount, stretchPad); if (premultipliedAlpha) { for (int x = 0; x < dtex.width; ++x) { for (int y = 0; y < dtex.height; ++y) { Color col = dtex.GetPixel(x, y); col.r *= col.a; col.g *= col.a; col.b *= col.a; col.a = additive?0.0f:col.a; dtex.SetPixel(x, y, col); } } } dtex.Apply(); spriteLut.rx = sx + x0; spriteLut.ry = sy + y0; spriteLut.rw = w1; spriteLut.rh = h1; spriteLut.tex = dtex; return(dtex); } else { return(null); } }
public static void Rebuild(tk2dSpriteCollection gen) { // avoid "recursive" build being triggered by texture watcher if (currentBuild == gen) { return; } currentBuild = gen; 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; } 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(); sourceTextures[i] = localTex; } else { sourceTextures[i] = gen.textureRefs[i]; } } // catalog all textures to atlas int numTexturesToAtlas = 0; List <SCGE.SpriteLut> spriteLuts = new List <SCGE.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 == "" || gen.textureParams[i].texture != currentTexture) { 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); SCGE.SpriteLut diceLut = new SCGE.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.premultipliedAlpha, gen.textureParams[i].additive, true, srcTex, sx, sy, tw, th, ref diceLut, GetPadAmount(gen)); if (dest) { diceLut.atlasIndex = numTexturesToAtlas++; spriteLuts.Add(diceLut); } } } } else { SCGE.SpriteLut lut = new SCGE.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.premultipliedAlpha, gen.textureParams[i].additive, stretchPad, currentTexture, 0, 0, currentTexture.width, currentTexture.height, ref lut, GetPadAmount(gen)); 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), stretchPad); lut.tex.Apply(); lut.rx = currentTexture.width / 2; lut.ry = currentTexture.height / 2; lut.rw = 1; lut.rh = 1; } } spriteLuts.Add(lut); } } // Create texture Texture2D[] textureList = new Texture2D[numTexturesToAtlas]; int titer = 0; for (int i = 0; i < spriteLuts.Count; ++i) { SCGE.SpriteLut _lut = spriteLuts[i]; if (!_lut.isDuplicate) { textureList[titer++] = _lut.tex; } } // Build atlas Atlas.AtlasBuilder atlasBuilder = new Atlas.AtlasBuilder(gen.maxTextureSize, gen.maxTextureSize, gen.allowMultipleAtlases?64:1); 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; } } // Fill atlas textures Atlas.AtlasData[] 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 for (int yy = 0; yy < tex.height; ++yy) { for (int xx = 0; xx < tex.width; ++xx) { tex.SetPixel(xx, yy, Color.black); } } 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(); 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) { Object p = EditorUtility.CreateEmptyPrefab(prefabObjectPath); GameObject go = new GameObject(); go.AddComponent <tk2dSpriteCollectionData>(); EditorUtility.ReplacePrefab(go, p); 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; 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; UpdateVertexCache(gen, atlasData, coll, spriteLuts); // refresh existing tk2dSprite[] sprs = Resources.FindObjectsOfTypeAll(typeof(tk2dSprite)) as tk2dSprite[]; foreach (tk2dSprite spr in sprs) { if (spr.collection == gen.spriteCollection) { if (spr.spriteId < 0 || spr.spriteId >= spr.collection.spriteDefinitions.Length) { spr.spriteId = 0; } spr.Build(); } } tk2dStaticSpriteBatcher[] batchedSprs = Resources.FindObjectsOfTypeAll(typeof(tk2dStaticSpriteBatcher)) as tk2dStaticSpriteBatcher[]; foreach (var spr in batchedSprs) { if (spr.spriteCollection == gen.spriteCollection) { spr.Build(); } } // save changes EditorUtility.SetDirty(gen.spriteCollection); EditorUtility.SetDirty(gen); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); sourceTextures = null; // need to clear, its static currentBuild = null; tk2dEditorUtility.GetOrCreateIndex().AddSpriteCollectionData(gen.spriteCollection); tk2dEditorUtility.CommitIndex(); }
public static bool Rebuild(tk2dSpriteCollection gen) { // avoid "recursive" build being triggered by texture watcher if (currentBuild == gen) return false; currentBuild = gen; 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(); sourceTextures[i] = localTex; } else { sourceTextures[i] = gen.textureRefs[i]; } } // catalog all textures to atlas int numTexturesToAtlas = 0; List<SCGE.SpriteLut> spriteLuts = new List<SCGE.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 == "" || gen.textureParams[i].texture != currentTexture) 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); SCGE.SpriteLut diceLut = new SCGE.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.premultipliedAlpha, gen.textureParams[i].additive, true, srcTex, sx, sy, tw, th, ref diceLut, GetPadAmount(gen)); if (dest) { diceLut.atlasIndex = numTexturesToAtlas++; spriteLuts.Add(diceLut); } } } } else { SCGE.SpriteLut lut = new SCGE.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.premultipliedAlpha, gen.textureParams[i].additive, stretchPad, currentTexture, 0, 0, currentTexture.width, currentTexture.height, ref lut, GetPadAmount(gen)); 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), stretchPad); lut.tex.Apply(); lut.rx = currentTexture.width / 2; lut.ry = currentTexture.height / 2; lut.rw = 1; lut.rh = 1; } } spriteLuts.Add(lut); } } // Create texture Texture2D[] textureList = new Texture2D[numTexturesToAtlas]; int titer = 0; for (int i = 0; i < spriteLuts.Count; ++i) { SCGE.SpriteLut _lut = spriteLuts[i]; if (!_lut.isDuplicate) { textureList[titer++] = _lut.tex; } } // Build atlas tk2dAtlas.AtlasBuilder atlasBuilder = new tk2dAtlas.AtlasBuilder(gen.maxTextureSize, gen.maxTextureSize, gen.allowMultipleAtlases?64:1); 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 tk2dAtlas.AtlasData[] 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(); 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) { Object p = EditorUtility.CreateEmptyPrefab(prefabObjectPath); GameObject go = new GameObject(); go.AddComponent<tk2dSpriteCollectionData>(); EditorUtility.ReplacePrefab(go, p); 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; 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.guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(coll)); UpdateVertexCache(gen, atlasData, coll, spriteLuts); // refresh existing tk2dSprite[] sprs = Resources.FindObjectsOfTypeAll(typeof(tk2dSprite)) as tk2dSprite[]; foreach (tk2dSprite spr in sprs) { if (spr.collection == gen.spriteCollection) { if (spr.spriteId < 0 || spr.spriteId >= spr.collection.spriteDefinitions.Length) spr.spriteId = 0; spr.Build(); spr.EditMode__CreateCollider(); } } tk2dStaticSpriteBatcher[] batchedSprs = Resources.FindObjectsOfTypeAll(typeof(tk2dStaticSpriteBatcher)) as tk2dStaticSpriteBatcher[]; foreach (var spr in batchedSprs) { if (spr.spriteCollection == gen.spriteCollection) { spr.Build(); } } // 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(); tk2dSpriteCollectionEditorPopup.OnRebuild(); return true; }