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 }