1 module dlangui.graphics.scene.objimport;
2 
3 public import dlangui.core.config;
4 static if (ENABLE_OPENGL):
5 static if (BACKEND_GUI):
6 
7 import dlangui.core.logger;
8 import dlangui.core.math3d;
9 import dlangui.dml.tokenizer;
10 import dlangui.graphics.scene.mesh;
11 
12 struct ObjModelImport {
13     alias FaceIndex = int[3];
14 
15     private float[] _vertexData;
16     private float[] _normalData;
17     private float[] _txData;
18     private int _vertexCount;
19     private int _normalCount;
20     private int _triangleCount;
21     private int _txCount;
22     private float[8] _buf;
23 
24     MeshRef mesh;
25 
26     protected float[] parseFloatList(Token[] tokens, int maxItems = 3, float padding = 0) return {
27         int i = 0;
28         int sgn = 1;
29         foreach(t; tokens) {
30             if (i >= maxItems)
31                 break;
32             if (t.type == TokenType.floating) {
33                 _buf[i++] = cast(float)(t.floatvalue * sgn);
34                 sgn = 1;
35             } else if (t.type == TokenType.integer) {
36                 _buf[i++] = cast(float)(t.intvalue * sgn);
37                 sgn = 1;
38             } else if (t.type == TokenType.minus) {
39                 sgn = -1;
40             }
41         }
42         while(i < maxItems)
43             _buf[i++] = padding;
44         if (i > 0)
45             return _buf[0 .. i];
46         return null;
47     }
48     //# List of geometric vertices, with (x,y,z[,w]) coordinates, w is optional and defaults to 1.0.
49     //v 0.123 0.234 0.345 1.0
50     protected bool parseVertexLine(Token[] tokens) {
51         float[] data = parseFloatList(tokens, 3, 0);
52         if (data.length == 3) {
53             _vertexData ~= data;
54             _vertexCount++;
55             return true;
56         }
57         return false;
58     }
59     //# List of texture coordinates, in (u, v [,w]) coordinates, these will vary between 0 and 1, w is optional and defaults to 0.
60     //vt 0.500 1 [0]
61     protected bool parseVertexTextureLine(Token[] tokens) {
62         float[] data = parseFloatList(tokens, 2, 0);
63         if (data.length == 2) {
64             _txData ~= data;
65             _txCount++;
66             return true;
67         }
68         return false;
69     }
70     //# List of vertex normals in (x,y,z) form; normals might not be unit vectors.
71     //vn 0.707 0.000 0.707
72     protected bool parseVertexNormalsLine(Token[] tokens) {
73         float[] data = parseFloatList(tokens, 3, 0);
74         if (data.length == 3) {
75             _normalData ~= data;
76             _normalCount++;
77             return true;
78         }
79         return false;
80     }
81 
82     static protected bool skipToken(ref Token[] tokens) {
83         tokens = tokens.length > 1 ? tokens[1 .. $] : null;
84         return tokens.length > 0;
85     }
86     static protected bool parseIndex(ref Token[] tokens, ref int data) {
87         int sign = 1;
88         if (tokens[0].type == TokenType.minus) {
89             sign = -1;
90             skipToken(tokens);
91         }
92         if (tokens[0].type == TokenType.integer) {
93             data = tokens[0].intvalue * sign;
94             skipToken(tokens);
95             return true;
96         }
97         return false;
98     }
99     static protected bool skip(ref Token[] tokens, TokenType type) {
100         if (tokens.length > 0 && tokens[0].type == type) {
101             skipToken(tokens);
102             return true;
103         }
104         return false;
105     }
106     static protected bool parseFaceIndex(ref Token[] tokens, ref FaceIndex data) {
107         int i = 0;
108         if (tokens.length == 0)
109             return false;
110         if (!parseIndex(tokens, data[0]))
111             return false;
112         if (skip(tokens, TokenType.divide)) {
113             parseIndex(tokens, data[1]);
114             if (skip(tokens, TokenType.divide)) {
115                 if (!parseIndex(tokens, data[2]))
116                     return false;
117             }
118         }
119         return tokens.length == 0 || skip(tokens, TokenType.whitespace);
120     }
121     //# Parameter space vertices in ( u [,v] [,w] ) form; free form geometry statement ( see below )
122     //vp 0.310000 3.210000 2.100000
123     protected bool parseParameterSpaceLine(Token[] tokens) {
124         // not supported
125 
126         return true;
127     }
128 
129     //f 1 2 3
130     //f 3/1 4/2 5/3
131     //f 6/4/1 3/5/3 7/6/5
132     protected bool parseFaceLine(Token[] tokens) {
133         FaceIndex[10] indexes;
134         int i = 0;
135         while(parseFaceIndex(tokens, indexes[i])) {
136             if (++i >= 10)
137                 break;
138         }
139         for (int j = 1; j + 1 < i; j++)
140             addTriangle(indexes[0], indexes[j], indexes[j + 1]);
141         return true;
142     }
143 
144     vec3 vertexForIndex(int index) {
145         if (index < 0)
146             index = _vertexCount + 1 + index;
147         if (index >= 1 && index <= _vertexCount) {
148             index = (index - 1) * 3;
149             return vec3(&_vertexData[index]);
150         }
151         return vec3.init;
152     }
153 
154     vec3 normalForIndex(int index) {
155         if (index < 0)
156             index = _normalCount + 1 + index;
157         if (index >= 1 && index <= _normalCount) {
158             index = (index - 1) * 3;
159             return vec3(&_normalData[index]);
160         }
161         return vec3(0, 0, 1);
162     }
163 
164     vec2 txForIndex(int index) {
165         if (index < 0)
166             index = _txCount + 1 + index;
167         if (index >= 1 && index <= _txCount) {
168             index = (index - 1) * 2;
169             return vec2(&_txData[index]);
170         }
171         return vec2.init;
172     }
173 
174     bool _meshHasTexture;
175     void createMeshIfNotExist() {
176         if (!mesh.isNull)
177             return;
178         if (_txCount) {
179             mesh = new Mesh(VertexFormat(VertexElementType.POSITION, VertexElementType.NORMAL, /*VertexElementType.COLOR, */ VertexElementType.TEXCOORD0));
180             _meshHasTexture = true;
181         } else {
182             mesh = new Mesh(VertexFormat(VertexElementType.POSITION, VertexElementType.NORMAL /*, VertexElementType.COLOR*/));
183             _meshHasTexture = false;
184         }
185     }
186     protected bool addTriangle(FaceIndex v1, FaceIndex v2, FaceIndex v3) {
187         createMeshIfNotExist();
188         float[16 * 3] data;
189         const (VertexFormat) * fmt = mesh.vertexFormatPtr;
190         int vfloats = fmt.vertexFloats;
191         vec3 p1 = vertexForIndex(v1[0]);
192         vec3 p2 = vertexForIndex(v2[0]);
193         vec3 p3 = vertexForIndex(v3[0]);
194         fmt.set(data.ptr, VertexElementType.POSITION, p1);
195         fmt.set(data.ptr + vfloats, VertexElementType.POSITION, p2);
196         fmt.set(data.ptr + vfloats * 2, VertexElementType.POSITION, p3);
197         if (fmt.hasElement(VertexElementType.TEXCOORD0)) {
198             fmt.set(data.ptr, VertexElementType.TEXCOORD0, txForIndex(v1[1]));
199             fmt.set(data.ptr + vfloats, VertexElementType.TEXCOORD0, txForIndex(v2[1]));
200             fmt.set(data.ptr + vfloats * 2, VertexElementType.TEXCOORD0, txForIndex(v3[1]));
201         }
202         if (fmt.hasElement(VertexElementType.COLOR)) {
203             const vec4 white = vec4(1, 1, 1, 1);
204             fmt.set(data.ptr, VertexElementType.COLOR, white);
205             fmt.set(data.ptr + vfloats, VertexElementType.COLOR, white);
206             fmt.set(data.ptr + vfloats * 2, VertexElementType.COLOR, white);
207         }
208         if (fmt.hasElement(VertexElementType.NORMAL)) {
209             vec3 normal;
210             if (!v1[2] || !v2[2] || !v3[2]) {
211                 // no normal specified, calculate it
212                 normal = triangleNormal(p1, p2, p3);
213             }
214             fmt.set(data.ptr, VertexElementType.NORMAL, v1[2] ? normalForIndex(v1[2]) : normal);
215             fmt.set(data.ptr + vfloats, VertexElementType.NORMAL, v2[2] ? normalForIndex(v2[2]) : normal);
216             fmt.set(data.ptr + vfloats * 2, VertexElementType.NORMAL, v3[2] ? normalForIndex(v3[2]) : normal);
217         }
218         int startVertex = mesh.addVertexes(data.ptr[0 .. vfloats * 3]);
219         mesh.addPart(PrimitiveType.triangles, [
220             cast(ushort)(startVertex + 0),
221             cast(ushort)(startVertex + 1),
222             cast(ushort)(startVertex + 2)]);
223         _triangleCount++;
224         return true;
225     }
226 
227     protected bool parseLine(Token[] tokens) {
228         tokens = trimSpaceTokens(tokens);
229         if (tokens.length) {
230             if (tokens[0].type == TokenType.comment)
231                 return true; // ignore comment
232             if (tokens[0].type == TokenType.ident) {
233                 string ident = tokens[0].text;
234                 tokens = trimSpaceTokens(tokens[1 .. $], true, false);
235                 if (ident == "v") // vertex
236                     return parseVertexLine(tokens);
237                 if (ident == "vt") // texture coords
238                     return parseVertexTextureLine(tokens);
239                 if (ident == "vn") // normals
240                     return parseVertexNormalsLine(tokens);
241                 if (ident == "vp") // parameter space
242                     return parseParameterSpaceLine(tokens);
243                 if (ident == "f") // face
244                     return parseFaceLine(tokens);
245             }
246         }
247         return true;
248     }
249     bool parse(string source) {
250         import dlangui.dml.tokenizer;
251         try {
252             Token[] tokens = tokenize(source, ["#"]);
253             int start = 0;
254             int i = 0;
255             for ( ; i <= tokens.length; i++) {
256                 if (i == tokens.length || tokens[i].type == TokenType.eol) {
257                     if (i > start && !parseLine(tokens[start .. i]))
258                         return false;
259                     start = i + 1;
260                 }
261             }
262         } catch (ParserException e) {
263             Log.d("failed to tokenize OBJ source", e);
264             return false;
265         }
266         return true;
267     }
268 
269 }
270