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