1 // Copyright (c) 2015-2021 OPEN CASCADE SAS
3 // This file is part of Open CASCADE Technology software library.
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
14 #include <RWObj_ObjWriterContext.hxx>
16 #include <Message.hxx>
17 #include <NCollection_IndexedMap.hxx>
18 #include <OSD_OpenFile.hxx>
20 // =======================================================================
21 // function : splitLines
23 // =======================================================================
24 static void splitLines (const TCollection_AsciiString& theString,
25 NCollection_IndexedMap<TCollection_AsciiString>& theLines)
27 if (theString.IsEmpty())
32 Standard_Integer aLineFrom = 1;
33 for (Standard_Integer aCharIter = 1;; ++aCharIter)
35 const char aChar = theString.Value (aCharIter);
38 && aCharIter != theString.Length())
43 if (aLineFrom != aCharIter)
45 TCollection_AsciiString aLine = theString.SubString (aLineFrom, aCharIter);
50 if (aCharIter == theString.Length())
54 else if (aChar == '\r'
55 && theString.Value (aCharIter + 1) == '\n')
60 aLineFrom = aCharIter + 1;
64 // ================================================================
65 // Function : RWObj_ObjWriterContext
67 // ================================================================
68 RWObj_ObjWriterContext::RWObj_ObjWriterContext (const TCollection_AsciiString& theName)
70 myFile (OSD_OpenFile (theName.ToCString(), "wb")),
72 myElemPosFirst (1, 1, 1, 1),
73 myElemNormFirst(1, 1, 1, 1),
74 myElemUVFirst (1, 1, 1, 1),
76 myHasTexCoords (false)
80 Message::SendFail (TCollection_AsciiString ("File cannot be created\n") + theName);
85 // ================================================================
86 // Function : ~RWObj_ObjWriterContext
88 // ================================================================
89 RWObj_ObjWriterContext::~RWObj_ObjWriterContext()
94 Message::SendFail (TCollection_AsciiString ("File cannot be written\n") + myName);
98 // ================================================================
101 // ================================================================
102 bool RWObj_ObjWriterContext::Close()
104 bool isOk = ::fclose (myFile) == 0;
109 // ================================================================
110 // Function : WriteHeader
112 // ================================================================
113 bool RWObj_ObjWriterContext::WriteHeader (const Standard_Integer theNbNodes,
114 const Standard_Integer theNbElems,
115 const TCollection_AsciiString& theMatLib,
116 const TColStd_IndexedDataMapOfStringString& theFileInfo)
118 bool isOk = ::Fprintf (myFile, "# Exported by Open CASCADE Technology [dev.opencascade.org]\n"
120 "# Faces: %d\n", theNbNodes, theNbElems) != 0;
121 for (TColStd_IndexedDataMapOfStringString::Iterator aKeyValueIter (theFileInfo); aKeyValueIter.More(); aKeyValueIter.Next())
123 NCollection_IndexedMap<TCollection_AsciiString> aKeyLines, aValLines;
124 splitLines (aKeyValueIter.Key(), aKeyLines);
125 splitLines (aKeyValueIter.Value(), aValLines);
126 for (Standard_Integer aLineIter = 1; aLineIter <= aKeyLines.Extent(); ++aLineIter)
128 const TCollection_AsciiString& aLine = aKeyLines.FindKey (aLineIter);
130 && ::Fprintf (myFile,
131 aLineIter > 1 ? "\n# %s" : "# %s",
132 aLine.ToCString()) != 0;
135 && ::Fprintf (myFile, !aKeyLines.IsEmpty() ? ":" : "# ") != 0;
136 for (Standard_Integer aLineIter = 1; aLineIter <= aValLines.Extent(); ++aLineIter)
138 const TCollection_AsciiString& aLine = aValLines.FindKey (aLineIter);
140 && ::Fprintf (myFile,
141 aLineIter > 1 ? "\n# %s" : " %s",
142 aLine.ToCString()) != 0;
145 && ::Fprintf (myFile, "\n") != 0;
148 if (!theMatLib.IsEmpty())
151 && ::Fprintf (myFile, "mtllib %s\n", theMatLib.ToCString()) != 0;
156 // ================================================================
157 // Function : WriteActiveMaterial
159 // ================================================================
160 bool RWObj_ObjWriterContext::WriteActiveMaterial (const TCollection_AsciiString& theMaterial)
162 myActiveMaterial = theMaterial;
163 return !theMaterial.IsEmpty()
164 ? Fprintf (myFile, "usemtl %s\n", theMaterial.ToCString()) != 0
165 : Fprintf (myFile, "usemtl\n") != 0;
168 // ================================================================
169 // Function : WriteTriangle
171 // ================================================================
172 bool RWObj_ObjWriterContext::WriteTriangle (const Graphic3d_Vec3i& theTri)
174 const Graphic3d_Vec3i aTriPos = theTri + myElemPosFirst.xyz();
177 const Graphic3d_Vec3i aTriNorm = theTri + myElemNormFirst.xyz();
180 const Graphic3d_Vec3i aTriUv = theTri + myElemUVFirst.xyz();
181 return Fprintf (myFile, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
182 aTriPos[0], aTriUv[0], aTriNorm[0],
183 aTriPos[1], aTriUv[1], aTriNorm[1],
184 aTriPos[2], aTriUv[2], aTriNorm[2]) != 0;
188 return Fprintf (myFile, "f %d//%d %d//%d %d//%d\n",
189 aTriPos[0], aTriNorm[0],
190 aTriPos[1], aTriNorm[1],
191 aTriPos[2], aTriNorm[2]) != 0;
196 const Graphic3d_Vec3i aTriUv = theTri + myElemUVFirst.xyz();
197 return Fprintf (myFile, "f %d/%d %d/%d %d/%d\n",
198 aTriPos[0], aTriUv[0],
199 aTriPos[1], aTriUv[1],
200 aTriPos[2], aTriUv[2]) != 0;
204 return Fprintf (myFile, "f %d %d %d\n", aTriPos[0], aTriPos[1], aTriPos[2]) != 0;
208 // ================================================================
209 // Function : WriteQuad
211 // ================================================================
212 bool RWObj_ObjWriterContext::WriteQuad (const Graphic3d_Vec4i& theQuad)
214 const Graphic3d_Vec4i aQPos = theQuad + myElemPosFirst;
217 const Graphic3d_Vec4i aQNorm = theQuad + myElemNormFirst;
220 const Graphic3d_Vec4i aQTex = theQuad + myElemUVFirst;
221 return Fprintf (myFile, "f %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d\n",
222 aQPos[0], aQTex[0], aQNorm[0],
223 aQPos[1], aQTex[1], aQNorm[1],
224 aQPos[2], aQTex[2], aQNorm[2],
225 aQPos[3], aQTex[3], aQNorm[3]) != 0;
229 return Fprintf (myFile, "f %d//%d %d//%d %d//%d %d//%d\n",
233 aQPos[3], aQNorm[3]) != 0;
238 const Graphic3d_Vec4i aQTex = theQuad + myElemUVFirst;
239 return Fprintf (myFile, "f %d/%d %d/%d %d/%d %d/%d\n",
243 aQPos[3], aQTex[3]) != 0;
247 return Fprintf (myFile, "f %d %d %d %d\n", aQPos[0], aQPos[1], aQPos[2], aQPos[3]) != 0;
251 // ================================================================
252 // Function : WriteVertex
254 // ================================================================
255 bool RWObj_ObjWriterContext::WriteVertex (const Graphic3d_Vec3& theValue)
257 return Fprintf (myFile, "v %f %f %f\n", theValue.x(), theValue.y(), theValue.z()) != 0;
260 // ================================================================
261 // Function : WriteNormal
263 // ================================================================
264 bool RWObj_ObjWriterContext::WriteNormal (const Graphic3d_Vec3& theValue)
266 return Fprintf (myFile, "vn %f %f %f\n", theValue.x(), theValue.y(), theValue.z()) != 0;
269 // ================================================================
270 // Function : WriteTexCoord
272 // ================================================================
273 bool RWObj_ObjWriterContext::WriteTexCoord (const Graphic3d_Vec2& theValue)
275 return Fprintf (myFile, "vt %f %f\n", theValue.x(), theValue.y()) != 0;
278 // ================================================================
279 // Function : WriteGroup
281 // ================================================================
282 bool RWObj_ObjWriterContext::WriteGroup (const TCollection_AsciiString& theValue)
284 return !theValue.IsEmpty()
285 ? Fprintf (myFile, "g %s\n", theValue.ToCString()) != 0
286 : Fprintf (myFile, "g\n") != 0;
289 // ================================================================
290 // Function : FlushFace
292 // ================================================================
293 void RWObj_ObjWriterContext::FlushFace (Standard_Integer theNbNodes)
295 Graphic3d_Vec4i aShift (theNbNodes, theNbNodes, theNbNodes, theNbNodes);
296 myElemPosFirst += aShift;
299 myElemNormFirst += aShift;
303 myElemUVFirst += aShift;