public override bool DrawToAtlas(TextureAtlas atlas,AtlasLocation location){ // Only ever called with a static image: Color32[] pixelBlock=Image.GetPixels32(); int index=0; int atlasIndex=location.BottomLeftPixel(); // How many pixels must we add on to the end of the row to get to // the start of the row above? This is RowPixelDelta: int rowDelta=location.RowPixelDelta(); int height=Image.height; int width=Image.width; for(int h=0;h<height;h++){ for(int w=0;w<width;w++){ atlas.Pixels[atlasIndex++]=pixelBlock[index++]; } atlasIndex+=rowDelta; } return true; }
/// <summary>Creates a new location on the given atlas with the given size.</summary> /// <param name="atlas">The atlas this location refers to.</param> /// <param name="x">The x coordinate of the left edge of this location in pixels from the left.</param> /// <param name="y">The y coordinate of the bototm edfe of this location in pixels from the bottom.</param> /// <param name="width">The width of the location in pixels.</param> /// <param name="height">The height of the location in pixels.</param> public AtlasLocation(TextureAtlas atlas,int x,int y,int width,int height){ X=x; Y=y; Atlas=atlas; Width=width; Height=height; Area=Width*Height; // BakeUV isn't called here as its results will be always overriden when select calls it. // As a result, empty locations have invalid UVs; this is ok. }
/// <summary>Changes the font atlas used by the default material.</summary> public void SetFontAtlas(TextureAtlas atlas){ Texture2D texture; if(atlas==null){ texture=null; }else{ texture=atlas.Texture; } Material.SetTexture("_Font",texture); }
/// <summary>Sets the isolated state of this batch.</summary> public void IsIsolated(DisplayableProperty property){ if(Isolated && Setup){ // No change. return; } Setup=true; Isolated=true; FontAtlas=null; GraphicsAtlas=null; IsolatedProperty=property; }
/// <summary>Sets the graphics atlas for this batch.</summary> public void SetGraphicsAtlas(TextureAtlas graphics){ GraphicsAtlas=graphics; Mesh.SetGraphicsAtlas(graphics); }
/// <summary>Sets the font atlas for this batch.</summary> public void SetFontAtlas(TextureAtlas font,float alias){ FontAtlas=font; Mesh.SetFontAtlas(font); Mesh.Material.SetFloat("TopFontAlias",Fonts.OutlineLocation+alias); Mesh.Material.SetFloat("BottomFontAlias",Fonts.OutlineLocation-alias); }
/// <summary>Sets the isolated state of this batch.</summary> public void NotIsolated(TextureAtlas graphics,TextureAtlas font,float alias){ if(!Isolated && Setup){ return; } Setup=true; Isolated=false; IsolatedProperty=null; Mesh.SetGlobalMaterial(); SetFontAtlas(font,alias); SetGraphicsAtlas(graphics); }
//--------------------------------------
/// <summary>Adds the given atlas to the top of this stack.</summary> public void Add(TextureAtlas atlas){ atlas.Next=null; atlas.Stack=this; if(First==null){ atlas.Previous=null; First=Last=atlas; return; } atlas.Previous=Last; Last=Last.Next=atlas; }
public bool DrawToAtlas(TextureAtlas atlas,AtlasLocation location){ // Only ever called with a static image: Color32[] pixelBlock=Image.GetPixels32(); int index=0; int atlasIndex=location.BottomLeftPixel(); int height=Image.height; int width=Image.width; // How many pixels must we add on to the end of the row to get to // the start of the row above? This is simply the dimension of the atlas: int rowDelta=atlas.Dimension; for(int h=0;h<height;h++){ Array.Copy(pixelBlock,index,atlas.Pixels,atlasIndex,width); index+=width; atlasIndex+=rowDelta; } return true; }
/// <summary>Sets a default material to this mesh.</summary> public void SetGraphicsAtlas(TextureAtlas atlas){ Texture2D texture; if(atlas==null){ texture=null; }else{ texture=atlas.Texture; } Material.SetTexture("_Atlas",texture); }
/// <summary>Sets up the current batch based on the isolation settings requested by a property.</summary> /// <param name="property">The displayable property which wants the batch.</param> /// <param name="fontTexture">The font texture to use with this batch.</param> public void SetupBatch(DisplayableProperty property,TextureAtlas graphics,TextureAtlas font){ if(UI.MainCameraPool!=null && InWorldUI==null){ // This is the main UI and it also has a camera pool. // Are we now attempting to create a batch on top of an inline camera? // Let the camera pool check: if(UI.MainCameraPool.CheckCameraRequired()){ // Clear the current batch - it can't be shared any further. CurrentBatch=null; } } if(property.Isolated){ if(property.GotBatchAlready){ // The property already got a batch on this layout - it doesn't need another. return; } // Isolated properties always get a new batch every time. CurrentBatch=UIBatchPool.Get(this); if(CurrentBatch==null){ CurrentBatch=new UIBatch(this); } property.GotBatchAlready=true; // And push it to the active stack: AddBatch(CurrentBatch); // Make sure it knows it's isolated: CurrentBatch.IsIsolated(property); }else{ if(CurrentBatch!=null && !CurrentBatch.Isolated){ // Re-use existing batch? if(font!=null){ if(CurrentBatch.FontAtlas==null){ // Didn't have one assigned before. Assign now: CurrentBatch.SetFontAtlas(font,FontAliasing); }else if(font!=CurrentBatch.FontAtlas){ // Font atlas changed. Can't share. CurrentBatch=null; } } if(graphics!=null){ if(CurrentBatch.GraphicsAtlas==null){ // Didn't have one assigned before. Assign now: CurrentBatch.SetGraphicsAtlas(graphics); }else if(graphics!=CurrentBatch.GraphicsAtlas){ // Atlas changed. Can't share. CurrentBatch=null; } } if(CurrentBatch!=null){ // Yep - reuse it. return; } } // Pull a batch from the pool and set it to currentbatch. May need to generate a new one. CurrentBatch=UIBatchPool.Get(this); if(CurrentBatch==null){ CurrentBatch=new UIBatch(this); } // And push it to the active stack: AddBatch(CurrentBatch); // Make sure it knows it's not isolated: CurrentBatch.NotIsolated(graphics,font,FontAliasing); } // Finally, prepare it for layout: CurrentBatch.PrepareForLayout(); }
public TextureAtlas Create(){ // Create the atlas: TextureAtlas atlas=new TextureAtlas(InitialSize,FilteringMode,Format); atlas.Spacing=Spacing; atlas.Mode=Mode; Add(atlas); return atlas; }
/// <summary>Optimises the frames on this stack if it's needed.</summary> public bool OptimiseIfNeeded() { if (!OptimizeRequested) { return(false); } OptimizeRequested = false; TextureAtlas requiresOptimise = null; TextureAtlas current = First; while (current != null) { if (current.OptimizeRequested) { // Pop it out: current.RemoveFromStack(); // And add it to our temp stack: current.Next = requiresOptimise; requiresOptimise = current; } current = current.Next; } if (requiresOptimise == null) { return(false); } Dictionary <int, AtlasLocation> allImages = ActiveImages; // Next, for each one.. current = requiresOptimise; while (current != null) { // Grab the next one: TextureAtlas next = current.Next; // Offload its images into the "remaining" stack. Note that we must retain the actual Location objects so we don't have to re-request/ re-generate all of them. // If none fit, we re-add current as the new top of stack and add the images back onto it. // Have we re-added current? bool added = false; // Next up, add them all back in, and that's it! // The optimizing comes from them trying to fit in the smallest possible gap they can when added. foreach (KeyValuePair <int, AtlasLocation> kvp in allImages) { AtlasLocation location = kvp.Value; if (location.Atlas == current) { // Try adding to the stack: TextureAtlas stackAtlas = Last; bool noAdd = true; while (stackAtlas != null) { // Try adding to the current frame: if (stackAtlas.OptimiseAdd(location)) { noAdd = false; break; } stackAtlas = stackAtlas.Previous; } if (noAdd && !added) { added = true; // Didn't fit in any of them! We now clear out the current atlas and re-add it like so: Add(current); // Ensure it's cleared: current.Reset(); // Add to it instead: current.OptimiseAdd(location); } } } if (!added) { // Destroy it: current.Destroy(); } current = next; } return(true); }
/// <summary>Draws this image to the given atlas.</summary> public virtual bool DrawToAtlas(TextureAtlas atlas,AtlasLocation location){ return false; }
/// <summary>Require the given image on any atlas. Note that this may reject the requirement if the image is too big and isn't worthwhile on an atlas.</summary> public AtlasLocation RequireImage(AtlasEntity image) { int entityID = image.GetAtlasID(); AtlasLocation result; if (ActiveImages.TryGetValue(entityID, out result)) { // Most calls fall through here. return(result); } int width; int height; image.GetDimensionsOnAtlas(out width, out height); if (width > InitialSize || height > InitialSize) { // Won't fit or is unsuitable for atlasing anyway. return(null); } if (Last == null) { Create(); } else { // Fast check - was this texture recently removed from any atlas? // We might have the chance of restoring it. // Their added at the back of the empty queue, so naturally, start at the end of the empty set // and go back until we hit one with a null texture. TextureAtlas currentAtlas = Last; while (currentAtlas != null) { AtlasLocation currentE = currentAtlas.LastEmpty; while (currentE != null) { if (currentE.Image == null) { // Nope! Shame. break; } else if (currentE.AtlasID == entityID) { // Ace! Time to bring it back from the dead. currentE.Select(image, width, height, Spacing); ActiveImages[entityID] = currentE; return(currentE); } currentE = currentE.EmptyBefore; } currentAtlas = currentAtlas.Previous; } } // Push to top of stack: result = Last.Add(image, entityID, width, height); if (result != null) { return(result); } // Non-fitter - try fitting in lower stack frames: TextureAtlas current = Last.Previous; while (current != null) { result = current.Add(image, entityID, width, height); if (result != null) { return(result); } current = current.Previous; } // Still not fitting! Create a new stack frame: Create(); return(Last.Add(image, entityID, width, height)); }
/// <summary>Empties this atlas stack.</summary> public void Clear(){ Last=null; First=null; // Clear all actives: ActiveImages.Clear(); }