/// <summary> /// Texture 合并管线第 3 步,创建 Atlas 并保存资源 /// </summary> /// <returns></returns> internal static void Step3_BuildAndSaveAtlasesAndStoreResults( TextureCombinePipelineData data, TextureCombineHandler combiner, ITextureCombineAtlasPacker packer, AtlasPackingResult atlasPackingResult, EditorMethodsInterface textureEditorMethods, AtlasesAndRects resultAtlasesAndRects) { //run the garbage collector to free up as much memory as possible before bake to reduce MissingReferenceException problems GC.Collect(); Texture2D[] atlases = new Texture2D[data.numAtlases]; //StringBuilder report = GenerateReport(data); //创建图集 packer.CreateAtlases(data, combiner, atlasPackingResult, atlases, textureEditorMethods); data.nonTexturePropertyBlender.AdjustNonTextureProperties(data.ResultMaterial, data.texPropertyNames, data.distinctMaterialTextures, textureEditorMethods); if (data.distinctMaterialTextures.Count > 0) { data.distinctMaterialTextures[0].AdjustResultMaterialNonTextureProperties(data.ResultMaterial, data.texPropertyNames); } List <MaterialAndUVRect> mat2rect_map = new List <MaterialAndUVRect>(); for (int i = 0; i < data.distinctMaterialTextures.Count; i++) { MaterialPropTexturesSet texSet = data.distinctMaterialTextures[i]; List <MatAndTransformToMerged> mats = texSet.matsAndGOs.mats; Rect allPropsUseSameTiling_encapsulatingSamplingRect; Rect propsUseDifferntTiling_obUVRect; texSet.GetRectsForTextureBakeResults(out allPropsUseSameTiling_encapsulatingSamplingRect, out propsUseDifferntTiling_obUVRect); for (int j = 0; j < mats.Count; j++) { Rect allPropsUseSameTiling_sourceMaterialTiling = texSet.GetMaterialTilingRectForTextureBakerResults(j); MaterialAndUVRect key = new MaterialAndUVRect( mats[j].mat, atlasPackingResult.rects[i], texSet.allTexturesUseSameMatTiling, allPropsUseSameTiling_sourceMaterialTiling, allPropsUseSameTiling_encapsulatingSamplingRect, propsUseDifferntTiling_obUVRect, texSet.tilingTreatment, mats[j].objName); if (!mat2rect_map.Contains(key)) { mat2rect_map.Add(key); } } } resultAtlasesAndRects.atlases = atlases; // one per texture on result shader resultAtlasesAndRects.texPropertyNames = ShaderTextureProperty.GetNames(data.texPropertyNames); // one per texture on source shader resultAtlasesAndRects.originMatToRect_map = mat2rect_map; combiner._destroyAllTemporaryTextures(); }
// 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); }
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(); } }
/// <summary> /// 创建图集 /// </summary> public static void CreateAtlases(List <GameObject> GameObjectsToCombine, bool saveAtlasesAsAssets = false) { try { combineData.ResultAtlasesAndRects = null; combineData.saveAtlasesAsAssets = saveAtlasesAsAssets; //--- 1、合并前检测 if (!ValidateCombineGameObject(GameObjectsToCombine)) { return; } //材质验证 if (combineData.DoMultiMaterial) { if (!_ValidateResultMaterials(GameObjectsToCombine, combineData.ResultMaterials)) { return; } } else { if (!ValidateSingleResultMaterial(GameObjectsToCombine, combineData.ResultMaterial)) { return; } } ////--- 2、初始化存储合并结果的数据结构 int numResults = 1; if (combineData.DoMultiMaterial) { numResults = combineData.ResultMaterials.Length; } combineData.ResultAtlasesAndRects = new AtlasesAndRects[numResults]; for (int i = 0; i < combineData.ResultAtlasesAndRects.Length; i++) { combineData.ResultAtlasesAndRects[i] = new AtlasesAndRects(); } //--- 3、开始合并材质(单个,多个) for (int i = 0; i < combineData.ResultAtlasesAndRects.Length; i++) { Material resultMat; List <Material> sourceMats; if (combineData.DoMultiMaterial) { sourceMats = combineData.ResultMaterials[i].sourceMaterials; resultMat = combineData.ResultMaterials[i].combinedMaterial; combineData.fixOutOfBoundsUVs = combineData.ResultMaterials[i].considerMeshUVs; } else { resultMat = combineData.ResultMaterial; sourceMats = null; //为空则为全部合并 } CombineTexturesIntoAtlases(combineData.ResultAtlasesAndRects[i], resultMat, GameObjectsToCombine, sourceMats, null); } //--- 4、TextureBakeResults 保存合并结果 //unpackMat2RectMap(textureBakeResults); //textureBakeResults.doMultiMaterial = _doMultiMaterial; //if (_doMultiMaterial) //{ // textureBakeResults.resultMaterials = resultMaterials; //} //else //{ // MultiMaterial[] resMats = new MultiMaterial[1]; // resMats[0] = new MultiMaterial(); // resMats[0].combinedMaterial = _resultMaterial; // resMats[0].considerMeshUVs = _fixOutOfBoundsUVs; // resMats[0].sourceMaterials = new List<Material>(); // for (int i = 0; i < textureBakeResults.materialsAndUVRects.Length; i++) // { // resMats[0].sourceMaterials.Add(textureBakeResults.materialsAndUVRects[i].material); // } // textureBakeResults.resultMaterials = resMats; //} ////--- 5、传递合并结果到 MeshCombiner //MeshBakerCommon[] mb = GetComponentsInChildren<MeshBakerCommon>(); //for (int i = 0; i < mb.Length; i++) //{ // mb[i].textureBakeResults = textureBakeResults; //} //coroutineResult.isFinished = true; ////--- 6、合并材质结束回调 //if (coroutineResult.success && onBuiltAtlasesSuccess != null) //{ // onBuiltAtlasesSuccess(); //} //if (!coroutineResult.success && onBuiltAtlasesFail != null) //{ // onBuiltAtlasesFail(); //} } catch (Exception e) { Debug.LogError(e); } finally { if (saveAtlasesAsAssets) { //Atlases were saved to project so we don't need these ones if (combineData.ResultAtlasesAndRects != null) { for (int j = 0; j < combineData.ResultAtlasesAndRects.Length; j++) { AtlasesAndRects mAndA = combineData.ResultAtlasesAndRects[j]; if (mAndA != null && mAndA.atlases != null) { for (int i = 0; i < mAndA.atlases.Length; i++) { if (mAndA.atlases[i] != null) { //if (editorMethods != null) //{ // editorMethods.Destroy(mAndA.atlases[i]); //} //else //{ MeshBakerUtility.Destroy(mAndA.atlases[i]); //} } } } } } } } }