unsafe static void _RepackLightmap() { var gos = Selection.gameObjects; var checkMSG = false; REOPEN: if (gos.Length == 0) { UDebug.LogError("Please select a GameObject in hierarchy to process!"); return; } var hierarchy = UnityUtils.GetHierarchyPath(gos[0].transform); if (!checkMSG) { checkMSG = true; if (!EditorUtility.DisplayDialog(Title, "请确认:处理的是原始场景,如果已经处理过,请重新加载场景。", "直接整", "好吧,重新加载场景")) { UnityEditor.SceneManagement.EditorSceneManager.OpenScene(currentScene); gos = new GameObject[] { GameObject.Find(hierarchy) }; goto REOPEN; } } // 经过测试,这个值确实是输出到了最终lightmap纹理中纹理单位的间隔 var Padding = LightmapEditorSettings.padding; Bounds bounds; var workingPath = CleanTempFolder(); var allRenderers = CollectAllLightmapedRenderers(gos, out bounds).Values.ToList(); // 把搜集到的原始Mesh重新保存成asset格式,便于后面的修改(FBX无法修改) var meshes = new Dictionary <Mesh, Mesh>(); for (int i = 0; i < allRenderers.Count; ++i) { var ri = allRenderers[i]; var srcMesh = ri.mesh; if (!srcMesh.isReadable) { UDebug.LogError("Mesh \"{0}\" is not readable!"); continue; } var mf = ri.renderer.transform.GetComponent <MeshFilter>(); if (mf == null) { continue; } Mesh dstMesh = null; if (!meshes.TryGetValue(srcMesh, out dstMesh)) { var assetPath = String.Format("{0}/{1}_{2}.mesh.asset", workingPath, srcMesh.name, meshes.Count); if (File.Exists(assetPath)) { AssetDatabase.DeleteAsset(assetPath); } AssetDatabase.CreateAsset(UnityEngine.Object.Instantiate <Mesh>(srcMesh), assetPath); dstMesh = AssetDatabase.LoadAssetAtPath <Mesh>(assetPath); meshes.Add(srcMesh, dstMesh); } //覆盖原始模型 mf.sharedMesh = dstMesh; } if (meshes.Count > 0) { var litmapData = LoadAllLightmapData(); if (litmapData == null) { UDebug.LogError("Load source lightmaps failed."); return; } var inputRects = new List <NativeAPI.stbrp_rect>(); var all_atlas_pixels = new Dictionary <int, AtlasPixelsPage>(); var lightmapRectInfoList = new List <LightmapRect>(); using (var tp = new TexturePacker()) { for (int i = 0; i < allRenderers.Count; ++i) { var ri = allRenderers[i]; var r = ri.renderer; var uv2BoundsInfo = GetMeshUV2Bounds(r); int lightmapIndex = uv2BoundsInfo.lightmapIndex; if (lightmapIndex >= 0 && lightmapIndex < litmapData.Count) { var lightmapData = litmapData[r.lightmapIndex]; var meshUVBounds = uv2BoundsInfo.srcUVBounds; var lightmapUVBounds = uv2BoundsInfo.lightmapUVBounds; var _lightmapUVBounds = lightmapUVBounds; // 当前发现有些美术自己展开的UV有超过01现象,尝试自己模拟一下Repeat采样纹理坐标的计算 _lightmapUVBounds.x = MathLib.ClampRepeatUV(_lightmapUVBounds.x); _lightmapUVBounds.y = MathLib.ClampRepeatUV(_lightmapUVBounds.y); _lightmapUVBounds.z = MathLib.ClampRepeatUV(_lightmapUVBounds.z); _lightmapUVBounds.w = MathLib.ClampRepeatUV(_lightmapUVBounds.w); // 防止包围盒上下左右颠倒,重新翻转一下 if (_lightmapUVBounds.x > _lightmapUVBounds.z) { var temp = _lightmapUVBounds.x; _lightmapUVBounds.x = _lightmapUVBounds.z; _lightmapUVBounds.z = temp; } if (_lightmapUVBounds.y > _lightmapUVBounds.w) { var temp = _lightmapUVBounds.y; _lightmapUVBounds.y = _lightmapUVBounds.w; _lightmapUVBounds.w = temp; } // 纹理坐标到像素坐标转换,此处代码查看了网上流传出来unity4.3里面源码 var i_minx = Mathf.FloorToInt(_lightmapUVBounds.x * ( float )lightmapData.width); var i_miny = Mathf.FloorToInt(_lightmapUVBounds.y * ( float )lightmapData.height); var i_width = Mathf.CeilToInt((_lightmapUVBounds.z - _lightmapUVBounds.x) * ( float )lightmapData.width); var i_height = Mathf.CeilToInt((_lightmapUVBounds.w - _lightmapUVBounds.y) * ( float )lightmapData.height); if (i_width <= 0 || i_height <= 0) { UDebug.LogError("Invalid LightmapUV: {0}", UnityUtils.GetHierarchyPath(r.transform)); continue; } var i_maxx = i_minx + i_width; var i_maxy = i_miny + i_height; i_minx = Mathf.Clamp(i_minx, 0, lightmapData.width); i_maxx = Mathf.Clamp(i_maxx, 0, lightmapData.width); i_miny = Mathf.Clamp(i_miny, 0, lightmapData.height); i_maxy = Mathf.Clamp(i_maxy, 0, lightmapData.height); // 传入自己的TexturePacker之前,把border大小加上去 var rt = default(NativeAPI.stbrp_rect); rt.w = ( ushort )(i_width + Padding); rt.h = ( ushort )(i_height + Padding); var lightmapRect = new LightmapRect(); lightmapRect.renderer = r; lightmapRect.lightmapUVBounds = lightmapUVBounds; lightmapRect.meshUVBounds = meshUVBounds; lightmapRect.lightmapPixelBounds = new Vector4(i_minx, i_miny, i_maxx, i_maxy); rt.id = lightmapRectInfoList.Count; inputRects.Add(rt); lightmapRectInfoList.Add(lightmapRect); } } tp.PackRects(inputRects.ToArray()); tp.ForEach( (page, atlasSize, rt) => { var lightmapRect = lightmapRectInfoList[rt.id]; var renderer = lightmapRect.renderer; var lightmapData = litmapData[renderer.lightmapIndex]; AtlasPixelsPage atlas_pixels; if (!all_atlas_pixels.TryGetValue(page, out atlas_pixels)) { atlas_pixels = new AtlasPixelsPage { pixels = new Vector4[atlasSize * atlasSize], size = atlasSize }; all_atlas_pixels.Add(page, atlas_pixels); } int i_minx = ( int )lightmapRect.lightmapPixelBounds.x; int i_miny = ( int )lightmapRect.lightmapPixelBounds.y; int i_maxx = ( int )lightmapRect.lightmapPixelBounds.z; int i_maxy = ( int )lightmapRect.lightmapPixelBounds.w; // lightmap像素块copy fixed(Vector4 * _atlas_pixels = atlas_pixels.pixels) { for (int y = i_miny; y < i_maxy; y++) { int dy = y - i_miny + rt.y; // 纹理坐标需要翻转一下,像素是翻转了的 int _dy = atlasSize - 1 - dy; int _sy = lightmapData.height - 1 - y; int _dy_stride = _dy * atlasSize; int _sy_stride = _sy * lightmapData.width; for (int x = i_minx; x < i_maxx; x++) { int dx = x - i_minx + rt.x; _atlas_pixels[_dy_stride + dx] = lightmapData.pixels[_sy_stride + x]; } } } // 计算在新的lightmap纹理下的纹理坐标包围盒,由于pack之前我们认为加了一个Padding,所以这里计算要减去 // 这里估计会有各种影藏的因素导致最终效果和原始效果出现偏差,黑边,偏移,防缩等 // 因为在复制像素的时候,包围盒因取整引入了误差,这里为了防止出现黑边,我人为的往里缩小了0.5 Vector4 uvBounds; uvBounds.x = ( float )(rt.x + 0.5f) / ( float )atlasSize; uvBounds.y = ( float )(rt.y + 0.5f) / ( float )atlasSize; uvBounds.z = ( float )((rt.x + rt.w) - Padding - 0.5f) / ( float )atlasSize; uvBounds.w = ( float )((rt.y + rt.h) - Padding - 0.5f) / ( float )atlasSize; if (Mathf.Approximately(lightmapRect.meshUVBounds.z, lightmapRect.meshUVBounds.x) || Mathf.Approximately(lightmapRect.meshUVBounds.w, lightmapRect.meshUVBounds.y)) { // 无效 renderer.lightmapIndex = -1; renderer.lightmapScaleOffset = new Vector4(1, 1, 0, 0); UDebug.LogError("Invalid LightmapUV's bounds: {0}", UnityUtils.GetHierarchyPath(lightmapRect.renderer.transform)); } else { // 计算新的ScaleOffset,映射到新的lightmap纹理 renderer.lightmapIndex = page; renderer.lightmapScaleOffset = MathLib.CalculateUVScaleOffset(lightmapRect.meshUVBounds, uvBounds); } } ); if (all_atlas_pixels.Count > 0) { var lightmapOutputPath = workingPath; var lightmaps = new LightmapData[all_atlas_pixels.Count]; var lightmapPathList = new List <KeyValuePair <String, int> >(all_atlas_pixels.Count); int pageIndex = 0; foreach (var pagePixels in all_atlas_pixels) { fixed(Vector4 *_atlas_pixels = pagePixels.Value.pixels) { EditorUtility.DisplayProgressBar(Title, "Saving lightmap atlas...", ( float )pageIndex / ( float )all_atlas_pixels.Count); pageIndex++; int atlasSize = pagePixels.Value.size; var path = String.Format("{0}/lightmap_{1}.exr", lightmapOutputPath, pagePixels.Key); if (File.Exists(path)) { AssetDatabase.DeleteAsset(path); } NativeAPI.SaveEXR(( IntPtr )_atlas_pixels, atlasSize, atlasSize, 4, 0, path); lightmapPathList.Add(new KeyValuePair <String, int>(path, pagePixels.Key)); } } try { AssetDatabase.Refresh(); pageIndex = 0; for (int j = 0; j < lightmapPathList.Count; j++) { var path = lightmapPathList[j].Key; var page = lightmapPathList[j].Value; EditorUtility.DisplayProgressBar(Title, "Apply lightmap atlas...", ( float )pageIndex / ( float )all_atlas_pixels.Count); pageIndex++; var ti = AssetImporter.GetAtPath(path) as TextureImporter; if (ti != null) { if (ti.textureType != TextureImporterType.Lightmap || ti.isReadable || ti.wrapMode != TextureWrapMode.Clamp) { ti.textureType = TextureImporterType.Lightmap; ti.isReadable = false; ti.wrapMode = TextureWrapMode.Clamp; AssetDatabase.ImportAsset(ti.assetPath); } var tex = AssetDatabase.LoadAssetAtPath(path, typeof(Texture2D)) as Texture2D; if (tex != null) { lightmaps[page] = new LightmapData(); lightmaps[page].lightmapLight = tex; } } } } finally { LightmapSettings.lightmaps = lightmaps; } } } } }