static void WriteTexture(string fileName, EXRData data)
    {
        string err = "";
        IntPtr ptr = Marshal.AllocHGlobal(sizeof(float) * data.pixels.Length);

        Marshal.Copy(data.pixels, 0, ptr, data.pixels.Length);
        if (TinyExr.TinyExr.SaveEXR(ptr, data.width, data.height, 4, 0, fileName, ref err) != 0)
        {
            throw new Exception(err);
        }
        Marshal.FreeHGlobal(ptr);
    }
    string CombineExr(LightmapHolder holder, LightmapData[] data)
    {
        var textures = data.Select(d => ReadTexture(AssetDatabase.GetAssetPath(d.lightmapColor), d.lightmapColor.width, d.lightmapColor.height)).ToArray();

        if (textures.Length == 0)
        {
            return("");
        }
        EXRData combine = new EXRData()
        {
            width = MAX_SIZE * 2, height = MAX_SIZE * 2, pixels = new float[MAX_SIZE * MAX_SIZE * 4 * 4]
        };

        for (int i = 0; i < combine.pixels.Length; ++i)
        {
            combine.pixels[i] = 1;
        }
        holder.scales = new float[textures.Length];
        for (int i = 0; i < textures.Length; ++i)
        {
            var texture = textures[i];
            if (texture == null)
            {
                continue;
            }
            var raw     = textures[i].pixels;
            var offsety = combine.height / 2 - texture.height;
            holder.scales[i] = (float)texture.height / (float)combine.height;
            for (int x = 0; x < texture.width; ++x)
            {
                for (int y = 0; y < texture.height; ++y)
                {
                    var index = (x + y * texture.width) * 4;
                    var destX = (i % 2) * (combine.width / 2) + x;
                    var destY = (1 - i / 2) * (combine.height / 2) + y + offsety;
                    var dest  = (destX + destY * combine.width) * 4;
                    combine.pixels[dest]     = raw[index];
                    combine.pixels[dest + 1] = raw[index + 1];
                    combine.pixels[dest + 2] = raw[index + 2];
                    combine.pixels[dest + 3] = raw[index + 3];
                }
            }
        }

        var fileName = AssetDatabase.GetAssetPath(data[0].lightmapColor);

        fileName = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName)) + "_combined" + Path.GetExtension(fileName);
        WriteTexture(fileName, combine);
        return(fileName);
    }
    static EXRData ReadTexture(string fileName, int width, int height)
    {
        EXRData ret = new EXRData();

        ret.pixels = new float[width * height * 4];
        string err = "";
        IntPtr ptr = IntPtr.Zero;

        if (TinyExr.TinyExr.LoadEXR(ref ptr, ref ret.width, ref ret.height, fileName, ref err) != 0)
        {
            throw new Exception(err);
        }
        Marshal.Copy(ptr, ret.pixels, 0, ret.pixels.Length);
        Marshal.FreeHGlobal(ptr);
        return(ret);
    }