// texPropertyNames 是 resultMaterial中纹理属性的列表 // allowedMaterialsFilter 是材料列表。 没有这些材料的物体将被忽略。这由多种材质过滤器使用 // textureEditorMethods 仅封装编辑器功能,例如保存资产和跟踪纹理资产谁的格式已更改。 如果在运行时使用,则为null。 static void BeginCombineTexturesIntoAtlases(AtlasesAndRects resultAtlasesAndRects, TextureCombinePipelineData data, bool splitAtlasWhenPackingIfTooBig, EditorMethodsInterface textureEditorMethods) { // --- 1、记录各合并物体的源材质的 Prop Texture 信息写入 Data.distinctMaterialTextures //每个图集(主图,凹凸等)都将有的 MaterialTextures.Count 个图像。 //每个 distinctMaterialTextures 对应一个游戏物体的某个材质,记录一组纹理,分别材质的在每个Prop图集一个。 List <GameObject> usedObjsToMesh = new List <GameObject>(); TextureCombinePipeline.Step1_CollectDistinctMatTexturesAndUsedObjects( data, textureEditorMethods, usedObjsToMesh); // --- 2、计算使每个材质属性中的多个材质的合理尺寸 TextureCombinePipeline.Step2_CalculateIdealSizesForTexturesInAtlasAndPadding( data, textureEditorMethods); // --- 3、创建特定打包方式的打包器 ITextureCombineAtlasPacker texturePaker = CreateAtlasPacker( data.IsOnlyOneTextureInAtlasReuseTextures(), data.packingAlgorithm); texturePaker.ConvertTexturesToReadableFormats(data, textureEditorMethods); // --- 4、计算各源材质在合并材质 Atlas 的排布 AtlasPackingResult[] uvRects = texturePaker.CalculateAtlasRectangles( data, splitAtlasWhenPackingIfTooBig); // --- 5、创建 Atlas 并保存 TextureCombinePipeline.Step3_BuildAndSaveAtlasesAndStoreResults( data, null, texturePaker, uvRects[0], textureEditorMethods, resultAtlasesAndRects); }
public override void CreateAtlases( TextureCombinePipelineData data, TextureCombineHandler combiner, AtlasPackingResult packedAtlasRects, Texture2D[] atlases, EditorMethodsInterface textureEditorMethods) { Rect[] uvRects = packedAtlasRects.rects; int atlasSizeX = packedAtlasRects.atlasX; int atlasSizeY = packedAtlasRects.atlasY; Debug.Log("Generated atlas will be " + atlasSizeX + "x" + atlasSizeY); for (int propIdx = 0; propIdx < data.numAtlases; propIdx++) { Texture2D atlas = null; ShaderTextureProperty property = data.texPropertyNames[propIdx]; if (!TextureCombinePipeline.ShouldWeCreateAtlasForThisProperty(propIdx, data.considerNonTextureProperties, data.allTexturesAreNullAndSameColor)) { atlas = null; Debug.Log("=== Not creating atlas for " + property.name + " because textures are null and default value parameters are the same."); } else { Debug.Log("=== Creating atlas for " + property.name); GC.Collect(); CreateTemporaryTexturesForAtlas(data.distinctMaterialTextures, combiner, propIdx, data); //use a jagged array because it is much more efficient in memory Color[][] atlasPixels = new Color[atlasSizeY][]; for (int j = 0; j < atlasPixels.Length; j++) { atlasPixels[j] = new Color[atlasSizeX]; } bool isNormalMap = false; if (property.isNormalMap) { isNormalMap = true; } for (int texSetIdx = 0; texSetIdx < data.distinctMaterialTextures.Count; texSetIdx++) { MaterialPropTexturesSet texSet = data.distinctMaterialTextures[texSetIdx]; MaterialPropTexture matTex = texSet.ts[propIdx]; string s = "Creating Atlas '" + property.name + "' texture " + matTex.GetTexName(); Debug.Log(string.Format("Adding texture {0} to atlas {1} for texSet {2} srcMat {3}", matTex.GetTexName(), property.name, texSetIdx, texSet.matsAndGOs.mats[0].GetMaterialName())); Rect r = uvRects[texSetIdx]; Texture2D t = texSet.ts[propIdx].GetTexture2D(); int x = Mathf.RoundToInt(r.x * atlasSizeX); int y = Mathf.RoundToInt(r.y * atlasSizeY); int ww = Mathf.RoundToInt(r.width * atlasSizeX); int hh = Mathf.RoundToInt(r.height * atlasSizeY); if (ww == 0 || hh == 0) { Debug.LogError("Image in atlas has no height or width " + r); } if (textureEditorMethods != null) { textureEditorMethods.SetReadWriteFlag(t, true, true); } DRect samplingRect = texSet.ts[propIdx].GetEncapsulatingSamplingRect(); CopyScaledAndTiledToAtlas(texSet.ts[propIdx], texSet, property, samplingRect, x, y, ww, hh, packedAtlasRects.padding[texSetIdx], atlasPixels, isNormalMap, data, combiner); } atlas = new Texture2D(atlasSizeX, atlasSizeY, TextureFormat.ARGB32, true); for (int j = 0; j < atlasPixels.Length; j++) { atlas.SetPixels(0, j, atlasSizeX, 1, atlasPixels[j]); } atlas.Apply(); Debug.Log("Saving atlas " + property.name + " w=" + atlas.width + " h=" + atlas.height); } atlases[propIdx] = atlas; if (data.saveAtlasesAsAssets && textureEditorMethods != null) { textureEditorMethods.SaveAtlasToAssetDatabase(atlases[propIdx], data.texPropertyNames[propIdx], propIdx, data.ResultMaterial); } else { data.ResultMaterial.SetTexture(data.texPropertyNames[propIdx].name, atlases[propIdx]); } data.ResultMaterial.SetTextureOffset(data.texPropertyNames[propIdx].name, Vector2.zero); data.ResultMaterial.SetTextureScale(data.texPropertyNames[propIdx].name, Vector2.one); combiner._destroyTemporaryTextures(data.texPropertyNames[propIdx].name); } }
static void CombineTexturesIntoAtlases(AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List <GameObject> objsToMesh, List <Material> allowedMaterialsFilter, EditorMethodsInterface textureEditorMethods, bool splitAtlasWhenPackingIfTooBig = false) { try { MaterialPropTexture.readyToBuildAtlases = false; // ---- 1.合并材质参数校验 if (objsToMesh == null || objsToMesh.Count == 0) { Debug.LogError("没有游戏物体参与合并"); return; } if (combineData.atlasPadding < 0) { Debug.LogError("Atlas padding 必须大于等于零"); return; } if (combineData.maxTilingBakeSize < 2 || combineData.maxTilingBakeSize > 4096) { Debug.LogError("无效Tilling尺寸的值Invalid value for max tiling bake size."); } for (int i = 0; i < objsToMesh.Count; i++) { Material[] ms = MeshBakerUtility.GetGOMaterials(objsToMesh[i]); for (int j = 0; j < ms.Length; j++) { Material m = ms[j]; if (m == null) { Debug.LogError("游戏物体" + objsToMesh[i] + " 材质为空 "); } } } if (combineData.fixOutOfBoundsUVs && (combineData.packingAlgorithm == PackingAlgorithmEnum.MeshBakerTexturePacker_Horizontal || combineData.packingAlgorithm == PackingAlgorithmEnum.MeshBakerTexturePacker_Vertical)) { Debug.LogWarning("合并算法为 MeshBakerTexturePacker_Horizontal 或 MeshBakerTexturePacker_Vertical,建议不打开 Consider Mesh UVs 选项"); } // ---- 2.将材质的 shader 各参数信息写入管线数据中 if (!TextureCombinePipeline.CollectPropertyNames(combineData)) { return; } // ---- 3.加载 Texture 混合器 combineData.nonTexturePropertyBlender.LoadTextureBlendersIfNeeded(combineData.ResultMaterial); // ---- 4.合并管道 BeginCombineTexturesIntoAtlases(resultAtlasesAndRects, combineData, splitAtlasWhenPackingIfTooBig, textureEditorMethods); } finally { // ---- 6.删除缓存,合并材质完成回调 //_destroyAllTemporaryTextures(); } }