1 /* 2 Copyright (c) 2017-2018 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 */ 27 28 module dagon.resource.obj; 29 30 import std.stdio; 31 import std.string; 32 import std.format; 33 34 import dlib.core.memory; 35 import dlib.core.stream; 36 import dlib.math.vector; 37 import dlib.geometry.triangle; 38 import dlib.filesystem.filesystem; 39 import dlib.filesystem.stdfs; 40 import dlib.container.array; 41 42 import dagon.core.libs; 43 import dagon.core.ownership; 44 import dagon.core.interfaces; 45 import dagon.resource.asset; 46 import dagon.graphics.mesh; 47 48 import std.regex; 49 50 struct ObjFace 51 { 52 uint[3] v; 53 uint[3] t; 54 uint[3] n; 55 } 56 57 class OBJAsset: Asset 58 { 59 Mesh mesh; 60 61 this(Owner o) 62 { 63 super(o); 64 mesh = New!Mesh(this); 65 } 66 67 ~this() 68 { 69 release(); 70 } 71 72 override bool loadThreadSafePart(string filename, InputStream istrm, ReadOnlyFileSystem fs, AssetManager mngr) 73 { 74 uint numVerts = 0; 75 uint numNormals = 0; 76 uint numTexcoords = 0; 77 uint numFaces = 0; 78 79 string fileStr = readText(istrm); 80 foreach(line; lineSplitter(fileStr)) 81 { 82 if (line.startsWith("v ")) 83 numVerts++; 84 else if (line.startsWith("vn ")) 85 numNormals++; 86 else if (line.startsWith("vt ")) 87 numTexcoords++; 88 else if (line.startsWith("f ")) 89 numFaces++; 90 } 91 92 Vector3f[] tmpVertices; 93 Vector3f[] tmpNormals; 94 Vector2f[] tmpTexcoords; 95 ObjFace[] tmpFaces; 96 97 bool needGenNormals = false; 98 99 if (!numVerts) 100 writeln("Warning: OBJ file \"", filename, "\" has no vertices"); 101 if (!numNormals) 102 { 103 writeln("Warning: OBJ file \"", filename, "\" has no normals (they will be generated)"); 104 numNormals = numVerts; 105 needGenNormals = true; 106 } 107 if (!numTexcoords) 108 { 109 writeln("Warning: OBJ file \"", filename, "\" has no texcoords"); 110 numTexcoords = numVerts; 111 } 112 113 if (numVerts) 114 tmpVertices = New!(Vector3f[])(numVerts); 115 if (numNormals) 116 tmpNormals = New!(Vector3f[])(numNormals); 117 if (numTexcoords) 118 tmpTexcoords = New!(Vector2f[])(numTexcoords); 119 if (numFaces) 120 tmpFaces = New!(ObjFace[])(numFaces); 121 122 tmpVertices[] = Vector3f(0, 0, 0); 123 tmpNormals[] = Vector3f(0, 0, 0); 124 tmpTexcoords[] = Vector2f(0, 0); 125 126 float x, y, z; 127 int v1, v2, v3, v4; 128 int t1, t2, t3, t4; 129 int n1, n2, n3, n4; 130 uint vi = 0; 131 uint ni = 0; 132 uint ti = 0; 133 uint fi = 0; 134 135 bool warnAboutQuads = false; 136 137 foreach(line; lineSplitter(fileStr)) 138 { 139 if (line.startsWith("v ")) 140 { 141 if (formattedRead(line, "v %s %s %s", &x, &y, &z)) 142 { 143 tmpVertices[vi] = Vector3f(x, y, z); 144 vi++; 145 } 146 } 147 else if (line.startsWith("vn")) 148 { 149 if (formattedRead(line, "vn %s %s %s", &x, &y, &z)) 150 { 151 tmpNormals[ni] = Vector3f(x, y, z); 152 ni++; 153 } 154 } 155 else if (line.startsWith("vt")) 156 { 157 if (formattedRead(line, "vt %s %s", &x, &y)) 158 { 159 tmpTexcoords[ti] = Vector2f(x, -y); 160 ti++; 161 } 162 } 163 else if (line.startsWith("vp")) 164 { 165 } 166 else if (line.startsWith("f")) 167 { 168 char[256] tmpStr; 169 tmpStr[0..line.length] = line[]; 170 tmpStr[line.length] = 0; 171 172 if (sscanf(tmpStr.ptr, "f %u/%u/%u %u/%u/%u %u/%u/%u %u/%u/%u", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3, &v4, &t4, &n4) == 12) 173 { 174 tmpFaces[fi].v[0] = v1-1; 175 tmpFaces[fi].v[1] = v2-1; 176 tmpFaces[fi].v[2] = v3-1; 177 178 tmpFaces[fi].t[0] = t1-1; 179 tmpFaces[fi].t[1] = t2-1; 180 tmpFaces[fi].t[2] = t3-1; 181 182 tmpFaces[fi].n[0] = n1-1; 183 tmpFaces[fi].n[1] = n2-1; 184 tmpFaces[fi].n[2] = n3-1; 185 186 fi++; 187 188 warnAboutQuads = true; 189 } 190 if (sscanf(tmpStr.ptr, "f %u/%u/%u %u/%u/%u %u/%u/%u", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3) == 9) 191 { 192 tmpFaces[fi].v[0] = v1-1; 193 tmpFaces[fi].v[1] = v2-1; 194 tmpFaces[fi].v[2] = v3-1; 195 196 tmpFaces[fi].t[0] = t1-1; 197 tmpFaces[fi].t[1] = t2-1; 198 tmpFaces[fi].t[2] = t3-1; 199 200 tmpFaces[fi].n[0] = n1-1; 201 tmpFaces[fi].n[1] = n2-1; 202 tmpFaces[fi].n[2] = n3-1; 203 204 fi++; 205 } 206 else if (sscanf(tmpStr.ptr, "f %u//%u %u//%u %u//%u %u//%u", &v1, &n1, &v2, &n2, &v3, &n3, &v4, &n4) == 8) 207 { 208 tmpFaces[fi].v[0] = v1-1; 209 tmpFaces[fi].v[1] = v2-1; 210 tmpFaces[fi].v[2] = v3-1; 211 212 tmpFaces[fi].n[0] = n1-1; 213 tmpFaces[fi].n[1] = n2-1; 214 tmpFaces[fi].n[2] = n3-1; 215 216 fi++; 217 218 warnAboutQuads = true; 219 } 220 else if (sscanf(tmpStr.ptr, "f %u/%u %u/%u %u/%u", &v1, &t1, &v2, &t2, &v3, &t3) == 6) 221 { 222 tmpFaces[fi].v[0] = v1-1; 223 tmpFaces[fi].v[1] = v2-1; 224 tmpFaces[fi].v[2] = v3-1; 225 226 tmpFaces[fi].t[0] = t1-1; 227 tmpFaces[fi].t[1] = t2-1; 228 tmpFaces[fi].t[2] = t3-1; 229 230 fi++; 231 } 232 else if (sscanf(tmpStr.ptr, "f %u//%u %u//%u %u//%u", &v1, &n1, &v2, &n2, &v3, &n3) == 6) 233 { 234 tmpFaces[fi].v[0] = v1-1; 235 tmpFaces[fi].v[1] = v2-1; 236 tmpFaces[fi].v[2] = v3-1; 237 238 tmpFaces[fi].n[0] = n1-1; 239 tmpFaces[fi].n[1] = n2-1; 240 tmpFaces[fi].n[2] = n3-1; 241 242 fi++; 243 } 244 else if (sscanf(tmpStr.ptr, "f %u %u %u %u", &v1, &v2, &v3, &v4) == 4) 245 { 246 tmpFaces[fi].v[0] = v1-1; 247 tmpFaces[fi].v[1] = v2-1; 248 tmpFaces[fi].v[2] = v3-1; 249 250 fi++; 251 252 warnAboutQuads = true; 253 } 254 else if (sscanf(tmpStr.ptr, "f %u %u %u", &v1, &v2, &v3) == 3) 255 { 256 tmpFaces[fi].v[0] = v1-1; 257 tmpFaces[fi].v[1] = v2-1; 258 tmpFaces[fi].v[2] = v3-1; 259 260 fi++; 261 } 262 else 263 assert(0); 264 } 265 } 266 267 Delete(fileStr); 268 269 if (warnAboutQuads) 270 writeln("Warning: OBJ file \"", filename, "\" includes quads, but Dagon supports only triangles"); 271 272 mesh.indices = New!(uint[3][])(tmpFaces.length); 273 uint numUniqueVerts = cast(uint)mesh.indices.length * 3; 274 mesh.vertices = New!(Vector3f[])(numUniqueVerts); 275 mesh.normals = New!(Vector3f[])(numUniqueVerts); 276 mesh.texcoords = New!(Vector2f[])(numUniqueVerts); 277 278 uint index = 0; 279 280 foreach(i, ref ObjFace f; tmpFaces) 281 { 282 if (numVerts) 283 { 284 mesh.vertices[index] = tmpVertices[f.v[0]]; 285 mesh.vertices[index+1] = tmpVertices[f.v[1]]; 286 mesh.vertices[index+2] = tmpVertices[f.v[2]]; 287 } 288 else 289 { 290 mesh.vertices[index] = Vector3f(0, 0, 0); 291 mesh.vertices[index+1] = Vector3f(0, 0, 0); 292 mesh.vertices[index+2] = Vector3f(0, 0, 0); 293 } 294 295 if (numNormals) 296 { 297 mesh.normals[index] = tmpNormals[f.n[0]]; 298 mesh.normals[index+1] = tmpNormals[f.n[1]]; 299 mesh.normals[index+2] = tmpNormals[f.n[2]]; 300 } 301 else 302 { 303 mesh.normals[index] = Vector3f(0, 0, 0); 304 mesh.normals[index+1] = Vector3f(0, 0, 0); 305 mesh.normals[index+2] = Vector3f(0, 0, 0); 306 } 307 308 if (numTexcoords) 309 { 310 mesh.texcoords[index] = tmpTexcoords[f.t[0]]; 311 mesh.texcoords[index+1] = tmpTexcoords[f.t[1]]; 312 mesh.texcoords[index+2] = tmpTexcoords[f.t[2]]; 313 } 314 else 315 { 316 mesh.texcoords[index] = Vector2f(0, 0); 317 mesh.texcoords[index+1] = Vector2f(0, 0); 318 mesh.texcoords[index+2] = Vector2f(0, 0); 319 } 320 321 mesh.indices[i][0] = index; 322 mesh.indices[i][1] = index + 1; 323 mesh.indices[i][2] = index + 2; 324 325 index += 3; 326 } 327 328 if (needGenNormals) 329 mesh.generateNormals(); 330 331 if (tmpVertices.length) 332 Delete(tmpVertices); 333 if (tmpNormals.length) 334 Delete(tmpNormals); 335 if (tmpTexcoords.length) 336 Delete(tmpTexcoords); 337 if (tmpFaces.length) 338 Delete(tmpFaces); 339 340 mesh.dataReady = true; 341 342 return true; 343 } 344 345 override bool loadThreadUnsafePart() 346 { 347 mesh.prepareVAO(); 348 return true; 349 } 350 351 override void release() 352 { 353 clearOwnedObjects(); 354 } 355 } 356