FreeWRL / FreeX3D 4.3.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
44static 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
59struct 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
68struct 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/****************************************************************/
77typedef 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
96void *STLHandler_constructor(){
97 void *v = malloc(sizeof(struct pSTLHandler));
98 memset(v,0,sizeof(struct pSTLHandler));
99 return v;
100}
101
102void 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 */
118static 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
148static 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 */
168int 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
180int 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
215static 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 */
231static 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 */
269static 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 */
310void 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
403static 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
540static 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
602static 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)
708static 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 */
714static 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
739void 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
766static 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
822char *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
1047char *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 */
1159float 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