dmf1.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2017 to 2019 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #include "../../../api/modelAPI.h"
  24. #include "../Model.h" // TODO: Only use the public API
  25. using namespace dsr;
  26. struct Vertex_DMF1 {
  27. FVector3D position;
  28. FVector4D texCoord;
  29. FVector4D color;
  30. Vertex_DMF1() : position(FVector3D(0.0f)), texCoord(FVector4D(0.0f)), color(FVector4D(1.0f)) {}
  31. };
  32. struct Triangle_DMF1 {
  33. Vertex_DMF1 vertices[3];
  34. Triangle_DMF1() {}
  35. };
  36. struct Part_DMF1 {
  37. String textures[16];
  38. String shaderZero;
  39. int minDetailLevel = 0, maxDetailLevel = 2;
  40. List<Triangle_DMF1> triangles;
  41. String name;
  42. Part_DMF1() {}
  43. void addEmptyTriangle() {
  44. this->triangles.push(Triangle_DMF1());
  45. }
  46. Triangle_DMF1* getLastTriangle() {
  47. if (this->triangles.length() > 0) {
  48. return &(triangles[this->triangles.length() - 1]);
  49. } else {
  50. return nullptr;
  51. }
  52. }
  53. };
  54. struct Model_DMF1 {
  55. Filter filter = Filter::Solid;
  56. List<Part_DMF1> parts;
  57. Model_DMF1() {}
  58. Part_DMF1* getLastPart() {
  59. if (this->parts.length() > 0) {
  60. return &(parts[this->parts.length() - 1]);
  61. } else {
  62. return nullptr;
  63. }
  64. }
  65. void addEmptyPart() {
  66. this->parts.push(Part_DMF1());
  67. }
  68. };
  69. #define LoopForward(min, var, max) for(var = min; var <= max; var++)
  70. #define PARSER_NOINDEX if (index != 0) { printText("This version of the engine does not have an index for the property ", propertyName, ".\n"); }
  71. #define PROPERTY_MATCH(NAME) (string_caseInsensitiveMatch(propertyName, U ## #NAME))
  72. #define CONTENT_MATCH(NAME) (string_caseInsensitiveMatch(content, U ## #NAME))
  73. static const DsrChar tab = 9;
  74. static const DsrChar space = 32;
  75. static const DsrChar lineFeed = 10;
  76. static const DsrChar carriageReturn = 13;
  77. static const int ParserState_WaitForStatement = 0; // NameSpace -> WaitForStatement, Identifier -> WaitForIndexOrProperty
  78. static const int ParserState_WaitForIndexOrProperty = 1; // index -> WaitForProperty, Property -> WaitForStatement
  79. static const int ParserState_WaitForProperty = 2; // Property -> WaitForStatement
  80. static const int ParserSpace_Main = 0;
  81. static const int ParserSpace_Part = 1;
  82. static const int ParserSpace_Triangle = 2;
  83. static const int ParserSpace_Bone = 3;
  84. static const int ParserSpace_Shape = 4;
  85. static const int ParserSpace_Point = 5;
  86. static const int ParserSpace_Unhandled = 6;
  87. struct ParserState {
  88. Model_DMF1 *model;
  89. int parserState, parserSpace, propertyIndex;
  90. String lastPropertyName;
  91. explicit ParserState(Model_DMF1 *model) :
  92. model(model),
  93. parserState(ParserState_WaitForStatement),
  94. parserSpace(ParserSpace_Main),
  95. propertyIndex(0) {}
  96. };
  97. // Precondition: value >= 0.0
  98. static int roundIndex(double value) {
  99. return (int)round(value);
  100. }
  101. static void setProperty(ParserState &state, const String &propertyName, int index, const ReadableString &content) {
  102. float value = (float)string_parseDouble(content);
  103. if (state.parserSpace == ParserSpace_Main) {
  104. if PROPERTY_MATCH(FilterType) {
  105. PARSER_NOINDEX
  106. if CONTENT_MATCH(Alpha) {
  107. state.model->filter = Filter::Alpha;
  108. } else { // None
  109. state.model->filter = Filter::Solid;
  110. }
  111. } else if PROPERTY_MATCH(CullingType) {
  112. PARSER_NOINDEX
  113. if CONTENT_MATCH(AABB) {
  114. // TODO: Use bounding box culling on state.model
  115. } else if CONTENT_MATCH(Radius) {
  116. // TODO: Use centered sphere culling on state.model
  117. } else { // None
  118. // TODO: Use no culling on state.model
  119. }
  120. } else if PROPERTY_MATCH(BoundMultiplier) {
  121. PARSER_NOINDEX
  122. // TODO: Use bound multiplier exactly like in the other engine
  123. //state.model->ModelProperties.BoundMultiplier = value;
  124. }
  125. } else if (state.parserSpace == ParserSpace_Part) {
  126. Part_DMF1* lastPart = state.model->getLastPart();
  127. if (!lastPart) {
  128. printText("Failed to find the last part!\n");
  129. } else if PROPERTY_MATCH(Name) {
  130. PARSER_NOINDEX
  131. lastPart->name = content;
  132. } else if PROPERTY_MATCH(Texture) {
  133. if (index < 0 || index >= 16) {
  134. printText("Texture index ", index, " is out of bound 0..15\n");
  135. } else {
  136. lastPart->textures[index] = content;
  137. }
  138. } else if PROPERTY_MATCH(Shader) {
  139. if (index == 0) {
  140. lastPart->shaderZero = content;
  141. }
  142. } else if PROPERTY_MATCH(TextureOverride) {
  143. // TODO: Set the texture override channel
  144. } else if PROPERTY_MATCH(MinDetailLevel) {
  145. lastPart->minDetailLevel = roundIndex(value);
  146. } else if PROPERTY_MATCH(MaxDetailLevel) {
  147. lastPart->maxDetailLevel = roundIndex(value);
  148. }
  149. } else if (state.parserSpace == ParserSpace_Triangle) {
  150. Part_DMF1* lastPart = state.model->getLastPart();
  151. if (!lastPart) {
  152. printText("Failed to find the last part!\n");
  153. } else {
  154. Triangle_DMF1 *lastTriangle = lastPart->getLastTriangle();
  155. if (!lastTriangle) {
  156. printText("Cannot define vertex data after failing to create a triangle!\n");
  157. } else if (index < 0 || index > 2) {
  158. printText("Triangle vertex index ", index, " is out of bound 0..2!\n");
  159. } else {
  160. if PROPERTY_MATCH(X) {
  161. lastTriangle->vertices[index].position.x = value;
  162. } else if PROPERTY_MATCH(Y) {
  163. lastTriangle->vertices[index].position.y = value;
  164. } else if PROPERTY_MATCH(Z) {
  165. lastTriangle->vertices[index].position.z = value;
  166. } else if PROPERTY_MATCH(CR) {
  167. lastTriangle->vertices[index].color.x = value;
  168. } else if PROPERTY_MATCH(CG) {
  169. lastTriangle->vertices[index].color.y = value;
  170. } else if PROPERTY_MATCH(CB) {
  171. lastTriangle->vertices[index].color.z = value;
  172. } else if PROPERTY_MATCH(CA) {
  173. lastTriangle->vertices[index].color.w = value;
  174. } else if PROPERTY_MATCH(U1) {
  175. lastTriangle->vertices[index].texCoord.x = value;
  176. } else if PROPERTY_MATCH(V1) {
  177. lastTriangle->vertices[index].texCoord.y = value;
  178. } else if PROPERTY_MATCH(U2) {
  179. lastTriangle->vertices[index].texCoord.z = value;
  180. } else if PROPERTY_MATCH(V2) {
  181. lastTriangle->vertices[index].texCoord.w = value;
  182. }
  183. }
  184. }
  185. }
  186. }
  187. static void changeNamespace(ParserState &state, const String &newNamespace) {
  188. if (string_caseInsensitiveMatch(newNamespace, U"Part")) {
  189. // Create a new part
  190. state.model->addEmptyPart();
  191. state.parserSpace = ParserSpace_Part;
  192. } else if (string_caseInsensitiveMatch(newNamespace, U"Triangle")) {
  193. if (state.parserSpace == ParserSpace_Part || state.parserSpace == ParserSpace_Triangle) {
  194. // Create a new triangle
  195. state.model->getLastPart()->addEmptyTriangle();
  196. state.parserSpace = ParserSpace_Triangle;
  197. } else {
  198. printText("Triangles must be created as members of a part!\n");
  199. }
  200. } else if (string_caseInsensitiveMatch(newNamespace, U"Bone")) {
  201. state.parserSpace = ParserSpace_Bone; // A bone for animation
  202. } else if (string_caseInsensitiveMatch(newNamespace, U"Shape")) {
  203. state.parserSpace = ParserSpace_Shape; // A physical shape.
  204. } else if (string_caseInsensitiveMatch(newNamespace, U"Point")) {
  205. state.parserSpace = ParserSpace_Point; // A physical point in a convex hull.
  206. } else {
  207. state.parserSpace = ParserSpace_Unhandled;
  208. }
  209. }
  210. // Start and end are in base zero
  211. // End is an inclusive index
  212. static void readToken(ParserState &state, const String &fileContent, int start, int end) {
  213. if (end >= start) {
  214. if (fileContent[start] == U'(' && fileContent[end] == U')') {
  215. // Property
  216. if (state.parserState == ParserState_WaitForProperty || state.parserState == ParserState_WaitForIndexOrProperty) {
  217. setProperty(state, state.lastPropertyName, state.propertyIndex, fileContent.inclusiveRange(start + 1, end - 1));
  218. state.parserState = ParserState_WaitForStatement;
  219. state.propertyIndex = 0; // Reset index for the next property
  220. } else {
  221. printText("Unexpected property!\n");
  222. }
  223. } else if (fileContent[start] == U'[' && fileContent[end] == U']') {
  224. // Index
  225. if (state.parserState == ParserState_WaitForIndexOrProperty) {
  226. state.propertyIndex = roundIndex(string_parseDouble(fileContent.inclusiveRange(start + 1, end - 1)));
  227. } else {
  228. printText("Unexpected index!\n");
  229. }
  230. } else if (fileContent[start] == U'<' && fileContent[end] == U'>') {
  231. // Namespace
  232. if (state.parserState == ParserState_WaitForStatement) {
  233. if (end - start > 258) {
  234. printText("Name of namespace is too long!\n");
  235. } else {
  236. // Change namespace and create things
  237. changeNamespace(state, fileContent.inclusiveRange(start + 1, end - 1));
  238. }
  239. } else {
  240. printText("Change of namespace before finishing the last statement!\n");
  241. }
  242. } else {
  243. // Identifier
  244. if (state.parserState == ParserState_WaitForStatement) {
  245. // Global property
  246. if (end - start > 258) {
  247. printText("Name of property is too long!\n");
  248. } else {
  249. state.lastPropertyName = fileContent.inclusiveRange(start, end);
  250. state.parserState = ParserState_WaitForIndexOrProperty;
  251. }
  252. }
  253. }
  254. }
  255. }
  256. // Parses a simplified native representation of the model as a syntax tree that can later be converted into a real model
  257. static Model_DMF1 loadNative_DMF1(const String &fileContent) {
  258. Model_DMF1 resultModel;
  259. ParserState state(&resultModel);
  260. if (fileContent.length() < 4 || (fileContent[0] != 'D' || fileContent[1] != 'M' || fileContent[2] != 'F' || fileContent[3] != '1')) {
  261. printText("The file does not start with \"DMF1\"!\n");
  262. return resultModel;
  263. }
  264. int tokenStart = 4; // Everything before this will no longer be used
  265. int readIndex = 4;
  266. // Scan the string and send tokens to the state machine
  267. DsrChar firstCharOfToken = U'\0';
  268. for (readIndex = tokenStart; readIndex < fileContent.length(); readIndex++) {
  269. DsrChar curChar = fileContent[readIndex];
  270. if (firstCharOfToken == U'\0' && (curChar == tab || curChar == space || curChar == lineFeed || curChar == carriageReturn)) {
  271. // Finish the current token and don't save this character
  272. readToken(state, fileContent, tokenStart, readIndex - 1);
  273. tokenStart = readIndex + 1;
  274. } else if (curChar == '<' || curChar == '(' || curChar == '[') {
  275. // Finish the last token and save this character
  276. readToken(state, fileContent, tokenStart, readIndex-1);
  277. tokenStart = readIndex;
  278. firstCharOfToken = curChar;
  279. } else if (firstCharOfToken == '<' && curChar == '>') {
  280. // Use this token
  281. readToken(state, fileContent, tokenStart, readIndex);
  282. tokenStart = readIndex + 1;
  283. firstCharOfToken = U'\0';
  284. } else if (firstCharOfToken == '(' && curChar == ')') {
  285. // Use this token
  286. readToken(state, fileContent, tokenStart, readIndex);
  287. tokenStart = readIndex + 1;
  288. firstCharOfToken = U'\0';
  289. } else if (firstCharOfToken == '[' && curChar == ']') {
  290. // Use this token
  291. readToken(state, fileContent, tokenStart, readIndex);
  292. tokenStart = readIndex + 1;
  293. firstCharOfToken = U'\0';
  294. }
  295. }
  296. readToken(state, fileContent, tokenStart, readIndex - 1);
  297. if (state.parserState != ParserState_WaitForStatement) {
  298. printText("The last statement in the model was not finished.\n");
  299. }
  300. return resultModel;
  301. }
  302. static Model convertFromDMF1(const Model_DMF1 &nativeModel, ResourcePool &pool, int detailLevel) {
  303. Model result = model_create();
  304. // Convert all parts from the native representation
  305. for (int inputPartIndex = 0; inputPartIndex < nativeModel.parts.length(); inputPartIndex++) {
  306. const Part_DMF1 *inputPart = &(nativeModel.parts[inputPartIndex]);
  307. if (detailLevel >= inputPart->minDetailLevel && detailLevel <= inputPart->maxDetailLevel) {
  308. int part = result->addEmptyPart(inputPart->name);
  309. if (string_caseInsensitiveMatch(inputPart->shaderZero, U"M_Diffuse_0Tex")) {
  310. // Color
  311. } else if (string_caseInsensitiveMatch(inputPart->shaderZero, U"M_Diffuse_1Tex")) {
  312. // Diffuse
  313. result->setDiffuseMapByName(pool, inputPart->textures[0], part);
  314. } else if (string_caseInsensitiveMatch(inputPart->shaderZero, U"M_Diffuse_2Tex")) {
  315. // Diffuse and light
  316. result->setDiffuseMapByName(pool, inputPart->textures[0], part);
  317. result->setLightMapByName(pool, inputPart->textures[1], part);
  318. } else {
  319. printText("The shader ", inputPart->shaderZero, " is not supported. Use M_Diffuse_0Tex, M_Diffuse_1Tex or M_Diffuse_2Tex.\n");
  320. }
  321. for (int inputTriangleIndex = 0; inputTriangleIndex < inputPart->triangles.length(); inputTriangleIndex++) {
  322. const Triangle_DMF1 *inputTriangle = &(inputPart->triangles[inputTriangleIndex]);
  323. const float threshold = 0.00001f;
  324. int posIndexA = result->addPointIfNeeded(inputTriangle->vertices[0].position, threshold);
  325. int posIndexB = result->addPointIfNeeded(inputTriangle->vertices[1].position, threshold);
  326. int posIndexC = result->addPointIfNeeded(inputTriangle->vertices[2].position, threshold);
  327. VertexData dataA(inputTriangle->vertices[0].texCoord, inputTriangle->vertices[0].color);
  328. VertexData dataB(inputTriangle->vertices[1].texCoord, inputTriangle->vertices[1].color);
  329. VertexData dataC(inputTriangle->vertices[2].texCoord, inputTriangle->vertices[2].color);
  330. Polygon polygon(Vertex(posIndexA, dataA), Vertex(posIndexB, dataB), Vertex(posIndexC, dataC));
  331. result->addPolygon(polygon, part);
  332. }
  333. }
  334. }
  335. return result;
  336. }
  337. Model dsr::importFromContent_DMF1(const String &fileContent, ResourcePool &pool, int detailLevel) {
  338. // Load the raw data
  339. Model_DMF1 nativeModel = loadNative_DMF1(fileContent);
  340. // Construct a model while loading resources
  341. return convertFromDMF1(nativeModel, pool, detailLevel);
  342. }