Optimizing my character setup...

Update: So I’ve made an odd discovery. Turns out if you put more than one material on an object, they get drawn together. Essentially this means that the script isn’t needed at all. Character Creation is much easier this way as well.

The downside? It increases draw calls with every material added. I wonder though, would Texture2D.PackTextures make the resulting texture layered as well? At that point, I could use that to pack the textures into a texture atlas, merging the materials and reducing it to a single draw call (assuming the clothing materials all matched types, but that’s an easy issue).

Old Post:

So I have a few functions so far, working together to set up a character for use. It’s pretty hefty, because it combines several layers of clothing into one single texture (like World of Warcraft, although my system doesn’t seem to be nearly as smooth).

Right now what I’m doing is simply calling this function on Awake(). I’ve managed to set up about 62 objects this way (with only a few layers) without failure, but beyond that the function can sometimes fail to finish, and makes the textures all black.

Also, about half the time, 512x512 can be too big for the function to process all at once. 256x256 seems to work fine, but at that point I would be sacrificing a lot.

Is there some way I could simply have my script work in the background, and only apply when it’s ready?

Also, can you see anything I could do to improve my script’s overall performance? Here’s the real meat of it:

public static TextureLayer FlattenLayers(List<TextureLayer> layers) {
		TextureLayer returnLayer = new TextureLayer();
		
		List<Texture2D> flattenTextures = new List<Texture2D>();
		List<Texture2D> flattenNormals = new List<Texture2D>();
		List<Color> addedColors = new List<Color>();
		
		foreach(TextureLayer texLayer in layers) {
			if(texLayer.texture != null) { flattenTextures.Add(texLayer.texture); }
			else { flattenTextures.Add(null); }
			if(texLayer.normalTexture != null) { flattenNormals.Add(texLayer.normalTexture); }
			else { flattenNormals.Add(null); }
			addedColors.Add(texLayer.color);
		}
		
		returnLayer.texture = TextureCombineUtility.Flatten(flattenTextures,flattenTextures,addedColors);
		returnLayer.normalTexture	= TextureCombineUtility.Flatten(flattenNormals,flattenTextures,addedColors);
		
		return returnLayer;
	}





public static Texture2D Flatten (List<Texture2D> combines, List<Texture2D> alphaSource, List<Color> colorSource) {
		
		Texture2D newTexture = new Texture2D(0,0,TextureFormat.ARGB32,true);
		newTexture.SetPixel(0,0,Color.white);
		if(combines[0]) {
			newTexture.Resize(combines[0].width,combines[0].height,TextureFormat.ARGB32,true);
		}
		else {
			newTexture.Resize(256,256,TextureFormat.ARGB32,true);
		}
		
		int xx = newTexture.width;
		int yy = newTexture.height;
		
		//Resize each layer to match the new texture, and draw the layer onto the new texture.
		int i = 0;
		int l = combines.Count;
		while(i < l) {
			
			if(combines[i]) {
				Texture2D addTex = new Texture2D(combines[i].width,combines[i].height,TextureFormat.ARGB32,true);
				addTex.SetPixels(combines[i].GetPixels(0),0);
				addTex.Resize(xx,yy,TextureFormat.ARGB32,true);
				
				Texture2D alphaTex = new Texture2D(0,0,TextureFormat.ARGB32,true);
				alphaTex.SetPixel(0,0,Color.white);
				if(alphaSource[i]) {
					alphaTex.Resize(alphaSource[i].width,alphaSource[i].height,TextureFormat.ARGB32,true);
					alphaTex.SetPixels(alphaSource[i].GetPixels(0),0);
				}
				
				alphaTex.Resize(xx,yy,TextureFormat.ARGB32,true);
				
				
				int y = 0;
				while(y < yy) {
					int x = 0;
					while(x < xx) {
						Color mainPixel = newTexture.GetPixel(x,y);
						Color newPixel = addTex.GetPixel(x,y);
						Color alphaPixel = alphaTex.GetPixel(x,y);
						Color colorChange = colorSource[i];
						
						mainPixel = Color.Lerp(mainPixel,newPixel*colorChange,alphaPixel.a);
						newTexture.SetPixel(x,y,mainPixel);
						
						++x;
					}
					++y;
				}
			}
			++i;
		}
		
		newTexture.Apply();
		return newTexture;
	}

It would be cool if you did all this in Corotines and yielded every line or so, or whatever makes sense as you built your textures. Then you get the benifit of things not faultering and failing to finish. You get to play as you go, and you eventually get textures.

Coroutines, eh? I’ve heard of those before. I shall attempt to learn more!

In the mean time though, I’m still open to more ideas to help improve it. :smile:

EDIT: Updated the first post. Still workin’ away.

Tuah, I’ve been needing a similar functionality for customizing my character… combining multiple overlapping textures into 1 for performance sake. I’m not a seasoned Unity programmer, so I don’t know if this will help at all, but I’ve been using a different technique to accomplish this…

Render Textures. I have 2 objects - a render texture and a camera dedicated to this function (separate from the main scene camera(s)) I’m holding all the textures that will be combined in a Texture2D array, then simply looping through the array rendering the camera each time with the new textures. (the camera clear flag is set to none, so they essentially combine on top of each other,with full transparency/translucency). then when it’s finished going through the loop, I set it to the active render texture and assign that to the character’s material…

 //define the rendertexture
RenderCam.targetTexture=RenderDiffuse;  
//-- Add Textures --
for (lp = 0; lp < BaseTextures.length; lp++) {   
	GuiTex.texture = BaseTextures[lp];   //set texture
	GuiTex.color = BaseColors[lp];   //set texture tint color
	RenderCam.Render();  //render texture
}

//-- Finalize Texture -- 
RenderTexture.active = RenderDiffuse;
transform.renderer.material.mainTexture = RenderDiffuse;
RenderTexture.active=null;

this might be quicker than using getPixels especially if combining so many textures. Also I can define the reolution of the image to be whatever I want, without any apparent limitation… right now I’m using 2046 x 2046. Hope this helps, or at least sparks some new ideas in your head!

This method requires Unity Pro, right?

If so, I suppose I’m out of luck for quite some time. xP

Oh, yeah… render to texture is a pro function only. :frowning: