0030964: Data Exchange - use Standard_ReadLineBuffer within OBJ reader
[occt.git] / src / RWObj / RWObj_Reader.cxx
CommitLineData
4151c94d 1// Author: Kirill Gavrilov
2// Copyright (c) 2017-2019 OPEN CASCADE SAS
3//
4// This file is part of Open CASCADE Technology software library.
5//
6// This library is free software; you can redistribute it and/or modify it under
7// the terms of the GNU Lesser General Public License version 2.1 as published
8// by the Free Software Foundation, with special exception defined in the file
9// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10// distribution for complete text of the license and disclaimer of any warranty.
11//
12// Alternatively, this file may be used under the terms of Open CASCADE
13// commercial license or contractual agreement.
14
15#include <RWObj_Reader.hxx>
16
17#include <RWObj_MtlReader.hxx>
18
19#include <BRepMesh_DataStructureOfDelaun.hxx>
20#include <BRepMesh_Delaun.hxx>
21#include <gp_XY.hxx>
22#include <Message.hxx>
23#include <Message_Messenger.hxx>
24#include <Message_ProgressSentry.hxx>
25#include <NCollection_IncAllocator.hxx>
26#include <OSD_OpenFile.hxx>
27#include <OSD_Path.hxx>
28#include <OSD_Timer.hxx>
29#include <Precision.hxx>
30#include <Standard_CLocaleSentry.hxx>
51ee6a7d 31#include <Standard_ReadLineBuffer.hxx>
4151c94d 32
33#include <algorithm>
34#include <limits>
35
36#if defined(_WIN32)
37 #define ftell64(a) _ftelli64(a)
38 #define fseek64(a,b,c) _fseeki64(a,b,c)
39#else
40 #define ftell64(a) ftello(a)
41 #define fseek64(a,b,c) fseeko(a,b,c)
42#endif
43
44IMPLEMENT_STANDARD_RTTIEXT(RWObj_Reader, Standard_Transient)
45
46namespace
47{
51ee6a7d 48 // The length of buffer to read (in bytes)
49 static const size_t THE_BUFFER_SIZE = 4 * 1024;
4151c94d 50
51 //! Simple wrapper.
52 struct RWObj_ReaderFile
53 {
54 FILE* File;
4151c94d 55 int64_t FileLen;
56
57 //! Constructor opening the file.
51ee6a7d 58 RWObj_ReaderFile (const TCollection_AsciiString& theFile)
4151c94d 59 : File (OSD_OpenFile (theFile.ToCString(), "rb")),
4151c94d 60 FileLen (0)
61 {
62 if (this->File != NULL)
63 {
64 // determine length of file
65 ::fseek64 (this->File, 0, SEEK_END);
66 FileLen = ::ftell64 (this->File);
67 ::fseek64 (this->File, 0, SEEK_SET);
68 }
69 }
70
71 //! Destructor closing the file.
72 ~RWObj_ReaderFile()
73 {
74 if (File != NULL)
75 {
76 ::fclose (File);
77 }
78 }
4151c94d 79 };
80
81 //! Return TRUE if given polygon has clockwise node order.
82 static bool isClockwisePolygon (const Handle(BRepMesh_DataStructureOfDelaun)& theMesh,
83 const IMeshData::VectorOfInteger& theIndexes)
84 {
85 double aPtSum = 0;
86 const int aNbElemNodes = theIndexes.Size();
87 for (int aNodeIter = theIndexes.Lower(); aNodeIter <= theIndexes.Upper(); ++aNodeIter)
88 {
89 int aNodeNext = theIndexes.Lower() + ((aNodeIter + 1) % aNbElemNodes);
90 const BRepMesh_Vertex& aVert1 = theMesh->GetNode (theIndexes.Value (aNodeIter));
91 const BRepMesh_Vertex& aVert2 = theMesh->GetNode (theIndexes.Value (aNodeNext));
92 aPtSum += (aVert2.Coord().X() - aVert1.Coord().X())
93 * (aVert2.Coord().Y() + aVert1.Coord().Y());
94 }
95 return aPtSum < 0.0;
96 }
97}
98
99// ================================================================
100// Function : Read
101// Purpose :
102// ================================================================
103RWObj_Reader::RWObj_Reader()
104: myMemLimitBytes (Standard_Size(-1)),
105 myMemEstim (0),
106 myNbLines (0),
107 myNbProbeNodes (0),
108 myNbProbeElems (0),
109 myNbElemsBig (0),
110 myToAbort (false)
111{
112 //
113}
114
115// ================================================================
116// Function : read
117// Purpose :
118// ================================================================
119Standard_Boolean RWObj_Reader::read (const TCollection_AsciiString& theFile,
120 const Handle(Message_ProgressIndicator)& theProgress,
121 const Standard_Boolean theToProbe)
122{
123 myMemEstim = 0;
124 myNbLines = 0;
125 myNbProbeNodes = 0;
126 myNbProbeElems = 0;
127 myNbElemsBig = 0;
128 myToAbort = false;
129 myObjVerts.Reset();
130 myObjVertsUV.Clear();
131 myObjNorms.Clear();
132 myPackedIndices.Clear();
133 myMaterials.Clear();
134 myFileComments.Clear();
135 myExternalFiles.Clear();
136 myActiveSubMesh = RWObj_SubMesh();
137
138 // determine file location to load associated files
139 TCollection_AsciiString aFileName;
140 OSD_Path::FolderAndFileFromPath (theFile, myFolder, aFileName);
141 myCurrElem.resize (1024, -1);
142
143 Standard_CLocaleSentry aLocaleSentry;
144 RWObj_ReaderFile aFile (theFile);
145 if (aFile.File == NULL)
146 {
147 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: file '") + theFile + "' is not found!", Message_Fail);
148 return Standard_False;
149 }
150
151 // determine length of file
152 const int64_t aFileLen = aFile.FileLen;
153 if (aFileLen <= 0L)
154 {
155 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: file '") + theFile + "' is empty!", Message_Fail);
156 return Standard_False;
157 }
158
51ee6a7d 159 Standard_ReadLineBuffer aBuffer (THE_BUFFER_SIZE);
160 aBuffer.SetMultilineMode (true);
161
4151c94d 162 const Standard_Integer aNbMiBTotal = Standard_Integer(aFileLen / (1024 * 1024));
163 Standard_Integer aNbMiBPassed = 0;
164 Message_ProgressSentry aPSentry (theProgress, "Reading text OBJ file", 0, aNbMiBTotal, 1);
165 OSD_Timer aTimer;
166 aTimer.Start();
167
168 bool isStart = true;
51ee6a7d 169 int64_t aPosition = 0;
170 size_t aLineLen = 0;
171 int64_t aReadBytes = 0;
172 const char* aLine = NULL;
173 for (;;)
4151c94d 174 {
51ee6a7d 175 aLine = aBuffer.ReadLine (aFile.File, aLineLen, aReadBytes);
176 if (aLine == NULL)
177 {
178 break;
179 }
4151c94d 180 ++myNbLines;
51ee6a7d 181 aPosition += aReadBytes;
4151c94d 182 if (aTimer.ElapsedTime() > 1.0)
183 {
184 if (!aPSentry.More())
185 {
186 return false;
187 }
188
51ee6a7d 189 const Standard_Integer aNbMiBRead = Standard_Integer(aPosition / (1024 * 1024));
4151c94d 190 for (; aNbMiBPassed < aNbMiBRead; ++aNbMiBPassed) { aPSentry.Next(); }
191 aTimer.Reset();
192 aTimer.Start();
193 }
194
195 if (*aLine == '#')
196 {
197 if (isStart)
198 {
199 TCollection_AsciiString aComment (aLine + 1);
200 aComment.LeftAdjust();
201 aComment.RightAdjust();
202 if (!aComment.IsEmpty())
203 {
204 if (!myFileComments.IsEmpty())
205 {
206 myFileComments += "\n";
207 }
208 myFileComments += aComment;
209 }
210 }
211 continue;
212 }
213 else if (*aLine == '\n'
214 || *aLine == '\0')
215 {
216
217 continue;
218 }
219 isStart = false;
220
221 if (theToProbe)
222 {
223 if (::memcmp (aLine, "mtllib", 6) == 0)
224 {
225 readMaterialLib (aLine + 7);
226 }
227 else if (aLine[0] == 'v' && RWObj_Tools::isSpaceChar (aLine[1]))
228 {
229 ++myNbProbeNodes;
230 }
231 else if (aLine[0] == 'f' && RWObj_Tools::isSpaceChar (aLine[1]))
232 {
233 ++myNbProbeElems;
234 }
235 continue;
236 }
237
238 if (aLine[0] == 'v' && RWObj_Tools::isSpaceChar (aLine[1]))
239 {
240 ++myNbProbeNodes;
241 pushVertex (aLine + 2);
242 }
243 else if (aLine[0] == 'v'
244 && aLine[1] == 'n'
245 && RWObj_Tools::isSpaceChar (aLine[2]))
246 {
247 pushNormal (aLine + 3);
248 }
249 else if (aLine[0] == 'v'
250 && aLine[1] == 't'
251 && RWObj_Tools::isSpaceChar (aLine[2]))
252 {
253 pushTexel (aLine + 3);
254 }
255 else if (aLine[0] == 'f' && RWObj_Tools::isSpaceChar (aLine[1]))
256 {
257 ++myNbProbeElems;
258 pushIndices (aLine + 2);
259 }
260 else if (aLine[0] == 'g' && IsSpace (aLine[1]))
261 {
262 pushGroup (aLine + 2);
263 }
264 else if (aLine[0] == 's' && IsSpace (aLine[1]))
265 {
266 pushSmoothGroup (aLine + 2);
267 }
268 else if (aLine[0] == 'o' && IsSpace (aLine[1]))
269 {
270 pushObject (aLine + 2);
271 }
272 else if (::memcmp (aLine, "mtllib", 6) == 0)
273 {
274 readMaterialLib (aLine + 7);
275 }
276 else if (::memcmp (aLine, "usemtl", 6) == 0)
277 {
278 pushMaterial (aLine + 7);
279 }
280
281 if (!checkMemory())
282 {
283 addMesh (myActiveSubMesh, RWObj_SubMeshReason_NewObject);
284 return false;
285 }
286 }
287
288 // collect external references
289 for (NCollection_DataMap<TCollection_AsciiString, RWObj_Material>::Iterator aMatIter (myMaterials); aMatIter.More(); aMatIter.Next())
290 {
291 const RWObj_Material& aMat = aMatIter.Value();
292 if (!aMat.DiffuseTexture.IsEmpty())
293 {
294 myExternalFiles.Add (aMat.DiffuseTexture);
295 }
296 if (!aMat.SpecularTexture.IsEmpty())
297 {
298 myExternalFiles.Add (aMat.SpecularTexture);
299 }
300 if (!aMat.BumpTexture.IsEmpty())
301 {
302 myExternalFiles.Add (aMat.BumpTexture);
303 }
304 }
305
306 // flush the last group
307 if (!theToProbe)
308 {
309 addMesh (myActiveSubMesh, RWObj_SubMeshReason_NewObject);
310 }
311 if (myNbElemsBig != 0)
312 {
313 Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: OBJ reader, ") + myNbElemsBig
314 + " polygon(s) have been split into triangles.", Message_Warning);
315 }
316
317 for (; aNbMiBPassed < aNbMiBTotal; ++aNbMiBPassed) { aPSentry.Next(); }
318 return true;
319}
320
321// =======================================================================
322// function : pushIndices
323// purpose :
324// =======================================================================
325void RWObj_Reader::pushIndices (const char* thePos)
326{
327 char* aNext = NULL;
328
329 Standard_Integer aNbElemNodes = 0;
330 for (Standard_Integer aNode = 0;; ++aNode)
331 {
332 Graphic3d_Vec3i a3Indices (-1, -1, -1);
333 a3Indices[0] = strtol (thePos, &aNext, 10) - 1;
334 if (aNext == thePos)
335 {
336 break;
337 }
338
339 // parse UV index
340 thePos = aNext;
341 if (*thePos == '/')
342 {
343 ++thePos;
344 a3Indices[1] = strtol (thePos, &aNext, 10) - 1;
345 thePos = aNext;
346
347 // parse Normal index
348 if (*thePos == '/')
349 {
350 ++thePos;
351 a3Indices[2] = strtol (thePos, &aNext, 10) - 1;
352 thePos = aNext;
353 }
354 }
355
356 // handle negative indices
357 if (a3Indices[0] < -1)
358 {
359 a3Indices[0] += myObjVerts.Upper() + 2;
360 }
361 if (a3Indices[1] < -1)
362 {
363 a3Indices[1] += myObjVertsUV.Upper() + 2;
364 }
365 if (a3Indices[2] < -1)
366 {
367 a3Indices[2] += myObjNorms.Upper() + 2;
368 }
369
370 Standard_Integer anIndex = -1;
371 if (!myPackedIndices.Find (a3Indices, anIndex))
372 {
373 if (a3Indices[0] >= 0)
374 {
375 myMemEstim += sizeof(Graphic3d_Vec3);
376 }
377 if (a3Indices[1] >= 0)
378 {
379 myMemEstim += sizeof(Graphic3d_Vec2);
380 }
381 if (a3Indices[2] >= 0)
382 {
383 myMemEstim += sizeof(Graphic3d_Vec3);
384 }
385 myMemEstim += sizeof(Graphic3d_Vec4i) + sizeof(Standard_Integer); // naive map
386 if (a3Indices[0] < myObjVerts.Lower() || a3Indices[0] > myObjVerts.Upper())
387 {
388 myToAbort = true;
389 Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: invalid OBJ syntax at line ") + myNbLines
390 + ": vertex index is out of range.", Message_Fail);
391 return;
392 }
393
394 anIndex = addNode (myObjVerts.Value (a3Indices[0]));
395 myPackedIndices.Bind (a3Indices, anIndex);
396 if (a3Indices[1] >= 0)
397 {
398 if (myObjVertsUV.IsEmpty())
399 {
400 Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: invalid OBJ syntax at line ") + myNbLines
401 + ": UV index is specified but no UV nodes are defined.", Message_Warning);
402 }
403 else if (a3Indices[1] < myObjVertsUV.Lower() || a3Indices[1] > myObjVertsUV.Upper())
404 {
405 Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: invalid OBJ syntax at line ") + myNbLines
406 + ": UV index is out of range.", Message_Warning);
407 setNodeUV (anIndex,Graphic3d_Vec2 (0.0f, 0.0f));
408 }
409 else
410 {
411 setNodeUV (anIndex, myObjVertsUV.Value (a3Indices[1]));
412 }
413 }
414 if (a3Indices[2] >= 0)
415 {
416 if (myObjNorms.IsEmpty())
417 {
418 Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: invalid OBJ syntax at line ") + myNbLines
419 + ": Normal index is specified but no Normals nodes are defined.", Message_Warning);
420 }
421 else if (a3Indices[2] < myObjNorms.Lower() || a3Indices[2] > myObjNorms.Upper())
422 {
423 Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: invalid OBJ syntax at line ") + myNbLines
424 + ": Normal index is out of range.", Message_Warning);
425 setNodeNormal (anIndex, Graphic3d_Vec3 (0.0f, 0.0f, 1.0f));
426 }
427 else
428 {
429 setNodeNormal (anIndex, myObjNorms.Value (a3Indices[2]));
430 }
431 }
432 }
433
434 if (myCurrElem.size() < size_t(aNode))
435 {
436 myCurrElem.resize (aNode * 2, -1);
437 }
438 myCurrElem[aNode] = anIndex;
439 aNbElemNodes = aNode + 1;
440
441 if (*thePos == '\n'
442 || *thePos == '\0')
443 {
444 break;
445 }
446
447 if (*thePos != ' ')
448 {
449 ++thePos;
450 }
451 }
452
453 if (myCurrElem[0] < 0
454 || myCurrElem[1] < 0
455 || myCurrElem[2] < 0
456 || aNbElemNodes < 3)
457 {
458 return;
459 }
460
461 if (aNbElemNodes == 3)
462 {
463 myMemEstim += sizeof(Graphic3d_Vec4i);
464 addElement (myCurrElem[0], myCurrElem[1], myCurrElem[2], -1);
465 }
466 else if (aNbElemNodes == 4)
467 {
468 myMemEstim += sizeof(Graphic3d_Vec4i);
469 addElement (myCurrElem[0], myCurrElem[1], myCurrElem[2], myCurrElem[3]);
470 }
471 else
472 {
473 const NCollection_Array1<Standard_Integer> aCurrElemArray1 (myCurrElem[0], 1, aNbElemNodes);
474 const Standard_Integer aNbAdded = triangulatePolygon (aCurrElemArray1);
475 if (aNbAdded < 1)
476 {
477 return;
478 }
479 ++myNbElemsBig;
480 myMemEstim += sizeof(Graphic3d_Vec4i) * aNbAdded;
481 }
482}
483
484//================================================================
485// Function : triangulatePolygonFan
486// Purpose :
487//================================================================
488Standard_Integer RWObj_Reader::triangulatePolygonFan (const NCollection_Array1<Standard_Integer>& theIndices)
489{
490 const Standard_Integer aNbElemNodes = theIndices.Size();
491 for (Standard_Integer aNodeIter = 0; aNodeIter < aNbElemNodes - 2; ++aNodeIter)
492 {
493 Graphic3d_Vec4i aTriNodes (-1, -1, -1, -1);
494 for (Standard_Integer aNodeInSubTriIter = 0; aNodeInSubTriIter < 3; ++aNodeInSubTriIter)
495 {
496 const Standard_Integer aCurrNodeIndex = (aNodeInSubTriIter == 0) ? 0 : (aNodeIter + aNodeInSubTriIter);
497 aTriNodes[aNodeInSubTriIter] = theIndices.Value (theIndices.Lower() + aCurrNodeIndex);
498 }
499 addElement (aTriNodes[0], aTriNodes[1], aTriNodes[2], -1);
500 }
501 return aNbElemNodes - 2;
502}
503
504//================================================================
505// Function : polygonCenter
506// Purpose :
507//================================================================
508gp_XYZ RWObj_Reader::polygonCenter (const NCollection_Array1<Standard_Integer>& theIndices)
509{
510 if (theIndices.Size() < 3)
511 {
512 return gp_XYZ (0.0, 0.0, 0.0);
513 }
514 else if (theIndices.Size() == 4)
515 {
516 gp_XYZ aCenter = getNode (theIndices.Value (theIndices.Lower() + 0)).XYZ()
517 + getNode (theIndices.Value (theIndices.Lower() + 2)).XYZ();
518 aCenter /= 2.0;
519 return aCenter;
520 }
521
522 gp_XYZ aCenter (0, 0, 0);
523 for (NCollection_Array1<Standard_Integer>::Iterator aPntIter (theIndices); aPntIter.More(); aPntIter.Next())
524 {
525 aCenter += getNode (aPntIter.Value()).XYZ();
526 }
527
528 aCenter /= (Standard_Real )theIndices.Size();
529 return aCenter;
530}
531
532//================================================================
533// Function : polygonNormal
534// Purpose :
535//================================================================
536gp_XYZ RWObj_Reader::polygonNormal (const NCollection_Array1<Standard_Integer>& theIndices)
537{
538 const gp_XYZ aCenter = polygonCenter (theIndices);
539 gp_XYZ aMaxDir = getNode (theIndices.First()).XYZ() - aCenter;
540 gp_XYZ aNormal = (getNode (theIndices.Last()).XYZ() - aCenter).Crossed (aMaxDir);
541 for (int aPntIter = theIndices.Lower(); aPntIter < theIndices.Upper(); ++aPntIter)
542 {
543 const gp_XYZ aTmpDir2 = getNode (theIndices.Value (aPntIter + 1)).XYZ() - aCenter;
544 if (aTmpDir2.SquareModulus() > aMaxDir.SquareModulus())
545 {
546 aMaxDir = aTmpDir2;
547 }
548
549 const gp_XYZ aTmpDir1 = getNode (theIndices.Value (aPntIter)).XYZ() - aCenter;
550 gp_XYZ aDelta = aTmpDir1.Crossed (aTmpDir2);
551 if (aNormal.Dot (aDelta) < 0.0)
552 {
553 aDelta *= -1.0;
554 }
555 aNormal += aDelta;
556 }
557
558 const Standard_Real aMod = aNormal.Modulus();
559 if (aMod > gp::Resolution())
560 {
561 aNormal /= aMod;
562 }
563 return aNormal;
564}
565
566//================================================================
567// Function : triangulatePolygon
568// Purpose :
569//================================================================
570Standard_Integer RWObj_Reader::triangulatePolygon (const NCollection_Array1<Standard_Integer>& theIndices)
571{
572 const Standard_Integer aNbElemNodes = theIndices.Size();
573 if (aNbElemNodes < 3)
574 {
575 return 0;
576 }
577
578 const gp_XYZ aPolygonNorm = polygonNormal (theIndices);
579
580 // map polygon onto plane
581 gp_XYZ aXDir;
582 {
583 const double aAbsXYZ[] = { Abs(aPolygonNorm.X()), Abs(aPolygonNorm.Y()), Abs(aPolygonNorm.Z()) };
584 Standard_Integer aMinI = (aAbsXYZ[0] < aAbsXYZ[1]) ? 0 : 1;
585 aMinI = (aAbsXYZ[aMinI] < aAbsXYZ[2]) ? aMinI : 2;
586 const Standard_Integer aI1 = (aMinI + 1) % 3 + 1;
587 const Standard_Integer aI2 = (aMinI + 2) % 3 + 1;
588 aXDir.ChangeCoord (aMinI + 1) = 0;
589 aXDir.ChangeCoord (aI1) = aPolygonNorm.Coord (aI2);
590 aXDir.ChangeCoord (aI2) = -aPolygonNorm.Coord (aI1);
591 }
592 const gp_XYZ aYDir = aPolygonNorm ^ aXDir;
593
594 Handle(NCollection_IncAllocator) anAllocator = new NCollection_IncAllocator();
595 Handle(BRepMesh_DataStructureOfDelaun) aMeshStructure = new BRepMesh_DataStructureOfDelaun (anAllocator);
596 IMeshData::VectorOfInteger anIndexes (aNbElemNodes, anAllocator);
597 for (Standard_Integer aNodeIter = 0; aNodeIter < aNbElemNodes; ++aNodeIter)
598 {
599 const Standard_Integer aNodeIndex = theIndices.Value (theIndices.Lower() + aNodeIter);
600 const gp_XYZ aPnt3d = getNode (aNodeIndex).XYZ();
601 gp_XY aPnt2d (aXDir * aPnt3d, aYDir * aPnt3d);
602 BRepMesh_Vertex aVertex (aPnt2d, aNodeIndex, BRepMesh_Frontier);
603 anIndexes.Append (aMeshStructure->AddNode (aVertex));
604 }
605
606 const bool isClockwiseOrdered = isClockwisePolygon (aMeshStructure, anIndexes);
607 for (Standard_Integer aIdx = anIndexes.Lower(); aIdx <= anIndexes.Upper(); ++aIdx)
608 {
609 const Standard_Integer aPtIdx = isClockwiseOrdered ? aIdx : (aIdx + 1) % anIndexes.Length();
610 const Standard_Integer aNextPtIdx = isClockwiseOrdered ? (aIdx + 1) % anIndexes.Length() : aIdx;
611 BRepMesh_Edge anEdge (anIndexes.Value (aPtIdx),
612 anIndexes.Value (aNextPtIdx),
613 BRepMesh_Frontier);
614 aMeshStructure->AddLink (anEdge);
615 }
616
617 try
618 {
619 BRepMesh_Delaun aTriangulation (aMeshStructure, anIndexes);
620 const IMeshData::MapOfInteger& aTriangles = aMeshStructure->ElementsOfDomain();
621 if (aTriangles.Extent() < 1)
622 {
623 return triangulatePolygonFan (theIndices);
624 }
625
626 Standard_Integer aNbTrisAdded = 0;
627 for (IMeshData::MapOfInteger::Iterator aTriIter (aTriangles); aTriIter.More(); aTriIter.Next())
628 {
629 const Standard_Integer aTriangleId = aTriIter.Key();
630 const BRepMesh_Triangle& aTriangle = aMeshStructure->GetElement (aTriangleId);
631 if (aTriangle.Movability() == BRepMesh_Deleted)
632 {
633 continue;
634 }
635
636 int aTri2d[3];
637 aMeshStructure->ElementNodes (aTriangle, aTri2d);
638 if (!isClockwiseOrdered)
639 {
640 std::swap (aTri2d[1], aTri2d[2]);
641 }
642 const BRepMesh_Vertex& aVertex1 = aMeshStructure->GetNode (aTri2d[0]);
643 const BRepMesh_Vertex& aVertex2 = aMeshStructure->GetNode (aTri2d[1]);
644 const BRepMesh_Vertex& aVertex3 = aMeshStructure->GetNode (aTri2d[2]);
645 addElement (aVertex1.Location3d(), aVertex2.Location3d(), aVertex3.Location3d(), -1);
646 ++aNbTrisAdded;
647 }
648 return aNbTrisAdded;
649 }
650 catch (Standard_Failure const& theFailure)
651 {
652 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: exception raised during polygon split\n[")
653 + theFailure.GetMessageString() + "]", Message_Warning);
654 }
655 return triangulatePolygonFan (theIndices);
656}
657
658// =======================================================================
659// function : pushObject
660// purpose :
661// =======================================================================
662void RWObj_Reader::pushObject (const char* theObjectName)
663{
664 TCollection_AsciiString aNewObject;
665 if (!RWObj_Tools::ReadName (theObjectName, aNewObject))
666 {
667 // empty group name is OK
668 }
669 if (addMesh (myActiveSubMesh, RWObj_SubMeshReason_NewObject))
670 {
671 myPackedIndices.Clear(); // vertices might be duplicated after this point...
672 }
673 myActiveSubMesh.Object = aNewObject;
674}
675
676// =======================================================================
677// function : pushGroup
678// purpose :
679// =======================================================================
680void RWObj_Reader::pushGroup (const char* theGroupName)
681{
682 TCollection_AsciiString aNewGroup;
683 if (!RWObj_Tools::ReadName (theGroupName, aNewGroup))
684 {
685 // empty group name is OK
686 }
687 if (addMesh (myActiveSubMesh, RWObj_SubMeshReason_NewGroup))
688 {
689 myPackedIndices.Clear(); // vertices might be duplicated after this point...
690 }
691 myActiveSubMesh.Group = aNewGroup;
692}
693
694// =======================================================================
695// function : pushSmoothGroup
696// purpose :
697// =======================================================================
698void RWObj_Reader::pushSmoothGroup (const char* theSmoothGroupIndex)
699{
700 TCollection_AsciiString aNewSmoothGroup;
701 RWObj_Tools::ReadName (theSmoothGroupIndex, aNewSmoothGroup);
702 if (aNewSmoothGroup == "off"
703 || aNewSmoothGroup == "0")
704 {
705 aNewSmoothGroup.Clear();
706 }
707 if (addMesh (myActiveSubMesh, RWObj_SubMeshReason_NewSmoothGroup))
708 {
709 myPackedIndices.Clear(); // vertices might be duplicated after this point...
710 }
711 myActiveSubMesh.SmoothGroup = aNewSmoothGroup;
712}
713
714// =======================================================================
715// function : pushMaterial
716// purpose :
717// =======================================================================
718void RWObj_Reader::pushMaterial (const char* theMaterialName)
719{
720 TCollection_AsciiString aNewMat;
721 if (!RWObj_Tools::ReadName (theMaterialName, aNewMat))
722 {
723 // empty material name is allowed by specs
724 }
725 else if (!myMaterials.IsBound (aNewMat))
726 {
727 Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: use of undefined OBJ material at line ")
728 + myNbLines, Message_Warning);
729 return;
730 }
731 if (myActiveSubMesh.Material.IsEqual (aNewMat))
732 {
733 return; // ignore
734 }
735
736 // implicitly create a new group to split materials
737 if (addMesh (myActiveSubMesh, RWObj_SubMeshReason_NewMaterial))
738 {
739 myPackedIndices.Clear(); // vertices might be duplicated after this point...
740 }
741 myActiveSubMesh.Material = aNewMat;
742}
743
744// =======================================================================
745// function : readMaterialLib
746// purpose :
747// =======================================================================
748void RWObj_Reader::readMaterialLib (const char* theFileName)
749{
750 TCollection_AsciiString aMatPath;
751 if (!RWObj_Tools::ReadName (theFileName, aMatPath))
752 {
753 Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: invalid OBJ syntax at line ")
754 + myNbLines, Message_Warning);
755 return;
756 }
757
758 RWObj_MtlReader aMatReader (myMaterials);
759 if (aMatReader.Read (myFolder, aMatPath))
760 {
761 myExternalFiles.Add (myFolder + aMatPath);
762 }
763}
764
765// =======================================================================
766// function : checkMemory
767// purpose :
768// =======================================================================
769bool RWObj_Reader::checkMemory()
770{
771 if (myMemEstim < myMemLimitBytes
772 || myToAbort)
773 {
774 return true;
775 }
776
777 Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: OBJ file content does not fit into ")
778 + Standard_Integer(myMemLimitBytes / (1024 * 1024)) + " MiB limit."
779 + "\nMesh data will be truncated.", Message_Fail);
780 myToAbort = true;
781 return false;
782}