Ejemplo n.º 1
0
        /// <summary>Clears all content from this atlas</summary>
        internal void Reset()
        {
            FirstEmpty     = LastEmpty = null;
            ColumnProgress = 0;

            // Add the root atlas location (NB: it adds itself internally)
            AtlasLocation root = new AtlasLocation(this, 0, 0, Dimension, Dimension);

            // Immediately mark this as empty:
            root.AddToEmptySet();
        }
Ejemplo n.º 2
0
        /// <summary>Adds the given texture to the atlas if it's not already on it,
        /// taking up a set amount of space on the atlas.</summary>
        /// <param name="texture">The texture to add.</param>
        /// <param name="width">The x amount of space to take up on the atlas.</param>
        /// <param name="height">The y amount of space to take up on the atlas.</param>
        /// <returns>The location of the texture on the atlas.</returns>
        internal AtlasLocation Add(AtlasEntity texture, int entityID, int width, int height)
        {
            // Pad width/height:
            int spacedWidth  = width + RawSpacing;
            int spacedHeight = height + RawSpacing;

            // Look for a spot to park this texture in the set of empty blocks.

            // The aim is to make it fit in the smallest empty block possible to save space.
            // This is done with a 'fitFactor' - this is simply the difference between the blocks area and the textures area.
            // We want this value to be as small as possible.
            AtlasLocation currentAccepted = null;

            int area = spacedWidth * spacedHeight;

            if (Mode == AtlasingMode.Columns)
            {
                // Space in this column?
                int max = ColumnProgressY + spacedHeight;

                if (max <= Dimension)
                {
                    // Yep! Create a location here:
                    currentAccepted = new AtlasLocation(this, ColumnProgressX, ColumnProgressY, spacedWidth, spacedHeight);

                    // Move it:
                    ColumnProgressY += spacedHeight;

                    if (spacedWidth > ColumnWidth)
                    {
                        // Update width:
                        ColumnWidth = spacedWidth;
                    }

                    // As it's a new location, it's empty by default:
                    // (Note that it must be in the empty set, otherwise Select will throw the empty set entirely)
                    currentAccepted.AddToEmptySet();
                }
                else
                {
                    // Space to generate a new column?
                    max = ColumnProgressX + ColumnWidth + spacedWidth;

                    if (max <= Dimension)
                    {
                        // Set Y:
                        ColumnProgressY = spacedHeight;

                        // Move X:
                        ColumnProgressX += ColumnWidth;

                        // Yep! Create a location here:
                        currentAccepted = new AtlasLocation(this, ColumnProgressX, 0, spacedWidth, spacedHeight);

                        // Reset width:
                        ColumnWidth = spacedWidth;

                        // As it's a new location, it's empty by default:
                        // (Note that it must be in the empty set, otherwise Select will throw the empty set entirely)
                        currentAccepted.AddToEmptySet();
                    }

                    // Otherwise, the atlas is practically full.
                    // We're gonna just reject the add (by falling below), and state that it can be optimised.
                    // This triggers another atlas to get created and this one will
                    // be optimised at some point in the near future.
                }
            }
            else
            {
                int fitFactor = 0;

                AtlasLocation currentEmpty = FirstEmpty;

                while (currentEmpty != null)
                {
                    int factor = currentEmpty.FitFactor(spacedWidth, spacedHeight, area);

                    if (factor == 0)
                    {
                        // Perfect fit - break right now; can't beat that!
                        currentAccepted = currentEmpty;
                        break;
                    }
                    else if (factor != -1)
                    {
                        // We can possibly fit here - is it the current smallest?
                        if (currentAccepted == null || factor < fitFactor)
                        {
                            // Yep! select it.
                            fitFactor       = factor;
                            currentAccepted = currentEmpty;
                        }
                    }

                    currentEmpty = currentEmpty.EmptyAfter;
                }
            }

            if (currentAccepted == null)
            {
                // No space in this atlas to fit it in. Stop there.

                if (CanOptimize)
                {
                    // Request an optimise:
                    OptimizeRequested       = true;
                    Stack.OptimizeRequested = true;
                }

                return(null);
            }

            Stack.ActiveImages[entityID] = currentAccepted;

            // And burn in the texture to the location (nb: it internally also writes the pixels to the atlas).
            currentAccepted.Select(texture, width, height, RawSpacing);

            return(currentAccepted);
        }
        /// <summary>Used during the optimise process. This texture has selected this location to fit into. This adds it to the atlas.</summary>
        /// <param name="location">The new location to write this region to.</param>
        public void OptimiseSelect(AtlasLocation location)
        {
            // The given texture wants to go in this location.

            // Remove from empty queue:
            if (EmptyBefore == null)
            {
                Atlas.FirstEmpty = EmptyAfter;
            }
            else
            {
                EmptyBefore.EmptyAfter = EmptyAfter;
            }

            if (EmptyAfter == null)
            {
                Atlas.LastEmpty = EmptyBefore;
            }
            else
            {
                EmptyAfter.EmptyBefore = EmptyBefore;
            }

            int width  = location.Width;
            int height = location.Height;

            Spacing = location.Spacing;
            int spacedWidth  = width + Spacing;
            int spacedHeight = height + Spacing;

            if (Atlas.Mode == AtlasingMode.SmallestSpace)
            {
                // Create the new empty zones:
                if (Width > width)
                {
                    // The textures a little thin.

                    // Generate a new area to the right of this one (NB: 0,0 is bottom left)
                    AtlasLocation newRight = new AtlasLocation(Atlas, X + spacedWidth, Y, Width - spacedWidth, height);
                    // Immediately mark this as empty:
                    newRight.AddToEmptySet();
                }

                if (Height > height)
                {
                    // The textures a little short.

                    // Generate a new area above this one (NB: 0,0 is bottom left)
                    AtlasLocation newTop = new AtlasLocation(Atlas, X, Y + spacedHeight, Width, Height - spacedHeight);
                    // Immediately mark this as empty:
                    newTop.AddToEmptySet();
                }
            }
            else
            {
                int maxX = X + spacedWidth;

                if (Atlas.ColumnProgress <= maxX)
                {
                    Atlas.ColumnProgress = maxX;
                }
                else
                {
                    // The textures a little thin.

                    // Generate a new area to the right of this one (NB: 0,0 is bottom left)
                    AtlasLocation newRight = new AtlasLocation(Atlas, X + spacedWidth, Y, Atlas.ColumnProgress - maxX, height);
                    // Immediately mark this as empty:
                    newRight.AddToEmptySet();
                }

                if (Height > spacedHeight)
                {
                    // The textures a little short.

                    // Generate a new area above this one (NB: 0,0 is bottom left)
                    AtlasLocation newTop = new AtlasLocation(Atlas, X, Y + spacedHeight, Width, Height - spacedHeight);
                    // Immediately mark this as empty:
                    newTop.AddToEmptySet();
                }
            }

            // Update atlas:
            location.Atlas = Atlas;

            // Move location so it acts like its in the same location as this:
            location.X = X;
            location.Y = Y;

            // Update the UV's:
            location.BakeUV();

            // Write it in:
            location.Flush();
        }
        /// <summary>This texture has selected this location to fit into. This adds it to the atlas.</summary>
        /// <param name="texture">The texture that wants to go here.</param>
        /// <param name="width">The width of the texture in pixels.</param>
        /// <param name="height">The height of the texture in pixels.</param>
        public void Select(AtlasEntity image, int width, int height, int spacing)
        {
            // The given texture wants to go in this location.
            // Width and height are given for dynamic textures - textures who's pixels are actually written straight to the atlas.
            Empty = false;

            int spacedWidth  = width + spacing;
            int spacedHeight = height + spacing;

            // Remove from empty queue:
            if (EmptyBefore == null)
            {
                Atlas.FirstEmpty = EmptyAfter;
            }
            else
            {
                EmptyBefore.EmptyAfter = EmptyAfter;
            }

            if (EmptyAfter == null)
            {
                Atlas.LastEmpty = EmptyBefore;
            }
            else
            {
                EmptyAfter.EmptyBefore = EmptyBefore;
            }

            int entityID = image.GetAtlasID();

            if (AtlasID == entityID)
            {
                // This is a restore - we don't need to do anything else.
                return;
            }

            Image   = image;
            AtlasID = entityID;
            // If it's not a perfect fit, generate new atlas locations to the right and above of this one.

            if (Atlas.Mode == AtlasingMode.SmallestSpace)
            {
                if (Width > spacedWidth)
                {
                    // The textures a little thin.

                    // Generate a new area to the right of this one (NB: 0,0 is bottom left)
                    AtlasLocation newRight = new AtlasLocation(Atlas, X + spacedWidth, Y, Width - spacedWidth, height);
                    // Immediately mark this as empty:
                    newRight.AddToEmptySet();
                }

                if (Height > spacedHeight)
                {
                    // The textures a little short.

                    // Generate a new area above this one (NB: 0,0 is bottom left)
                    AtlasLocation newTop = new AtlasLocation(Atlas, X, Y + spacedHeight, Width, Height - spacedHeight);
                    // Immediately mark this as empty:
                    newTop.AddToEmptySet();
                }
            }
            else
            {
                int maxX = X + spacedWidth;

                if (Atlas.ColumnProgress < maxX)
                {
                    Atlas.ColumnProgress = maxX;
                }

                if (Height > spacedHeight)
                {
                    // The textures a little short.

                    // Generate a new area above this one (NB: 0,0 is bottom left)
                    AtlasLocation newTop = new AtlasLocation(Atlas, X, Y + spacedHeight, Width, Height - spacedHeight);
                    // Immediately mark this as empty:
                    newTop.AddToEmptySet();
                }
            }

            // Set the new size of this location for UV baking:
            Width   = width;
            Height  = height;
            Spacing = spacing;

            // Update the UV's:
            BakeUV();

            // Make sure the area is up to date:
            Area = spacedWidth * spacedHeight;

            // Write it in:
            Flush();
        }
