step.h 32 KB

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