step.h 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. /*
  2. * m3dconv/step.h
  3. *
  4. * Copyright (C) 2019 bzt (bztsrc@gitlab)
  5. *
  6. * Permission is hereby granted, free of charge, to any person
  7. * obtaining a copy of this software and associated documentation
  8. * files (the "Software"), to deal in the Software without
  9. * restriction, including without limitation the rights to use, copy,
  10. * modify, merge, publish, distribute, sublicense, and/or sell copies
  11. * of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. * DEALINGS IN THE SOFTWARE.
  25. *
  26. * @brief simple 3D model to M3D converter STEP (ISO-10303-21-4 and ISO-10303-24-2) importer (Work In Progress)
  27. * https://gitlab.com/bztsrc/model3d
  28. *
  29. */
  30. /* this code does not need to be fast. Being correct is more important. the standard is seriously fucked up
  31. * and NOT documented properly. You'll find dead links and password encrypted sites when searching for the spec...
  32. *
  33. * I'd like to say thanks to D. Sreeramulu, C.S.P. Rao, National Institute of Technologu, Warangal and
  34. * Vangipurapu, Naga Malleswari at Andhra University for their algorithm and thesis being the only readable and
  35. * understandable source on STEP files and extracting geometry.
  36. *
  37. * https://en.wikipedia.org/wiki/ISO_10303-21
  38. * https://www.iso.org/standard/76142.html
  39. * https://scolarworks.waldenu.edu/cgi/viewcontent.cgi?article=1054&context=ijamt
  40. * http://shodhganga.inflibnet.ac.in/bitstream/10603/14116/11/11_chapter_3.pdf
  41. * https://link.springer.com/content/pdf/10.1007%2F978-0-387-35490-3_9.pdf
  42. */
  43. /* we took 4 bits from 32, that leaves us 256 million unique strings and again that much unique numbers.
  44. * If that's not enough for your control points, then you're doing the parameterized surfaces thing wrong. */
  45. #define ST_TOKEN(t, i) (((i) << 4) | (t))
  46. #define ST_TYPE(t) ((t) & 15)
  47. #define ST_PARAM(t) ((t) >> 4)
  48. #define ST_ISENTITY(t, n) (ST_TYPE(t) == ST_ID && lines[ST_PARAM(t)] && lines[ST_PARAM(t)][0] > (n))
  49. /* STEP tokens */
  50. #define ST_END 0 /* end of statement */
  51. #define ST_COMMA 1 /* , attribute separator */
  52. #define ST_PAROP 2 /* ( */
  53. #define ST_PARCL 3 /* ) */
  54. #define ST_DOLLAR 4 /* $ */
  55. #define ST_ASTERIX 5 /* * */
  56. #define ST_ID 6 /* entity reference */
  57. #define ST_ENUM 7 /* .enum. */
  58. #define ST_STRING 8 /* string literal */
  59. #define ST_NUM 9 /* number literal */
  60. #define ST_LABEL 10 /* some label for an unrecognized entity, probably function name if followed by ( */
  61. #define ST_FUNC 11 /* recognized entity */
  62. /* list of recognized entitites, their tokens and labels */
  63. #define ST_PRODUCT ST_TOKEN(ST_FUNC, 0)
  64. #define ST_ADVANCED_BREP_SHAPE_REPRESENTATION ST_TOKEN(ST_FUNC, 1)
  65. #define ST_MANIFOLD_SURFACE_SHAPE_REPRESENTATION ST_TOKEN(ST_FUNC, 2)
  66. #define ST_FACETED_BREP_SHAPE_REPRESENTATION ST_TOKEN(ST_FUNC, 3)
  67. #define ST_SHELL_BASED_SURFACE_MODEL ST_TOKEN(ST_FUNC, 4)
  68. #define ST_MANIFOLD_SOLID_BREP ST_TOKEN(ST_FUNC, 5)
  69. #define ST_FACETED_BREP ST_TOKEN(ST_FUNC, 6)
  70. #define ST_CLOSED_SHELL ST_TOKEN(ST_FUNC, 7)
  71. #define ST_AXIS2_PLACEMENT_3D ST_TOKEN(ST_FUNC, 8)
  72. #define ST_CARTESIAN_POINT ST_TOKEN(ST_FUNC, 9)
  73. #define ST_DIRECTION ST_TOKEN(ST_FUNC,10)
  74. #define ST_VECTOR ST_TOKEN(ST_FUNC,11)
  75. #define ST_FACE_SURFACE ST_TOKEN(ST_FUNC,12)
  76. #define ST_ADVANCED_FACE ST_TOKEN(ST_FUNC,13)
  77. #define ST_FACE_OUTER_BOUND ST_TOKEN(ST_FUNC,14)
  78. #define ST_FACE_BOUND ST_TOKEN(ST_FUNC,15)
  79. #define ST_BOUNDED_SURFACE ST_TOKEN(ST_FUNC,16)
  80. #define ST_PLANE ST_TOKEN(ST_FUNC,17)
  81. #define ST_CYLINDRICAL_SURFACE ST_TOKEN(ST_FUNC,18)
  82. #define ST_SPHERICAL_SURFACE ST_TOKEN(ST_FUNC,19)
  83. #define ST_POLY_LOOP ST_TOKEN(ST_FUNC,20)
  84. #define ST_EDGE_LOOP ST_TOKEN(ST_FUNC,21)
  85. #define ST_ORIENTED_EDGE ST_TOKEN(ST_FUNC,22)
  86. #define ST_EDGE_CURVE ST_TOKEN(ST_FUNC,23)
  87. #define ST_VERTEX_POINT ST_TOKEN(ST_FUNC,24)
  88. #define ST_CIRCLE ST_TOKEN(ST_FUNC,25)
  89. #define ST_LINE ST_TOKEN(ST_FUNC,26)
  90. char *labels[] = { "PRODUCT", "ADVANCED_BREP_SHAPE_REPRESENTATION", "MANIFOLD_SURFACE_SHAPE_REPRESENTATION",
  91. "FACETED_BREP_SHAPE_REPRESENTATION", "SHELL_BASED_SURFACE_MODEL", "MANIFOLD_SOLID_BREP", "FACETED_BREP",
  92. "CLOSED_SHELL", "AXIS2_PLACEMENT_3D", "CARTESIAN_POINT", "DIRECTION", "VECTOR", "FACE_SURFACE", "ADVANCED_FACE",
  93. "FACE_OUTER_BOUND", "FACE_BOUND", "BOUNDED_SURFACE", "PLANE", "CYLINDRICAL_SURFACE", "SPHERICAL_SURFACE",
  94. "POLY_LOOP", "EDGE_LOOP", "ORIENTED_EDGE", "EDGE_CURVE", "VERTEX_POINT", "CIRCLE", "LINE"};
  95. /* closed shells in STEP file */
  96. typedef struct {
  97. unsigned int id, type;
  98. char *name;
  99. struct aiMatrix4x4 transform;
  100. } steproot_t;
  101. unsigned int numclosedshells = 0;
  102. steproot_t *closedshells = NULL;
  103. /* cache for tokenized lines */
  104. unsigned int numlines = 0;
  105. unsigned int **lines = NULL;
  106. /* cache for unique strings */
  107. unsigned int numstrings = 0, maxstrings = 0;
  108. char **strings = NULL;
  109. /* cache for unique numbers */
  110. unsigned int numnum = 0, maxnum = 0;
  111. float *num = NULL;
  112. /**
  113. * Dump a STEP entity record recursively
  114. */
  115. void step_dump(unsigned int id, int lvl, int max)
  116. {
  117. unsigned int i;
  118. for(i = 0; (int)i < lvl; i++) printf(" ");
  119. printf("#%d ", id);
  120. if(!lines || id >= numlines || !lines[id] || !lines[id][0])
  121. printf("*not defined*\n");
  122. else {
  123. for(i = 1; i < lines[id][0] && lines[id][i] != ST_END; i++)
  124. switch(ST_TYPE(lines[id][i])) {
  125. case ST_COMMA: printf(", "); break;
  126. case ST_PAROP: printf("( "); break;
  127. case ST_PARCL: printf(") "); break;
  128. case ST_DOLLAR: printf("$ "); break;
  129. case ST_ASTERIX: printf("* "); break;
  130. case ST_ID: printf("#%d ", ST_PARAM(lines[id][i])); break;
  131. case ST_ENUM: printf("%s ", strings[ST_PARAM(lines[id][i])]); break;
  132. case ST_STRING: printf("\"%s\" ", strings[ST_PARAM(lines[id][i])]); break;
  133. case ST_NUM: printf("%g ", num[ST_PARAM(lines[id][i])]); break;
  134. case ST_LABEL: printf("%s ", strings[ST_PARAM(lines[id][i])]); break;
  135. case ST_FUNC: printf("%s ", labels[ST_PARAM(lines[id][i])]); break;
  136. }
  137. printf("\n");
  138. if(lvl >= max) return;
  139. for(i = 1; i < lines[id][0] && lines[id][i] != ST_END; i++)
  140. if(ST_TYPE(lines[id][i]) == ST_ID)
  141. step_dump(ST_PARAM(lines[id][i]), lvl + 1, max);
  142. }
  143. }
  144. /**
  145. * Convert an axis2_placement_3d into a transformation matrix
  146. */
  147. void step_placement(unsigned int id, struct aiMatrix4x4 *m)
  148. {
  149. _assimp_identity4x4(m);
  150. /* huh. anybody has the slightest doubt that STEP files are insane? */
  151. if(lines[id] && lines[id][0] > 9 && lines[id][1] == ST_AXIS2_PLACEMENT_3D &&
  152. ST_ISENTITY(lines[id][5], 10) &&
  153. lines[ST_PARAM(lines[id][5])][1] == ST_CARTESIAN_POINT && ST_TYPE(lines[ST_PARAM(lines[id][5])][6]) == ST_NUM &&
  154. ST_TYPE(lines[ST_PARAM(lines[id][5])][8]) == ST_NUM && ST_TYPE(lines[ST_PARAM(lines[id][5])][10]) == ST_NUM &&
  155. ST_ISENTITY(lines[id][7], 10) &&
  156. lines[ST_PARAM(lines[id][7])][1] == ST_DIRECTION && ST_TYPE(lines[ST_PARAM(lines[id][7])][6]) == ST_NUM &&
  157. ST_TYPE(lines[ST_PARAM(lines[id][7])][8]) == ST_NUM && ST_TYPE(lines[ST_PARAM(lines[id][7])][10]) == ST_NUM &&
  158. ST_ISENTITY(lines[id][9], 10) &&
  159. lines[ST_PARAM(lines[id][9])][1] == ST_DIRECTION && ST_TYPE(lines[ST_PARAM(lines[id][9])][6]) == ST_NUM &&
  160. ST_TYPE(lines[ST_PARAM(lines[id][9])][8]) == ST_NUM && ST_TYPE(lines[ST_PARAM(lines[id][9])][10]) == ST_NUM) {
  161. /* position */
  162. m->a4 = num[ST_PARAM(lines[ST_PARAM(lines[id][5])][6])];
  163. m->b4 = num[ST_PARAM(lines[ST_PARAM(lines[id][5])][8])];
  164. m->c4 = num[ST_PARAM(lines[ST_PARAM(lines[id][5])][10])];
  165. /* rotation */
  166. /* TODO: check if this is actually a transposed rotation matrix */
  167. /* X */
  168. m->a1 = num[ST_PARAM(lines[ST_PARAM(lines[id][7])][6])];
  169. m->a2 = num[ST_PARAM(lines[ST_PARAM(lines[id][7])][8])];
  170. m->a3 = num[ST_PARAM(lines[ST_PARAM(lines[id][7])][10])];
  171. /* Z */
  172. m->c1 = num[ST_PARAM(lines[ST_PARAM(lines[id][9])][6])];
  173. m->c2 = num[ST_PARAM(lines[ST_PARAM(lines[id][9])][8])];
  174. m->c3 = num[ST_PARAM(lines[ST_PARAM(lines[id][9])][10])];
  175. /* Y = X * Z */
  176. m->b1 = m->a2 * m->c3 - m->a3 * m->c2;
  177. m->b2 = m->a3 * m->c1 - m->a1 * m->c3;
  178. m->b3 = m->a1 * m->c2 - m->a2 * m->c1;
  179. _assimp_fixmat(m);
  180. }
  181. }
  182. /**
  183. * Recursively parse face geometry into a shape
  184. */
  185. m3d_t *step_geom(unsigned int id, unsigned int type, _unused struct aiMatrix4x4 *m, _unused m3dh_t *shape, m3d_t *m3d, unsigned int lvl)
  186. {
  187. /*
  188. unsigned int i, ok = 0;
  189. */
  190. if(lvl > 64) return m3d;
  191. if(lines[id] && lines[id][0] > 5) {
  192. step_dump(type, 0, 0);
  193. step_dump(id, 0, 6);
  194. printf("\n");
  195. fprintf(stderr,"m3dconv: parsing STEP geometry is under development.\n");
  196. /*
  197. if(ST_TYPE(lines[id][1]) == ST_FUNC) {
  198. switch(lines[id][1]) {
  199. case ST_FACE_OUTER_BOUND:
  200. case ST_FACE_BOUND:
  201. case ST_POLY_LOOP:
  202. case ST_EDGE_LOOP:
  203. case ST_ORIENTED_EDGE:
  204. case ST_EDGE_CURVE:
  205. for(i = 5; i < lines[id][0]; i++)
  206. if(ST_ISENTITY(lines[id][i], 5))
  207. step_geom(ST_PARAM(lines[id][i]), type, m, shape, m3d, lvl + 1);
  208. ok = 1;
  209. break;
  210. case ST_VERTEX_POINT: ok = 1; break;
  211. }
  212. } else
  213. if(lines[id][1] == ST_PAROP && lines[id][2] == ST_BOUNDED_SURFACE) {
  214. }
  215. */
  216. }
  217. /*
  218. if(!ok) {
  219. fprintf(stderr, "m3dconv: unrecognized geometry entity #%d\n", id);
  220. step_dump(type, lvl, lvl);
  221. step_dump(id, lvl, lvl + 2);
  222. }
  223. */
  224. return m3d;
  225. }
  226. /**
  227. * The actual STEP parser
  228. *
  229. * We don't need to link with opencascade neither do we need that monstrocity called NIST SCL just to parse a
  230. * text file for some coordinates... we are only interested in the part 21-4 and 24-2 schemas, and we only need
  231. * shells and faces, nothing else (maybe annotations perhaps). This function reads STEP tokens from lines[].
  232. */
  233. m3d_t *step_parse(m3d_t *m3d)
  234. {
  235. struct aiMatrix4x4 nm, tm;
  236. unsigned int i, j, k, l, m, n, b, s, t, ni, *shids = NULL;
  237. char name[6];
  238. if(!lines) return m3d;
  239. /* get the closed shell root node(s). There can be more roots in a STEP file... both vertically and horizontally */
  240. for(i = 0; i < numlines; i++)
  241. if(lines[i] && lines[i][0] > 8 && ST_TYPE(lines[i][1]) == ST_FUNC && lines[i][2] == ST_PAROP &&
  242. ST_TYPE(lines[i][3]) == ST_STRING) {
  243. /* use product name as model name */
  244. if(!m3d->name && lines[i][1] == ST_PRODUCT)
  245. m3d->name = _m3d_safestr(strings[ST_PARAM(lines[i][3])], 2);
  246. /* get the closed shell with name and transformation matrix */
  247. if((lines[i][1] == ST_ADVANCED_BREP_SHAPE_REPRESENTATION ||
  248. lines[i][1] == ST_MANIFOLD_SURFACE_SHAPE_REPRESENTATION ||
  249. lines[i][1] == ST_FACETED_BREP_SHAPE_REPRESENTATION) &&
  250. ST_ISENTITY(lines[i][6], 4)) {
  251. for(m = -1U, ni = t = 0, l = 6; l < 9 && lines[i][l] != ST_PARCL; l++) {
  252. k = ST_PARAM(lines[i][l]);
  253. if(ST_ISENTITY(lines[i][l], 4) && ST_TYPE(lines[k][1]) == ST_FUNC) {
  254. if( lines[k][1] == ST_SHELL_BASED_SURFACE_MODEL ||
  255. lines[k][1] == ST_MANIFOLD_SOLID_BREP ||
  256. lines[k][1] == ST_FACETED_BREP) {
  257. t = lines[k][1];
  258. shids = (unsigned int*)realloc(shids, (lines[k][0] - 4) * sizeof(unsigned int));
  259. if(!shids) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  260. for(j = 5; j < lines[k][0] && lines[k][j] != ST_PARCL; j++)
  261. if(ST_ISENTITY(lines[k][j], 5) &&
  262. ST_TYPE(lines[ST_PARAM(lines[k][j])][3]) == ST_STRING &&
  263. lines[ST_PARAM(lines[k][j])][1] == ST_CLOSED_SHELL)
  264. shids[ni++] = ST_PARAM(lines[k][j]);
  265. }
  266. if(lines[k][1] == ST_AXIS2_PLACEMENT_3D && ST_TYPE(lines[k][5]) == ST_ID &&
  267. ST_TYPE(lines[k][7]) == ST_ID && ST_TYPE(lines[k][9]) == ST_ID)
  268. m = k;
  269. }
  270. }
  271. if(ni) {
  272. l = numclosedshells; numclosedshells += ni;
  273. closedshells = (steproot_t*)realloc(closedshells, numclosedshells * sizeof(steproot_t));
  274. if(!closedshells) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  275. for(j = 0; j < ni; j++, l++) {
  276. /* add closed shells */
  277. closedshells[l].id = shids[j];
  278. closedshells[l].type = t;
  279. /* find out its name */
  280. if(ST_PARAM(lines[shids[j]][3]) && strings[ST_PARAM(lines[shids[j]][3])][0]) {
  281. closedshells[l].name = _m3d_safestr(strings[ST_PARAM(lines[shids[j]][3])], 0);
  282. } else {
  283. sprintf(name, "b%04x", l);
  284. closedshells[l].name = _m3d_safestr(name, 0);
  285. }
  286. /* closed shells in shell based surface records share the same matrix which is conceptually wrong, and
  287. * a clear sign that the people who designed STEP were absent from school when they learned geometry */
  288. if(m != -1U) {
  289. /* convert axis2_placement_3d in lines[m] to transformation matrix */
  290. step_placement(m, &closedshells[l].transform);
  291. } else
  292. _assimp_identity4x4(&closedshells[l].transform);
  293. }
  294. }
  295. }
  296. }
  297. if(shids) free(shids);
  298. /* if the above failed, simply look for closed shells */
  299. if(!numclosedshells)
  300. for(i = 0; i < numlines; i++)
  301. if(lines[i] && lines[i][0] > 5 && lines[i][2] == ST_PAROP && ST_TYPE(lines[i][3]) == ST_STRING &&
  302. lines[i][1] == ST_CLOSED_SHELL) {
  303. j = numclosedshells++;
  304. closedshells = (steproot_t*)realloc(closedshells, numclosedshells * sizeof(steproot_t));
  305. if(!closedshells) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  306. closedshells[j].id = i;
  307. closedshells[j].type = ST_MANIFOLD_SOLID_BREP;
  308. if(ST_PARAM(lines[i][3]) && strings[ST_PARAM(lines[i][3])][0])
  309. closedshells[j].name = _m3d_safestr(strings[ST_PARAM(lines[i][3])], 0);
  310. else {
  311. sprintf(name, "b%04x", j);
  312. closedshells[j].name = _m3d_safestr(name, 0);
  313. }
  314. /* no transformation */
  315. _assimp_identity4x4(&closedshells[j].transform);
  316. }
  317. /* parse them (if we have found any) into M3D shapes */
  318. if(numclosedshells) {
  319. /* add shells as bones */
  320. b = m3d->numbone; m3d->numbone += numclosedshells;
  321. m3d->bone = (m3db_t*)realloc(m3d->bone, m3d->numbone * sizeof(m3db_t));
  322. if(!m3d->bone) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  323. memset(&m3d->bone[b], 0, numclosedshells * sizeof(m3db_t));
  324. /* add faces as shapes */
  325. for(i = 0, s = m3d->numshape; i < numclosedshells; i++)
  326. for(l = 6, k = closedshells[i].id; l < lines[k][0] && lines[k][l] != ST_PARCL; l++)
  327. if(ST_ISENTITY(lines[k][l], 5)) m3d->numshape++;
  328. m3d->shape = (m3dh_t*)realloc(m3d->shape, m3d->numshape * sizeof(m3dh_t));
  329. if(!m3d->shape) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  330. memset(&m3d->shape[s], 0, (m3d->numshape - s) * sizeof(m3dh_t));
  331. /* parse geometry for each face */
  332. for(i = 0; i < numclosedshells; i++, b++) {
  333. m3d->bone[b].parent = (M3D_INDEX)-1U;
  334. m3d->bone[b].name = closedshells[i].name;
  335. m3d->vertex = _assimp_addspace(m3d->vertex, &m3d->numvertex, &closedshells[i].transform, VT_WORLD, &k);
  336. m3d->bone[b].pos = (M3D_INDEX)k;
  337. m3d->bone[b].ori = (M3D_INDEX)(k + 1);
  338. /* we don't need the scaling part */
  339. m3d->numvertex--;
  340. k = closedshells[i].id;
  341. for(l = 6; l < lines[k][0] && lines[k][l] != ST_PARCL; l++) {
  342. j = ST_PARAM(lines[k][l]);
  343. if(ST_ISENTITY(lines[k][l], 5)) {
  344. memcpy(&nm, &closedshells[i].transform, sizeof(struct aiMatrix4x4));
  345. for(m = t = -1U, n = 6; m == -1U && n < lines[j][0]; n++) {
  346. t = ST_PARAM(lines[j][n]);
  347. if(ST_ISENTITY(lines[j][n], 5) && ST_ISENTITY(lines[t][5], 5) &&
  348. lines[ST_PARAM(lines[t][5])][1] == ST_AXIS2_PLACEMENT_3D &&
  349. ST_TYPE(lines[ST_PARAM(lines[t][5])][5]) == ST_ID && ST_TYPE(lines[ST_PARAM(lines[t][5])][7]) == ST_ID &&
  350. ST_TYPE(lines[ST_PARAM(lines[t][5])][9]) == ST_ID)
  351. m = ST_PARAM(lines[t][5]);
  352. }
  353. if(m != -1U) {
  354. step_placement(m, &tm);
  355. _assimp_multiply4x4(&nm, &tm);
  356. }
  357. if(t == -1U || !lines[t] || !lines[t][0] || ST_TYPE(lines[t][1]) != ST_FUNC) {
  358. fprintf(stderr, "m3dconv: unknown surface type in #%d\n", j);
  359. step_dump(j, 0, 1);
  360. continue;
  361. }
  362. if(ST_TYPE(lines[j][3]) == ST_STRING && ST_PARAM(lines[j][3]) && strings[ST_PARAM(lines[j][3])][0]) {
  363. m3d->shape[s].name = _m3d_safestr(strings[ST_PARAM(lines[j][3])], 0);
  364. } else {
  365. sprintf(name, "s%04x", s);
  366. m3d->shape[s].name = _m3d_safestr(name, 0);
  367. }
  368. m3d->shape[s].group = b;
  369. for(n = 6; n < lines[j][0]; n++)
  370. if(ST_ISENTITY(lines[j][n], 5) && ST_PARAM(lines[j][n]) != t)
  371. m3d = step_geom(ST_PARAM(lines[j][n]), t, &nm, &m3d->shape[s], m3d, 0);
  372. if(m3d->shape[s].numcmd) s++;
  373. }
  374. }
  375. }
  376. m3d->numshape = s;
  377. }
  378. /* free cache */
  379. for(i = 0; i < numlines; i++)
  380. if(lines[i]) free(lines[i]);
  381. for(i = 0; i < numstrings; i++)
  382. if(strings[i]) free(strings[i]);
  383. if(lines) { free(lines); lines = NULL; }
  384. if(strings) { free(strings); strings = NULL; }
  385. if(num) { free(num); num = NULL; }
  386. if(closedshells) { free(closedshells); closedshells = NULL; }
  387. numlines = numstrings = numnum = numclosedshells = 0;
  388. return m3d;
  389. }
  390. /**
  391. * Add a string to a unique list
  392. */
  393. unsigned int step_addstr(char *s, char *e)
  394. {
  395. unsigned int i, l;
  396. if(!s) return 0;
  397. if(e) l = (unsigned int)(e - s); else l = strlen(s);
  398. if(!l || !memcmp(s, "none\'", 5) || !memcmp(s, "NONE\'", 5) || !memcmp(s, "void\'", 5) || !memcmp(s, "VOID\'", 5) ||
  399. !memcmp(s, "nil\'", 4) || !memcmp(s, "NIL\'", 4) || !memcmp(s, "null\'", 5) || !memcmp(s, "NULL\'", 5)) return 0;
  400. for(i = 0; i < numstrings; i++)
  401. if(strlen(strings[i]) == l && !memcmp(s, strings[i], l)) return i;
  402. if(numstrings >= 0x0FFFFFFF) { fprintf(stderr, "m3dconv: too many unique strings\n"); exit(1); }
  403. i = numstrings++;
  404. if(numstrings >= maxstrings) {
  405. maxstrings += 1024;
  406. strings = (char**)realloc(strings, maxstrings * sizeof(char*));
  407. if(!strings) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  408. }
  409. strings[i] = (char*)malloc(l + 1);
  410. if(!strings[i]) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  411. memcpy(strings[i], s, l);
  412. strings[i][l] = 0;
  413. return i;
  414. }
  415. /**
  416. * Add a number to a unique list
  417. */
  418. unsigned int step_addnum(float f)
  419. {
  420. unsigned int i;
  421. for(i = 0; i < numnum; i++)
  422. if(num[i] == f) return i;
  423. if(numnum >= 0x0FFFFFFF) { fprintf(stderr, "m3dconv: too many unique numbers\n"); exit(1); }
  424. i = numnum++;
  425. if(numnum >= maxnum) {
  426. maxnum += 1024;
  427. num = (float*)realloc(num, maxnum * sizeof(float));
  428. if(!num) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  429. }
  430. num[i] = f;
  431. return i;
  432. }
  433. /**
  434. * Load a model and convert it's structures into a Model 3D in-memory format
  435. */
  436. m3d_t *step_load(char *data)
  437. {
  438. unsigned int idx, i, j, k, line = 1;
  439. char *s, *e, np;
  440. m3d_t *m3d;
  441. float f = 0.0f;
  442. m3d = (m3d_t*)malloc(sizeof(m3d_t));
  443. if(!m3d) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  444. memset(m3d, 0, sizeof(m3d_t));
  445. m3d->flags = M3D_FLG_FREESTR;
  446. /* parse header */
  447. while(*data && memcmp(data, "ENDSEC;", 7)) {
  448. if(data[0] == '/' && data[1] == '*') {
  449. for(data += 2; *data && (data[-1] != '/' || data[-2] != '*'); data++)
  450. if(data[0] == '\n') line++;
  451. continue;
  452. }
  453. if(!memcmp(data, "FILE_NAME", 9)) {
  454. for(data += 9, np = 0; *data && *data != ';'; data++) {
  455. if(data[0] == '/' && data[1] == '*') {
  456. for(data += 2; *data && (data[0] != '/' || data[-1] != '*'); data++)
  457. if(data[0] == '\n') line++;
  458. continue;
  459. }
  460. if(data[0] == '\'') {
  461. data++;
  462. for(s = data; data[0] && data[0] != '\''; data++);
  463. if(np == 2) { *data++ = 0; m3d->author = _m3d_safestr(s, 2); }
  464. }
  465. if(*data == ',') np++;
  466. }
  467. }
  468. if(!memcmp(data, "FILE_DESCRIPTION", 16)) {
  469. data += 16;
  470. while(*data && *data != '\'') {
  471. if(data[0] == '/' && data[1] == '*') {
  472. for(data += 2; *data && (data[-1] != '/' || data[-2] != '*'); data++)
  473. if(data[0] == '\n') line++;
  474. continue;
  475. }
  476. data++;
  477. }
  478. if(*data == '\'') {
  479. data++;
  480. for(s = data; data[0] && data[0] != '\''; data++);
  481. *data++ = 0;
  482. s = _m3d_safestr((char*)s, 2);
  483. if(s && *s) {
  484. m3d->desc = (char*)malloc(strlen(s) + 56);
  485. if(!m3d->desc) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  486. sprintf(m3d->desc, "ISO-10303\nGENERATOR=m3dconv-1.0.0\nFILE_DESCRIPTION=%s\n", s);
  487. free(s);
  488. }
  489. }
  490. }
  491. if(data[0] == '\n') line++;
  492. data++;
  493. }
  494. if(*data) data++;
  495. /* parse data */
  496. step_addstr("(null)", NULL);
  497. while(*data) {
  498. /* find data section blocks */
  499. while(*data && memcmp(data, "DATA;", 5)) {
  500. /* skip comments and keep track of the actual line number */
  501. if(data[0] == '/' && data[1] == '*') {
  502. for(data += 2; *data && (data[-1] != '/' || data[-2] != '*'); data++)
  503. if(data[0] == '\n') line++;
  504. continue;
  505. }
  506. if(data[0] == '\n') line++;
  507. data++;
  508. }
  509. /* clean up STEP statements. This is a nightmare, some idiots must have designed the spec */
  510. while(*data && memcmp(data, "ENDSEC;", 7)) {
  511. data = _m3d_findnl(data);
  512. line++;
  513. for(; *data == ' ' || *data == '\t' || *data == '\r' || *data == '\n' || *data == '/' || *data == ';'; data++) {
  514. if(data[0] == '/' && data[1] == '*') {
  515. for(data += 2; *data && (data[0] != '/' || data[-1] != '*'); data++)
  516. if(data[0] == '\n') line++;
  517. }
  518. if(data[0] == '\n') line++;
  519. }
  520. /* first, read the lines into a clean tokenized form. We must read *EVERYTHING* in advance, because identifier
  521. * references can be backward and forward as well... Even worse, lines can be in *ANY* order... What kind of fool
  522. * designed this file format??? This supposed to be a declerative language only, not a fully blown, insane
  523. * programming language structure! */
  524. if(*data == '#') {
  525. data = _m3d_getint(data + 1, &idx);
  526. for(; *data == ' ' || *data == '\t' || *data == '\r' || *data == '\n' || *data == '/' || *data == '='; data++) {
  527. if(data[0] == '/' && data[1] == '*') {
  528. for(data += 2; *data && (data[0] != '/' || data[-1] != '*'); data++)
  529. if(data[0] == '\n') line++;
  530. }
  531. if(data[0] == '\n') line++;
  532. }
  533. if(idx >= numlines) {
  534. i = numlines; numlines += 512;
  535. if(numlines >= 0x0FFFFFFF) { fprintf(stderr, "m3dconv: too many entities\n"); exit(1); }
  536. lines = (unsigned int**)realloc(lines, numlines * sizeof(unsigned int*));
  537. if(!lines) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  538. memset(&lines[i], 0, (numlines - i) * sizeof(unsigned int*));
  539. }
  540. /* find the actual end of the statement... don't increment line yet */
  541. for(s = data, i = 0; *data && (i || *data != ';'); data++) {
  542. if(data[0] == '/' && data[1] == '*') {
  543. for(data += 2; *data && (data[0] != '/' || data[-1] != '*'); data++);
  544. continue;
  545. }
  546. if(data[0] == '\'') {
  547. for(data++; *data && data[0] != '\''; data++)
  548. if(data[0] == '\\') data++;
  549. continue;
  550. }
  551. if(*data == '(') i++;
  552. if(*data == ')') i--;
  553. }
  554. /* assume every character is a different token */
  555. lines[idx] = (unsigned int*)realloc(lines[idx], ((int)(data - s) + 1) * sizeof(unsigned int));
  556. if(!lines[idx]) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  557. /* tokenize statement */
  558. for(i = 1; s < data;) {
  559. if(s[0] == '/' && s[1] == '*') {
  560. for(s += 2; s < data && *s && (s[-1] != '/' || s[-2] != '*'); s++)
  561. if(s[0] == '\n') line++;
  562. continue;
  563. } else
  564. if(*s == ' ' || *s == '\r' || *s == '\n' || *s == '\t' || *s == ';') {
  565. if(s[0] == '\n') line++;
  566. s++;
  567. } else
  568. if(*s == ',') { lines[idx][i++] = ST_COMMA; s++; } else
  569. if(*s == '(') { lines[idx][i++] = ST_PAROP; s++; } else
  570. if(*s == ')') { lines[idx][i++] = ST_PARCL; s++; } else
  571. if(*s == '$') { lines[idx][i++] = ST_DOLLAR; s++; } else
  572. if(*s == '*') { lines[idx][i++] = ST_ASTERIX; s++; } else
  573. if(*s == '.') {
  574. for(e = s + 1; e < data && *e && *e != '.'; e++);
  575. e++;
  576. lines[idx][i++] = ST_TOKEN(ST_ENUM, step_addstr(s, e));
  577. s = e;
  578. } else
  579. if(*s == '\'') {
  580. for(s++, e = s; e < data && *e && *e != '\''; e++) { if(*e == '\\') e++; }
  581. lines[idx][i++] = ST_TOKEN(ST_STRING, step_addstr(s, e));
  582. s = e + 1;
  583. } else
  584. if((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z')) {
  585. for(e = s; e < data && *e && *e != '\'' && *e != '\"' && *e != '/' && *e != '(' && *e != ')' &&
  586. *e != ',' && *e != ';' && *e != ' ' && *e != '\t' && *e != '\r' && *e != '\n'; e++);
  587. for(k = 0, j = -1U; k < (unsigned int)(sizeof(labels)/sizeof(labels[0])); k++)
  588. if(strlen(labels[k]) == (unsigned int)(e - s) && !memcmp(labels[k], s, e - s)) { j = k; break; }
  589. if(j == -1U)
  590. lines[idx][i++] = ST_TOKEN(ST_LABEL, step_addstr(s, e));
  591. else
  592. lines[idx][i++] = ST_TOKEN(ST_FUNC, j);
  593. s = e;
  594. } else if(*s == '#') {
  595. s = _m3d_getint(s + 1, &j);
  596. lines[idx][i++] = ST_TOKEN(ST_ID, j);
  597. } else if(*s == '\"') {
  598. /* FIXME: WTF is unused bits indicator? is this BE really or sometimes LE? Why couldn't they
  599. * just simply use the well-known 0x1234 format like everybody else on the entire planet??? */
  600. /*
  601. idx = 0;
  602. s = _m3d_gethex(s + 2, &j);
  603. ((unsigned char*)&f)[3] = ((unsigned char*)&j)[0];
  604. ((unsigned char*)&f)[2] = ((unsigned char*)&j)[1];
  605. ((unsigned char*)&f)[1] = ((unsigned char*)&j)[2];
  606. ((unsigned char*)&f)[0] = ((unsigned char*)&j)[3];
  607. lines[idx][i++] = ST_TOKEN(ST_NUM, step_addnum(f));
  608. */
  609. fprintf(stderr, "m3dconv: warning, unparsed hex value '%c%c%c%c...' in line %d\n",
  610. s[0], s[1], s[2], s[3], line);
  611. for(; s < data && *s && *s != '\"'; s++);
  612. s++;
  613. } else if(*s == '-' || (*s >= '0' && *s <= '9')) {
  614. for(e = s; *e == '-' || *e == '+' || *e == '.' || (*e >= '0' && *e <= '9') || *e == 'e' || *e == 'E'; e++);
  615. f = (float)strtod(s, &s);
  616. lines[idx][i++] = ST_TOKEN(ST_NUM, step_addnum(f));
  617. } else {
  618. /* should never reach this */
  619. fprintf(stderr, "m3dconv: warning, unrecognized token '%c%c%c%c...' in line %d\n",
  620. s[0], s[1], s[2], s[3], line);
  621. s++;
  622. }
  623. }
  624. lines[idx][0] = i;
  625. lines[idx][i++] = ST_END;
  626. /* free extra memory */
  627. lines[idx] = (unsigned int*)realloc(lines[idx], i * sizeof(unsigned int));
  628. if(!lines[idx]) { fprintf(stderr, "m3dconv: unable to allocate memory\n"); exit(1); }
  629. }
  630. }
  631. /* when we reached endsec, then we have all the identifiers, so it is time to parse the tokens */
  632. m3d = step_parse(m3d);
  633. }
  634. if(!m3d->numshape)
  635. fprintf(stderr, "m3dconv: no closed shell b-rep was found in STEP file.\n");
  636. return m3d;
  637. }