Example #1
0
    /// <summary>
    /// 为Renderer计算新的lightmapScaleOffset
    /// </summary>
    /// <param name="meshUVBounds">模型资源的原始UV包围盒</param>
    /// <param name="uvBounds">定向到Lightmap上的一块用于指定Renderer的Lightmap纹理区间</param>
    /// <returns></returns>
    static Vector4d CalculateUVScaleOffset(Vector4d meshUVBounds, Vector4d uvBounds)
    {
        var scaleOffset = new Vector4d();

        scaleOffset.x = (uvBounds.z - uvBounds.x) / (meshUVBounds.z - meshUVBounds.x);
        scaleOffset.y = (uvBounds.w - uvBounds.y) / (meshUVBounds.w - meshUVBounds.y);
        scaleOffset.z = uvBounds.z - meshUVBounds.z * scaleOffset.x;
        scaleOffset.w = uvBounds.w - meshUVBounds.w * scaleOffset.y;
        return(scaleOffset);
    }
Example #2
0
    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;
                        Real fMinX            = lightmapUVBounds.x * lightmapData.width;
                        Real fMaxX            = lightmapUVBounds.z * lightmapData.width;
                        Real fMinY            = lightmapUVBounds.y * lightmapData.height;
                        Real fMaxY            = lightmapUVBounds.w * lightmapData.height;
#if LIGHTMAPREPACKER_PRECISION_HIGH
                        int pixMinX = ( int )Math.Max(Math.Floor(lightmapUVBounds.x * lightmapData.width), 0);
                        int pixMaxX = ( int )Math.Min(Math.Ceiling(lightmapUVBounds.z * lightmapData.width), lightmapData.width);
                        int pixMinY = ( int )Math.Max(Math.Floor(lightmapUVBounds.y * lightmapData.height), 0);
                        int pixMaxY = ( int )Math.Min(Math.Ceiling(lightmapUVBounds.w * lightmapData.height), lightmapData.height);
#else
                        int pixMinX = ( int )Math.Max(Mathf.Floor(lightmapUVBounds.x * lightmapData.width), 0);
                        int pixMaxX = ( int )Math.Min(Mathf.Ceil(lightmapUVBounds.z * lightmapData.width), lightmapData.width);
                        int pixMinY = ( int )Math.Max(Mathf.Floor(lightmapUVBounds.y * lightmapData.height), 0);
                        int pixMaxY = ( int )Math.Min(Mathf.Ceil(lightmapUVBounds.w * lightmapData.height), lightmapData.height);
#endif
                        if (pixMaxX <= pixMinX || pixMaxY <= pixMinY)
                        {
                            UDebug.LogError("Invalid LightmapUV: {0}", UnityUtils.GetHierarchyPath(r.transform));
                            continue;
                        }
                        // 传入自己的TexturePacker之前,把border大小加上去
                        var rt = default(NativeAPI.stbrp_rect);
                        rt.w = ( ushort )((pixMaxX - pixMinX) + Padding);
                        rt.h = ( ushort )((pixMaxY - pixMinY) + Padding);

                        var lightmapRect = new LightmapRect();
                        lightmapRect.renderer             = r;
                        lightmapRect.lightmapUVBounds     = lightmapUVBounds;
                        lightmapRect.meshUVBounds         = meshUVBounds;
                        lightmapRect.lightmapPixelBounds  = new Vector4i(pixMinX, pixMinY, pixMaxX, pixMaxY);
                        lightmapRect.lightmapPixelFBounds = new Vector4d(fMinX, fMinY, fMaxX, fMaxY);
                        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);
                    }
                    // 原始像素包围盒,不包含Border
                    var i_minx = ( int )lightmapRect.lightmapPixelBounds.x;
                    var i_miny = ( int )lightmapRect.lightmapPixelBounds.y;
                    var i_maxx = ( int )lightmapRect.lightmapPixelBounds.z;
                    var i_maxy = ( int )lightmapRect.lightmapPixelBounds.w;

#if FIXING_LIGHTMAP_UVBORDER
                    int rtOffset = Padding / 2;
                    if (Padding > 0)
                    {
                        // 扩大边框,多拷贝一些边缘像素消除黑边
                        i_minx -= Padding / 2;
                        i_miny -= Padding / 2;
                        i_maxx += Padding - Padding / 2;
                        i_maxy += Padding - Padding / 2;
                    }
#else
                    int rtOffset = 0;
#endif
                    // 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 - rtOffset);
                            if (dy < 0 || dy >= atlasSize)
                            {
                                continue;
                            }
                            // 纹理坐标需要翻转一下,像素是翻转了的
                            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 - rtOffset);
                                if (dx < 0 || dx >= atlasSize)
                                {
                                    continue;
                                }
                                _atlas_pixels[_dy_stride + dx] = lightmapData.GetPixelClamped(x, _sy);
                            }
                        }
                    }

                    var uvBounds = new Vector4d();

                    Real errorX = 0;
                    Real errorY = 0;
                    Real errorZ = 0;
                    Real errorW = 0;

#if FIXING_LIGHTMAP_UVERROR
                    errorX = lightmapRect.lightmapPixelFBounds.x - lightmapRect.lightmapPixelBounds.x;
                    errorY = lightmapRect.lightmapPixelFBounds.y - lightmapRect.lightmapPixelBounds.y;
                    errorZ = lightmapRect.lightmapPixelFBounds.z - lightmapRect.lightmapPixelBounds.z;
                    errorW = lightmapRect.lightmapPixelFBounds.w - lightmapRect.lightmapPixelBounds.w;
#endif
                    uvBounds.x = ((rt.x + errorX) / ( Real )atlasSize);
                    uvBounds.y = ((rt.y + errorY) / ( Real )atlasSize);
                    // 计算在新的lightmap纹理下的纹理坐标包围盒,由于pack之前我们人为加了一个Padding,所以这里计算要减去
                    uvBounds.z = (((rt.x + rt.w + errorZ) - Padding) / ( Real )atlasSize);
                    uvBounds.w = (((rt.y + rt.h + errorW) - Padding) / ( Real )atlasSize);

                    if (Mathf.Approximately(( float )lightmapRect.meshUVBounds.z, ( float )lightmapRect.meshUVBounds.x) ||
                        Mathf.Approximately(( float )lightmapRect.meshUVBounds.w, ( float )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;
                        var sf = CalculateUVScaleOffset(lightmapRect.meshUVBounds, uvBounds);
                        renderer.lightmapScaleOffset = new Vector4(( float )sf.x, ( float )sf.y, ( float )sf.z, ( float )sf.w);
                    }
                }
                    );
                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].lightmapColor = tex;
                                }
                            }
                        }
                    } finally {
                        LightmapSettings.lightmaps = lightmaps;
                    }
                }
            }
        }
    }