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