//Awake. void Awake() { //Detect whether the materials can be found. If not, report an error. if (shapeMaterial == null || pillowAndGlowMeshMaterial == null || pillowAndGlowRenderTextureMaterial == null) { Debug.LogError("At least one of the material properties on this Vector Sprites instance have been removed. The materials need to be assigned in " + "order to create the sprites. Please re-assign them to this Vector Sprites instance (you may need to comment out the [HideInInspector] " + "attributes on the material properties in \"VectorSprites.cs\" in order to assign them again)."); return; } //Clear the sprites dictionary. sprites.Clear(); //If the version is not up to date, don't do anything. if (!vectorSpritesProperties.updateVersion()) { return; } //Set all meshes to dirty to ensure they are re-generated (the game quality might not match the editor quality). for (int i = 0; i < vectorSpritesProperties.shapeGroups.Count; i++) { for (int j = 0; j < vectorSpritesProperties.shapeGroups[i].shapes.Count; j++) { vectorSpritesProperties.shapeGroups[i].shapes[j].resetAllMeshes(); } } //Create a Vector Sprites Renderer game object so the Vector Sprites can be rendered to textures. GameObject vectorSpritesRendererGameObject = new GameObject("Vector Sprites Renderer"); vectorSpritesRendererGameObject.hideFlags = HideFlags.HideAndDontSave; VectorSpritesRenderer vectorSpritesRenderer = vectorSpritesRendererGameObject.AddComponent <VectorSpritesRenderer>(); vectorSpritesRenderer.createdFromVectorSpritesInstance.flag = true; //Store the old selection so it can be restored after the sprites are generated. SelectableEntity oldSelectedEntity = vectorSpritesProperties.selectedEntity; List <int> oldSelectedEntityPrimaryIDs = new List <int>(); List <int> oldSelectedEntitySecondaryIDs = new List <int>(); for (int i = 0; i < vectorSpritesProperties.selectedEntities.Count; i++) { oldSelectedEntityPrimaryIDs.Add(vectorSpritesProperties.selectedEntities[i].primaryID); oldSelectedEntitySecondaryIDs.Add(vectorSpritesProperties.selectedEntities[i].secondaryID); } //Create the array of sprites, one for each Vector Sprite and loop over them in order to create them. for (int i = 0; i < vectorSpritesProperties.vectorSprites.Count; i++) { //Work out the sprite's scale. Vector2 meshScale = new Vector2(2, 2); if (vectorSpritesProperties.vectorSprites[i].spriteRectangleTransform == SpriteRectangleTransform.Crop) { if (vectorSpritesProperties.vectorSprites[i].width > vectorSpritesProperties.vectorSprites[i].height) { meshScale.y *= (float)vectorSpritesProperties.vectorSprites[i].width / vectorSpritesProperties.vectorSprites[i].height; } else { meshScale.x *= (float)vectorSpritesProperties.vectorSprites[i].height / vectorSpritesProperties.vectorSprites[i].width; } } //Render the sprite to a render texture. Double the size of the render texture if anti-aliasing is enabled (so it can be scaled back down). RenderTexture renderTexture = new RenderTexture(vectorSpritesProperties.vectorSprites[i].width * (vectorSpritesProperties.vectorSprites[i].antialias ? 2 : 1), vectorSpritesProperties.vectorSprites[i].height * (vectorSpritesProperties.vectorSprites[i].antialias ? 2 : 1), 16, RenderTextureFormat.ARGB32); renderTexture.hideFlags = HideFlags.HideAndDontSave; vectorSpritesProperties.selectedEntity = SelectableEntity.Sprite; vectorSpritesProperties.selectedEntities.Clear(); vectorSpritesProperties.selectedEntities.Add(new SelectedEntity(i)); vectorSpritesRenderer.render(vectorSpritesProperties, shapeMaterial, pillowAndGlowMeshMaterial, pillowAndGlowRenderTextureMaterial, renderTexture, meshScale, false); //Get the texture directly from the render texture. RenderTexture.active = renderTexture; Texture2D texture = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false); texture.wrapMode = TextureWrapMode.Clamp; texture.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0, false); texture.Apply(); RenderTexture.active = null; DestroyImmediate(renderTexture); //Copy the texture to the master texture, scaling down for anti-aliasing purposes if required. if (vectorSpritesProperties.vectorSprites[i].antialias) { renderTexture = new RenderTexture(vectorSpritesProperties.vectorSprites[i].width, vectorSpritesProperties.vectorSprites[i].height, 16, RenderTextureFormat.ARGB32); renderTexture.hideFlags = HideFlags.HideAndDontSave; Graphics.Blit(texture, renderTexture); DestroyImmediate(texture); texture = new Texture2D(vectorSpritesProperties.vectorSprites[i].width, vectorSpritesProperties.vectorSprites[i].height, TextureFormat.ARGB32, false); RenderTexture.active = renderTexture; texture.ReadPixels(new Rect(0, 0, vectorSpritesProperties.vectorSprites[i].width, vectorSpritesProperties.vectorSprites[i].height), 0, 0, false); RenderTexture.active = null; DestroyImmediate(renderTexture); texture.Apply(); } //Create the sprite. vectorSpritesProperties.vectorSprites[i].sprite = Sprite.Create(texture, new Rect(0, 0, vectorSpritesProperties.vectorSprites[i].width, vectorSpritesProperties.vectorSprites[i].height), new Vector2(0.5f, 0.5f)); //Add the sprite to the dictionary if it doesn't contain one with the same name. if (!sprites.ContainsKey(vectorSpritesProperties.vectorSprites[i].name)) { sprites.Add(vectorSpritesProperties.vectorSprites[i].name, vectorSpritesProperties.vectorSprites[i].sprite); } } //Restore the previous selected entity. vectorSpritesProperties.selectedEntity = oldSelectedEntity; vectorSpritesProperties.selectedEntities.Clear(); for (int i = 0; i < oldSelectedEntityPrimaryIDs.Count; i++) { vectorSpritesProperties.selectedEntities.Add(new VectorSprites.SelectedEntity(oldSelectedEntityPrimaryIDs[i], oldSelectedEntitySecondaryIDs[i])); } //Reset all shapes' meshes and render textures now that it has been rendered. for (int i = 0; i < vectorSpritesProperties.shapeGroups.Count; i++) { for (int j = 0; j < vectorSpritesProperties.shapeGroups[i].shapes.Count; j++) { vectorSpritesProperties.shapeGroups[i].shapes[j].resetAllMeshes(); } } //Destroy the temporary Vector Sprites Renderer game object. Destroy(vectorSpritesRendererGameObject); }
//Initialise. public void initialise(VectorSprites.VectorSpritesProperties vectorSpritesProperties, Material shapeMaterial, Material pillowAndGlowMeshMaterial, Material pillowAndGlowRenderTextureMaterial, VectorSpritesRenderer vectorSpritesRenderer) { //Store properties. this.vectorSpritesProperties = vectorSpritesProperties; if (vectorSpritesProperties.selectedEntity == VectorSprites.SelectableEntity.SpriteSheet) { spriteSheetIndex = vectorSpritesProperties.selectedEntities[0].primaryID; spriteIndex = -1; } else { spriteSheetIndex = -1; spriteIndex = vectorSpritesProperties.selectedEntities[0].primaryID; } //Set all meshes to dirty to ensure they are re-generated (the export quality might not match the editor quality). for (int i = 0; i < vectorSpritesProperties.shapeGroups.Count; i++) { for (int j = 0; j < vectorSpritesProperties.shapeGroups[i].shapes.Count; j++) { vectorSpritesProperties.shapeGroups[i].shapes[j].resetAllMeshes(); } } //Create a list of packing spaces to pack all sprites in. If there is a single sprite, just create a single packing space, otherwise try to fit all //sprites in as smaller space as possible. packingSpaces.Clear(); if (spriteSheetIndex == -1) { packingSpaces.Add(new PackingSpace(0, 0, vectorSpritesProperties.vectorSprites[vectorSpritesProperties.selectedEntities[0].primaryID].width, vectorSpritesProperties.vectorSprites[vectorSpritesProperties.selectedEntities[0].primaryID].height, vectorSpritesProperties.selectedEntities[0].primaryID)); } //If multiple sprites are being added to the sprite sheet, pack the sprites on the sprite sheet in the most efficient way possible. else { //Sort the sprites in descending order of size (maximum dimension), which should produce more efficient packing. List <int> spriteOrder = new List <int>(); for (int i = 0; i < vectorSpritesProperties.vectorSprites.Count; i++) { if (vectorSpritesProperties.vectorSprites[i].spriteSheet == spriteSheetIndex) { spriteOrder.Add(i); } } bool spriteOrderSwapChanges = true; while (spriteOrderSwapChanges) { spriteOrderSwapChanges = false; for (int i = 0; i < spriteOrder.Count - 1; i++) { int thisMaxCoordinate = Math.Max(vectorSpritesProperties.vectorSprites[spriteOrder[i]].width, vectorSpritesProperties.vectorSprites[spriteOrder[i]].height); int nextMaxCoordinate = Math.Max(vectorSpritesProperties.vectorSprites[spriteOrder[i + 1]].width, vectorSpritesProperties.vectorSprites[spriteOrder[i + 1]].height); int thisMinCoordinate = Math.Min(vectorSpritesProperties.vectorSprites[spriteOrder[i]].width, vectorSpritesProperties.vectorSprites[spriteOrder[i]].height); int nextMinCoordinate = Math.Min(vectorSpritesProperties.vectorSprites[spriteOrder[i + 1]].width, vectorSpritesProperties.vectorSprites[spriteOrder[i + 1]].height); if (thisMaxCoordinate < nextMaxCoordinate || (thisMaxCoordinate == nextMaxCoordinate && thisMinCoordinate < nextMinCoordinate)) { int spriteOrderSwap = spriteOrder[i]; spriteOrder[i] = spriteOrder[i + 1]; spriteOrder[i + 1] = spriteOrderSwap; spriteOrderSwapChanges = true; } } } //Start with a single packing space, and loop over the sprites to place into spaces. packingSpaces.Add(new PackingSpace(0, 0, 65536, 65536)); for (int j = 0; j < spriteOrder.Count; j++) { //Get the Vector Sprite. VectorSprites.VectorSprite vectorSprite = vectorSpritesProperties.vectorSprites[spriteOrder[j]]; //Find the best space to put this sprite such that it keeps the overall area as small as possible. ulong lowestTotalArea = ulong.MaxValue; int smallestWastedArea = int.MaxValue; int bestPackingSpaceIndex = -1; for (int k = 0; k < packingSpaces.Count; k++) { if (packingSpaces[k].vectorSpriteIndex == -1) { packingSpaces[k].vectorSpriteIndex = spriteOrder[j]; int oldWidth = packingSpaces[k].width, oldHeight = packingSpaces[k].height; packingSpaces[k].width = vectorSprite.width; packingSpaces[k].height = vectorSprite.height; int newTextureSizeX, newTextureSizeY; getMaximumPackingSize(out newTextureSizeX, out newTextureSizeY); ulong newArea = ((ulong)newTextureSizeX * (ulong)newTextureSizeX) + ((ulong)newTextureSizeY * (ulong)newTextureSizeY); int newWastedArea = (oldWidth * oldHeight) - (vectorSprite.width * vectorSprite.height); if (newArea < lowestTotalArea || (newArea == lowestTotalArea && newWastedArea < smallestWastedArea)) { lowestTotalArea = newArea; smallestWastedArea = newWastedArea; bestPackingSpaceIndex = k; } packingSpaces[k].vectorSpriteIndex = -1; packingSpaces[k].width = oldWidth; packingSpaces[k].height = oldHeight; } } //Put the sprite in the assigned space. int originalWidth = packingSpaces[bestPackingSpaceIndex].width, originalHeight = packingSpaces[bestPackingSpaceIndex].height; packingSpaces[bestPackingSpaceIndex].width = vectorSprite.width; packingSpaces[bestPackingSpaceIndex].height = vectorSprite.height; packingSpaces[bestPackingSpaceIndex].vectorSpriteIndex = spriteOrder[j]; if (originalWidth - packingSpaces[bestPackingSpaceIndex].width > 0) { packingSpaces.Add(new PackingSpace(packingSpaces[bestPackingSpaceIndex].X + packingSpaces[bestPackingSpaceIndex].width, packingSpaces[bestPackingSpaceIndex].Y, originalWidth - packingSpaces[bestPackingSpaceIndex].width, packingSpaces[bestPackingSpaceIndex].height)); } if (originalHeight - packingSpaces[bestPackingSpaceIndex].height > 0) { packingSpaces.Add(new PackingSpace(packingSpaces[bestPackingSpaceIndex].X, packingSpaces[bestPackingSpaceIndex].Y + packingSpaces[bestPackingSpaceIndex].height, originalWidth, originalHeight - packingSpaces[bestPackingSpaceIndex].height)); } } } //Create an output texture, the size of which is the maximum size of all packing spaces. int textureSizeX, textureSizeY; getMaximumPackingSize(out textureSizeX, out textureSizeY); if (textureSizeX > 4096 || textureSizeY > 4096) { Close(); EditorUtility.DisplayDialog("Texture Too Big", "The sprite sheet cannot fit within a texture of size 4096 by 4096 pixels. Try reducing the size " + "of some of the individual sprites, or split the sprites into two sprite sheets.", "OK"); return; } texture = new Texture2D(textureSizeX, textureSizeY, TextureFormat.ARGB32, false); texture.hideFlags = HideFlags.HideAndDontSave; texture.wrapMode = TextureWrapMode.Clamp; RenderTexture clearRenderTexture = new RenderTexture(textureSizeX, textureSizeY, 16, RenderTextureFormat.ARGB32); clearRenderTexture.hideFlags = HideFlags.HideAndDontSave; RenderTexture.active = clearRenderTexture; GL.Clear(true, true, new Color(0, 0, 0, 0)); texture.ReadPixels(new Rect(0, 0, textureSizeX, textureSizeY), 0, 0, false); RenderTexture.active = null; DestroyImmediate(clearRenderTexture); //Loop over all the packing spaces that contain sprites, and add those sprites to the sprite sheet. VectorSprites.SelectableEntity oldSelectedEntity = vectorSpritesProperties.selectedEntity; List <int> oldSelectedEntityPrimaryIDs = new List <int>(); List <int> oldSelectedEntitySecondaryIDs = new List <int>(); for (int i = 0; i < vectorSpritesProperties.selectedEntities.Count; i++) { oldSelectedEntityPrimaryIDs.Add(vectorSpritesProperties.selectedEntities[i].primaryID); oldSelectedEntitySecondaryIDs.Add(vectorSpritesProperties.selectedEntities[i].secondaryID); } vectorSpritesProperties.selectedEntity = VectorSprites.SelectableEntity.Sprite; for (int i = 0; i < packingSpaces.Count; i++) { if (packingSpaces[i].vectorSpriteIndex == -1) { continue; } //Get the sprite and work out its scale. VectorSprites.VectorSprite vectorSprite = vectorSpritesProperties.vectorSprites[packingSpaces[i].vectorSpriteIndex]; Vector2 meshScale = new Vector2(2, 2); if (vectorSprite.spriteRectangleTransform == VectorSprites.SpriteRectangleTransform.Crop) { if (vectorSprite.width > vectorSprite.height) { meshScale.y *= (float)vectorSprite.width / vectorSprite.height; } else { meshScale.x *= (float)vectorSprite.height / vectorSprite.width; } } //Render the sprite to a render texture. Double the size of the render texture if anti-aliasing is enabled (so it can be scaled back down). RenderTexture renderTexture = new RenderTexture(vectorSprite.width * (vectorSprite.antialias ? 2 : 1), vectorSprite.height * (vectorSprite.antialias ? 2 : 1), 16, RenderTextureFormat.ARGB32); renderTexture.hideFlags = HideFlags.HideAndDontSave; vectorSpritesProperties.selectedEntities.Clear(); vectorSpritesProperties.selectedEntities.Add(new VectorSprites.SelectedEntity(packingSpaces[i].vectorSpriteIndex)); vectorSpritesRenderer.render(vectorSpritesProperties, shapeMaterial, pillowAndGlowMeshMaterial, pillowAndGlowRenderTextureMaterial, renderTexture, meshScale, false); //Get the texture directly from the render texture. RenderTexture.active = renderTexture; Texture2D textureFromRenderTexture = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false); textureFromRenderTexture.hideFlags = HideFlags.HideAndDontSave; textureFromRenderTexture.wrapMode = TextureWrapMode.Clamp; textureFromRenderTexture.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0, false); textureFromRenderTexture.Apply(); RenderTexture.active = null; DestroyImmediate(renderTexture); //Copy the texture to the master texture, scaling down for anti-aliasing purposes if required. renderTexture = new RenderTexture(vectorSprite.width, vectorSprite.height, 16, RenderTextureFormat.ARGB32); renderTexture.hideFlags = HideFlags.HideAndDontSave; Graphics.Blit(textureFromRenderTexture, renderTexture); RenderTexture.active = renderTexture; texture.ReadPixels(new Rect(0, 0, vectorSprite.width, vectorSprite.height), packingSpaces[i].X, packingSpaces[i].Y, false); RenderTexture.active = null; DestroyImmediate(textureFromRenderTexture); DestroyImmediate(renderTexture); } //Apply the changes to the texture. texture.Apply(); //Restore the previous selected entity. vectorSpritesProperties.selectedEntity = oldSelectedEntity; vectorSpritesProperties.selectedEntities.Clear(); for (int i = 0; i < oldSelectedEntityPrimaryIDs.Count; i++) { vectorSpritesProperties.selectedEntities.Add(new VectorSprites.SelectedEntity(oldSelectedEntityPrimaryIDs[i], oldSelectedEntitySecondaryIDs[i])); } //Set all meshes to dirty again so they can be re-generated in the editor, which may have different quality settings from export. for (int i = 0; i < vectorSpritesProperties.shapeGroups.Count; i++) { for (int j = 0; j < vectorSpritesProperties.shapeGroups[i].shapes.Count; j++) { vectorSpritesProperties.shapeGroups[i].shapes[j].resetAllMeshes(); } } }