1 module dlangui.graphics.scene.fbximport;
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 import std.string : startsWith;
12 
13 struct FbxModelImport {
14     Token[] tokens;
15     ParseState[] stateStack;
16     ParseState state;
17     string filename;
18     static class ParseState {
19         ParseState parent;
20         string paramName;
21         Token[] literalParams;
22         ParseState[] children;
23         Token[] additionalLiteralParams;
24         this(string name) {
25             paramName = name;
26         }
27         void addLiteral(Token token) {
28             literalParams ~= token;
29         }
30     }
31     static class FBXProperties {
32         FBXProperty[string] map;
33         void add(FBXProperty p) {
34             map[p.name] = p;
35         }
36     }
37     static class FBXProperty {
38         string name;
39         string type;
40         Token[] params;
41     }
42     protected void pushState(ParseState state) {
43         state.parent = this.state;
44         if (this.state) {
45             this.state.children ~= state;
46         }
47         stateStack ~= state;
48         this.state = state;
49         Log.d("pushState:[", stateStack.length, "] name=", state.paramName);
50     }
51     protected ParseState popState() {
52         if (!state || stateStack.length < 1)
53             error("stack is empty");
54         Log.d("popState: [", stateStack.length, "] name=", state.paramName, " params:", state.literalParams, " addParams: ", state.additionalLiteralParams );
55         stateStack.length = stateStack.length - 1;
56         state = stateStack.length ? stateStack[$ - 1] : null;
57         return state;
58     }
59     protected bool matchTypes(TokenType t1, TokenType t2) {
60         return (tokens.length > 1 && tokens[0].type == t1 && tokens[1].type == t2);
61     }
62     protected bool skip(int count) {
63         if (count >= tokens.length) {
64             tokens = null;
65             return false;
66         }
67         tokens = tokens[count .. $];
68         return true;
69     }
70     protected string parseParamName() {
71         if (matchParamName()) {
72             string name = tokens[0].text;
73             skip(2);
74             return name;
75         }
76         return null;
77     }
78     protected bool matchParamName() {
79         return matchTypes(TokenType.ident, TokenType.colon);
80     }
81     protected void error(string msg) {
82         if (tokens.length)
83             throw new ParserException(msg, filename, tokens[0].line, tokens[0].pos);
84         throw new ParserException(msg, filename, tokens[0].line, tokens[0].pos);
85     }
86     // current token is {, parse till matching }
87     protected void parseObject() {
88         if (!skip(1))
89             error("unexpected eof");
90         pushState(new ParseState(null));
91         for (;;) {
92             if (string name = parseParamName()) {
93                 parseParam(name);
94             } else {
95                 break;
96             }
97         }
98         if (!tokens.length)
99             error("eof while looking for }");
100         if (tokens[0].type != TokenType.curlyClose)
101             error("}  expected");
102         skip(1);
103         popState();
104     }
105     protected Token[] parseLiteralList() {
106         Token[] res;
107         if (!tokens.length)
108             error("unexpected eof");
109         Token t = tokens[0];
110         while (t.type == TokenType.str || t.type == TokenType.integer || t.type == TokenType.floating || t.type == TokenType.minus || (t.type == TokenType.ident && !matchParamName())) {
111             // unary minus handling
112             if (t.type == TokenType.minus) {
113                 if (!skip(1))
114                     error("Unexpected eof");
115                 t = tokens[0];
116                 if (t.type == TokenType.integer)
117                     t.intvalue = -t.intvalue;
118                 else if (t.type == TokenType.floating)
119                     t.floatvalue = -t.floatvalue;
120                 else
121                     error("number expected");
122             }
123             res ~= t;
124             if (!skip(1)) {
125                 break;
126             }
127             t = tokens[0];
128             if (t.type != TokenType.comma)
129                 break;
130             if (!skip(1))
131                 error("Unexpected eof");
132             t = tokens[0];
133         }
134         return res;
135     }
136     protected FBXProperties parseProperties(string name) {
137         FBXProperties res = new FBXProperties();
138         return res;
139     }
140     protected void parseParam(string name) {
141         //if (name.startsWith("Properties")) {
142         //    parseProperties(name);
143         //    return;
144         //}
145         pushState(new ParseState(name));
146         if (!tokens.length)
147             error("unexpected eof");
148         if (matchParamName()) {
149             // next param
150             popState();
151             return;
152         }
153         // process non-named parameter list
154         Token t = tokens[0];
155         if (t.type == TokenType.str || t.type == TokenType.integer || t.type == TokenType.floating || t.type == TokenType.minus || (t.type == TokenType.ident && !matchParamName())) {
156             state.literalParams = parseLiteralList();
157             if (!tokens.length) {
158                 popState();
159                 return;
160             }
161             t = tokens[0];
162         }
163         if (t.type == TokenType.curlyOpen) {
164             parseObject();
165             if (tokens.length) {
166                 t = tokens[0];
167                 if (t.type == TokenType.comma) {
168                     // additional params
169                     if (!skip(1))
170                         error("unexpected eof");
171                     t = tokens[0];
172                     if (t.type == TokenType.str || t.type == TokenType.integer || t.type == TokenType.floating || t.type == TokenType.minus || (t.type == TokenType.ident && !matchParamName())) {
173                         state.additionalLiteralParams = parseLiteralList();
174                     }
175                 }
176             }
177             popState();
178             return;
179         }
180         if (matchParamName() || t.type == TokenType.curlyClose) {
181             // next param
182             popState();
183             return;
184         } else {
185             error("parameter name expected");
186         }
187     }
188     protected bool parseAll() {
189         while (tokens.length) {
190             if (string name = parseParamName()) {
191                 parseParam(name);
192             } else {
193                 if (tokens.length)
194                     error("Parameter name expected");
195             }
196         }
197         return true;
198     }
199     bool parse(string source) {
200         import dlangui.dml.tokenizer;
201         try {
202             tokens = tokenize(source, [";"], true, true, true);
203             return parseAll();
204         } catch (ParserException e) {
205             Log.d("failed to tokenize OBJ source", e);
206             return false;
207         }
208     }
209 }
210