1 /** 2 * Copyright: Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved 3 * Authors: $(Gabriel Gheorghe) 4 * License: $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007) 5 * Source: $(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/model/io.d) 6 * Documentation: 7 * Coverage: 8 **/ 9 module liberty.model.io; 10 11 import liberty.model.data; 12 import liberty.graphics.buffer; 13 14 /** 15 * 16 **/ 17 final abstract class ModelIO { 18 /// Load a raw model into memory using vertex data. 19 /// Returns newly created raw model. 20 static RawModel loadRawModel(VERTEX)(VERTEX[] data) { 21 // Create vertex array object for the model 22 GfxVertexArray vao = GfxVertexArray.createArray; 23 vao.appendToVAOs(vao.handle); 24 25 // Create vertex buffer object for the model 26 GfxBuffer vbo = GfxBuffer.createBuffer(GfxBufferTarget.ARRAY, GfxDataUsage.STATIC_DRAW, data); 27 vbo.appendToVBOs(vbo.handle); 28 29 // Bind vertex attribute pointer 30 VERTEX.bindAttributePointer; 31 32 // Unbind vertex buffer object 33 vbo.unbind; 34 35 // Unbind vertex array object 36 vao.unbind; 37 38 return RawModel(vao.handle, data.length, false); 39 } 40 41 /// Load a raw model into memory using vertex data and indices. 42 /// Indices are stored into the internal vertex buffer object static array. 43 /// Returns newly created raw model. 44 static RawModel loadRawModel(VERTEX)(VERTEX[] data, uint[] indices) { 45 // Create vertex array object for the model 46 GfxVertexArray vao = GfxVertexArray.createArray(); 47 vao.appendToVAOs(vao.handle); 48 49 // Create vertex buffer object for the model 50 GfxBuffer vbo = GfxBuffer.createBuffer(GfxBufferTarget.ARRAY, GfxDataUsage.STATIC_DRAW, data); 51 vbo.appendToVBOs(vbo.handle); 52 53 // Create element buffer object for the model 54 // This shouldn't be unbinded 55 GfxBuffer ebo = GfxBuffer.createBuffer(GfxBufferTarget.ELEMENT_ARRAY, GfxDataUsage.STATIC_DRAW, indices); 56 ebo.appendToVBOs(ebo.handle); 57 58 // Bind vertex attribute pointer 59 VERTEX.bindAttributePointer; 60 61 // Unbind vertex buffer object 62 vbo.unbind(); 63 64 // Unbind vertex array object 65 vao.unbind(); 66 67 return RawModel(vao.handle, indices.length, true); 68 } 69 70 /** 71 * Load a model into memory using a file with .obj extension. 72 * Indices are stored into the internal vertex buffer object static array. 73 * Returns newly created model. 74 **/ 75 static Model loadModel(VERTEX)(string path) { 76 import std.array : split; 77 78 // Check extension 79 string[] splitArray = path.split("."); 80 immutable extension = splitArray[$ - 1]; 81 switch (extension) { 82 case "obj": 83 return loadOBJFile!VERTEX(path); 84 default: 85 Logger.error( 86 "File format not supported for mesh data: " ~ extension, 87 typeof(this).stringof 88 ); 89 } 90 91 assert(0, "Unreachable"); 92 } 93 94 private static Model loadOBJFile(VERTEX)(string path) { 95 VERTEX[] vertices; 96 97 Vector3F[] positions; 98 Vector3F[] normals; 99 Vector2F[] uvs; 100 101 Vector3F[] tmpPositions; 102 Vector3F[] tmpNormals; 103 Vector2F[] tmpUvs; 104 105 uint[] vertexIndices; 106 uint[] normalIndices; 107 uint[] uvIndices; 108 109 // Open the file 110 auto file = File(path); 111 scope(exit) file.close(); 112 113 // Read the file and build mesh data 114 auto range = file.byLine(); 115 foreach (line; range) { 116 line = line.strip(); 117 char[][] tokens = line.split(" "); 118 119 if (tokens.length == 0 || tokens[0] == "#") { 120 continue; 121 } else if (tokens[0] == "v") { 122 tmpPositions ~= Vector3F(tokens[1].to!float, tokens[2].to!float, tokens[3].to!float); 123 } else if (tokens[0] == "vn") { 124 tmpNormals ~= Vector3F(tokens[1].to!float, tokens[2].to!float, tokens[3].to!float); 125 } else if (tokens[0] == "vt") { 126 tmpUvs ~= Vector2F(tokens[1].to!float, tokens[2].to!float); 127 } else if (tokens[0] == "f") { 128 uint v1, v2, v3, v4; 129 uint t1, t2, t3, t4; 130 uint n1, n2, n3, n4; 131 132 char[256] tmpLine; 133 tmpLine[0..line.length] = line[]; 134 tmpLine[line.length] = 0; 135 136 if (sscanf(line.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) { 137 vertexIndices ~= v1 - 1; 138 vertexIndices ~= v2 - 1; 139 vertexIndices ~= v3 - 1; 140 141 uvIndices ~= t1 - 1; 142 uvIndices ~= t2 - 1; 143 uvIndices ~= t3 - 1; 144 145 normalIndices ~= n1-1; 146 normalIndices ~= n2-1; 147 normalIndices ~= n3-1; 148 } else if (sscanf(line.ptr, "f %u/%u/%u %u/%u/%u %u/%u/%u", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3) == 9) { 149 vertexIndices ~= v1 - 1; 150 vertexIndices ~= v2 - 1; 151 vertexIndices ~= v3 - 1; 152 153 uvIndices ~= t1 - 1; 154 uvIndices ~= t2 - 1; 155 uvIndices ~= t3 - 1; 156 157 normalIndices ~= n1 - 1; 158 normalIndices ~= n2 - 1; 159 normalIndices ~= n3 - 1; 160 } else if (sscanf(line.ptr, "f %u//%u %u//%u %u//%u %u//%u", &v1, &n1, &v2, &n2, &v3, &n3, &v4, &n4) == 8) { 161 vertexIndices ~= v1 - 1; 162 vertexIndices ~= v2 - 1; 163 vertexIndices ~= v3 - 1; 164 165 normalIndices ~= n1-1; 166 normalIndices ~= n2-1; 167 normalIndices ~= n3-1; 168 } else if (sscanf(line.ptr, "f %u/%u %u/%u %u/%u", &v1, &t1, &v2, &t2, &v3, &t3) == 6) { 169 vertexIndices ~= v1 - 1; 170 vertexIndices ~= v2 - 1; 171 vertexIndices ~= v3 - 1; 172 173 uvIndices ~= t1 - 1; 174 uvIndices ~= t2 - 1; 175 uvIndices ~= t3 - 1; 176 } else if (sscanf(line.ptr, "f %u//%u %u//%u %u//%u", &v1, &n1, &v2, &n2, &v3, &n3) == 6) { 177 vertexIndices ~= v1 - 1; 178 vertexIndices ~= v2 - 1; 179 vertexIndices ~= v3 - 1; 180 181 normalIndices ~= n1 - 1; 182 normalIndices ~= n2 - 1; 183 normalIndices ~= n3 - 1; 184 } else if (sscanf(line.ptr, "f %u %u %u %u", &v1, &v2, &v3, &v4) == 4) { 185 vertexIndices ~= v1 - 1; 186 vertexIndices ~= v2 - 1; 187 vertexIndices ~= v3 - 1; 188 } else if (sscanf(line.ptr, "f %u %u %u", &v1, &v2, &v3) == 3) { 189 vertexIndices ~= v1 - 1; 190 vertexIndices ~= v2 - 1; 191 vertexIndices ~= v3 - 1; 192 } else { 193 Logger.error("Unreachable", "ResourceManager: loadOBJFile"); 194 } 195 } 196 } 197 198 foreach (i; 0..vertexIndices.length) { 199 positions ~= tmpPositions[vertexIndices[i]]; 200 normals ~= tmpNormals[normalIndices[i]]; 201 uvs ~= tmpUvs[uvIndices[i]]; 202 203 vertices ~= VERTEX(positions[i], normals[i], uvs[i]); 204 } 205 206 return new Model(loadRawModel(vertices), [Material.getDefault()]); 207 } 208 }