1b6b8afc |
1 | // Copyright (c) 2015-2021 OPEN CASCADE SAS |
2 | // |
3 | // This file is part of Open CASCADE Technology software library. |
4 | // |
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. |
10 | // |
11 | // Alternatively, this file may be used under the terms of Open CASCADE |
12 | // commercial license or contractual agreement. |
13 | |
14 | #include <RWObj_ObjWriterContext.hxx> |
15 | |
16 | #include <Message.hxx> |
17 | #include <NCollection_IndexedMap.hxx> |
18 | #include <OSD_OpenFile.hxx> |
19 | |
20 | // ======================================================================= |
21 | // function : splitLines |
22 | // purpose : |
23 | // ======================================================================= |
24 | static void splitLines (const TCollection_AsciiString& theString, |
25 | NCollection_IndexedMap<TCollection_AsciiString>& theLines) |
26 | { |
27 | if (theString.IsEmpty()) |
28 | { |
29 | return; |
30 | } |
31 | |
32 | Standard_Integer aLineFrom = 1; |
33 | for (Standard_Integer aCharIter = 1;; ++aCharIter) |
34 | { |
35 | const char aChar = theString.Value (aCharIter); |
36 | if (aChar != '\r' |
37 | && aChar != '\n' |
38 | && aCharIter != theString.Length()) |
39 | { |
40 | continue; |
41 | } |
42 | |
43 | if (aLineFrom != aCharIter) |
44 | { |
45 | TCollection_AsciiString aLine = theString.SubString (aLineFrom, aCharIter); |
46 | aLine.RightAdjust(); |
47 | theLines.Add (aLine); |
48 | } |
49 | |
50 | if (aCharIter == theString.Length()) |
51 | { |
52 | break; |
53 | } |
54 | else if (aChar == '\r' |
55 | && theString.Value (aCharIter + 1) == '\n') |
56 | { |
57 | // CRLF |
58 | ++aCharIter; |
59 | } |
60 | aLineFrom = aCharIter + 1; |
61 | } |
62 | } |
63 | |
64 | // ================================================================ |
65 | // Function : RWObj_ObjWriterContext |
66 | // Purpose : |
67 | // ================================================================ |
68 | RWObj_ObjWriterContext::RWObj_ObjWriterContext (const TCollection_AsciiString& theName) |
69 | : NbFaces (0), |
70 | myFile (OSD_OpenFile (theName.ToCString(), "wb")), |
71 | myName (theName), |
72 | myElemPosFirst (1, 1, 1, 1), |
73 | myElemNormFirst(1, 1, 1, 1), |
74 | myElemUVFirst (1, 1, 1, 1), |
75 | myHasNormals (false), |
76 | myHasTexCoords (false) |
77 | { |
78 | if (myFile == NULL) |
79 | { |
80 | Message::SendFail (TCollection_AsciiString ("File cannot be created\n") + theName); |
81 | return; |
82 | } |
83 | } |
84 | |
85 | // ================================================================ |
86 | // Function : ~RWObj_ObjWriterContext |
87 | // Purpose : |
88 | // ================================================================ |
89 | RWObj_ObjWriterContext::~RWObj_ObjWriterContext() |
90 | { |
91 | if (myFile != NULL) |
92 | { |
93 | ::fclose (myFile); |
94 | Message::SendFail (TCollection_AsciiString ("File cannot be written\n") + myName); |
95 | } |
96 | } |
97 | |
98 | // ================================================================ |
99 | // Function : Close |
100 | // Purpose : |
101 | // ================================================================ |
102 | bool RWObj_ObjWriterContext::Close() |
103 | { |
104 | bool isOk = ::fclose (myFile) == 0; |
105 | myFile = NULL; |
106 | return isOk; |
107 | } |
108 | |
109 | // ================================================================ |
110 | // Function : WriteHeader |
111 | // Purpose : |
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) |
117 | { |
118 | bool isOk = ::Fprintf (myFile, "# Exported by Open CASCADE Technology [dev.opencascade.org]\n" |
119 | "# Vertices: %d\n" |
120 | "# Faces: %d\n", theNbNodes, theNbElems) != 0; |
121 | for (TColStd_IndexedDataMapOfStringString::Iterator aKeyValueIter (theFileInfo); aKeyValueIter.More(); aKeyValueIter.Next()) |
122 | { |
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) |
127 | { |
128 | const TCollection_AsciiString& aLine = aKeyLines.FindKey (aLineIter); |
129 | isOk = isOk |
130 | && ::Fprintf (myFile, |
131 | aLineIter > 1 ? "\n# %s" : "# %s", |
132 | aLine.ToCString()) != 0; |
133 | } |
134 | isOk = isOk |
135 | && ::Fprintf (myFile, !aKeyLines.IsEmpty() ? ":" : "# ") != 0; |
136 | for (Standard_Integer aLineIter = 1; aLineIter <= aValLines.Extent(); ++aLineIter) |
137 | { |
138 | const TCollection_AsciiString& aLine = aValLines.FindKey (aLineIter); |
139 | isOk = isOk |
140 | && ::Fprintf (myFile, |
141 | aLineIter > 1 ? "\n# %s" : " %s", |
142 | aLine.ToCString()) != 0; |
143 | } |
144 | isOk = isOk |
145 | && ::Fprintf (myFile, "\n") != 0; |
146 | } |
147 | |
148 | if (!theMatLib.IsEmpty()) |
149 | { |
150 | isOk = isOk |
151 | && ::Fprintf (myFile, "mtllib %s\n", theMatLib.ToCString()) != 0; |
152 | } |
153 | return isOk; |
154 | } |
155 | |
156 | // ================================================================ |
157 | // Function : WriteActiveMaterial |
158 | // Purpose : |
159 | // ================================================================ |
160 | bool RWObj_ObjWriterContext::WriteActiveMaterial (const TCollection_AsciiString& theMaterial) |
161 | { |
162 | myActiveMaterial = theMaterial; |
163 | return !theMaterial.IsEmpty() |
164 | ? Fprintf (myFile, "usemtl %s\n", theMaterial.ToCString()) != 0 |
165 | : Fprintf (myFile, "usemtl\n") != 0; |
166 | } |
167 | |
168 | // ================================================================ |
169 | // Function : WriteTriangle |
170 | // Purpose : |
171 | // ================================================================ |
172 | bool RWObj_ObjWriterContext::WriteTriangle (const Graphic3d_Vec3i& theTri) |
173 | { |
174 | const Graphic3d_Vec3i aTriPos = theTri + myElemPosFirst.xyz(); |
175 | if (myHasNormals) |
176 | { |
177 | const Graphic3d_Vec3i aTriNorm = theTri + myElemNormFirst.xyz(); |
178 | if (myHasTexCoords) |
179 | { |
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; |
185 | } |
186 | else |
187 | { |
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; |
192 | } |
193 | } |
194 | if (myHasTexCoords) |
195 | { |
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; |
201 | } |
202 | else |
203 | { |
204 | return Fprintf (myFile, "f %d %d %d\n", aTriPos[0], aTriPos[1], aTriPos[2]) != 0; |
205 | } |
206 | } |
207 | |
208 | // ================================================================ |
209 | // Function : WriteQuad |
210 | // Purpose : |
211 | // ================================================================ |
212 | bool RWObj_ObjWriterContext::WriteQuad (const Graphic3d_Vec4i& theQuad) |
213 | { |
214 | const Graphic3d_Vec4i aQPos = theQuad + myElemPosFirst; |
215 | if (myHasNormals) |
216 | { |
217 | const Graphic3d_Vec4i aQNorm = theQuad + myElemNormFirst; |
218 | if (myHasTexCoords) |
219 | { |
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; |
226 | } |
227 | else |
228 | { |
229 | return Fprintf (myFile, "f %d//%d %d//%d %d//%d %d//%d\n", |
230 | aQPos[0], aQNorm[0], |
231 | aQPos[1], aQNorm[1], |
232 | aQPos[2], aQNorm[2], |
233 | aQPos[3], aQNorm[3]) != 0; |
234 | } |
235 | } |
236 | if (myHasTexCoords) |
237 | { |
238 | const Graphic3d_Vec4i aQTex = theQuad + myElemUVFirst; |
239 | return Fprintf (myFile, "f %d/%d %d/%d %d/%d %d/%d\n", |
240 | aQPos[0], aQTex[0], |
241 | aQPos[1], aQTex[1], |
242 | aQPos[2], aQTex[2], |
243 | aQPos[3], aQTex[3]) != 0; |
244 | } |
245 | else |
246 | { |
247 | return Fprintf (myFile, "f %d %d %d %d\n", aQPos[0], aQPos[1], aQPos[2], aQPos[3]) != 0; |
248 | } |
249 | } |
250 | |
251 | // ================================================================ |
252 | // Function : WriteVertex |
253 | // Purpose : |
254 | // ================================================================ |
255 | bool RWObj_ObjWriterContext::WriteVertex (const Graphic3d_Vec3& theValue) |
256 | { |
257 | return Fprintf (myFile, "v %f %f %f\n", theValue.x(), theValue.y(), theValue.z()) != 0; |
258 | } |
259 | |
260 | // ================================================================ |
261 | // Function : WriteNormal |
262 | // Purpose : |
263 | // ================================================================ |
264 | bool RWObj_ObjWriterContext::WriteNormal (const Graphic3d_Vec3& theValue) |
265 | { |
266 | return Fprintf (myFile, "vn %f %f %f\n", theValue.x(), theValue.y(), theValue.z()) != 0; |
267 | } |
268 | |
269 | // ================================================================ |
270 | // Function : WriteTexCoord |
271 | // Purpose : |
272 | // ================================================================ |
273 | bool RWObj_ObjWriterContext::WriteTexCoord (const Graphic3d_Vec2& theValue) |
274 | { |
275 | return Fprintf (myFile, "vt %f %f\n", theValue.x(), theValue.y()) != 0; |
276 | } |
277 | |
278 | // ================================================================ |
279 | // Function : WriteGroup |
280 | // Purpose : |
281 | // ================================================================ |
282 | bool RWObj_ObjWriterContext::WriteGroup (const TCollection_AsciiString& theValue) |
283 | { |
284 | return !theValue.IsEmpty() |
285 | ? Fprintf (myFile, "g %s\n", theValue.ToCString()) != 0 |
286 | : Fprintf (myFile, "g\n") != 0; |
287 | } |
288 | |
289 | // ================================================================ |
290 | // Function : FlushFace |
291 | // Purpose : |
292 | // ================================================================ |
293 | void RWObj_ObjWriterContext::FlushFace (Standard_Integer theNbNodes) |
294 | { |
295 | Graphic3d_Vec4i aShift (theNbNodes, theNbNodes, theNbNodes, theNbNodes); |
296 | myElemPosFirst += aShift; |
297 | if (myHasNormals) |
298 | { |
299 | myElemNormFirst += aShift; |
300 | } |
301 | if (myHasTexCoords) |
302 | { |
303 | myElemUVFirst += aShift; |
304 | } |
305 | } |