1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 */
16
17 /** \file
18 * \ingroup freestyle
19 */
20
21 #include "BlenderFileLoader.h"
22
23 #include "BLI_utildefines.h"
24
25 #include "BKE_global.h"
26 #include "BKE_object.h"
27
28 #include <sstream>
29
30 namespace Freestyle {
31
BlenderFileLoader(Render * re,ViewLayer * view_layer,Depsgraph * depsgraph)32 BlenderFileLoader::BlenderFileLoader(Render *re, ViewLayer *view_layer, Depsgraph *depsgraph)
33 {
34 _re = re;
35 _depsgraph = depsgraph;
36 _Scene = NULL;
37 _numFacesRead = 0;
38 #if 0
39 _minEdgeSize = DBL_MAX;
40 #endif
41 _smooth = (view_layer->freestyle_config.flags & FREESTYLE_FACE_SMOOTHNESS_FLAG) != 0;
42 _pRenderMonitor = NULL;
43 }
44
~BlenderFileLoader()45 BlenderFileLoader::~BlenderFileLoader()
46 {
47 _Scene = NULL;
48 }
49
Load()50 NodeGroup *BlenderFileLoader::Load()
51 {
52 if (G.debug & G_DEBUG_FREESTYLE) {
53 cout << "\n=== Importing triangular meshes into Blender ===" << endl;
54 }
55
56 // creation of the scene root node
57 _Scene = new NodeGroup;
58
59 _viewplane_left = _re->viewplane.xmin;
60 _viewplane_right = _re->viewplane.xmax;
61 _viewplane_bottom = _re->viewplane.ymin;
62 _viewplane_top = _re->viewplane.ymax;
63
64 if (_re->clip_start < 0.f) {
65 // Adjust clipping start/end and set up a Z offset when the viewport preview
66 // is used with the orthographic view. In this case, _re->clip_start is negative,
67 // while Freestyle assumes that imported mesh data are in the camera coordinate
68 // system with the view point located at origin [bug T36009].
69 _z_near = -0.001f;
70 _z_offset = _re->clip_start + _z_near;
71 _z_far = -_re->clip_end + _z_offset;
72 }
73 else {
74 _z_near = -_re->clip_start;
75 _z_far = -_re->clip_end;
76 _z_offset = 0.f;
77 }
78
79 #if 0
80 if (G.debug & G_DEBUG_FREESTYLE) {
81 cout << "Frustum: l " << _viewplane_left << " r " << _viewplane_right << " b "
82 << _viewplane_bottom << " t " << _viewplane_top << " n " << _z_near << " f " << _z_far
83 << endl;
84 }
85 #endif
86
87 int id = 0;
88 const eEvaluationMode eval_mode = DEG_get_mode(_depsgraph);
89
90 DEG_OBJECT_ITER_BEGIN (_depsgraph,
91 ob,
92 DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
93 DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET | DEG_ITER_OBJECT_FLAG_VISIBLE |
94 DEG_ITER_OBJECT_FLAG_DUPLI) {
95 if (_pRenderMonitor && _pRenderMonitor->testBreak()) {
96 break;
97 }
98
99 if (ob->base_flag & (BASE_HOLDOUT | BASE_INDIRECT_ONLY)) {
100 continue;
101 }
102
103 if (!(BKE_object_visibility(ob, eval_mode) & OB_VISIBLE_SELF)) {
104 continue;
105 }
106
107 Mesh *mesh = BKE_object_to_mesh(NULL, ob, false);
108
109 if (mesh) {
110 insertShapeNode(ob, mesh, ++id);
111 BKE_object_to_mesh_clear(ob);
112 }
113 }
114 DEG_OBJECT_ITER_END;
115
116 // Return the built scene.
117 return _Scene;
118 }
119
120 #define CLIPPED_BY_NEAR -1
121 #define NOT_CLIPPED 0
122 #define CLIPPED_BY_FAR 1
123
124 // check if each vertex of a triangle (V1, V2, V3) is clipped by the near/far plane
125 // and calculate the number of triangles to be generated by clipping
countClippedFaces(float v1[3],float v2[3],float v3[3],int clip[3])126 int BlenderFileLoader::countClippedFaces(float v1[3], float v2[3], float v3[3], int clip[3])
127 {
128 float *v[3];
129 int numClipped, sum, numTris = 0;
130
131 v[0] = v1;
132 v[1] = v2;
133 v[2] = v3;
134 numClipped = sum = 0;
135 for (int i = 0; i < 3; i++) {
136 if (v[i][2] > _z_near) {
137 clip[i] = CLIPPED_BY_NEAR;
138 numClipped++;
139 }
140 else if (v[i][2] < _z_far) {
141 clip[i] = CLIPPED_BY_FAR;
142 numClipped++;
143 }
144 else {
145 clip[i] = NOT_CLIPPED;
146 }
147 #if 0
148 if (G.debug & G_DEBUG_FREESTYLE) {
149 printf("%d %s\n",
150 i,
151 (clip[i] == NOT_CLIPPED) ? "not" : (clip[i] == CLIPPED_BY_NEAR) ? "near" : "far");
152 }
153 #endif
154 sum += clip[i];
155 }
156 switch (numClipped) {
157 case 0:
158 numTris = 1; // triangle
159 break;
160 case 1:
161 numTris = 2; // tetragon
162 break;
163 case 2:
164 if (sum == 0) {
165 numTris = 3; // pentagon
166 }
167 else {
168 numTris = 1; // triangle
169 }
170 break;
171 case 3:
172 if (sum == 3 || sum == -3) {
173 numTris = 0;
174 }
175 else {
176 numTris = 2; // tetragon
177 }
178 break;
179 }
180 return numTris;
181 }
182
183 // find the intersection point C between the line segment from V1 to V2 and
184 // a clipping plane at depth Z (i.e., the Z component of C is known, while
185 // the X and Y components are unknown).
clipLine(float v1[3],float v2[3],float c[3],float z)186 void BlenderFileLoader::clipLine(float v1[3], float v2[3], float c[3], float z)
187 {
188 // Order v1 and v2 by Z values to make sure that clipLine(P, Q, c, z)
189 // and clipLine(Q, P, c, z) gives exactly the same numerical result.
190 float *p, *q;
191 if (v1[2] < v2[2]) {
192 p = v1;
193 q = v2;
194 }
195 else {
196 p = v2;
197 q = v1;
198 }
199 double d[3];
200 for (int i = 0; i < 3; i++) {
201 d[i] = q[i] - p[i];
202 }
203 double t = (z - p[2]) / d[2];
204 c[0] = p[0] + t * d[0];
205 c[1] = p[1] + t * d[1];
206 c[2] = z;
207 }
208
209 // clip the triangle (V1, V2, V3) by the near and far clipping plane and
210 // obtain a set of vertices after the clipping. The number of vertices
211 // is at most 5.
clipTriangle(int numTris,float triCoords[][3],float v1[3],float v2[3],float v3[3],float triNormals[][3],float n1[3],float n2[3],float n3[3],bool edgeMarks[],bool em1,bool em2,bool em3,const int clip[3])212 void BlenderFileLoader::clipTriangle(int numTris,
213 float triCoords[][3],
214 float v1[3],
215 float v2[3],
216 float v3[3],
217 float triNormals[][3],
218 float n1[3],
219 float n2[3],
220 float n3[3],
221 bool edgeMarks[],
222 bool em1,
223 bool em2,
224 bool em3,
225 const int clip[3])
226 {
227 float *v[3], *n[3];
228 bool em[3];
229 int i, j, k;
230
231 v[0] = v1;
232 n[0] = n1;
233 v[1] = v2;
234 n[1] = n2;
235 v[2] = v3;
236 n[2] = n3;
237 em[0] = em1; /* edge mark of the edge between v1 and v2 */
238 em[1] = em2; /* edge mark of the edge between v2 and v3 */
239 em[2] = em3; /* edge mark of the edge between v3 and v1 */
240 k = 0;
241 for (i = 0; i < 3; i++) {
242 j = (i + 1) % 3;
243 if (clip[i] == NOT_CLIPPED) {
244 copy_v3_v3(triCoords[k], v[i]);
245 copy_v3_v3(triNormals[k], n[i]);
246 edgeMarks[k] = em[i];
247 k++;
248 if (clip[j] != NOT_CLIPPED) {
249 clipLine(v[i], v[j], triCoords[k], (clip[j] == CLIPPED_BY_NEAR) ? _z_near : _z_far);
250 copy_v3_v3(triNormals[k], n[j]);
251 edgeMarks[k] = false;
252 k++;
253 }
254 }
255 else if (clip[i] != clip[j]) {
256 if (clip[j] == NOT_CLIPPED) {
257 clipLine(v[i], v[j], triCoords[k], (clip[i] == CLIPPED_BY_NEAR) ? _z_near : _z_far);
258 copy_v3_v3(triNormals[k], n[i]);
259 edgeMarks[k] = em[i];
260 k++;
261 }
262 else {
263 clipLine(v[i], v[j], triCoords[k], (clip[i] == CLIPPED_BY_NEAR) ? _z_near : _z_far);
264 copy_v3_v3(triNormals[k], n[i]);
265 edgeMarks[k] = em[i];
266 k++;
267 clipLine(v[i], v[j], triCoords[k], (clip[j] == CLIPPED_BY_NEAR) ? _z_near : _z_far);
268 copy_v3_v3(triNormals[k], n[j]);
269 edgeMarks[k] = false;
270 k++;
271 }
272 }
273 }
274 BLI_assert(k == 2 + numTris);
275 (void)numTris; /* Ignored in release builds. */
276 }
277
addTriangle(struct LoaderState * ls,float v1[3],float v2[3],float v3[3],float n1[3],float n2[3],float n3[3],bool fm,bool em1,bool em2,bool em3)278 void BlenderFileLoader::addTriangle(struct LoaderState *ls,
279 float v1[3],
280 float v2[3],
281 float v3[3],
282 float n1[3],
283 float n2[3],
284 float n3[3],
285 bool fm,
286 bool em1,
287 bool em2,
288 bool em3)
289 {
290 float *fv[3], *fn[3];
291 #if 0
292 float len;
293 #endif
294 unsigned int i, j;
295 IndexedFaceSet::FaceEdgeMark marks = 0;
296
297 // initialize the bounding box by the first vertex
298 if (ls->currentIndex == 0) {
299 copy_v3_v3(ls->minBBox, v1);
300 copy_v3_v3(ls->maxBBox, v1);
301 }
302
303 fv[0] = v1;
304 fn[0] = n1;
305 fv[1] = v2;
306 fn[1] = n2;
307 fv[2] = v3;
308 fn[2] = n3;
309 for (i = 0; i < 3; i++) {
310
311 copy_v3_v3(ls->pv, fv[i]);
312 copy_v3_v3(ls->pn, fn[i]);
313
314 // update the bounding box
315 for (j = 0; j < 3; j++) {
316 if (ls->minBBox[j] > ls->pv[j]) {
317 ls->minBBox[j] = ls->pv[j];
318 }
319
320 if (ls->maxBBox[j] < ls->pv[j]) {
321 ls->maxBBox[j] = ls->pv[j];
322 }
323 }
324
325 #if 0
326 len = len_v3v3(fv[i], fv[(i + 1) % 3]);
327 if (_minEdgeSize > len) {
328 _minEdgeSize = len;
329 }
330 #endif
331
332 *ls->pvi = ls->currentIndex;
333 *ls->pni = ls->currentIndex;
334 *ls->pmi = ls->currentMIndex;
335
336 ls->currentIndex += 3;
337 ls->pv += 3;
338 ls->pn += 3;
339
340 ls->pvi++;
341 ls->pni++;
342 ls->pmi++;
343 }
344
345 if (fm) {
346 marks |= IndexedFaceSet::FACE_MARK;
347 }
348 if (em1) {
349 marks |= IndexedFaceSet::EDGE_MARK_V1V2;
350 }
351 if (em2) {
352 marks |= IndexedFaceSet::EDGE_MARK_V2V3;
353 }
354 if (em3) {
355 marks |= IndexedFaceSet::EDGE_MARK_V3V1;
356 }
357 *(ls->pm++) = marks;
358 }
359
360 // With A, B and P indicating the three vertices of a given triangle, returns:
361 // 1 if points A and B are in the same position in the 3D space;
362 // 2 if the distance between point P and line segment AB is zero; and
363 // zero otherwise.
testDegenerateTriangle(float v1[3],float v2[3],float v3[3])364 int BlenderFileLoader::testDegenerateTriangle(float v1[3], float v2[3], float v3[3])
365 {
366 const float eps = 1.0e-6;
367 const float eps_sq = eps * eps;
368
369 #if 0
370 float area = area_tri_v3(v1, v2, v3);
371 bool verbose = (area < 1.0e-6);
372 #endif
373
374 if (equals_v3v3(v1, v2) || equals_v3v3(v2, v3) || equals_v3v3(v1, v3)) {
375 #if 0
376 if (verbose && G.debug & G_DEBUG_FREESTYLE) {
377 printf("BlenderFileLoader::testDegenerateTriangle = 1\n");
378 }
379 #endif
380 return 1;
381 }
382 if (dist_squared_to_line_segment_v3(v1, v2, v3) < eps_sq ||
383 dist_squared_to_line_segment_v3(v2, v1, v3) < eps_sq ||
384 dist_squared_to_line_segment_v3(v3, v1, v2) < eps_sq) {
385 #if 0
386 if (verbose && G.debug & G_DEBUG_FREESTYLE) {
387 printf("BlenderFileLoader::testDegenerateTriangle = 2\n");
388 }
389 #endif
390 return 2;
391 }
392 #if 0
393 if (verbose && G.debug & G_DEBUG_FREESTYLE) {
394 printf("BlenderFileLoader::testDegenerateTriangle = 0\n");
395 }
396 #endif
397 return 0;
398 }
399
testEdgeMark(Mesh * me,FreestyleEdge * fed,const MLoopTri * lt,int i)400 static bool testEdgeMark(Mesh *me, FreestyleEdge *fed, const MLoopTri *lt, int i)
401 {
402 MLoop *mloop = &me->mloop[lt->tri[i]];
403 MLoop *mloop_next = &me->mloop[lt->tri[(i + 1) % 3]];
404 MEdge *medge = &me->medge[mloop->e];
405
406 if (!ELEM(mloop_next->v, medge->v1, medge->v2)) {
407 /* Not an edge in the original mesh before triangulation. */
408 return false;
409 }
410
411 return (fed[mloop->e].flag & FREESTYLE_EDGE_MARK) != 0;
412 }
413
insertShapeNode(Object * ob,Mesh * me,int id)414 void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id)
415 {
416 char *name = ob->id.name + 2;
417
418 // Compute loop triangles
419 int tottri = poly_to_tri_count(me->totpoly, me->totloop);
420 MLoopTri *mlooptri = (MLoopTri *)MEM_malloc_arrayN(tottri, sizeof(*mlooptri), __func__);
421 BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mlooptri);
422
423 // Compute loop normals
424 BKE_mesh_calc_normals_split(me);
425 float(*lnors)[3] = NULL;
426
427 if (CustomData_has_layer(&me->ldata, CD_NORMAL)) {
428 lnors = (float(*)[3])CustomData_get_layer(&me->ldata, CD_NORMAL);
429 }
430
431 // Get other mesh data
432 MVert *mvert = me->mvert;
433 MLoop *mloop = me->mloop;
434 MPoly *mpoly = me->mpoly;
435 FreestyleEdge *fed = (FreestyleEdge *)CustomData_get_layer(&me->edata, CD_FREESTYLE_EDGE);
436 FreestyleFace *ffa = (FreestyleFace *)CustomData_get_layer(&me->pdata, CD_FREESTYLE_FACE);
437
438 // Compute view matrix
439 Object *ob_camera_eval = DEG_get_evaluated_object(_depsgraph, RE_GetCamera(_re));
440 float viewinv[4][4], viewmat[4][4];
441 RE_GetCameraModelMatrix(_re, ob_camera_eval, viewinv);
442 invert_m4_m4(viewmat, viewinv);
443
444 // Compute matrix including camera transform
445 float obmat[4][4], nmat[4][4];
446 mul_m4_m4m4(obmat, viewmat, ob->obmat);
447 invert_m4_m4(nmat, obmat);
448 transpose_m4(nmat);
449
450 // We count the number of triangles after the clipping by the near and far view
451 // planes is applied (Note: mesh vertices are in the camera coordinate system).
452 unsigned numFaces = 0;
453 float v1[3], v2[3], v3[3];
454 float n1[3], n2[3], n3[3], facenormal[3];
455 int clip[3];
456 for (int a = 0; a < tottri; a++) {
457 const MLoopTri *lt = &mlooptri[a];
458
459 copy_v3_v3(v1, mvert[mloop[lt->tri[0]].v].co);
460 copy_v3_v3(v2, mvert[mloop[lt->tri[1]].v].co);
461 copy_v3_v3(v3, mvert[mloop[lt->tri[2]].v].co);
462
463 mul_m4_v3(obmat, v1);
464 mul_m4_v3(obmat, v2);
465 mul_m4_v3(obmat, v3);
466
467 v1[2] += _z_offset;
468 v2[2] += _z_offset;
469 v3[2] += _z_offset;
470
471 numFaces += countClippedFaces(v1, v2, v3, clip);
472 }
473 #if 0
474 if (G.debug & G_DEBUG_FREESTYLE) {
475 cout << "numFaces " << numFaces << endl;
476 }
477 #endif
478 if (numFaces == 0) {
479 MEM_freeN(mlooptri);
480 return;
481 }
482
483 // We allocate memory for the meshes to be imported
484 NodeGroup *currentMesh = new NodeGroup;
485 NodeShape *shape = new NodeShape;
486
487 unsigned vSize = 3 * 3 * numFaces;
488 float *vertices = new float[vSize];
489 unsigned nSize = vSize;
490 float *normals = new float[nSize];
491 unsigned *numVertexPerFaces = new unsigned[numFaces];
492 vector<Material *> meshMaterials;
493 vector<FrsMaterial> meshFrsMaterials;
494
495 IndexedFaceSet::TRIANGLES_STYLE *faceStyle = new IndexedFaceSet::TRIANGLES_STYLE[numFaces];
496 unsigned i;
497 for (i = 0; i < numFaces; i++) {
498 faceStyle[i] = IndexedFaceSet::TRIANGLES;
499 numVertexPerFaces[i] = 3;
500 }
501
502 IndexedFaceSet::FaceEdgeMark *faceEdgeMarks = new IndexedFaceSet::FaceEdgeMark[numFaces];
503
504 unsigned viSize = 3 * numFaces;
505 unsigned *VIndices = new unsigned[viSize];
506 unsigned niSize = viSize;
507 unsigned *NIndices = new unsigned[niSize];
508 unsigned *MIndices = new unsigned[viSize]; // Material Indices
509
510 struct LoaderState ls;
511 ls.pv = vertices;
512 ls.pn = normals;
513 ls.pm = faceEdgeMarks;
514 ls.pvi = VIndices;
515 ls.pni = NIndices;
516 ls.pmi = MIndices;
517 ls.currentIndex = 0;
518 ls.currentMIndex = 0;
519
520 FrsMaterial tmpMat;
521
522 // We parse the vlak nodes again and import meshes while applying the clipping
523 // by the near and far view planes.
524 for (int a = 0; a < tottri; a++) {
525 const MLoopTri *lt = &mlooptri[a];
526 const MPoly *mp = &mpoly[lt->poly];
527 Material *mat = BKE_object_material_get(ob, mp->mat_nr + 1);
528
529 copy_v3_v3(v1, mvert[mloop[lt->tri[0]].v].co);
530 copy_v3_v3(v2, mvert[mloop[lt->tri[1]].v].co);
531 copy_v3_v3(v3, mvert[mloop[lt->tri[2]].v].co);
532
533 mul_m4_v3(obmat, v1);
534 mul_m4_v3(obmat, v2);
535 mul_m4_v3(obmat, v3);
536
537 v1[2] += _z_offset;
538 v2[2] += _z_offset;
539 v3[2] += _z_offset;
540
541 if (_smooth && (mp->flag & ME_SMOOTH) && lnors) {
542 copy_v3_v3(n1, lnors[lt->tri[0]]);
543 copy_v3_v3(n2, lnors[lt->tri[1]]);
544 copy_v3_v3(n3, lnors[lt->tri[2]]);
545
546 mul_mat3_m4_v3(nmat, n1);
547 mul_mat3_m4_v3(nmat, n2);
548 mul_mat3_m4_v3(nmat, n3);
549
550 normalize_v3(n1);
551 normalize_v3(n2);
552 normalize_v3(n3);
553 }
554 else {
555 normal_tri_v3(facenormal, v3, v2, v1);
556
557 copy_v3_v3(n1, facenormal);
558 copy_v3_v3(n2, facenormal);
559 copy_v3_v3(n3, facenormal);
560 }
561
562 unsigned int numTris = countClippedFaces(v1, v2, v3, clip);
563 if (numTris == 0) {
564 continue;
565 }
566
567 bool fm = (ffa) ? (ffa[lt->poly].flag & FREESTYLE_FACE_MARK) != 0 : false;
568 bool em1 = false, em2 = false, em3 = false;
569
570 if (fed) {
571 em1 = testEdgeMark(me, fed, lt, 0);
572 em2 = testEdgeMark(me, fed, lt, 1);
573 em3 = testEdgeMark(me, fed, lt, 2);
574 }
575
576 if (mat) {
577 tmpMat.setLine(mat->line_col[0], mat->line_col[1], mat->line_col[2], mat->line_col[3]);
578 tmpMat.setDiffuse(mat->r, mat->g, mat->b, 1.0f);
579 tmpMat.setSpecular(mat->specr, mat->specg, mat->specb, 1.0f);
580 tmpMat.setShininess(128.f);
581 tmpMat.setPriority(mat->line_priority);
582 }
583
584 if (meshMaterials.empty()) {
585 meshMaterials.push_back(mat);
586 meshFrsMaterials.push_back(tmpMat);
587 shape->setFrsMaterial(tmpMat);
588 }
589 else {
590 // find if the Blender material is already in the list
591 unsigned int i = 0;
592 bool found = false;
593
594 for (vector<Material *>::iterator it = meshMaterials.begin(), itend = meshMaterials.end();
595 it != itend;
596 it++, i++) {
597 if (*it == mat) {
598 ls.currentMIndex = i;
599 found = true;
600 break;
601 }
602 }
603
604 if (!found) {
605 meshMaterials.push_back(mat);
606 meshFrsMaterials.push_back(tmpMat);
607 ls.currentMIndex = meshFrsMaterials.size() - 1;
608 }
609 }
610
611 float triCoords[5][3], triNormals[5][3];
612 bool edgeMarks[5]; // edgeMarks[i] is for the edge between i-th and (i+1)-th vertices
613
614 clipTriangle(
615 numTris, triCoords, v1, v2, v3, triNormals, n1, n2, n3, edgeMarks, em1, em2, em3, clip);
616 for (i = 0; i < numTris; i++) {
617 addTriangle(&ls,
618 triCoords[0],
619 triCoords[i + 1],
620 triCoords[i + 2],
621 triNormals[0],
622 triNormals[i + 1],
623 triNormals[i + 2],
624 fm,
625 (i == 0) ? edgeMarks[0] : false,
626 edgeMarks[i + 1],
627 (i == numTris - 1) ? edgeMarks[i + 2] : false);
628 _numFacesRead++;
629 }
630 }
631
632 MEM_freeN(mlooptri);
633
634 // We might have several times the same vertex. We want a clean
635 // shape with no real-vertex. Here, we are making a cleaning pass.
636 float *cleanVertices = NULL;
637 unsigned int cvSize;
638 unsigned int *cleanVIndices = NULL;
639
640 GeomCleaner::CleanIndexedVertexArray(
641 vertices, vSize, VIndices, viSize, &cleanVertices, &cvSize, &cleanVIndices);
642
643 float *cleanNormals = NULL;
644 unsigned int cnSize;
645 unsigned int *cleanNIndices = NULL;
646
647 GeomCleaner::CleanIndexedVertexArray(
648 normals, nSize, NIndices, niSize, &cleanNormals, &cnSize, &cleanNIndices);
649
650 // format materials array
651 FrsMaterial **marray = new FrsMaterial *[meshFrsMaterials.size()];
652 unsigned int mindex = 0;
653 for (vector<FrsMaterial>::iterator m = meshFrsMaterials.begin(), mend = meshFrsMaterials.end();
654 m != mend;
655 ++m) {
656 marray[mindex] = new FrsMaterial(*m);
657 ++mindex;
658 }
659
660 // deallocates memory:
661 delete[] vertices;
662 delete[] normals;
663 delete[] VIndices;
664 delete[] NIndices;
665
666 // Fix for degenerated triangles
667 // A degenerate triangle is a triangle such that
668 // 1) A and B are in the same position in the 3D space; or
669 // 2) the distance between point P and line segment AB is zero.
670 // Only those degenerate triangles in the second form are resolved here
671 // by adding a small offset to P, whereas those in the first form are
672 // addressed later in WShape::MakeFace().
673 vector<detri_t> detriList;
674 Vec3r zero(0.0, 0.0, 0.0);
675 unsigned vi0, vi1, vi2;
676 for (i = 0; i < viSize; i += 3) {
677 detri_t detri;
678 vi0 = cleanVIndices[i];
679 vi1 = cleanVIndices[i + 1];
680 vi2 = cleanVIndices[i + 2];
681 Vec3r v0(cleanVertices[vi0], cleanVertices[vi0 + 1], cleanVertices[vi0 + 2]);
682 Vec3r v1(cleanVertices[vi1], cleanVertices[vi1 + 1], cleanVertices[vi1 + 2]);
683 Vec3r v2(cleanVertices[vi2], cleanVertices[vi2 + 1], cleanVertices[vi2 + 2]);
684 if (v0 == v1 || v0 == v2 || v1 == v2) {
685 continue; // do nothing for now
686 }
687 if (GeomUtils::distPointSegment<Vec3r>(v0, v1, v2) < 1.0e-6) {
688 detri.viP = vi0;
689 detri.viA = vi1;
690 detri.viB = vi2;
691 }
692 else if (GeomUtils::distPointSegment<Vec3r>(v1, v0, v2) < 1.0e-6) {
693 detri.viP = vi1;
694 detri.viA = vi0;
695 detri.viB = vi2;
696 }
697 else if (GeomUtils::distPointSegment<Vec3r>(v2, v0, v1) < 1.0e-6) {
698 detri.viP = vi2;
699 detri.viA = vi0;
700 detri.viB = vi1;
701 }
702 else {
703 continue;
704 }
705
706 detri.v = zero;
707 detri.n = 0;
708 for (unsigned int j = 0; j < viSize; j += 3) {
709 if (i == j) {
710 continue;
711 }
712 vi0 = cleanVIndices[j];
713 vi1 = cleanVIndices[j + 1];
714 vi2 = cleanVIndices[j + 2];
715 Vec3r v0(cleanVertices[vi0], cleanVertices[vi0 + 1], cleanVertices[vi0 + 2]);
716 Vec3r v1(cleanVertices[vi1], cleanVertices[vi1 + 1], cleanVertices[vi1 + 2]);
717 Vec3r v2(cleanVertices[vi2], cleanVertices[vi2 + 1], cleanVertices[vi2 + 2]);
718 if (detri.viP == vi0 && (detri.viA == vi1 || detri.viB == vi1)) {
719 detri.v += (v2 - v0);
720 detri.n++;
721 }
722 else if (detri.viP == vi0 && (detri.viA == vi2 || detri.viB == vi2)) {
723 detri.v += (v1 - v0);
724 detri.n++;
725 }
726 else if (detri.viP == vi1 && (detri.viA == vi0 || detri.viB == vi0)) {
727 detri.v += (v2 - v1);
728 detri.n++;
729 }
730 else if (detri.viP == vi1 && (detri.viA == vi2 || detri.viB == vi2)) {
731 detri.v += (v0 - v1);
732 detri.n++;
733 }
734 else if (detri.viP == vi2 && (detri.viA == vi0 || detri.viB == vi0)) {
735 detri.v += (v1 - v2);
736 detri.n++;
737 }
738 else if (detri.viP == vi2 && (detri.viA == vi1 || detri.viB == vi1)) {
739 detri.v += (v0 - v2);
740 detri.n++;
741 }
742 }
743 if (detri.n > 0) {
744 detri.v.normalizeSafe();
745 }
746 detriList.push_back(detri);
747 }
748
749 if (!detriList.empty()) {
750 vector<detri_t>::iterator v;
751 for (v = detriList.begin(); v != detriList.end(); v++) {
752 detri_t detri = (*v);
753 if (detri.n == 0) {
754 cleanVertices[detri.viP] = cleanVertices[detri.viA];
755 cleanVertices[detri.viP + 1] = cleanVertices[detri.viA + 1];
756 cleanVertices[detri.viP + 2] = cleanVertices[detri.viA + 2];
757 }
758 else if (detri.v.norm() > 0.0) {
759 cleanVertices[detri.viP] += 1.0e-5 * detri.v.x();
760 cleanVertices[detri.viP + 1] += 1.0e-5 * detri.v.y();
761 cleanVertices[detri.viP + 2] += 1.0e-5 * detri.v.z();
762 }
763 }
764 if (G.debug & G_DEBUG_FREESTYLE) {
765 printf("Warning: Object %s contains %lu degenerated triangle%s (strokes may be incorrect)\n",
766 name,
767 (long unsigned int)detriList.size(),
768 (detriList.size() > 1) ? "s" : "");
769 }
770 }
771
772 // Create the IndexedFaceSet with the retrieved attributes
773 IndexedFaceSet *rep;
774 rep = new IndexedFaceSet(cleanVertices,
775 cvSize,
776 cleanNormals,
777 cnSize,
778 marray,
779 meshFrsMaterials.size(),
780 0,
781 0,
782 numFaces,
783 numVertexPerFaces,
784 faceStyle,
785 faceEdgeMarks,
786 cleanVIndices,
787 viSize,
788 cleanNIndices,
789 niSize,
790 MIndices,
791 viSize,
792 0,
793 0,
794 0);
795 // sets the id of the rep
796 rep->setId(Id(id, 0));
797 rep->setName(ob->id.name + 2);
798 rep->setLibraryPath(ob->id.lib ? ob->id.lib->filepath : "");
799
800 const BBox<Vec3r> bbox = BBox<Vec3r>(Vec3r(ls.minBBox[0], ls.minBBox[1], ls.minBBox[2]),
801 Vec3r(ls.maxBBox[0], ls.maxBBox[1], ls.maxBBox[2]));
802 rep->setBBox(bbox);
803 shape->AddRep(rep);
804
805 currentMesh->AddChild(shape);
806 _Scene->AddChild(currentMesh);
807 }
808
809 } /* namespace Freestyle */
810