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 /** 41 Adds a new vertex 42 */ 43 void add(vec2 vertex, vec2 uv) { 44 vertices ~= vertex; 45 uvs ~= uv; 46 } 47 48 /** 49 Clear connections/indices 50 */ 51 void clearConnections() { 52 indices.length = 0; 53 } 54 55 /** 56 Connects 2 vertices together 57 */ 58 void connect(ushort first, ushort second) { 59 indices ~= [first, second]; 60 } 61 62 /** 63 Find the index of a vertex 64 */ 65 int find(vec2 vert) { 66 foreach(idx, v; vertices) { 67 if (v == vert) return cast(int)idx; 68 } 69 return -1; 70 } 71 72 /** 73 Whether the mesh data is ready to be used 74 */ 75 bool isReady() { 76 return indices.length != 0 && indices.length % 3 == 0; 77 } 78 79 /** 80 Whether the mesh data is ready to be triangulated 81 */ 82 bool canTriangulate() { 83 return indices.length != 0 && indices.length % 3 == 0; 84 } 85 86 /** 87 Fixes the winding order of a mesh. 88 */ 89 void fixWinding() { 90 if (!isReady) return; 91 92 foreach(j; 0..indices.length/3) { 93 size_t i = j*3; 94 95 vec2 vertA = vertices[indices[i+0]]; 96 vec2 vertB = vertices[indices[i+1]]; 97 vec2 vertC = vertices[indices[i+2]]; 98 bool cw = cross(vec3(vertB-vertA, 0), vec3(vertC-vertA, 0)).z < 0; 99 100 // Swap winding 101 if (cw) { 102 ushort swap = indices[i+1]; 103 indices[i+1] = indices[i+2]; 104 indices[i+2] = swap; 105 } 106 } 107 } 108 109 /** 110 Gets connections at a certain point 111 */ 112 int connectionsAtPoint(vec2 point) { 113 int p = find(point); 114 if (p == -1) return 0; 115 return connectionsAtPoint(cast(ushort)p); 116 } 117 118 /** 119 Gets connections at a certain point 120 */ 121 int connectionsAtPoint(ushort point) { 122 int found = 0; 123 foreach(index; indices) { 124 if (index == point) found++; 125 } 126 return found; 127 } 128 129 MeshData copy() { 130 MeshData newData; 131 132 // Copy verts 133 newData.vertices.length = vertices.length; 134 newData.vertices[] = vertices[]; 135 136 // Copy UVs 137 newData.uvs.length = uvs.length; 138 newData.uvs[] = uvs[]; 139 140 // Copy UVs 141 newData.indices.length = indices.length; 142 newData.indices[] = indices[]; 143 144 newData.origin = vec2(origin.x, origin.y); 145 146 return newData; 147 } 148 149 void serialize(S)(ref S serializer) { 150 auto state = serializer.objectBegin(); 151 serializer.putKey("verts"); 152 auto arr = serializer.arrayBegin(); 153 foreach(vertex; vertices) { 154 serializer.elemBegin; 155 serializer.serializeValue(vertex.x); 156 serializer.elemBegin; 157 serializer.serializeValue(vertex.y); 158 } 159 serializer.arrayEnd(arr); 160 161 if (uvs.length > 0) { 162 serializer.putKey("uvs"); 163 arr = serializer.arrayBegin(); 164 foreach(uv; uvs) { 165 serializer.elemBegin; 166 serializer.serializeValue(uv.x); 167 serializer.elemBegin; 168 serializer.serializeValue(uv.y); 169 } 170 serializer.arrayEnd(arr); 171 } 172 173 serializer.putKey("indices"); 174 serializer.serializeValue(indices); 175 176 serializer.putKey("origin"); 177 origin.serialize(serializer); 178 serializer.objectEnd(state); 179 } 180 181 SerdeException deserializeFromFghj(Fghj data) { 182 import std.stdio : writeln; 183 import std.algorithm.searching: count; 184 if (data.isEmpty) return null; 185 186 auto elements = data["verts"].byElement; 187 while(!elements.empty) { 188 float x; 189 float y; 190 elements.front.deserializeValue(x); 191 elements.popFront; 192 elements.front.deserializeValue(y); 193 elements.popFront; 194 vertices ~= vec2(x, y); 195 } 196 197 if (!data["uvs"].isEmpty) { 198 elements = data["uvs"].byElement; 199 while(!elements.empty) { 200 float x; 201 float y; 202 elements.front.deserializeValue(x); 203 elements.popFront; 204 elements.front.deserializeValue(y); 205 elements.popFront; 206 uvs ~= vec2(x, y); 207 } 208 } 209 210 if (!data["origin"].isEmpty) { 211 origin.deserialize(data["origin"]); 212 } 213 214 foreach(indiceData; data["indices"].byElement) { 215 ushort indice; 216 indiceData.deserializeValue(indice); 217 218 indices ~= indice; 219 } 220 return null; 221 } 222 223 224 /** 225 Generates a quad based mesh which is cut `cuts` amount of times 226 227 vec2i size - size of the mesh 228 uvBounds - x, y UV coordinates + width/height in UV coordinate space 229 cuts - how many time to cut the mesh on the X and Y axis 230 231 Example: 232 Size of Texture Uses all of UV width > height 233 MeshData.createQuadMesh(vec2i(texture.width, texture.height), vec4(0, 0, 1, 1), vec2i(32, 16)) 234 */ 235 static MeshData createQuadMesh(vec2i size, vec4 uvBounds, vec2i cuts = vec2i(6, 6), vec2i origin = vec2i(0)) { 236 237 // Splits may not be below 2. 238 if (cuts.x < 2) cuts.x = 2; 239 if (cuts.y < 2) cuts.y = 2; 240 241 MeshData data; 242 ushort[int[2]] m; 243 int sw = size.x/cuts.x; 244 int sh = size.y/cuts.y; 245 float uvx = uvBounds.w/cast(float)cuts.x; 246 float uvy = uvBounds.z/cast(float)cuts.y; 247 248 // Generate vertices and UVs 249 foreach(y; 0..cuts.y+1) { 250 foreach(x; 0..cuts.x+1) { 251 data.vertices ~= vec2( 252 (x*sw)-origin.x, 253 (y*sh)-origin.y 254 ); 255 data.uvs ~= vec2( 256 uvBounds.x+cast(float)x*uvx, 257 uvBounds.y+cast(float)y*uvy 258 ); 259 m[[x, y]] = cast(ushort)(data.vertices.length-1); 260 } 261 } 262 263 // Generate indices 264 vec2i center = vec2i(cuts.x/2, cuts.y/2); 265 foreach(y; 0..cuts.y) { 266 foreach(x; 0..cuts.x) { 267 268 // Indices 269 int[2] indice0 = [x, y]; 270 int[2] indice1 = [x, y+1]; 271 int[2] indice2 = [x+1, y]; 272 int[2] indice3 = [x+1, y+1]; 273 274 // We want the verticies to generate in an X pattern so that we won't have too many distortion problems 275 if ((x < center.x && y < center.y) || (x >= center.x && y >= center.y)) { 276 data.indices ~= [ 277 m[indice0], 278 m[indice2], 279 m[indice3], 280 m[indice0], 281 m[indice3], 282 m[indice1], 283 ]; 284 } else { 285 data.indices ~= [ 286 m[indice0], 287 m[indice1], 288 m[indice2], 289 m[indice1], 290 m[indice2], 291 m[indice3], 292 ]; 293 } 294 } 295 } 296 297 298 return data; 299 } 300 301 void dbg() { 302 import std.stdio : writefln; 303 writefln("%s %s %s", vertices.length, uvs.length, indices.length); 304 } 305 }