summaryrefslogtreecommitdiff
path: root/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas.cs
blob: f27f9c32b790c89d9178fb7abbb3e9821d773843 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public class xatlas
{
    //#define UV_HINT

    public static List<Vector2> newUVBuffer;
    public static List<int> newXrefBuffer;

    [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)]
    public static extern System.IntPtr xatlasCreateAtlas();

    [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)]
    public static extern int xatlasAddMesh(System.IntPtr atlas, int vertexCount, System.IntPtr positions, System.IntPtr normals, System.IntPtr uv, int indexCount, int[] indices32);

    [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)]
    public static extern int xatlasAddUVMesh(System.IntPtr atlas, int vertexCount, System.IntPtr uv, int indexCount, int[] indices32, bool allowRotate);

    [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)]
    public static extern void xatlasParametrize(System.IntPtr atlas);

    [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)]
    public static extern void xatlasPack(System.IntPtr atlas, int attempts, float texelsPerUnit, int resolution, int maxChartSize, int padding, bool bruteForce, bool blockAlign);//, bool allowRotate);

    [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)]
    public static extern void xatlasNormalize(System.IntPtr atlas, int[] atlasSizes);

    [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)]
    public static extern int xatlasGetAtlasCount(System.IntPtr atlas);

    [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)]
    public static extern int xatlasGetAtlasIndex(System.IntPtr atlas, int meshIndex, int chartIndex);

    [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)]
    public static extern int xatlasGetVertexCount(System.IntPtr atlas, int meshIndex);

    [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)]
    public static extern int xatlasGetIndexCount(System.IntPtr atlas, int meshIndex);

    [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)]
    public static extern void xatlasGetData(System.IntPtr atlas, int meshIndex, System.IntPtr outUV, System.IntPtr outRef, System.IntPtr outIndices);

    [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)]
    public static extern int xatlasClear(System.IntPtr atlas);

    static T[] FillAtrribute<T>(List<int> xrefArray, T[] origArray)
    {
        if (origArray == null || origArray.Length == 0) return origArray;

        var arr = new T[xrefArray.Count];
        for(int i=0; i<xrefArray.Count; i++)
        {
            int xref = xrefArray[i];
            arr[i] = origArray[xref];
        }
        return arr;

        /*
        var finalAttr = new T[vertCount + xrefCount];
        for(int i=0; i<vertCount; i++) finalAttr[i] = origArray[i];
        for(int i=0; i<xrefCount; i++) finalAttr[i + vertCount] = origArray[ xrefArray[i] ];
        return finalAttr;
        */
    }

    public static double GetTime()
    {
        return (System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond) / 1000.0;
    }

    public static void Unwrap(Mesh m, UnwrapParam uparams)
    {
        //EditorUtility.DisplayDialog("Bakery", "xatlas start", "OK");
        int padding = (int)(uparams.packMargin*1024);
        //Debug.Log("xatlas! " + padding);

        newUVBuffer = null;
        newXrefBuffer = null;

        var t = GetTime();

        var positions = m.vertices;
        var normals = m.normals;
        var existingUV = m.uv;
        var handlePos = GCHandle.Alloc(positions, GCHandleType.Pinned);
        var handleNorm = GCHandle.Alloc(normals, GCHandleType.Pinned);
        var handleUV = GCHandle.Alloc(existingUV, GCHandleType.Pinned);
        int err = 0;

        var atlas = xatlasCreateAtlas();

        //EditorUtility.DisplayDialog("Bakery", "xatlas created", "OK");

        try
        {
            var pointerPos = handlePos.AddrOfPinnedObject();
            var pointerNorm = handleNorm.AddrOfPinnedObject();

#if UV_HINT
            var pointerUV = handleUV.AddrOfPinnedObject();
#else
            var pointerUV = (System.IntPtr)0;
#endif

            for(int i=0; i<m.subMeshCount; i++)
            {
                err = xatlasAddMesh(atlas, m.vertexCount, pointerPos, pointerNorm, pointerUV, (int)m.GetIndexCount(i), m.GetIndices(i));
                if (err == 1)
                {
                    Debug.LogError("xatlas::AddMesh: indices are out of range");
                }
                else if (err == 2)
                {
                    Debug.LogError("xatlas::AddMesh: index count is incorrect");
                }
                else if (err != 0)
                {
                    Debug.LogError("xatlas::AddMesh: unknown error");
                }
                if (err != 0) break;
            }
            //EditorUtility.DisplayDialog("Bakery", "xatlas added", "OK");
            if (err == 0)
            {
                xatlasParametrize(atlas);
                //EditorUtility.DisplayDialog("Bakery", "xatlas param done", "OK");

                xatlasPack(atlas, 4096, 0, 0, 1024, padding, false, true);//, true);
                //EditorUtility.DisplayDialog("Bakery", "xatlas pack done", "OK");
            }
        }
        finally
        {
            if (handlePos.IsAllocated) handlePos.Free();
            if (handleNorm.IsAllocated) handleNorm.Free();
            if (handleUV.IsAllocated) handleUV.Free();
        }
        if (err != 0)
        {
            //EditorUtility.DisplayDialog("Bakery", "xatlas cancel", "OK");
            xatlasClear(atlas);
            return;
        }

        Debug.Log("xatlas time: " + (GetTime() - t));
        t = GetTime();

        //EditorUtility.DisplayDialog("Bakery", "xatlas unwrap start", "OK");
        //var uv2 = new Vector2[m.vertexCount];
        //int vertexOffset = m.vertexCount;
        //var newUV2 = new List<Vector2>();
        //var newXref = new List<int>();
        var indexBuffers = new List<int[]>();

        newUVBuffer = new List<Vector2>();
        newXrefBuffer = new List<int>();
        while(newUVBuffer.Count < m.vertexCount)
        {
            newUVBuffer.Add(new Vector2(-100, -100));
            newXrefBuffer.Add(0);
        }

        xatlasNormalize(atlas, null);

        // Collect UVs/xrefs/indices
        for(int i=0; i<m.subMeshCount; i++)
        {
            // Get data from xatlas
            int newVertCount = xatlasGetVertexCount(atlas, i);
            int indexCount = xatlasGetIndexCount(atlas, i); // should be unchanged

            var uvBuffer = new Vector2[newVertCount];
            var xrefBuffer = new int[newVertCount];
            var indexBuffer = new int[indexCount];

            var handleT = GCHandle.Alloc(uvBuffer, GCHandleType.Pinned);
            var handleX = GCHandle.Alloc(xrefBuffer, GCHandleType.Pinned);
            var handleI = GCHandle.Alloc(indexBuffer, GCHandleType.Pinned);

            try
            {
                var pointerT = handleT.AddrOfPinnedObject();
                var pointerX = handleX.AddrOfPinnedObject();
                var pointerI = handleI.AddrOfPinnedObject();

                xatlasGetData(atlas, i, pointerT, pointerX, pointerI);
            }
            finally
            {
                if (handleT.IsAllocated) handleT.Free();
                if (handleX.IsAllocated) handleX.Free();
                if (handleI.IsAllocated) handleI.Free();
            }

            // Generate new UV buffer and xatlas->final index mappings
            var xatlasIndexToNewIndex = new int[newVertCount];
            for(int j=0; j<newVertCount; j++)
            {
                int xref = xrefBuffer[j];
                Vector2 uv = uvBuffer[j];

                if (newUVBuffer[xref].x < 0)
                {
                    // first xref encounter gets UV
                    xatlasIndexToNewIndex[j] = xref;
                    newUVBuffer[xref] = uv;
                    newXrefBuffer[xref] = xref;
                }
                else if (newUVBuffer[xref].x == uv.x && newUVBuffer[xref].y == uv.y)
                {
                    // vertex already added
                    xatlasIndexToNewIndex[j] = xref;
                }
                else
                {
                    // duplicate vertex
                    xatlasIndexToNewIndex[j] = newUVBuffer.Count;
                    newUVBuffer.Add(uv);
                    newXrefBuffer.Add(xref);
                }
            }

            // Generate final index buffer
            for(int j=0; j<indexCount; j++)
            {
                indexBuffer[j] = xatlasIndexToNewIndex[ indexBuffer[j] ];
            }
            indexBuffers.Add(indexBuffer);
        }

        //EditorUtility.DisplayDialog("Bakery", "xatlas unwrap end", "OK");

        int vertCount = m.vertexCount;

        bool origIs16bit = true;
#if UNITY_2017_3_OR_NEWER
        origIs16bit = m.indexFormat == UnityEngine.Rendering.IndexFormat.UInt16;
#endif
        bool is32bit = newUVBuffer.Count >= 65000;//0xFFFF;
        if (is32bit && origIs16bit)
        {
            Debug.LogError("Unwrap failed: original mesh (" + m.name + ") has 16 bit indices, but unwrapped requires 32 bit.");
            return;
        }

        // Duplicate attributes
        //if (newXrefBuffer.Count > m.vertexCount) // commented because can be also swapped around
        {
            m.vertices = FillAtrribute<Vector3>(newXrefBuffer, positions);
            m.normals =  FillAtrribute<Vector3>(newXrefBuffer, normals);
            m.boneWeights =  FillAtrribute<BoneWeight>(newXrefBuffer, m.boneWeights);
            m.colors32 =  FillAtrribute<Color32>(newXrefBuffer, m.colors32);
            m.tangents =  FillAtrribute<Vector4>(newXrefBuffer, m.tangents);
            m.uv =  FillAtrribute<Vector2>(newXrefBuffer, m.uv);
            m.uv3 =  FillAtrribute<Vector2>(newXrefBuffer, m.uv3);
            m.uv4 =  FillAtrribute<Vector2>(newXrefBuffer, m.uv4);
#if UNITY_2018_2_OR_NEWER
            m.uv5 =  FillAtrribute<Vector2>(newXrefBuffer, m.uv5);
            m.uv6 =  FillAtrribute<Vector2>(newXrefBuffer, m.uv6);
            m.uv7 =  FillAtrribute<Vector2>(newXrefBuffer, m.uv7);
            m.uv8 =  FillAtrribute<Vector2>(newXrefBuffer, m.uv8);
#endif
        }

        m.uv2 = newUVBuffer.ToArray();

/*

        // Set new UV2
        var finalUV2 = new Vector2[vertCount + newUV2.Count];
        for(int i=0; i<vertCount; i++) finalUV2[i] = uv2[i];
        for(int i=0; i<newUV2.Count; i++) finalUV2[i + vertCount] = newUV2[i];
        m.uv2 = finalUV2;
*/
        // Set indices
        for(int i=0; i<m.subMeshCount; i++)
        {
            m.SetTriangles(indexBuffers[i], i);
        }

        //Debug.Log("post-xatlas mesh building time: " + GetTime() - t));

        xatlasClear(atlas);
    }
}