Ejemplo n.º 5
0
        /// <summary>Adds the given texture to the atlas if it's not already on it,
        /// taking up a set amount of space on the atlas.</summary>
        /// <param name="texture">The texture to add.</param>
        /// <param name="width">The x amount of space to take up on the atlas.</param>
        /// <param name="height">The y amount of space to take up on the atlas.</param>
        /// <returns>The location of the texture on the atlas.</returns>
        internal AtlasLocation Add(AtlasEntity texture, int entityID, int width, int height)
        {
            // Pad width/height:
            int spacedWidth  = width + RawSpacing;
            int spacedHeight = height + RawSpacing;

            // Look for a spot to park this texture in the set of empty blocks.

            // The aim is to make it fit in the smallest empty block possible to save space.
            // This is done with a 'fitFactor' - this is simply the difference between the blocks area and the textures area.
            // We want this value to be as small as possible.
            int           fitFactor       = 0;
            AtlasLocation currentAccepted = null;

            int area = spacedWidth * spacedHeight;

            AtlasLocation currentEmpty = FirstEmpty;

            while (currentEmpty != null)
            {
                int factor = currentEmpty.FitFactor(spacedWidth, spacedHeight, area);

                if (factor == 0)
                {
                    // Perfect fit - break right now; can't beat that!
                    currentAccepted = currentEmpty;
                    break;
                }
                else if (factor != -1)
                {
                    // We can possibly fit here - is it the current smallest?
                    if (currentAccepted == null || factor < fitFactor)
                    {
                        // Yep! select it.
                        fitFactor       = factor;
                        currentAccepted = currentEmpty;
                    }
                }

                currentEmpty = currentEmpty.EmptyAfter;
            }

            if (Mode == AtlasingMode.Columns && currentAccepted == null)
            {
                // Is there any more column space?
                int max = ColumnProgress + spacedWidth;

                if (max <= Dimension)
                {
                    // Yep! Create a location using the remaining space:
                    currentAccepted = new AtlasLocation(this, ColumnProgress, 0, Dimension - ColumnProgress, Dimension);

                    // As it's a new location, it's empty by default:
                    // (Note that it must be in the empty set, otherwise Select will throw the empty set entirely)
                    currentAccepted.AddToEmptySet();
                }
            }

            if (currentAccepted == null)
            {
                // No space in this atlas to fit it in. Stop there.

                if (CanOptimize)
                {
                    // Request an optimise:
                    OptimizeRequested       = true;
                    Stack.OptimizeRequested = true;
                }

                return(null);
            }

            Stack.ActiveImages[entityID] = currentAccepted;

            // And burn in the texture to the location (nb: it internally also writes the pixels to the atlas).
            currentAccepted.Select(texture, width, height, RawSpacing);

            return(currentAccepted);
        }
		/// <summary>Adds the given texture to the atlas if it's not already on it,
		/// taking up a set amount of space on the atlas.</summary>
		/// <param name="texture">The texture to add.</param>
		/// <param name="width">The x amount of space to take up on the atlas.</param>
		/// <param name="height">The y amount of space to take up on the atlas.</param>
		/// <returns>The location of the texture on the atlas.</returns>
		internal AtlasLocation Add(AtlasEntity texture,int entityID,int width,int height){
			
			// Pad width/height:
			int spacedWidth=width+RawSpacing;
			int spacedHeight=height+RawSpacing;
			
			// Look for a spot to park this texture in the set of empty blocks.
			
			// The aim is to make it fit in the smallest empty block possible to save space.
			// This is done with a 'fitFactor' - this is simply the difference between the blocks area and the textures area.
			// We want this value to be as small as possible.
			int fitFactor=0;
			AtlasLocation currentAccepted=null;
			
			int area=spacedWidth*spacedHeight;
			
			AtlasLocation currentEmpty=FirstEmpty;
			
			while(currentEmpty!=null){
				int factor=currentEmpty.FitFactor(spacedWidth,spacedHeight,area);
				
				if(factor==0){
					// Perfect fit - break right now; can't beat that!
					currentAccepted=currentEmpty;
					break;
					
				}else if(factor!=-1){
					// We can possibly fit here - is it the current smallest?
					if(currentAccepted==null||factor<fitFactor){
						// Yep! select it.
						fitFactor=factor;
						currentAccepted=currentEmpty;
					}
				}
				
				currentEmpty=currentEmpty.EmptyAfter;
			}
			
			if(Mode==AtlasingMode.Columns && currentAccepted==null){
				
				// Is there any more column space?
				int max=ColumnProgress + spacedWidth;
				
				if(max<=Dimension){
					
					// Yep! Create a location using the remaining space:
					currentAccepted=new AtlasLocation(this,ColumnProgress,0,Dimension-ColumnProgress,Dimension);
					
					// As it's a new location, it's empty by default:
					// (Note that it must be in the empty set, otherwise Select will throw the empty set entirely)
					currentAccepted.AddToEmptySet();
					
				}
				
			}
			
			if(currentAccepted==null){
				// No space in this atlas to fit it in. Stop there.
				
				if(CanOptimize){
					// Request an optimise:
					OptimizeRequested=true;
					Stack.OptimizeRequested=true;
				}
				
				return null;
			}
			
			Stack.ActiveImages[entityID]=currentAccepted;
			
			// And burn in the texture to the location (nb: it internally also writes the pixels to the atlas).
			currentAccepted.Select(texture,width,height,RawSpacing);
			
			return currentAccepted;
		}
		/// <summary>Clears all content from this atlas</summary>
		internal void Reset(){
			FirstEmpty=LastEmpty=null;
			ColumnProgress=0;
			
			// Add the root atlas location (NB: it adds itself internally)
			AtlasLocation root=new AtlasLocation(this,0,0,Dimension,Dimension);
			
			// Immediately mark this as empty:
			root.AddToEmptySet();
		}
		/// <summary>Used during the optimise process. This texture has selected this location to fit into. This adds it to the atlas.</summary>
		/// <param name="location">The new location to write this region to.</param>
		public void OptimiseSelect(AtlasLocation location){
			// The given texture wants to go in this location.
			
			// Remove from empty queue:
			if(EmptyBefore==null){
				Atlas.FirstEmpty=EmptyAfter;
			}else{
				EmptyBefore.EmptyAfter=EmptyAfter;
			}
			
			if(EmptyAfter==null){
				Atlas.LastEmpty=EmptyBefore;
			}else{
				EmptyAfter.EmptyBefore=EmptyBefore;
			}
			
			int width=location.Width;
			int height=location.Height;
			Spacing=location.Spacing;
			int spacedWidth=width+Spacing;
			int spacedHeight=height+Spacing;
			
			if(Atlas.Mode==AtlasingMode.SmallestSpace){
				
				// Create the new empty zones:
				if(Width>width){
					// The textures a little thin.
					
					// Generate a new area to the right of this one (NB: 0,0 is bottom left)
					AtlasLocation newRight=new AtlasLocation(Atlas,X+spacedWidth,Y,Width-spacedWidth,height);
					// Immediately mark this as empty:
					newRight.AddToEmptySet();
				}
				
				if(Height>height){
					// The textures a little short.
					
					// Generate a new area above this one (NB: 0,0 is bottom left)
					AtlasLocation newTop=new AtlasLocation(Atlas,X,Y+spacedHeight,Width,Height-spacedHeight);
					// Immediately mark this as empty:
					newTop.AddToEmptySet();
				}
				
			}else{
				
				int maxX=X+spacedWidth;
				
				if(Atlas.ColumnProgress <= maxX){
					
					Atlas.ColumnProgress=maxX;
					
				}else{
					
					// The textures a little thin.
					
					// Generate a new area to the right of this one (NB: 0,0 is bottom left)
					AtlasLocation newRight=new AtlasLocation(Atlas,X+spacedWidth,Y,Atlas.ColumnProgress-maxX,height);
					// Immediately mark this as empty:
					newRight.AddToEmptySet();
					
				}
				
				if(Height>spacedHeight){
					// The textures a little short.
					
					// Generate a new area above this one (NB: 0,0 is bottom left)
					AtlasLocation newTop=new AtlasLocation(Atlas,X,Y+spacedHeight,Width,Height-spacedHeight);
					// Immediately mark this as empty:
					newTop.AddToEmptySet();
				}
				
			}
			
			// Update atlas:
			location.Atlas=Atlas;
			
			// Move location so it acts like its in the same location as this:
			location.X=X;
			location.Y=Y;
			
			// Update the UV's:
			location.BakeUV();
			
			// Write it in:
			location.Flush();
		}
		/// <summary>This texture has selected this location to fit into. This adds it to the atlas.</summary>
		/// <param name="texture">The texture that wants to go here.</param>
		/// <param name="width">The width of the texture in pixels.</param>
		/// <param name="height">The height of the texture in pixels.</param>
		public void Select(AtlasEntity image,int width,int height,int spacing){
			// The given texture wants to go in this location.
			// Width and height are given for dynamic textures - textures who's pixels are actually written straight to the atlas.
			Empty=false;
			
			int spacedWidth=width+spacing;
			int spacedHeight=height+spacing;
			
			// Remove from empty queue:
			if(EmptyBefore==null){
				Atlas.FirstEmpty=EmptyAfter;
			}else{
				EmptyBefore.EmptyAfter=EmptyAfter;
			}
			
			if(EmptyAfter==null){
				Atlas.LastEmpty=EmptyBefore;
			}else{
				EmptyAfter.EmptyBefore=EmptyBefore;
			}
			
			int entityID=image.GetAtlasID();
			
			if(AtlasID==entityID){
				// This is a restore - we don't need to do anything else.
				return;
			}
			
			Image=image;
			AtlasID=entityID;
			// If it's not a perfect fit, generate new atlas locations to the right and above of this one.
			
			if(Atlas.Mode==AtlasingMode.SmallestSpace){
			
				if(Width>spacedWidth){
					// The textures a little thin.
					
					// Generate a new area to the right of this one (NB: 0,0 is bottom left)
					AtlasLocation newRight=new AtlasLocation(Atlas,X+spacedWidth,Y,Width-spacedWidth,height);
					// Immediately mark this as empty:
					newRight.AddToEmptySet();
				}
				
				if(Height>spacedHeight){
					// The textures a little short.
					
					// Generate a new area above this one (NB: 0,0 is bottom left)
					AtlasLocation newTop=new AtlasLocation(Atlas,X,Y+spacedHeight,Width,Height-spacedHeight);
					// Immediately mark this as empty:
					newTop.AddToEmptySet();
				}
				
			}else{
				
				int maxX=X+spacedWidth;
				
				if(Atlas.ColumnProgress < maxX){
					
					Atlas.ColumnProgress=maxX;
					
				}
				
				if(Height>spacedHeight){
					// The textures a little short.
					
					// Generate a new area above this one (NB: 0,0 is bottom left)
					AtlasLocation newTop=new AtlasLocation(Atlas,X,Y+spacedHeight,Width,Height-spacedHeight);
					// Immediately mark this as empty:
					newTop.AddToEmptySet();
				}
				
			}
			
			// Set the new size of this location for UV baking:
			Width=width;
			Height=height;
			Spacing=spacing;
			
			// Update the UV's:
			BakeUV();
			
			// Make sure the area is up to date:
			Area=spacedWidth*spacedHeight;
			
			// Write it in:
			Flush();
		}