1 /* 2 Copyright (c) 2017-2019 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 //import std.regex; 34 35 import dlib.core.memory; 36 import dlib.core.ownership; 37 import dlib.core.stream; 38 import dlib.math.vector; 39 import dlib.geometry.triangle; 40 import dlib.filesystem.filesystem; 41 import dlib.filesystem.stdfs; 42 import dlib.container.array; 43 44 import dagon.core.bindings; 45 import dagon.resource.asset; 46 import dagon.graphics.mesh; 47 48 struct ObjFace 49 { 50 uint[3] v; 51 uint[3] t; 52 uint[3] n; 53 } 54 55 class OBJAsset: Asset 56 { 57 Mesh mesh; 58 59 this(Owner o) 60 { 61 super(o); 62 mesh = New!Mesh(this); 63 } 64 65 ~this() 66 { 67 release(); 68 } 69 70 override bool loadThreadSafePart(string filename, InputStream istrm, ReadOnlyFileSystem fs, AssetManager mngr) 71 { 72 uint numVerts = 0; 73 uint numNormals = 0; 74 uint numTexcoords = 0; 75 uint numFaces = 0; 76 77 string fileStr = readText(istrm); 78 foreach(line; lineSplitter(fileStr)) 79 { 80 if (line.startsWith("v ")) 81 numVerts++; 82 else if (line.startsWith("vn ")) 83 numNormals++; 84 else if (line.startsWith("vt ")) 85 numTexcoords++; 86 else if (line.startsWith("f ")) 87 numFaces++; 88 } 89 90 Vector3f[] tmpVertices; 91 Vector3f[] tmpNormals; 92 Vector2f[] tmpTexcoords; 93 ObjFace[] tmpFaces; 94 95 bool needGenNormals = false; 96 97 if (!numVerts) 98 writeln("Warning: OBJ file \"", filename, "\" has no vertices"); 99 if (!numNormals) 100 { 101 writeln("Warning: OBJ file \"", filename, "\" has no normals (they will be generated)"); 102 numNormals = numVerts; 103 needGenNormals = true; 104 } 105 if (!numTexcoords) 106 { 107 writeln("Warning: OBJ file \"", filename, "\" has no texcoords"); 108 numTexcoords = numVerts; 109 } 110 111 if (numVerts) 112 tmpVertices = New!(Vector3f[])(numVerts); 113 if (numNormals) 114 tmpNormals = New!(Vector3f[])(numNormals); 115 if (numTexcoords) 116 tmpTexcoords = New!(Vector2f[])(numTexcoords); 117 if (numFaces) 118 tmpFaces = New!(ObjFace[])(numFaces); 119 120 tmpVertices[] = Vector3f(0, 0, 0); 121 tmpNormals[] = Vector3f(0, 0, 0); 122 tmpTexcoords[] = Vector2f(0, 0); 123 124 float x, y, z; 125 uint v1, v2, v3, v4; 126 uint t1, t2, t3, t4; 127 uint n1, n2, n3, n4; 128 uint vi = 0; 129 uint ni = 0; 130 uint ti = 0; 131 uint fi = 0; 132 133 bool warnAboutQuads = false; 134 135 foreach(line; lineSplitter(fileStr)) 136 { 137 if (line.startsWith("v ")) 138 { 139 if (formattedRead(line, "v %s %s %s", &x, &y, &z)) 140 { 141 tmpVertices[vi] = Vector3f(x, y, z); 142 vi++; 143 } 144 } 145 else if (line.startsWith("vn")) 146 { 147 if (formattedRead(line, "vn %s %s %s", &x, &y, &z)) 148 { 149 tmpNormals[ni] = Vector3f(x, y, z); 150 ni++; 151 } 152 } 153 else if (line.startsWith("vt")) 154 { 155 if (formattedRead(line, "vt %s %s", &x, &y)) 156 { 157 tmpTexcoords[ti] = Vector2f(x, -y); 158 ti++; 159 } 160 } 161 else if (line.startsWith("vp")) 162 { 163 } 164 else if (line.startsWith("f")) 165 { 166 char[256] tmpStr; 167 tmpStr[0..line.length] = line[]; 168 tmpStr[line.length] = 0; 169 170 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) 171 { 172 tmpFaces[fi].v[0] = v1-1; 173 tmpFaces[fi].v[1] = v2-1; 174 tmpFaces[fi].v[2] = v3-1; 175 176 tmpFaces[fi].t[0] = t1-1; 177 tmpFaces[fi].t[1] = t2-1; 178 tmpFaces[fi].t[2] = t3-1; 179 180 tmpFaces[fi].n[0] = n1-1; 181 tmpFaces[fi].n[1] = n2-1; 182 tmpFaces[fi].n[2] = n3-1; 183 184 fi++; 185 186 warnAboutQuads = true; 187 } 188 if (sscanf(tmpStr.ptr, "f %u/%u/%u %u/%u/%u %u/%u/%u", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3) == 9) 189 { 190 tmpFaces[fi].v[0] = v1-1; 191 tmpFaces[fi].v[1] = v2-1; 192 tmpFaces[fi].v[2] = v3-1; 193 194 tmpFaces[fi].t[0] = t1-1; 195 tmpFaces[fi].t[1] = t2-1; 196 tmpFaces[fi].t[2] = t3-1; 197 198 tmpFaces[fi].n[0] = n1-1; 199 tmpFaces[fi].n[1] = n2-1; 200 tmpFaces[fi].n[2] = n3-1; 201 202 fi++; 203 } 204 else if (sscanf(tmpStr.ptr, "f %u//%u %u//%u %u//%u %u//%u", &v1, &n1, &v2, &n2, &v3, &n3, &v4, &n4) == 8) 205 { 206 tmpFaces[fi].v[0] = v1-1; 207 tmpFaces[fi].v[1] = v2-1; 208 tmpFaces[fi].v[2] = v3-1; 209 210 tmpFaces[fi].n[0] = n1-1; 211 tmpFaces[fi].n[1] = n2-1; 212 tmpFaces[fi].n[2] = n3-1; 213 214 fi++; 215 216 warnAboutQuads = true; 217 } 218 else if (sscanf(tmpStr.ptr, "f %u/%u %u/%u %u/%u", &v1, &t1, &v2, &t2, &v3, &t3) == 6) 219 { 220 tmpFaces[fi].v[0] = v1-1; 221 tmpFaces[fi].v[1] = v2-1; 222 tmpFaces[fi].v[2] = v3-1; 223 224 tmpFaces[fi].t[0] = t1-1; 225 tmpFaces[fi].t[1] = t2-1; 226 tmpFaces[fi].t[2] = t3-1; 227 228 fi++; 229 } 230 else if (sscanf(tmpStr.ptr, "f %u//%u %u//%u %u//%u", &v1, &n1, &v2, &n2, &v3, &n3) == 6) 231 { 232 tmpFaces[fi].v[0] = v1-1; 233 tmpFaces[fi].v[1] = v2-1; 234 tmpFaces[fi].v[2] = v3-1; 235 236 tmpFaces[fi].n[0] = n1-1; 237 tmpFaces[fi].n[1] = n2-1; 238 tmpFaces[fi].n[2] = n3-1; 239 240 fi++; 241 } 242 else if (sscanf(tmpStr.ptr, "f %u %u %u %u", &v1, &v2, &v3, &v4) == 4) 243 { 244 tmpFaces[fi].v[0] = v1-1; 245 tmpFaces[fi].v[1] = v2-1; 246 tmpFaces[fi].v[2] = v3-1; 247 248 fi++; 249 250 warnAboutQuads = true; 251 } 252 else if (sscanf(tmpStr.ptr, "f %u %u %u", &v1, &v2, &v3) == 3) 253 { 254 tmpFaces[fi].v[0] = v1-1; 255 tmpFaces[fi].v[1] = v2-1; 256 tmpFaces[fi].v[2] = v3-1; 257 258 fi++; 259 } 260 else 261 assert(0); 262 } 263 } 264 265 Delete(fileStr); 266 267 if (warnAboutQuads) 268 writeln("Warning: OBJ file \"", filename, "\" includes quads, but Dagon supports only triangles"); 269 270 mesh.indices = New!(uint[3][])(tmpFaces.length); 271 uint numUniqueVerts = cast(uint)mesh.indices.length * 3; 272 mesh.vertices = New!(Vector3f[])(numUniqueVerts); 273 mesh.normals = New!(Vector3f[])(numUniqueVerts); 274 mesh.texcoords = New!(Vector2f[])(numUniqueVerts); 275 276 uint index = 0; 277 278 foreach(i, ref ObjFace f; tmpFaces) 279 { 280 if (numVerts) 281 { 282 mesh.vertices[index] = tmpVertices[f.v[0]]; 283 mesh.vertices[index+1] = tmpVertices[f.v[1]]; 284 mesh.vertices[index+2] = tmpVertices[f.v[2]]; 285 } 286 else 287 { 288 mesh.vertices[index] = Vector3f(0, 0, 0); 289 mesh.vertices[index+1] = Vector3f(0, 0, 0); 290 mesh.vertices[index+2] = Vector3f(0, 0, 0); 291 } 292 293 if (numNormals) 294 { 295 mesh.normals[index] = tmpNormals[f.n[0]]; 296 mesh.normals[index+1] = tmpNormals[f.n[1]]; 297 mesh.normals[index+2] = tmpNormals[f.n[2]]; 298 } 299 else 300 { 301 mesh.normals[index] = Vector3f(0, 0, 0); 302 mesh.normals[index+1] = Vector3f(0, 0, 0); 303 mesh.normals[index+2] = Vector3f(0, 0, 0); 304 } 305 306 if (numTexcoords) 307 { 308 mesh.texcoords[index] = tmpTexcoords[f.t[0]]; 309 mesh.texcoords[index+1] = tmpTexcoords[f.t[1]]; 310 mesh.texcoords[index+2] = tmpTexcoords[f.t[2]]; 311 } 312 else 313 { 314 mesh.texcoords[index] = Vector2f(0, 0); 315 mesh.texcoords[index+1] = Vector2f(0, 0); 316 mesh.texcoords[index+2] = Vector2f(0, 0); 317 } 318 319 mesh.indices[i][0] = index; 320 mesh.indices[i][1] = index + 1; 321 mesh.indices[i][2] = index + 2; 322 323 index += 3; 324 } 325 326 if (needGenNormals) 327 mesh.generateNormals(); 328 329 if (tmpVertices.length) 330 Delete(tmpVertices); 331 if (tmpNormals.length) 332 Delete(tmpNormals); 333 if (tmpTexcoords.length) 334 Delete(tmpTexcoords); 335 if (tmpFaces.length) 336 Delete(tmpFaces); 337 338 mesh.calcBoundingBox(); 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