// private static float timestart; /// <summary> /// Custom BuildR texture packer. Uses the BuildR Data class to create a single texture used by the generated building, taking into account tiling. /// </summary> /// <param name="data">BuildR Building Data</param> public static void Pack(BuildrData data) { // timestart = Time.realtimeSinceStartup; data.texturesNotPacked.Clear(); data.texturesPacked.Clear(); int numberOfTextures = data.textures.Count; List <Rect> packedTexturePositions = new List <Rect>(); List <int> texturePacked = new List <int>(); for (int t = 0; t < numberOfTextures; t++) { BuildrTexture texture = data.textures[t]; if (texture.tiled) { int textureWidth = texture.texture.width; int textureHeight = texture.texture.height; int targetTextureWidth = Mathf.RoundToInt(textureWidth * texture.maxUVTile.x); int targetTextureHeight = Mathf.RoundToInt(textureHeight * texture.maxUVTile.y); float resizeToLargest = Mathf.Min((float)MAX_SOURCE_TEXTURE_SIZE / (float)targetTextureWidth, (float)MAX_SOURCE_TEXTURE_SIZE / (float)targetTextureHeight, 1);//ensure texture fits smallest size int useTextureWidth = Mathf.RoundToInt(resizeToLargest * targetTextureWidth); int useTextureHeight = Mathf.RoundToInt(resizeToLargest * targetTextureHeight); if (useTextureWidth == 0 || useTextureHeight == 0)//texture no used on model { data.texturesNotPacked.Add(t); continue; } packedTexturePositions.Add(new Rect(0, 0, useTextureWidth, useTextureHeight)); texturePacked.Add(t); } else { int useTextureWidth = texture.texture.width; int useTextureHeight = texture.texture.height; packedTexturePositions.Add(new Rect(0, 0, useTextureWidth, useTextureHeight)); texturePacked.Add(t); } } int atlasedTextureWidth = RectanglePack.Pack(packedTexturePositions, ATLAS_PADDING); //determine the resize scale and apply that to the rects float packedScale = 1; int numberOfRects = packedTexturePositions.Count; if (atlasedTextureWidth > MAXIMUM_TEXTURESIZE) { packedScale = MAXIMUM_TEXTURESIZE / (float)atlasedTextureWidth; for (int i = 0; i < numberOfRects; i++) { Rect thisRect = packedTexturePositions[i]; thisRect.x *= packedScale; thisRect.y *= packedScale; thisRect.width *= packedScale; thisRect.height *= packedScale; packedTexturePositions[i] = thisRect; } atlasedTextureWidth = Mathf.RoundToInt(packedScale * atlasedTextureWidth); } else { atlasedTextureWidth = (int)Mathf.Pow(2, (Mathf.FloorToInt(Mathf.Log(atlasedTextureWidth - 1, 2)) + 1));//find the next power of two } int textureSize = atlasedTextureWidth * atlasedTextureWidth; Color32[] colourArray = new Color32[textureSize]; //Build colour array for (int t = 0; t < numberOfTextures; t++) { BuildrTexture texture = data.textures[t]; Rect packedPosition = packedTexturePositions[t]; int targetTextureWidth = (int)packedPosition.width; int targetTextureHeight = (int)packedPosition.height; int sourceTextureWidth = texture.texture.width; int sourceTextureHeight = texture.texture.height; // int sourceTextureSize = sourceTextureWidth * sourceTextureHeight; int paintTextureWidth = Mathf.RoundToInt(targetTextureWidth / (texture.tiled ? texture.maxUVTile.x : texture.tiledX)); int paintTextureHeight = Mathf.RoundToInt(targetTextureHeight / (texture.tiled ? texture.maxUVTile.y : texture.tiledY)); int paintTextureSize = paintTextureWidth * paintTextureHeight; Color32[] paintColourArray = TextureScale.NearestNeighbourSample(texture.texture.GetPixels32(), sourceTextureWidth, sourceTextureHeight, paintTextureWidth, paintTextureHeight); for (int x = 0; x < targetTextureWidth; x++) { for (int y = 0; y < targetTextureHeight; y++) { int drawX = Mathf.FloorToInt(x + packedPosition.x); int drawY = Mathf.FloorToInt(y + packedPosition.y); int colourIndex = drawX + drawY * atlasedTextureWidth; int sx = x % paintTextureWidth; int sy = y % paintTextureHeight; int paintIndex = sx + sy * paintTextureWidth; if (paintIndex >= paintTextureSize) { Debug.Log("Source Index too big " + sx + " " + sy + " " + paintTextureWidth + " " + paintTextureSize + " " + texture.maxUVTile + " " + texture.name); } Color32 sourceColour = paintColourArray[paintIndex]; if (colourIndex >= textureSize) { Debug.Log("Output Index Too big " + drawX + " " + drawY + " " + colourIndex + " " + textureSize + " " + packedPosition); } colourArray[colourIndex] = sourceColour; } } } Texture2D packedTexture = new Texture2D(atlasedTextureWidth, atlasedTextureWidth, TextureFormat.ARGB32, true); packedTexture.filterMode = FilterMode.Bilinear; packedTexture.SetPixels32(colourArray); packedTexture.Apply(true, false); //remove textures from memory if (data.textureAtlas != null) { DestroyImmediate(data.textureAtlas); } data.textureAtlas = packedTexture; data.texturesPacked = texturePacked; data.atlasCoords = RectanglePack.ConvertToUVSpace(packedTexturePositions.ToArray(), atlasedTextureWidth); System.GC.Collect(); //Debug.Log("BuildR Texutre Pack Complete: " + (Time.realtimeSinceStartup - timestart)+" sec"); }
private static void BuildTextures() { List <TexturePaintObject> buildSourceTextures = new List <TexturePaintObject>(); foreach (BuildrTexture btexture in data.textures)//Gather the source textures, resized into Color32 arrays { TexturePaintObject texturePaintObject = new TexturePaintObject(); texturePaintObject.pixels = ((Texture2D)btexture.texture).GetPixels32(); texturePaintObject.width = btexture.texture.width; texturePaintObject.height = btexture.texture.height; texturePaintObject.tiles = new Vector2(btexture.tiledX, btexture.tiledY); if (btexture.tiled) { int resizedTextureWidth = Mathf.RoundToInt(btexture.textureUnitSize.x * PIXELS_PER_METER * packedScale); int resizedTextureHeight = Mathf.RoundToInt(btexture.textureUnitSize.y * PIXELS_PER_METER * packedScale); texturePaintObject.pixels = TextureScale.NearestNeighbourSample(texturePaintObject.pixels, texturePaintObject.width, texturePaintObject.height, resizedTextureWidth, resizedTextureHeight); texturePaintObject.width = resizedTextureWidth; texturePaintObject.height = resizedTextureHeight; } else { texturePaintObject.tiled = false; } buildSourceTextures.Add(texturePaintObject); } LogTimer("Gather Source into Arrays"); TexturePaintObject[] sourceTextures = buildSourceTextures.ToArray(); textures = data.textures.ToArray(); BuildrFacadeDesign facadeDesign = data.facades[0]; BuildrPlan plan = data.plan; int numberOfVolumes = data.plan.numberOfVolumes; int facadeNumber = 0; for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; for (int f = 0; f < numberOfVolumePoints; f++) { if (!volume.renderFacade[f]) { continue; } int indexA, indexB; Vector3 p0, p1; indexA = f; indexB = (f < numberOfVolumePoints - 1) ? f + 1 : 0; p0 = plan.points[volume.points[indexA]].vector3; p1 = plan.points[volume.points[indexB]].vector3; Rect packedPosition = packedTexturePositions[facadeNumber]; float facadeWidth = Vector3.Distance(p0, p1); int floorBase = plan.GetFacadeFloorHeight(s, volume.points[indexA], volume.points[indexB]); int numberOfFloors = volume.numberOfFloors - floorBase; if (numberOfFloors < 1) { //no facade - adjacent facade is taller and covers this one continue; } float floorHeight = data.floorHeight; BuildrVolumeStylesUnit[] styleUnits = volume.styles.GetContentsByFacade(volume.points[indexA]); int floorPatternSize = 0; List <int> facadePatternReference = new List <int>(); //this contains a list of all the facade style indices to refence when looking for the appropriate style per floor int patternCount = 0; foreach (BuildrVolumeStylesUnit styleUnit in styleUnits) //need to knw how big all the styles are together so we can loop through them { floorPatternSize += styleUnit.floors; for (int i = 0; i < styleUnit.floors; i++) { facadePatternReference.Add(patternCount); } patternCount++; } facadePatternReference.Reverse(); int rows = numberOfFloors; Vector2 bayBase = Vector2.zero; float currentFloorBase = 0; for (int r = 0; r < rows; r++) { currentFloorBase = floorHeight * r; int modFloor = (r % floorPatternSize); facadeDesign = data.facades[styleUnits[facadePatternReference[modFloor]].styleID]; bool isBlankWall = !facadeDesign.hasWindows; if (facadeDesign.type == BuildrFacadeDesign.types.patterned) { BuildrBay firstBay = data.bays[facadeDesign.bayPattern[0]]; if (firstBay.openingWidth > facadeWidth) { isBlankWall = true; } if (facadeDesign.bayPattern.Count == 0) { isBlankWall = true; } } else { if (facadeDesign.simpleBay.openingWidth + facadeDesign.simpleBay.minimumBayWidth > facadeWidth) { isBlankWall = true; } } if (!isBlankWall) { float patternSize = 0;//the space the pattern fills, there will be a gap that will be distributed to all bay styles int numberOfBays = 0; BuildrBay[] bays; int numberOfBayDesigns = 0; if (facadeDesign.type == BuildrFacadeDesign.types.patterned) { numberOfBayDesigns = facadeDesign.bayPattern.Count; bays = new BuildrBay[numberOfBayDesigns]; for (int i = 0; i < numberOfBayDesigns; i++) { bays[i] = data.bays[facadeDesign.bayPattern[i]]; } } else { bays = new[] { facadeDesign.simpleBay }; numberOfBayDesigns = 1; } //start with first window width - we'll be adding to this until we have filled the facade width int it = 100; while (true) { int patternModIndex = numberOfBays % numberOfBayDesigns; float patternAddition = bays[patternModIndex].openingWidth + bays[patternModIndex].minimumBayWidth; if (patternSize + patternAddition < facadeWidth) { patternSize += patternAddition; numberOfBays++; } else { break; } it--; if (it < 0) { break; } } float perBayAdditionalSpacing = (facadeWidth - patternSize) / numberOfBays; float windowXBase = 0; for (int c = 0; c < numberOfBays; c++) { BuildrBay bayStyle; if (facadeDesign.type == BuildrFacadeDesign.types.patterned) { int numberOfBayStyles = facadeDesign.bayPattern.Count; bayStyle = bays[c % numberOfBayStyles]; } else { bayStyle = facadeDesign.simpleBay; } float actualWindowSpacing = bayStyle.minimumBayWidth + perBayAdditionalSpacing; float leftSpace = actualWindowSpacing * bayStyle.openingWidthRatio; float rightSpace = actualWindowSpacing - leftSpace; float openingSpace = bayStyle.openingWidth; Vector3 bayDimensions; int subMesh; bool flipped; if (!bayStyle.isOpening) { subMesh = bayStyle.GetTexture(BuildrBay.TextureNames.WallTexture); flipped = bayStyle.IsFlipped(BuildrBay.TextureNames.WallTexture); bayBase.x = windowXBase; bayBase.y = currentFloorBase; float bayWidth = (openingSpace + actualWindowSpacing); float bayHeight = floorHeight; bayDimensions = new Vector2(bayWidth, bayHeight); DrawFacadeTexture(sourceTextures, bayBase, bayDimensions, subMesh, flipped, packedPosition); windowXBase += bayWidth; //move base vertor to next bay continue; //bay filled - move onto next bay } float rowBottomHeight = ((floorHeight - bayStyle.openingHeight) * bayStyle.openingHeightRatio); float rowTopHeight = (floorHeight - rowBottomHeight - bayStyle.openingHeight); //Window subMesh = bayStyle.GetTexture(BuildrBay.TextureNames.OpeningBackTexture); flipped = bayStyle.IsFlipped(BuildrBay.TextureNames.OpeningBackTexture); bayBase.x = windowXBase + leftSpace; bayBase.y = currentFloorBase + rowBottomHeight; bayDimensions = new Vector2(bayStyle.openingWidth, bayStyle.openingHeight); DrawFacadeTexture(sourceTextures, bayBase, bayDimensions, subMesh, flipped, packedPosition); //Column Left if (leftSpace > 0) { subMesh = bayStyle.GetTexture(BuildrBay.TextureNames.ColumnTexture); flipped = bayStyle.IsFlipped(BuildrBay.TextureNames.ColumnTexture); bayBase.x = windowXBase; bayBase.y = currentFloorBase + rowBottomHeight; bayDimensions = new Vector2(leftSpace, bayStyle.openingHeight); DrawFacadeTexture(sourceTextures, bayBase, bayDimensions, subMesh, flipped, packedPosition); } //Column Right if (rightSpace > 0) { subMesh = bayStyle.GetTexture(BuildrBay.TextureNames.ColumnTexture); flipped = bayStyle.IsFlipped(BuildrBay.TextureNames.ColumnTexture); bayBase.x = windowXBase + leftSpace + openingSpace; bayBase.y = currentFloorBase + rowBottomHeight; bayDimensions = new Vector2(rightSpace, bayStyle.openingHeight); DrawFacadeTexture(sourceTextures, bayBase, bayDimensions, subMesh, flipped, packedPosition); } //Row Bottom if (rowBottomHeight > 0) { subMesh = bayStyle.GetTexture(BuildrBay.TextureNames.RowTexture); flipped = bayStyle.IsFlipped(BuildrBay.TextureNames.RowTexture); bayBase.x = windowXBase + leftSpace; bayBase.y = currentFloorBase; bayDimensions = new Vector2(openingSpace, rowBottomHeight); DrawFacadeTexture(sourceTextures, bayBase, bayDimensions, subMesh, flipped, packedPosition); } //Row Top if (rowTopHeight > 0) { subMesh = bayStyle.GetTexture(BuildrBay.TextureNames.RowTexture); flipped = bayStyle.IsFlipped(BuildrBay.TextureNames.RowTexture); bayBase.x = windowXBase + leftSpace; bayBase.y = currentFloorBase + rowBottomHeight + bayStyle.openingHeight; bayDimensions = new Vector2(openingSpace, rowTopHeight); DrawFacadeTexture(sourceTextures, bayBase, bayDimensions, subMesh, flipped, packedPosition); } //Cross Left if (leftSpace > 0) { //Cross Left Bottom subMesh = bayStyle.GetTexture(BuildrBay.TextureNames.CrossTexture); flipped = bayStyle.IsFlipped(BuildrBay.TextureNames.CrossTexture); bayBase.x = windowXBase; bayBase.y = currentFloorBase; bayDimensions = new Vector2(leftSpace, rowBottomHeight); DrawFacadeTexture(sourceTextures, bayBase, bayDimensions, subMesh, flipped, packedPosition); //Cross Left Top subMesh = bayStyle.GetTexture(BuildrBay.TextureNames.CrossTexture); flipped = bayStyle.IsFlipped(BuildrBay.TextureNames.CrossTexture); bayBase.x = windowXBase; bayBase.y = currentFloorBase + rowBottomHeight + bayStyle.openingHeight; bayDimensions = new Vector2(leftSpace, rowTopHeight); DrawFacadeTexture(sourceTextures, bayBase, bayDimensions, subMesh, flipped, packedPosition); } //Cross Right if (rightSpace > 0) { //Cross Left Bottom subMesh = bayStyle.GetTexture(BuildrBay.TextureNames.CrossTexture); flipped = bayStyle.IsFlipped(BuildrBay.TextureNames.CrossTexture); bayBase.x = windowXBase + leftSpace + openingSpace; bayBase.y = currentFloorBase; bayDimensions = new Vector2(rightSpace, rowBottomHeight); DrawFacadeTexture(sourceTextures, bayBase, bayDimensions, subMesh, flipped, packedPosition); //Cross Left Top subMesh = bayStyle.GetTexture(BuildrBay.TextureNames.CrossTexture); flipped = bayStyle.IsFlipped(BuildrBay.TextureNames.CrossTexture); bayBase.x = windowXBase + leftSpace + openingSpace; bayBase.y = currentFloorBase + rowBottomHeight + bayStyle.openingHeight; bayDimensions = new Vector2(rightSpace, rowTopHeight); DrawFacadeTexture(sourceTextures, bayBase, bayDimensions, subMesh, flipped, packedPosition); } windowXBase += leftSpace + openingSpace + rightSpace;//move base vertor to next bay } } else { // windowless wall int subMesh = facadeDesign.simpleBay.GetTexture(BuildrBay.TextureNames.WallTexture); bool flipped = facadeDesign.simpleBay.IsFlipped(BuildrBay.TextureNames.WallTexture); bayBase.x = 0; bayBase.y = currentFloorBase; Vector2 dimensions = new Vector2(facadeWidth, floorHeight); DrawFacadeTexture(sourceTextures, bayBase, dimensions, subMesh, flipped, packedPosition); } } facadeNumber++; } } LogTimer("generate facade textures"); //add roof textures int numberOfroofTextures = roofTextures.Count; int scaledPadding = Mathf.FloorToInt(ATLAS_PADDING * packedScale); for (int i = 0; i < numberOfroofTextures; i++) { Rect roofTexturePosition = packedTexturePositions[i + facadeNumber]; BuildrTexture bTexture = roofTextures[i]; int roofTextureWidth = bTexture.texture.width; int roofTextureHeight = bTexture.texture.height; int targetTextureWidth = Mathf.RoundToInt(roofTexturePosition.width); int targetTextureHeight = Mathf.RoundToInt(roofTexturePosition.height); if (bTexture.maxUVTile == Vector2.zero) { LogTimer("BuildTextures: Skip texture " + bTexture.name + " as it appears it's not used"); continue; } int sourceTextureWidth = Mathf.RoundToInt(targetTextureWidth / (bTexture.tiled ? bTexture.maxUVTile.x : bTexture.tiledX)); int sourceTextureHeight = Mathf.RoundToInt(targetTextureHeight / (bTexture.tiled ? bTexture.maxUVTile.y : bTexture.tiledY)); int sourceTextureSize = sourceTextureWidth * sourceTextureHeight; if (sourceTextureSize == 0) { //Debug.Log(sourceTextureWidth+" "+sourceTextureHeight+" "+bTexture.tiledX+" "+bTexture.maxUVTile+" "+bTexture.tiledX+","+bTexture.tiledY); continue; } Color32[] roofColourArray = TextureScale.NearestNeighbourSample(((Texture2D)bTexture.texture).GetPixels32(), roofTextureWidth, roofTextureHeight, sourceTextureWidth, sourceTextureHeight); //Color32[] roofColourArray = bTexture.texture.GetPixels32(); for (int x = 0; x < targetTextureWidth; x++) { for (int y = 0; y < targetTextureHeight; y++) { int drawX = Mathf.FloorToInt(x + roofTexturePosition.x); int drawY = Mathf.FloorToInt(y + roofTexturePosition.y); int colourIndex = drawX + drawY * textureWidth; int sx = x % sourceTextureWidth; int sy = y % sourceTextureHeight; int sourceIndex = sx + sy * sourceTextureWidth; if (sourceIndex >= sourceTextureSize) { Debug.Log("Source Index too big " + sx + " " + sy + " " + sourceTextureWidth + " " + sourceTextureSize + " " + bTexture.maxUVTile + " " + bTexture.name); } Color32 sourceColour = roofColourArray[sourceIndex]; if (colourIndex >= textureSize) { Debug.Log("Output Index Too big " + drawX + " " + drawY + " " + colourIndex + " " + textureSize + " " + roofTexturePosition); } colourArray[colourIndex] = sourceColour; //Padding if (x == 0) { for (int p = 0; p < scaledPadding; p++) { colourArray[colourIndex - p] = sourceColour; } } if (x == targetTextureWidth - 1) { for (int p = 0; p < scaledPadding; p++) { colourArray[colourIndex + p] = sourceColour; } } if (y == 0) { for (int p = 0; p < scaledPadding; p++) { colourArray[colourIndex - (p * textureWidth)] = sourceColour; } } if (y == targetTextureHeight - 1) { for (int p = 0; p < scaledPadding; p++) { colourArray[colourIndex + (p * textureWidth)] = sourceColour; } } } } } LogTimer("generate roof textures"); }
/// <summary> /// Packs an array of Texture2Ds into a sinlgle Texture2D with a maximum size specified. /// </summary> /// <param name="output">The output Texture2D</param> /// <param name="data">And array of input Texture2Ds</param> /// <param name="maximumSize">The maximum size of the output texture</param> /// <returns></returns> public static Rect[] Pack(out Texture2D output, Texture2D[] data, int maximumSize) { // timestart = Time.realtimeSinceStartup; int numberOfTextures = data.Length; List <Rect> packedTexturePositions = new List <Rect>(); List <int> texturePacked = new List <int>(); for (int t = 0; t < numberOfTextures; t++) { Texture2D texture = data[t]; int useTextureWidth = texture.width; int useTextureHeight = texture.height; packedTexturePositions.Add(new Rect(0, 0, useTextureWidth, useTextureHeight)); texturePacked.Add(t); } int atlasedTextureWidth = RectanglePack.Pack(packedTexturePositions, ATLAS_PADDING); //determine the resize scale and apply that to the rects int numberOfRects = packedTexturePositions.Count; if (atlasedTextureWidth > maximumSize) { float packedScale = maximumSize / (float)atlasedTextureWidth; for (int i = 0; i < numberOfRects; i++) { Rect thisRect = packedTexturePositions[i]; thisRect.x *= packedScale; thisRect.y *= packedScale; thisRect.width *= packedScale; thisRect.height *= packedScale; packedTexturePositions[i] = thisRect; } atlasedTextureWidth = Mathf.RoundToInt(packedScale * atlasedTextureWidth); } else { atlasedTextureWidth = (int)Mathf.Pow(2, (Mathf.FloorToInt(Mathf.Log(atlasedTextureWidth - 1, 2)) + 1));//find the next power of two } int textureSize = atlasedTextureWidth * atlasedTextureWidth; Color32[] colourArray = new Color32[textureSize]; //Build colour array for (int t = 0; t < numberOfTextures; t++) { Texture2D texture = data[t]; Rect packedPosition = packedTexturePositions[t]; int targetTextureWidth = (int)packedPosition.width; int targetTextureHeight = (int)packedPosition.height; int sourceTextureWidth = texture.width; int sourceTextureHeight = texture.height; Color32[] paintColourArray = TextureScale.NearestNeighbourSample(texture.GetPixels32(), sourceTextureWidth, sourceTextureHeight, targetTextureWidth, targetTextureHeight); for (int x = 0; x < targetTextureWidth; x++) { for (int y = 0; y < targetTextureHeight; y++) { int drawX = Mathf.FloorToInt(x + packedPosition.x); int drawY = Mathf.FloorToInt(y + packedPosition.y); int colourIndex = drawX + drawY * atlasedTextureWidth; int paintIndex = x + y * targetTextureWidth; Color32 sourceColour = paintColourArray[paintIndex]; colourArray[colourIndex] = sourceColour; } } } output = new Texture2D(atlasedTextureWidth, atlasedTextureWidth, TextureFormat.ARGB32, true); output.filterMode = FilterMode.Bilinear; output.SetPixels32(colourArray); output.Apply(true, false); //Debug.Log("BuildR Texutre Pack Complete: " + (Time.realtimeSinceStartup - timestart) + " sec"); return(RectanglePack.ConvertToUVSpace(packedTexturePositions.ToArray(), atlasedTextureWidth)); }