1 /*
2     Inochi2D Part Mesh Data
3     
4     Copyright © 2020, Inochi2D Project
5     Distributed under the 2-Clause BSD License, see LICENSE file.
6     
7     Authors: Luna Nielsen
8 */
9 module inochi2d.core.meshdata;
10 import inochi2d.math;
11 import inochi2d.core.texture;
12 import inochi2d.fmt.serialize;
13 
14 /**
15     Mesh data
16 */
17 struct MeshData {
18     /**
19         Vertices in the mesh
20     */
21     vec2[] vertices;
22 
23     /**
24         Base uvs
25     */
26     @Optional
27     vec2[] uvs;
28 
29     /**
30         Indices in the mesh
31     */
32     ushort[] indices;
33 
34     /**
35         Origin of the mesh
36     */
37     @Optional
38     vec2 origin = vec2(0, 0);
39 
40     @Optional
41     float[][] gridAxes;
42 
43     /**
44         Adds a new vertex
45     */
46     void add(vec2 vertex, vec2 uv) {
47         vertices ~= vertex;
48         uvs ~= uv;
49     }
50 
51     /**
52         Clear connections/indices
53     */
54     void clearConnections() {
55         indices.length = 0;
56     }
57 
58     /**
59         Connects 2 vertices together
60     */
61     void connect(ushort first, ushort second) {
62         indices ~= [first, second];
63     }
64 
65     /**
66         Find the index of a vertex
67     */
68     int find(vec2 vert) {
69         foreach(idx, v; vertices) {
70             if (v == vert) return cast(int)idx;
71         }
72         return -1;
73     }
74 
75     /**
76         Whether the mesh data is ready to be used
77     */
78     bool isReady() {
79         return indices.length != 0 && indices.length % 3 == 0;
80     }
81 
82     /**
83         Whether the mesh data is ready to be triangulated
84     */
85     bool canTriangulate() {
86         return indices.length != 0 && indices.length % 3 == 0;
87     }
88 
89     /**
90         Fixes the winding order of a mesh.
91     */
92     void fixWinding() {
93         if (!isReady) return;
94         
95         foreach(j; 0..indices.length/3) {
96             size_t i = j*3;
97 
98             vec2 vertA = vertices[indices[i+0]];
99             vec2 vertB = vertices[indices[i+1]];
100             vec2 vertC = vertices[indices[i+2]];
101             bool cw = cross(vec3(vertB-vertA, 0), vec3(vertC-vertA, 0)).z < 0;
102 
103             // Swap winding
104             if (cw) {
105                 ushort swap = indices[i+1];
106                 indices[i+1] = indices[i+2];
107                 indices[i+2] = swap;   
108             }
109         }
110     }
111 
112     /**
113         Gets connections at a certain point
114     */
115     int connectionsAtPoint(vec2 point) {
116         int p = find(point);
117         if (p == -1) return 0;
118         return connectionsAtPoint(cast(ushort)p);
119     }
120 
121     /**
122         Gets connections at a certain point
123     */
124     int connectionsAtPoint(ushort point) {
125         int found = 0;
126         foreach(index; indices) {
127             if (index == point) found++;
128         }
129         return found;
130     }
131 
132     MeshData copy() {
133         MeshData newData;
134 
135         // Copy verts
136         newData.vertices.length = vertices.length;
137         newData.vertices[] = vertices[];
138 
139         // Copy UVs
140         newData.uvs.length = uvs.length;
141         newData.uvs[] = uvs[];
142 
143         // Copy UVs
144         newData.indices.length = indices.length;
145         newData.indices[] = indices[];
146 
147         // Copy axes
148         newData.gridAxes = gridAxes[];
149 
150         newData.origin = vec2(origin.x, origin.y);
151 
152         return newData;
153     }
154 
155     void serialize(S)(ref S serializer) {
156         auto state = serializer.objectBegin();
157             serializer.putKey("verts");
158             auto arr = serializer.arrayBegin();
159                 foreach(vertex; vertices) {
160                     serializer.elemBegin;
161                     serializer.serializeValue(vertex.x);
162                     serializer.elemBegin;
163                     serializer.serializeValue(vertex.y);
164                 }
165             serializer.arrayEnd(arr);
166 
167             if (uvs.length > 0) {
168                 serializer.putKey("uvs");
169                 arr = serializer.arrayBegin();
170                     foreach(uv; uvs) {
171                         serializer.elemBegin;
172                         serializer.serializeValue(uv.x);
173                         serializer.elemBegin;
174                         serializer.serializeValue(uv.y);
175                     }
176                 serializer.arrayEnd(arr);
177             }
178 
179             serializer.putKey("indices");
180             serializer.serializeValue(indices);
181 
182             serializer.putKey("origin");
183             origin.serialize(serializer);
184             if (isGrid()) {
185                 serializer.putKey("grid_axes");
186                 serializer.serializeValue(gridAxes);
187             }
188         serializer.objectEnd(state);
189     }
190 
191     SerdeException deserializeFromFghj(Fghj data) {
192         import std.stdio : writeln;
193         import std.algorithm.searching: count;
194         if (data.isEmpty) return null;
195 
196         auto elements = data["verts"].byElement;
197         while(!elements.empty) {
198             float x;
199             float y;
200             elements.front.deserializeValue(x);
201             elements.popFront;
202             elements.front.deserializeValue(y);
203             elements.popFront;
204             vertices ~= vec2(x, y);
205         }
206 
207         if (!data["uvs"].isEmpty) {
208             elements = data["uvs"].byElement;
209             while(!elements.empty) {
210                 float x;
211                 float y;
212                 elements.front.deserializeValue(x);
213                 elements.popFront;
214                 elements.front.deserializeValue(y);
215                 elements.popFront;
216                 uvs ~= vec2(x, y);
217             }
218         }
219 
220         if (!data["origin"].isEmpty) {
221             origin.deserialize(data["origin"]);
222         }
223 
224         gridAxes.length = 0;
225         if (!data["grid_axes"].isEmpty) {
226             data["grid_axes"].deserializeValue(gridAxes);
227         }
228 
229         foreach(indiceData; data["indices"].byElement) {
230             ushort indice;
231             indiceData.deserializeValue(indice);
232             
233             indices ~= indice;
234         }
235         return null;
236     }
237 
238 
239     /**
240         Generates a quad based mesh which is cut `cuts` amount of times
241 
242         vec2i size - size of the mesh
243         uvBounds - x, y UV coordinates + width/height in UV coordinate space
244         cuts - how many time to cut the mesh on the X and Y axis
245 
246         Example:
247                                 Size of Texture                       Uses all of UV    width > height
248         MeshData.createQuadMesh(vec2i(texture.width, texture.height), vec4(0, 0, 1, 1), vec2i(32, 16))
249     */
250     static MeshData createQuadMesh(vec2i size, vec4 uvBounds, vec2i cuts = vec2i(6, 6), vec2i origin = vec2i(0)) {
251         
252         // Splits may not be below 2.
253         if (cuts.x < 2) cuts.x = 2;
254         if (cuts.y < 2) cuts.y = 2;
255 
256         MeshData data;
257         ushort[int[2]] m;
258         int sw = size.x/cuts.x;
259         int sh = size.y/cuts.y;
260         float uvx = uvBounds.w/cast(float)cuts.x;
261         float uvy = uvBounds.z/cast(float)cuts.y;
262 
263         // Generate vertices and UVs
264         foreach(y; 0..cuts.y+1) {
265             data.gridAxes[0] ~= y*sh - origin.y;
266             foreach(x; 0..cuts.x+1) {
267                 data.gridAxes[1] ~= x*sw - origin.x;
268                 data.vertices ~= vec2(
269                     (x*sw)-origin.x, 
270                     (y*sh)-origin.y
271                 );
272                 data.uvs ~= vec2(
273                     uvBounds.x+cast(float)x*uvx, 
274                     uvBounds.y+cast(float)y*uvy
275                 );
276                 m[[x, y]] = cast(ushort)(data.vertices.length-1); 
277             }	
278         }
279 
280         // Generate indices
281         vec2i center = vec2i(cuts.x/2, cuts.y/2);
282         foreach(y; 0..cuts.y) {
283             foreach(x; 0..cuts.x) {
284 
285                 // Indices
286                 int[2] indice0 = [x, y];
287                 int[2] indice1 = [x, y+1];
288                 int[2] indice2 = [x+1, y];
289                 int[2] indice3 = [x+1, y+1];
290 
291                 // We want the verticies to generate in an X pattern so that we won't have too many distortion problems
292                 if ((x < center.x && y < center.y) || (x >= center.x && y >= center.y)) {
293                     data.indices ~= [
294                         m[indice0],
295                         m[indice2],
296                         m[indice3],
297                         m[indice0],
298                         m[indice3],
299                         m[indice1],
300                     ];
301                 } else {
302                     data.indices ~= [
303                         m[indice0],
304                         m[indice1],
305                         m[indice2],
306                         m[indice1],
307                         m[indice2],
308                         m[indice3],
309                     ];
310                 }
311             }
312         }
313 
314         return data;
315     }
316 
317     bool isGrid() {
318         return gridAxes.length == 2 && gridAxes[0].length > 2 && gridAxes[1].length > 2;
319     }
320 
321     bool clearGridIsDirty() {
322         if (gridAxes.length < 2 || gridAxes[0].length == 0 || gridAxes[1].length == 0)
323             return false;
324 
325         bool clearGrid() {
326             gridAxes[0].length = 0;
327             gridAxes[1].length = 0;
328             return true;
329         }
330 
331         if (vertices.length != gridAxes[0].length * gridAxes[1].length) {
332             return clearGrid();
333         }
334 
335         int index = 0;
336         foreach (y; gridAxes[0]) {
337             foreach (x; gridAxes[1]) {
338                 vec2 vert = vec2(x, y);
339                 if (vert != vertices[index]) {
340                     return clearGrid();
341                 }
342                 index += 1;
343             }
344         }
345         return false;
346     }
347 
348     bool regenerateGrid() {
349         if (gridAxes[0].length < 2 || gridAxes[1].length < 2)
350             return false;
351 
352         vertices.length = 0;
353         uvs.length = 0;
354         indices.length = 0;
355 
356         ushort[int[2]] m;
357 
358         float minY = gridAxes[0][0], maxY = gridAxes[0][$-1];
359         float minX = gridAxes[1][0], maxX = gridAxes[1][$-1];
360         float width = maxY - minY;
361         float height = maxX - minX;
362         foreach (i, y; gridAxes[0]) {
363             foreach (j, x; gridAxes[1]) {
364                 vertices ~= vec2(x, y);
365                 uvs ~= vec2((x - minX) / width, (y - minY) / height);
366                 m[[cast(int)j, cast(int)i]] = cast(ushort)(vertices.length - 1);
367             }
368         }
369 
370         vec2 center = vec2(minX + width / 2, minY + height / 2);
371         foreach(i; 0..gridAxes[0].length - 1) {
372             auto yValue = gridAxes[0][i];
373             foreach(j; 0..gridAxes[1].length - 1) {
374 
375                 auto xValue = gridAxes[1][j];
376                 int x = cast(int)j, y = cast(int)i;
377 
378                 // Indices
379                 int[2] indice0 = [x  , y  ];
380                 int[2] indice1 = [x  , y+1];
381                 int[2] indice2 = [x+1, y  ];
382                 int[2] indice3 = [x+1, y+1];
383 
384                 // We want the verticies to generate in an X pattern so that we won't have too many distortion problems
385                 if ((xValue < center.x && yValue < center.y) || (xValue >= center.x && yValue >= center.y)) {
386                     indices ~= [
387                         m[indice0],
388                         m[indice2],
389                         m[indice3],
390                         m[indice0],
391                         m[indice3],
392                         m[indice1],
393                     ];
394                 } else {
395                     indices ~= [
396                         m[indice0],
397                         m[indice1],
398                         m[indice2],
399                         m[indice1],
400                         m[indice2],
401                         m[indice3],
402                     ];
403                 }
404             }
405         }
406         return true;
407     }
408 
409     void dbg() {
410         import std.stdio : writefln;
411         writefln("%s %s %s", vertices.length, uvs.length, indices.length);
412     }
413 }