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