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