FreeWRL/FreeX3D  3.0.0
convertSTL.c
1 /****************************************************************************
2  This file is part of the FreeWRL/FreeX3D Distribution.
3 
4  Copyright 2014 CRC Canada. (http://www.crc.gc.ca)
5 
6  FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
7  it under the terms of the GNU Lesser Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  FreeWRL/FreeX3D is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
18 ****************************************************************************/
19 
20 
21 #include <config.h>
22 #include <system.h>
23 #include <display.h>
24 #include <internal.h>
25 #include <stdio.h>
26 
27 #include <stdint.h>
28 #include <float.h>
29 
30 #include <libFreeWRL.h>
31 
32 #include "input/convertSTL.h"
33 
34 #if defined (INCLUDE_STL_FILES)
35 
36 // the STL_FLOAT_TOLERANCE is chosen to be small enough
37 // to not trip Polyrep degenerate finding, but big enough
38 // to actually catch some surfaces.
39 
40 //#define STL_FLOAT_TOLERANCE_TIGHT 0.000001
41 #define STL_FLOAT_TOLERANCE_USUAL 0.00001
42 
43 //#define STL_FLOAT_TOLERANCE_LOOSE 0.001
44 static double stl_vertex_tolerance = STL_FLOAT_TOLERANCE_USUAL;
45 
46 
47 
48 
49 #define DO_QUICKSORT
50 
51 /* JAS STL */
52 #define STL_ASCII_HEADER "solid "
53 #define STL_BINARY_HEADER_LEN 84 // header + uint32
54 #define STL_BINARY_VERTEX_SIZE 50 // 12 * 4 + uint16
55 
56 #define tmpFile "/vrmlSTLFile.wrl"
57 
58 
59 struct tbinarySTLvertexIn {
60  struct SFVec3f normal;
61  struct SFVec3f V1;
62  struct SFVec3f V2;
63  struct SFVec3f V3;
64  unsigned char attrib1;
65  unsigned char attrib2;
66 }binarySTLvertexIn;
67 
68 struct tstlVertexStruct {
69  struct SFVec3f vertex; // original x,y,z
70  int replacementVertex; // if this is the same as another vertex
71  int condensedVertexNo; // when finding duplicate vertices, we need a new index for coordIndex
72  double dist; // distance from origin - used in sorting verticies
73 }stlVertexStruct;
74 
75 
76 /****************************************************************/
77 typedef struct pSTLHandler{
78  // stats for us to display, should we wish.
79  int degenerateCount; //=0;
80  int triangleInCount; //=0;
81  int finalCoordsWritten; //=0;
82 
83  float scaleFactor; // = 1.0;
84 
85  // do we generate our own normals, or just use what's given?
86  // if true, we just use the normals as supplied by the author
87  int analyzeSTL; // = true;
88 
89  // do we check and display edge errors and 2-manifold errors?
90  int checkSTL_for_3Dprinting; // = false;
91 
92  unsigned char *vectorArray; // =NULL;
93 }* ppSTLHandler;
94 
95 
96 void *STLHandler_constructor(){
97  void *v = malloc(sizeof(struct pSTLHandler));
98  memset(v,0,sizeof(struct pSTLHandler));
99  return v;
100 }
101 
102 void STL_Handler_init(struct tSTLHandler *t){
103  //public
104  //private
105  t->prv = STLHandler_constructor();
106  {
107  ppSTLHandler p = (ppSTLHandler)t->prv;
108  p->scaleFactor=1.0;
109  p->analyzeSTL = TRUE;
110  p->checkSTL_for_3Dprinting = FALSE;
111  }
112 }
113 /****************************************************************/
114 
115 /* if looking for bad things in STL rendering for 3D printing */
116 /* shove 4 vertexes into 1 byte, as we only care about 0, 1, 2, 3 for
117  vector uses */
118 static void recordVector(int a, int b, ppSTLHandler p) {
119  int ind;
120  int box,bits;
121  if (a>b) { int x; x=a; a=b; b=x;}
122  //ConsoleMessage ("a %d b %d size %d ind %d\n", a,b,size,a*size+b);
123  ind = a*p->finalCoordsWritten +b;
124  box = ind/4, bits = ind %4;
125 
126  //ConsoleMessage ("ori: ind %d box %d bits %d content %x\n",ind, box, bits,vectorArray[box]);
127  switch (bits) {
128  case 0: { int z = p->vectorArray[box] & 0x03;
129  if (z !=0x03) p->vectorArray[box]+= 0x01;
130  break; }
131  case 1: {
132  int z = p->vectorArray[box] & 0x0C;
133  if (z !=0x0C) p->vectorArray[box]+=0x04;
134  break; }
135  case 2: {
136  int z= p->vectorArray[box] & 0x30;
137  if (z !=0x30) p->vectorArray[box]+= 0x10;
138  break; }
139  case 3:{
140  int z= p->vectorArray[box] & 0xC0;
141  if (z !=0xC0) p->vectorArray[box]+= 0x40;
142  break; }
143  default: {}// should never get here
144  }
145  //ConsoleMessage ("now: ind %d box %d bits %d content %x\n\n",ind, box, bits,p->vectorArray[box]);
146 }
147 
148 static char returnIndex (int size, int a, int b, ppSTLHandler p) {
149  int ind;
150  int box,bits;
151  if (a>b) { int x; x=a; a=b; b=x;}
152  ind = a*size+b;
153  box = ind/4, bits = ind %4;
154 
155  switch (bits) {
156  case 0: return p->vectorArray[box] & 0x03; break;
157  case 1: return (p->vectorArray[box] & 0x0C) >> 2; break;
158  case 2: return (p->vectorArray[box] & 0x30) >> 4; break;
159  case 3: return (p->vectorArray[box] & 0xC0) >> 6; break;
160  default: {}// should never get here
161  }
162  return 0;
163 }
164 
165 
166 /* if we are running fast, we can throw away vertices, if checking for
167  printing, do exact comparisons */
168 int SAPPROX(double a, double b) {
169 
170 /*
171  if (checkSTL_for_3Dprinting) {
172  return (a==b);
173  }
174 */
175 
176  return (fabs(a-b) < stl_vertex_tolerance);
177 }
178 
179 
180 int stlDTFT(const unsigned char* buffer, int len)
181 {
182  int32_t *stllen;
183 
184  unsigned char *tmp = (unsigned char *)buffer;
185  while ((tmp != NULL) && (*tmp<=' ')) tmp++;
186 
187  // lets see if this is a binary stl file.
188  ConsoleMessage ("stldtft, file len %x\n",len);
189  if (len > 85) {
190  stllen = offsetPointer_deref(int32_t *, buffer, 80);
191  ConsoleMessage ("triangle count %x\n",*stllen);
192 
193  ConsoleMessage ("sizeof struct binarySTLvertexIn %d\n",sizeof(binarySTLvertexIn));
194  ConsoleMessage ("binarySTLvertexIn * triangles + 80 + 1 + 4 = %x\n" , 50 * (*stllen) + 80 + 4);
195  ConsoleMessage ("and, passed in length is %x\n",len);
196  ConsoleMessage ("last two bytes %x %x\n",buffer[len-2],buffer[len-1]);
197 
198 
199  if (len==((*stllen)*50+STL_BINARY_HEADER_LEN)) return IS_TYPE_BINARY_STL;
200 
201  // have a file with two nulls on end, and len is one byte more, so
202  // try just doing this:
203  if (len==((*stllen)*50+STL_BINARY_HEADER_LEN+1)) return IS_TYPE_BINARY_STL;
204  }
205 
206  // no, maybe it is an ASCII stl file?
207  if (strncmp((const char*)tmp,STL_ASCII_HEADER,strlen(STL_ASCII_HEADER)) == 0) {
208  ConsoleMessage ("is ASCII stl");
209  return IS_TYPE_ASCII_STL;
210  }
211 
212  return IS_TYPE_UNKNOWN;
213 }
214 
215 static void calcExtent_dist(float *_extent, struct tstlVertexStruct *me) {
216  if (me->vertex.c[0] > EXTENT_MAX_X) EXTENT_MAX_X = me->vertex.c[0];
217  if (me->vertex.c[0] < EXTENT_MIN_X) EXTENT_MIN_X = me->vertex.c[0];
218  if (me->vertex.c[1] > EXTENT_MAX_Y) EXTENT_MAX_Y = me->vertex.c[1];
219  if (me->vertex.c[1] < EXTENT_MIN_Y) EXTENT_MIN_Y = me->vertex.c[1];
220  if (me->vertex.c[2] > EXTENT_MAX_Z) EXTENT_MAX_Z = me->vertex.c[2];
221  if (me->vertex.c[2] < EXTENT_MIN_Z) EXTENT_MIN_Z = me->vertex.c[2];
222 
223  me->dist = me->vertex.c[0]*me->vertex.c[0] +
224  me->vertex.c[1]*me->vertex.c[1] +
225  me->vertex.c[2]*me->vertex.c[2];
226 }
227 
228 
229 #ifdef DO_QUICKSORT
230 /* quickSort vertices by distance */
231 static void quickSort(struct Vector* vertices,int *newVertexIndex, int left, int right) {
232 //void quickSort(int arr[], int left, int right) {
233  int i = left, j = right;
234  int tmp;
235  struct tstlVertexStruct *a, *pivot;
236 
237  pivot= vector_get(struct tstlVertexStruct*, vertices, newVertexIndex[(left+right)/2]);
238  /* partition */
239  while (i <= j) {
240  a = vector_get(struct tstlVertexStruct*, vertices, newVertexIndex[i]);
241  while (a->dist < pivot->dist) {
242  i++;
243  a = vector_get(struct tstlVertexStruct*, vertices, newVertexIndex[i]);
244  }
245  a = vector_get(struct tstlVertexStruct*, vertices, newVertexIndex[j]);
246  while (a->dist > pivot->dist) {
247  j--;
248  a = vector_get(struct tstlVertexStruct*, vertices, newVertexIndex[j]);
249  }
250  if (i <= j) {
251  tmp = newVertexIndex[i];
252  newVertexIndex[i] = newVertexIndex[j];
253  newVertexIndex[j] = tmp;
254  i++;
255  j--;
256  }
257  };
258 
259  /* recursion */
260  if (left < j)
261  quickSort(vertices,newVertexIndex, left, j);
262  if (i < right)
263  quickSort(vertices,newVertexIndex, i, right);
264 }
265 
266 #else //DO_QUICKSORT
267 
268 /* bubble sort vertices by distance */
269 static void bubbleSort(struct Vector* vertices,int *newVertexIndex) {
270  int i,j,k;
271  int totIn = vectorSize(vertices);
272  bool noswitch;
273 
274 
275  // go through, and use this newVertexIndex as a "sorted" index to vertices.
276  // we sort based on squared distance from (0,0,0), assuming STL files are
277  // positive based; we'll do a final verification for vertex matching later.
278  for(i=0; i<totIn; i++) {
279  noswitch = TRUE;
280  for (j=(totIn-1); j>i; j--) {
281  struct tstlVertexStruct *a, *b;
282 
283  /* printf ("comparing %d %d\n",i,j); */
284  a = vector_get(struct tstlVertexStruct*, vertices, newVertexIndex[j-1]);
285  b = vector_get(struct tstlVertexStruct*, vertices, newVertexIndex[j]);
286 
287  /* check to see if a child is NULL - if so, skip it */
288  if (a && b) {
289  if (a->dist > b->dist) {
290  k = newVertexIndex[j-1];
291  newVertexIndex[j-1] = newVertexIndex[j];
292  newVertexIndex[j] = k;
293  noswitch = FALSE;
294 
295  }
296  }
297  }
298  /* did we have a clean run? */
299  if (noswitch) {
300  break;
301  }
302  }
303 }
304 #endif //DO_QUICKSORT
305 
306 
307 
308 
309 /* go through, and make this compact and good for displaying */
310 void analyzeSTLdata(struct Vector* vertices) {
311  int i,j;
312  int totIn = vectorSize(vertices);
313 
314  int replacedVertexCount=0;
315  int newVertexNumber=0; // for new coordIndex to point to
316 
317  int* newVertexIndex = MALLOC(int*, sizeof (int)*totIn+1);
318 /*
319  for (i=0; i<totIn; i++) {
320  struct tstlVertexStruct *thisVertex = vector_get(struct tstlVertexStruct *,vertices,i);
321  ConsoleMessage ("vertex %d, %f %f %f, dist %lf",i,thisVertex->vertex.c[0],thisVertex->vertex.c[1],thisVertex->vertex.c[2]
322  ,thisVertex->dist);
323  }
324 */
325 
326 #include <sys/time.h>
327  struct timeval tv;
328  struct timeval tve;
329 
330  gettimeofday(&tv, NULL);
331 
332  // initialize the newVertexIndex to do a 1:1 match with original index
333  for (i=0; i<totIn; i++) newVertexIndex[i] = i;
334 
335 #ifdef DO_QUICKSORT
336  quickSort(vertices,newVertexIndex,0,totIn-1);
337 #else
338  bubbleSort(vertices,newVertexIndex);
339 #endif
340 
341  gettimeofday(&tve,NULL);
342  //ConsoleMessage("step 1, time %ld usec %d\n",tve.tv_sec-tv.tv_sec, tve.tv_usec-tv.tv_usec);
343  gettimeofday(&tv, NULL);
344 
345  // ok, vertices sorted by distance. How many duplicates might we have?
346  for (i=0; i<totIn-1; i++) {
347  struct tstlVertexStruct *startVertex = vector_get(struct tstlVertexStruct *,vertices,newVertexIndex[i]);
348 
349  // if this vertex has not been replaced yet, see if any other vertices are
350  // close enough to have THIS one replace THEM
351  if (startVertex->replacementVertex == -1) {
352  j=i+1;
353  struct tstlVertexStruct *runVertex = vector_get(struct tstlVertexStruct *,
354  vertices,newVertexIndex[j]);
355 
356  while (SAPPROX(startVertex->dist,runVertex->dist) && (j<totIn)) {
357  // do the vertexes actually match?
358  if ((SAPPROX(startVertex->vertex.c[0],runVertex->vertex.c[0]) &&
359  SAPPROX(startVertex->vertex.c[1],runVertex->vertex.c[1]) &&
360  SAPPROX(startVertex->vertex.c[2],runVertex->vertex.c[2]))) {
361  // distance AND vertex xyz are the same
362  //JAS - runVertex->replacementVertex = i;
363  runVertex->replacementVertex = newVertexIndex[i];
364 
365  replacedVertexCount++;
366  }
367  j++;
368  if (j<totIn)
369  runVertex=vector_get(struct tstlVertexStruct *, vertices, newVertexIndex[j]);
370  }
371  }
372  }
373 
374 
375 
376  for (i=0; i<totIn; i++) {
377  //JAS struct tstlVertexStruct *thisVertex = vector_get(struct tstlVertexStruct *,vertices,newVertexIndex[i]);
378  struct tstlVertexStruct *thisVertex = vector_get(struct tstlVertexStruct *,vertices,i);
379  if (thisVertex->replacementVertex== -1) {
380  thisVertex->condensedVertexNo = newVertexNumber;
381  newVertexNumber++;
382  }
383 
384  /*
385  ConsoleMessage ("vertex %d, rep %d, nv %d, %f %f %f, dist %lf",i,
386  thisVertex->replacementVertex,
387  thisVertex->condensedVertexNo,
388  thisVertex->vertex.c[0],thisVertex->vertex.c[1],thisVertex->vertex.c[2]
389  ,thisVertex->dist);
390  */
391  }
392  //ConsoleMessage ("ConvertToSTL: totVertexCount %d, replacedVertexCount %d",totIn,replacedVertexCount);
393 
394  gettimeofday(&tve,NULL);
395  //ConsoleMessage ("step 2, time %ld usec %d\n",tve.tv_sec-tv.tv_sec, tve.tv_usec-tv.tv_usec);
396 
397  FREE_IF_NZ(newVertexIndex);
398 
399 
400 }
401 
402 
403 static char *finishThisX3DFile (FILE *fp, int cp, char *tfn, float* _extent, int vertexCount,int coordCount, ppSTLHandler p) {
404  char *retval = NULL;
405  int fread_val = 0;
406  float extentX = EXTENT_MAX_X - EXTENT_MIN_X;
407  float extentY = EXTENT_MAX_Y - EXTENT_MIN_Y;
408  float extentZ = EXTENT_MAX_Z - EXTENT_MIN_Z;
409  p->scaleFactor = -1000.0f;
410 
411 
412  //ConsoleMessage ("extentX %f extentY %f extentZ %f",extentX,extentY,extentZ);
413 
414  // move this shape to 0,0,0
415  if (vertexCount == 0) {
416  int i;
417  for (i=0; i<6; i++) _extent[i]=0.0f;
418  ConsoleMessage ("No vertices found in STL file");
419  } else {
420  float midX, midY, midZ;
421 
422  //ConsoleMessage ("Extent, %f %f %f\n",extentX, extentY, extentZ);
423  if (extentX > p->scaleFactor) p->scaleFactor = extentX;
424  if (extentY > p->scaleFactor) p->scaleFactor = extentY;
425  if (extentZ > p->scaleFactor) p->scaleFactor = extentZ;
426 
427  //ConsoleMessage ("scaling is %f",10.0f/scaleFactor);
428 
429  midX = -EXTENT_MIN_X - (extentX/2.0f);
430  midY = -EXTENT_MIN_Y - (extentY/2.0f);
431  midZ = -EXTENT_MIN_Z - (extentZ/2.0f);
432 
433 
434  // make the shape fit within a 10x10x10 X3D box.
435  cp += fprintf (fp,"}} \n");
436 
437  if (p->checkSTL_for_3Dprinting && (coordCount>0) && (p->vectorArray)) {
438  int x;
439  int edgesFound = 0; int manifoldErrorsFound = 0;
440  bool issuesFound = false;
441 
442  /* quick check - any issues? */
443  for (x=0; x<(coordCount * coordCount/4);x++) {
444  if ((p->vectorArray[x]& 0x55) != 0x00) {
445  issuesFound = TRUE;
446  break;
447  }
448  }
449 
450  //if (issuesFound) ConsoleMessage ("have issues = go through and get info ");
451 
452  if (issuesFound) {
453  int a,b;
454 
455  cp += fprintf (fp,"Shape{appearance Appearance{\n");
456  cp += fprintf (fp,"lineProperties LineProperties {linewidthScaleFactor 4.0}\n");
457  cp += fprintf (fp, "material Material{emissiveColor 1 0 0}}geometry IndexedLineSet {\n");
458  cp += fprintf (fp," coord USE STL_COORDS\n");
459  cp += fprintf (fp," coordIndex [\n");
460 
461  /* look for abnormal counts here. For vectors,
462  if count = 0, ok;
463  if count = 1, edge, one side is open.
464  if count = 2, ok;
465  if count = 3, manifold problem. (note, never care if >3)
466  */
467 
468  for (a=0; a<coordCount; a++) {
469  for (b=a; b<coordCount; b++) {
470  unsigned char vc = returnIndex(coordCount,a,b,p);
471  //ConsoleMessage ("count for %d,%d is %d\n",a,b,vc);
472  if ((vc & 0x01) == 0x01) {
473  cp += fprintf (fp, "%d, %d, -1,\n",a,b);
474  if (vc == 0x01) edgesFound++; else manifoldErrorsFound++;
475 
476  }
477 
478  }
479  }
480 
481  cp += fprintf (fp,"]\n");
482  cp += fprintf (fp,"}}\n");
483  ConsoleMessage("Checking STL file - %d edges %d manifold issues",edgesFound,manifoldErrorsFound);
484 
485  }
486  }
487 
488  cp += fprintf (fp," ] translation %f %f %f}] scale %f %f %f}\n",midX, midY, midZ,10.0f/p->scaleFactor,
489  10.0f/p->scaleFactor, 10.0f/p->scaleFactor);
490 
491  midX = EXTENT_MAX_X-EXTENT_MIN_X;
492  midY = EXTENT_MAX_Y-EXTENT_MIN_Y;
493  //midZ = EXTENT_MAX_Z-EXTENT_MIN_Z;
494  //printf ("midX %f midY %f\n",midX,midY);
495  if (midX<midY)midX=midY;
496  }
497  cp += fprintf (fp,"Viewpoint {jump FALSE position 0 0 20} \n");
498  cp += fprintf (fp,"Viewpoint {jump FALSE position 0 0 25} \n");
499  cp += fprintf (fp,"Viewpoint {jump FALSE position 0 0 40} \n");
500 
501  cp += fprintf (fp,"Viewpoint {jump FALSE orientation 0.0 -1.0 0.0 -1.57 position 20.0 0.0 0.0} \n");
502  cp += fprintf (fp,"Viewpoint {jump FALSE orientation 0.0 -1.0 0.0 -3.14 position 0.0 0.0 -20.0} \n");
503  cp += fprintf (fp,"Viewpoint {jump FALSE orientation 0.0 -1.0 0.0 -4.748 position -20.0 0 0} \n");
504 
505  cp += fprintf (fp,"Viewpoint {jump FALSE orientation -1.0 0.0 0.0 -1.57 position 0 -20.0 0} \n");
506 
507 
508 
509 
510 
511  cp += fprintf (fp,"Viewpoint {jump FALSE orientation -0.5888, -0.5688, -0.5743, -2.125 position 20.0 0 0} \n");
512 
513  cp += fprintf (fp,"Viewpoint {jump FALSE orientation -0.04558, -0.4841, -0.8739, 3.108 position 1.928, 16.93, 11.01} \n");
514 
515 
516  //cp += fprintf(fp,"Shape { appearance Appearance {material Material{}}geometry Sphere{radius 3.0}}\n");
517 
518 
519  ConsoleMessage ("STL size: (%4.2f,%4.2f), (%4.2f,%4.2f) (%4.2f,%4.2f)",EXTENT_MIN_X,EXTENT_MAX_X,
520  EXTENT_MIN_Y,EXTENT_MAX_Y, EXTENT_MIN_Z,EXTENT_MAX_Z);
521 
522  fclose (fp);
523 
524  retval = MALLOC (char *, cp+10);
525  fp = fopen(tfn,"r");
526  fread_val = fread(retval,cp,1,fp);
527  ConsoleMessage ("fread is %d\n",fread_val);
528  retval[cp]='\0';
529  fclose (fp);
530  unlink(tfn);
531 
532  FREE_IF_NZ(tfn);
533  FREE_IF_NZ(p->vectorArray);
534 
535  //printf ("file is\n%s",retval);
536  return retval;
537 }
538 
539 
540 static char *makeX3D_orig_STL_File(struct Vector* vertices,
541  struct Vector* normals,
542  struct Vector* colours,
543  float* _extent) {
544  char *tfn = NULL;
545  FILE *fp;
546  ttglobal tg = gglobal();
547  int cp = 0;
548  int i;
549 
550  ppSTLHandler p = tg->STLHandler.prv;
551 
552  tfn=MALLOC(char *,strlen(tg->Mainloop.tmpFileLocation) +strlen (tmpFile) + 10);
553  strcpy(tfn,tg->Mainloop.tmpFileLocation);
554  strcat(tfn,tmpFile);
555 
556  // Android 2.2, TEMPNAM does not work, gives back a file that can not be opened.
557  // however, OSX, etc, can use TEMPNAM, so we can use it here.
558 
559  #if !defined (_ANDROID)
560  ConsoleMessage ("starting makeX3D_analyzed_STL_File; tmpFileLocation is :%s:\n",tg->Mainloop.tmpFileLocation);
561  tfn = TEMPNAM(tg->Mainloop.tmpFileLocation,"/freewrl_tmp");
562 #endif
563 
564  fp = fopen(tfn,"w");
565  cp += fprintf (fp,"#VRML V2.0 utf8\n");
566  cp += fprintf (fp,"Background {skyColor [ 0.7 0.7 1.0 ]}\n");
567  cp += fprintf (fp,"Transform { children [Transform { children [Shape{\n");
568  cp += fprintf (fp,"appearance Appearance{material TwoSidedMaterial{separateBackColor TRUE diffuseColor 0.8 0.8 0.8 backDiffuseColor 0.8 0 0}}\n");
569  cp += fprintf (fp,"geometry TriangleSet {\n");
570  cp += fprintf (fp,"normalPerVertex FALSE\n");
571  cp += fprintf (fp,"solid FALSE\n");
572  cp += fprintf (fp,"coord DEF STL_COORDS Coordinate { point [\n");
573 
574  for (i=0; i<vectorSize(vertices); i++) {
575  struct tstlVertexStruct *thisVertex = vector_get(struct tstlVertexStruct *,vertices,i);
576  //calcExtent_dist(_extent,thisVertex);
577  cp += fprintf (fp,"%f %f %f,\n",thisVertex->vertex.c[0],thisVertex->vertex.c[1],thisVertex->vertex.c[2]);
578  }
579 
580  cp += fprintf (fp,"]}\n");
581 
582  //ConsoleMessage ("skipping normals");
583 
584  if (normals!=NULL) {
585  cp += fprintf (fp,"normal Normal { vector [\n");
586  for (i=0; i<vectorSize(normals); i++) {
587  struct SFVec3f *thisVertex = vector_get(struct SFVec3f *,normals,i);
588  cp += fprintf (fp,"%f %f %f,\n",thisVertex->c[0],thisVertex->c[1],thisVertex->c[2]);
589  }
590 
591  cp += fprintf (fp,"]}\n");
592  }
593 
594 
595  // 3 vertices makes for 1 triangle, but we keep track of vertices here
596  p->finalCoordsWritten = vectorSize(vertices);
597 
598  return finishThisX3DFile (fp, cp, tfn, _extent,vectorSize(vertices),0,p);
599 }
600 
601 
602 static char *makeX3D_analyzed_STL_File(struct Vector* vertices,
603  struct Vector* normals,
604  struct Vector* colours,
605  float* _extent) {
606  char *tfn = NULL;
607  FILE *fp;
608  ttglobal tg = gglobal();
609  int cp = 0;
610  int i;
611 
612  ppSTLHandler p = tg->STLHandler.prv;
613 
614 
615  tfn=MALLOC(char *,strlen(tg->Mainloop.tmpFileLocation) +strlen (tmpFile) + 10);
616  strcpy(tfn,tg->Mainloop.tmpFileLocation);
617  strcat(tfn,tmpFile);
618 
619  // Android 2.2, TEMPNAM does not work, gives back a file that can not be opened.
620  // however, OSX, etc, can use TEMPNAM, so we can use it here.
621 
622  #if !defined (_ANDROID)
623  ConsoleMessage ("starting makeX3D_analyzed_STL_File; tmpFileLocation is :%s:\n",tg->Mainloop.tmpFileLocation);
624  tfn = TEMPNAM(tg->Mainloop.tmpFileLocation,"/freewrl_tmp");
625 #endif
626 
627  fp = fopen(tfn,"w");
628 
629  cp += fprintf (fp,"#VRML V2.0 utf8\n");
630  cp += fprintf (fp,"Background {skyColor [ 0.7 0.7 1.0 ]}\n");
631  cp += fprintf (fp,"Transform {children [Transform { children [Shape{\n");
632  cp += fprintf (fp,"appearance Appearance{material Material{}}\n");
633  cp += fprintf (fp,"geometry IndexedFaceSet {\n");
634  cp += fprintf (fp,"normalPerVertex FALSE\n");
635  cp += fprintf (fp,"solid FALSE\n");
636  cp += fprintf (fp,"creaseAngle 0.75\n");
637  cp += fprintf (fp,"coord DEF STL_COORDS Coordinate { point [\n");
638 
639  for (i=0; i<vectorSize(vertices); i++) {
640  struct tstlVertexStruct *thisVertex = vector_get(struct tstlVertexStruct *,vertices,i);
641  if (thisVertex->replacementVertex == -1) {
642  // this one did NOT get replaced
643  //calcExtent_dist(_extent,thisVertex);
644  p->finalCoordsWritten++;
645  cp += fprintf (fp,"%f %f %f, #%d\n",thisVertex->vertex.c[0],thisVertex->vertex.c[1],thisVertex->vertex.c[2],
646  thisVertex->condensedVertexNo);
647  }
648  }
649 
650  // finish off the Coordinate here
651  cp += fprintf (fp,"]}\n");
652 
653 
654 
655  int size=p->finalCoordsWritten;
656  if (p->checkSTL_for_3Dprinting) {
657  /* we need an array to hold vectors to see how often they are used,
658  but we can pack 4 vector indexes into 1 byte, to save memory space
659  on Android device (at the expense of speed) */
660 
661  p->vectorArray = MALLOC(unsigned char *,(size*size/4));
662  if (p->vectorArray) bzero(p->vectorArray,(size_t)size*size/4);
663  }
664 
665  // Now do the coordIndex
666  {
667  int j = 0;
668  int face=0;
669  int tv[4];
670  int curVertex;
671 
672  cp += fprintf (fp, "coordIndex [\n");
673  for (i=0; i<vectorSize(vertices); i++) {
674  j++; // will be 1,2,3
675  struct tstlVertexStruct *thisVertex = vector_get(struct tstlVertexStruct *,vertices,i);
676 
677  //ConsoleMessage ("coord vector %d...replac %d cond %d",i,thisVertex->replacementVertex,thisVertex->condensedVertexNo);
678  if (thisVertex->replacementVertex == -1) {
679  curVertex = thisVertex->condensedVertexNo;
680  } else {
681  struct tstlVertexStruct *rpv = vector_get(struct tstlVertexStruct *,vertices,
682  thisVertex->replacementVertex);
683  curVertex = rpv->condensedVertexNo;
684  }
685  cp += fprintf (fp,"%d, ",curVertex);
686  tv[j] = curVertex;
687 
688  if (j==3) {
689  j=0;
690  cp += fprintf (fp,"-1, #face %d\n",face);
691  if (p->vectorArray) {
692  recordVector(tv[1],tv[2],p);
693  recordVector(tv[1],tv[3],p);
694  recordVector(tv[2],tv[3],p);
695  }
696 
697  face ++;
698  }
699  }
700  // finish the CoordIndex
701  cp += fprintf (fp,"]\n");
702  }
703 
704  return finishThisX3DFile (fp, cp, tfn, _extent,vectorSize(vertices),p->finalCoordsWritten,p);
705 }
706 
707 #define calc_vector_length(pt) veclength(pt)
708 static float veclength( struct point_XYZ p )
709 {
710  return (float) sqrt(p.x*p.x + p.y*p.y + p.z*p.z);
711 }
712 
713 /* Check to see if this triangle is one that we can use for a surface */
714 static bool degenerate (struct SFVec3f *c1, struct SFVec3f *c2,
715  struct SFVec3f *c3) {
716  struct point_XYZ thisfaceNorms;
717  float a[3]; float b[3];
718 
719 
720  a[0] = c2->c[0] - c1->c[0];
721  a[1] = c2->c[1] - c1->c[1];
722  a[2] = c2->c[2] - c1->c[2];
723  b[0] = c3->c[0] - c1->c[0];
724  b[1] = c3->c[1] - c1->c[1];
725  b[2] = c3->c[2] - c1->c[2];
726 
727  //printf ("a0 %f a1 %f a2 %f b0 %f b1 %f b2 %f\n", a[0],a[1],a[2],b[0],b[1],b[2]);
728 
729  thisfaceNorms.x = a[1]*b[2] - b[1]*a[2];
730  thisfaceNorms.y = -(a[0]*b[2] - b[0]*a[2]);
731  thisfaceNorms.z = a[0]*b[1] - b[0]*a[1];
732 
733  //ConsoleMessage ("vl is %f",calc_vector_length(thisfaceNorms));
734 
735  return calc_vector_length(thisfaceNorms) > stl_vertex_tolerance;
736 }
737 
738 
739 void fwl_stl_set_rendering_type(int nv) {
740  ppSTLHandler p = gglobal()->STLHandler.prv;
741 
742  switch (nv) {
743  case 1:
744  // original
745  p->analyzeSTL = FALSE;
746  p->checkSTL_for_3Dprinting = FALSE;
747  break;
748  case 2:
749  // Checked for 2-Manifold and Watertight
750  p->analyzeSTL = TRUE;
751  p->checkSTL_for_3Dprinting = TRUE;
752  break;
753  case 3:
754  // zippy and nice rendering.
755  p->analyzeSTL = TRUE;
756  p->checkSTL_for_3Dprinting = FALSE;
757  break;
758  default: {}
759  }
760  //ConsoleMessage("fwl_stl_set_rendering_type is %d",nv);
761 
762 }
763 
764 //-------------------------------
765 
766 static char *analyzeAndGenerate (float *_extent, struct Vector *vertices, struct Vector *normals,ppSTLHandler p) {
767  char *retval = NULL;
768  int i;
769 
770  //calcExtent_dist(_extent,thisVertex);
771 
772  // if we read normals in from the file, we are reading "as-is".
773  if (normals == NULL) {
774  // analyze the file for duplicate vertices.
775  analyzeSTLdata(vertices);
776  }
777 
778  // get the VRML file from this.
779  if (p->analyzeSTL)
780  retval = makeX3D_analyzed_STL_File (vertices,normals,NULL,_extent);
781  else
782  retval = makeX3D_orig_STL_File(vertices,normals,NULL,_extent);
783 
784  //ConsoleMessage ("we have a file now of :%s:",retval);
785 
786  ConsoleMessage ("generating - degenerateTriangleCount %d, Triangles In %d Vertices out %d\n",
787  p->degenerateCount,p->triangleInCount,p->finalCoordsWritten);
788  // final coords are vertices; we read in triangles (binary STL has 3 vertices per "record", but we write vertices out and
789  // index them for analyzed IndexedFaceSets, or just write them out for TriangleSets.
790  {
791  // stats - work in vertex counts
792  float fcw = (float) p->finalCoordsWritten;
793  float fcin = (float) p->triangleInCount * 3;
794  if (fcin < 0.5) fcin = 1; // do not want to divide by zero here
795 
796  ConsoleMessage ("Vertex memory savings %4.1f %% \n", (1-(fcw/fcin))*100.0);
797  }
798 
799  // delete the Vectors
800  for (i=0; i<vectorSize(vertices); i++) {
801  FREE_IF_NZ(vector_get(struct tstlVertexStruct *,vertices,i));
802  }
803  deleteVector(struct Vector*, vertices);
804 
805  if (normals!=NULL) {
806  //ConsoleMessage ("deleting normals here ");
807  for (i=0; i<vectorSize(normals); i++) {
808  FREE_IF_NZ(vector_get(struct SFVec3f *,normals,i));
809  }
810  deleteVector(struct Vector*, normals);
811  }
812 
813  //ConsoleMessage (retval);
814  return (retval);
815 
816 }
817 
818 // read in ascii stl file.
819 // ASSUME that all faces consist of 3 vertices, much like a binary
820 // STL file would.
821 
822 char *convertAsciiSTL (const char *inp) {
823 
824  int i=0;
825 
826  struct Vector *vertices = NULL;
827  struct Vector *normals = NULL;
828 
829  char *normalPtr = NULL;
830  char *vertexPtr = NULL;
831 
832  int haveNormalHere = false;
833  int haveValidNormals = true;
834  float NX,NY,NZ; // last read normal
835 
836 
837  float _extent[6];
838  struct tstlVertexStruct *thisVertex[3];
839 
840 #include <sys/time.h>
841  struct timeval tv;
842  struct timeval tve;
843 
844  gettimeofday(&tv, NULL);
845 
846  char *tptr = (char *)inp;
847 
848  int messCount = 0;
849 
850  ppSTLHandler p = gglobal()->STLHandler.prv;
851 
852  ConsoleMessage ("start reading AsciiSTL - this can take a while");
853 
854  //global stats
855  p->degenerateCount=0;
856  p->triangleInCount = 0;
857  p->finalCoordsWritten = 0;
858 
859 
860  // set these up to defaults
861  NX=0.0; NY=0.0; NZ=1.0;
862 
863  // first, read all the vertices.
864  vertices = newVector(sizeof (stlVertexStruct),1024);
865  EXTENT_MAX_X = -FLT_MAX; EXTENT_MAX_Y = -FLT_MAX; EXTENT_MAX_Z = -FLT_MAX;
866  EXTENT_MIN_X = FLT_MAX; EXTENT_MIN_Y = FLT_MAX; EXTENT_MIN_Z = FLT_MAX;
867 
868 
869  if (!p->analyzeSTL) {
870  // use supplied normals
871  normals = newVector(sizeof(struct SFVec3f), 1024);
872  }
873 
874 #define USE_STRING_BUILTINS
875 #ifdef USE_STRING_BUILTINS
876  // skip to either the "vertex" or to the "normal"
877  normalPtr = strcasestr(tptr,"normal ");
878  vertexPtr = strcasestr(tptr,"vertex ");
879  if ((normalPtr != NULL) &&(normalPtr < vertexPtr)) {
880  tptr = normalPtr;
881  haveNormalHere = true;
882  } else {
883  tptr = vertexPtr;
884  }
885  if (tptr!=NULL) tptr += strlen("vertex "); // same length as "normal "
886 #else
887 
888  while ((*tptr != '\0') && (*tptr != 'm') && (*tptr != 'x')) tptr++;
889  if (*tptr == 'x') {
890  tptr++; // skip past the 'x'
891  //if (*tptr != ' ')
892  } else if (*tptr == 'm') {
893  tptr++;
894  if (*tptr == 'a') {
895  tptr++;
896  if (*tptr == 'l') {
897  tptr ++;
898  haveNormalHere = true;
899  }
900  }
901  } else {
902  // end of file
903  tptr = NULL;
904  }
905 #endif// USE_STRING_BUILTINS
906 
907  //ConsoleMessage ("currently here: %s",tptr);
908 
909  // we save the vertices only if this is not degenerate.
910  while (tptr != NULL) {
911  float X,Y,Z;
912  if (haveNormalHere) {
913 
914  //ConsoleMessage("looking for normals here:%s",tptr);
915 
916  #define USE_STRTOF
917  #ifdef USE_STRTOF
918  NX = strtof(tptr,&tptr);
919  NY = strtof(tptr,&tptr);
920  NZ = strtof(tptr,&tptr);
921  if (1!=1) {
922  #else
923  if (3!=sscanf (tptr,"%f %f %f", &NX,&NY,&NZ)) {
924  #endif
925  if (haveValidNormals) {
926  char mys[50];
927  ConsoleMessage ("expected normal, did not get it...");
928  strncpy(mys,tptr,40); mys[40] = '\0';
929  ConsoleMessage ("got %s",mys);
930  NX=0.0; NY=0.0; NZ=1.0;
931  haveValidNormals = false;
932  }
933  }
934  } else {
935  //ConsoleMessage("Looking for vertexes here:%s",tptr);
936  #ifdef USE_STRTOF
937  X=strtof(tptr,&tptr);
938  Y=strtof(tptr,&tptr);
939  Z=strtof(tptr,&tptr);
940  {
941  #else
942  if (3==sscanf (tptr,"%f %f %f", &X,&Y,&Z)) {
943  #endif
944  thisVertex[i] = MALLOC (struct tstlVertexStruct *, sizeof (stlVertexStruct));
945 
946  thisVertex[i]->vertex.c[0] = X;
947  thisVertex[i]->vertex.c[1] = Y;
948  thisVertex[i]->vertex.c[2] = Z;
949  thisVertex[i]->replacementVertex = -1; // no replacement, yet!
950  thisVertex[i]->condensedVertexNo = -1; // not done duplicates yet!
951  //ConsoleMessage ("read in %f %f %f",X,Y,Z);
952 
953  // next vertex, or is the end of a triangle?
954  i++;
955  if (i==3) {
956  p->triangleInCount++;
957 
958  // valid triangle? if so, push this all, including normal
959  if (degenerate(&thisVertex[0]->vertex,
960  &thisVertex[1]->vertex, &thisVertex[2]->vertex)) {
961  struct SFVec3f *norm;
962 
963  calcExtent_dist(_extent,thisVertex[0]);
964  calcExtent_dist(_extent,thisVertex[1]);
965  calcExtent_dist(_extent,thisVertex[2]);
966  vector_pushBack(struct tstlVertexStruct *,vertices,thisVertex[0]);
967  vector_pushBack(struct tstlVertexStruct *,vertices,thisVertex[1]);
968  vector_pushBack(struct tstlVertexStruct *,vertices,thisVertex[2]);
969  //ConsoleMessage ("ascii stl, pushed 3 vertices");
970 
971  if (normals!=NULL) {
972  norm = MALLOC(struct SFVec3f*, sizeof (struct SFVec3f));
973  norm->c[0] = NX; norm->c[1]=NY; norm->c[2]=NZ;
974  vector_pushBack(struct SFVec3f *,normals,norm);
975  NX = 0.0; NY = 0.0, NZ = 1.0;
976  }
977 
978  } else {
979  //ConsoleMessage ("degenerate, skipping %d",i);
980  p->degenerateCount++;
981  }
982 
983  i=0;
984  }
985  }
986 
987  }
988  // skip to either the "vertex" or to the "normal"
989 #ifdef USE_STRING_BUILTINS
990  normalPtr = strcasestr(tptr,"normal ");
991  vertexPtr = strcasestr(tptr,"vertex ");
992  if ((normalPtr != NULL) &&(normalPtr < vertexPtr)) {
993 
994  tptr = normalPtr;
995  haveNormalHere = true;
996  } else {
997  tptr = vertexPtr;
998  haveNormalHere = false;
999 
1000  messCount ++;
1001  if (messCount >750) {
1002  ConsoleMessage("still parsing ASCII STL file... %d triangles, %d degenerates",p->triangleInCount,p->degenerateCount);
1003  messCount = 0;
1004  }
1005 
1006  }
1007  if (tptr!=NULL) tptr += strlen("vertex "); // same length as "normal "
1008  //ConsoleMessage ("currently here: %s",tptr);
1009 #else
1010  haveNormalHere = false;
1011  while ((*tptr != '\0') && (*tptr != 'm') && (*tptr != 'x')) tptr++;
1012  if (*tptr == 'x') {
1013  messCount ++;
1014  if (messCount >750) {
1015  ConsoleMessage("still parsing ASCII STL file... %d triangles, %d degenerates",p->triangleInCount,p->degenerateCount);
1016  messCount = 0;
1017  }
1018  tptr++; // skip past the 'x'
1019  //if (*tptr != ' ')
1020  } else if (*tptr == 'm') {
1021  tptr++;
1022  if (*tptr == 'a') {
1023  tptr++;
1024  if (*tptr == 'l') {
1025  tptr ++;
1026  haveNormalHere = true;
1027  }
1028  }
1029  } else {
1030  // end of file
1031  tptr = NULL;
1032  }
1033 
1034 #endif
1035 
1036  }
1037 
1038  gettimeofday(&tve,NULL);
1039  ConsoleMessage ("AsciiSTL - took %ld seconds to parse",tve.tv_sec-tv.tv_sec);
1040 
1041 
1042  //ConsoleMessage ("asciiSTL, degenerateCount %d",degenerateCount);
1043 
1044  return analyzeAndGenerate(_extent,vertices,normals,p);
1045 }
1046 
1047 char *convertBinarySTL (const unsigned char *buffer) {
1048  int i;
1049  struct Vector *vertices = NULL;
1050  struct Vector *normals = NULL;
1051  float _extent[6];
1052  int32_t *stllen;
1053  bool haveAttributeInfo = false;
1054 
1055  unsigned char *tmp = (unsigned char *)buffer;
1056 
1057  ppSTLHandler p = gglobal()->STLHandler.prv;
1058 
1059  //global stats
1060  p->degenerateCount=0;
1061  p->triangleInCount = 0;
1062  p->finalCoordsWritten = 0;
1063 
1064 
1065  // create pointers to length and data areas
1066  stllen = offsetPointer_deref(int32_t *, buffer, 80);
1067  tmp = offsetPointer_deref(unsigned char*, buffer, STL_BINARY_HEADER_LEN);
1068 
1069  //ConsoleMessage ("triangle input count %d\n",*stllen);
1070 
1071  //for (i=0; i<80; i++) {
1072  // ConsoleMessage("header, i: %d char %x (%c)",i,buffer[i],buffer[i]);
1073  //}
1074  //ConsoleMessage ("Binary STL header :%s:",buffer);
1075 
1076  // read all the vertices.
1077  vertices = newVector(sizeof (stlVertexStruct),(*stllen)*3);
1078 
1079  // if we want to use the supplied normals
1080  if (!p->analyzeSTL) {
1081  // use supplied normals
1082  normals = newVector(sizeof(struct SFVec3f), (*stllen));
1083  //ConsoleMessage ("binary STL - SFVec3f is %d",sizeof (struct SFVec3f));
1084  if (sizeof (struct SFVec3f) != 12) {
1085  ConsoleMessage ("binary reading of STL - SFVec3f wrong size");
1086  }
1087  }
1088 
1089 
1090  // set extents, and do it
1091  EXTENT_MAX_X = -FLT_MAX; EXTENT_MAX_Y = -FLT_MAX; EXTENT_MAX_Z = -FLT_MAX;
1092  EXTENT_MIN_X = FLT_MAX; EXTENT_MIN_Y = FLT_MAX; EXTENT_MIN_Z = FLT_MAX;
1093 
1094  for (i=0; i<*stllen; i++) {
1095 
1096  p->triangleInCount++;
1097 
1098  struct tstlVertexStruct *vertex1 = MALLOC (struct tstlVertexStruct *, sizeof (stlVertexStruct));
1099  struct tstlVertexStruct *vertex2 = MALLOC (struct tstlVertexStruct *, sizeof (stlVertexStruct));
1100  struct tstlVertexStruct *vertex3 = MALLOC (struct tstlVertexStruct *, sizeof (stlVertexStruct));
1101  // binary normal - skip
1102 
1103  // vertex 1
1104  memcpy (vertex1->vertex.c,&tmp[12],12);
1105  vertex1->replacementVertex = -1; // no replacement, yet!
1106  vertex1->condensedVertexNo = -1; // not done duplicates yet!
1107 
1108  // vertex 2
1109  vertex2 = MALLOC (struct tstlVertexStruct *, sizeof (stlVertexStruct));
1110  memcpy (vertex2->vertex.c,&(tmp[24]),12);
1111  vertex2->replacementVertex = -1; // no replacement, yet!
1112  vertex2->condensedVertexNo = -1; // not done duplicates yet!
1113 
1114  // vertex 3
1115  vertex3 = MALLOC (struct tstlVertexStruct *, sizeof (stlVertexStruct));
1116  memcpy (vertex3->vertex.c,&(tmp[36]),12);
1117  vertex3->replacementVertex = -1; // no replacement, yet!
1118  vertex3->condensedVertexNo = -1; // not done duplicates yet!
1119 
1120  // check for degenerate triangles
1121  if (degenerate(&vertex1->vertex, &vertex2->vertex, &vertex3->vertex)) {
1122  calcExtent_dist(_extent,vertex1);
1123  calcExtent_dist(_extent,vertex2);
1124  calcExtent_dist(_extent,vertex3);
1125  vector_pushBack(struct tstlVertexStruct *,vertices,vertex1);
1126  vector_pushBack(struct tstlVertexStruct *,vertices,vertex2);
1127  vector_pushBack(struct tstlVertexStruct *,vertices,vertex3);
1128 
1129  // are we using the old normals, not calculating our own?
1130  if (normals != NULL) {
1131  struct SFVec3f *norm;
1132  norm = MALLOC(struct SFVec3f*, sizeof (struct SFVec3f));
1133  memcpy(norm, tmp, 12);
1134  vector_pushBack(struct SFVec3f *,normals,norm);
1135 
1136  }
1137  if ((tmp[48] != 0) || (tmp[49]!=0)) {
1138 
1139  haveAttributeInfo = true;
1140  }
1141  } else {
1142  //ConsoleMessage ("degenerate, skipping %d",i);
1143  p->degenerateCount++;
1144  }
1145 
1146  tmp = offsetPointer_deref(unsigned char*, tmp, STL_BINARY_VERTEX_SIZE);
1147  }
1148 
1149  if (haveAttributeInfo) {
1150  //ConsoleMessage ("BINARY STL with Colour info");
1151  }
1152 
1153  //ConsoleMessage ("Triangles in %d, degenerates %d",*stllen,p->degenerateCount);
1154  return analyzeAndGenerate(_extent,vertices,normals,p);
1155 }
1156 
1157 /* STL files will get scaled to fit into a good-sized box. Return this for
1158  FillProperties, etc */
1159 float getLastSTLScale(void) {
1160  // force it to 10 per meter, not 1 per meter.
1161  //ConsoleMessage ("getLastSTLScale, in convertSTL.c - sf %f",scaleFactor/10.0);
1162  ppSTLHandler p = gglobal()->STLHandler.prv;
1163 
1164  if (p->scaleFactor < 0.0) return 1.0;
1165  return p->scaleFactor/10.0;
1166 }
1167 
1168 #endif //INCLUDE_STL_FILES
Definition: Vector.h:36