1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 /*!
34 \class SoWriteAction SoWriteAction.h Inventor/actions/SoWriteAction.h
35 \brief The SoWriteAction class writes a scene graph to file.
36
37 \ingroup actions
38
39 When applied to a scene, this action writes its contents to the
40 stream contained within an SoOutput instance. This can be a file, a
41 memory buffer or a system filehandle like \c stdout, for instance.
42
43 \e All information considered part of the scene graph should be
44 written out, including not only nodes, but also the nodes' field
45 values, global fields (at least those with connections inside the
46 scene the action is applied to), engines in the scene, paths, etc.
47
48 The scene is written in the Open Inventor file format. Files in this
49 format can be parsed into their scene graph structures by using the
50 SoDB::readAll() method (SoDB also contains a few other import
51 methods you can use).
52
53 Here's a complete, stand-alone usage example which shows how to
54 write a scene graph to a memory buffer:
55
56 \code
57 #include <Inventor/SoDB.h>
58 #include <Inventor/actions/SoWriteAction.h>
59 #include <Inventor/nodes/SoCone.h>
60 #include <Inventor/nodes/SoSeparator.h>
61
62 static char * buffer;
63 static size_t buffer_size = 0;
64
65 static void *
66 buffer_realloc(void * bufptr, size_t size)
67 {
68 buffer = (char *)realloc(bufptr, size);
69 buffer_size = size;
70 return buffer;
71 }
72
73 static SbString
74 buffer_writeaction(SoNode * root)
75 {
76 SoOutput out;
77 buffer = (char *)malloc(1024);
78 buffer_size = 1024;
79 out.setBuffer(buffer, buffer_size, buffer_realloc);
80
81 SoWriteAction wa(&out);
82 wa.apply(root);
83
84 SbString s(buffer);
85 free(buffer);
86 return s;
87 }
88
89 int
90 main(int argc, char ** argv)
91 {
92 SoDB::init();
93
94 SoSeparator * root = new SoSeparator;
95 root->ref();
96
97 root->addChild(new SoCone);
98
99 SbString s = buffer_writeaction(root);
100 (void)fprintf(stdout, "%s\n", s.getString());
101
102 root->unref();
103 return 0;
104 }
105 \endcode
106
107 \sa SoOutput
108 */
109
110 #include <Inventor/actions/SoWriteAction.h>
111
112 #include <Inventor/SoOutput.h>
113 #include <Inventor/nodes/SoNode.h>
114 #include <Inventor/sensors/SoNodeSensor.h>
115 #include <Inventor/errors/SoDebugError.h>
116
117 #include "coindefs.h"
118 #include "actions/SoSubActionP.h"
119 #include "io/SoWriterefCounter.h"
120
121 class SoWriteActionP {
122 public:
123 };
124
125 SO_ACTION_SOURCE(SoWriteAction);
126
127
128 // Overridden from parent class.
129 void
initClass(void)130 SoWriteAction::initClass(void)
131 {
132 SO_ACTION_INTERNAL_INIT_CLASS(SoWriteAction, SoAction);
133 }
134
135 /*!
136 Default constructor. Output will be written to \c stdout in ASCII
137 format.
138 */
SoWriteAction(void)139 SoWriteAction::SoWriteAction(void)
140 {
141 this->commonConstructor(new SoOutput);
142 this->localoutputalloc = TRUE;
143 }
144
145 /*!
146 Constructor. Output will be written via the \a out object.
147 */
SoWriteAction(SoOutput * out)148 SoWriteAction::SoWriteAction(SoOutput * out)
149 {
150 this->commonConstructor(out);
151 this->localoutputalloc = FALSE;
152 }
153
154 void
commonConstructor(SoOutput * out)155 SoWriteAction::commonConstructor(SoOutput * out)
156 {
157 SO_ACTION_CONSTRUCTOR(SoWriteAction);
158
159 this->outobj = out;
160 this->continuing = FALSE;
161 }
162
163 /*!
164 Destructor.
165 */
~SoWriteAction(void)166 SoWriteAction::~SoWriteAction(void)
167 {
168 if (this->localoutputalloc) delete this->outobj;
169 }
170
171 /*!
172 Returns a pointer to the SoOutput object we're using when writing
173 the scene graph.
174 */
175 SoOutput *
getOutput(void) const176 SoWriteAction::getOutput(void) const
177 {
178 return this->outobj;
179 }
180
181 /*!
182 Applies the write method to the subgraph starting at \a node with
183 the current SoOutput instance, without resetting any of the internal
184 state of the action instance.
185
186 This should normally be for internal use only.
187 */
188 void
continueToApply(SoNode * node)189 SoWriteAction::continueToApply(SoNode * node)
190 {
191 SbBool wascontinuing = this->continuing;
192 this->continuing = TRUE;
193 this->apply(node);
194 this->continuing = wascontinuing;
195 }
196
197 /*!
198 Applies the write method to \a path with the current SoOutput
199 instance, without resetting any of the internal state of the action
200 instance.
201
202 This should normally be for internal use only.
203 */
204 void
continueToApply(SoPath * path)205 SoWriteAction::continueToApply(SoPath * path)
206 {
207 SbBool wascontinuing = this->continuing;
208 this->continuing = TRUE;
209 this->apply(path);
210 this->continuing = wascontinuing;
211 }
212
213 #if COIN_DEBUG
sensorCB(void * COIN_UNUSED_ARG (data),SoSensor * COIN_UNUSED_ARG (sensor))214 static void sensorCB(void * COIN_UNUSED_ARG(data), SoSensor * COIN_UNUSED_ARG(sensor))
215 {
216 SoDebugError::postWarning("SoWriteAction::SoWriteAction",
217 "Scenegraph changed during SoWriteAction().");
218 }
219 #endif
220
221 // Documented for Doxygen in superclass.
222 //
223 // Overridden from parent class, as the write action is actually done
224 // in two passes.
225 //
226 // The first pass is done to count the references of the objects in
227 // the scene graph and otherwise prepare instance in the scene for
228 // export. The second pass does the actual writing.
229 void
beginTraversal(SoNode * node)230 SoWriteAction::beginTraversal(SoNode * node)
231 {
232 #if COIN_DEBUG
233 SoNodeSensor *sensor = NULL;
234 #endif
235 if (this->continuing == FALSE) { // Run through both stages.
236 // call SoWriterefCounter::instance() before traversing to set the
237 // "current" pointer in SoWriterefCounter. This is needed to be
238 // backwards compatible with old code that uses the writeref
239 // system in SoBase.
240
241 #if COIN_DEBUG
242 if (SoWriterefCounter::debugWriterefs()) {
243 sensor = new SoNodeSensor(sensorCB, NULL);
244 sensor->setPriority(0);
245 sensor->attach(node);
246 }
247 #endif
248
249 (void) SoWriterefCounter::instance(this->getOutput());
250 this->outobj->setStage(SoOutput::COUNT_REFS);
251 this->traverse(node);
252 this->outobj->setStage(SoOutput::WRITE);
253 }
254 this->traverse(node);
255 if (!this->outobj->isBinary() && !this->continuing) {
256 outobj->write('\n');
257 outobj->resolveRoutes();
258 }
259 if (!this->continuing) {
260 SoWriterefCounter::instance(this->getOutput())->debugCleanup();
261 #if COIN_DEBUG
262 delete sensor;
263 #endif
264 }
265 }
266
267 /*!
268 \COININTERNAL
269
270 Compact path lists are not implemented in Coin (yet), but if they
271 are, SoWriteAction should return \c FALSE here -- it would only be
272 extra overhead for the SoWriteAction to have pathlists compacted
273 before traversal.
274
275 Seems like a silly optimization to me, though.. :^/ 20000306 mortene.
276 */
277 SbBool
shouldCompactPathLists(void) const278 SoWriteAction::shouldCompactPathLists(void) const
279 {
280 return FALSE;
281 }
282
283 #ifdef COIN_TEST_SUITE
284
285 // check that the realTime GlobalField is written if it has any
286 // forward connections.
287
288 #include <Inventor/SoDB.h>
289 #include <Inventor/SoInput.h>
290 #include <Inventor/SoOutput.h>
291 #include <Inventor/fields/SoSFTime.h>
292 #include <Inventor/nodes/SoSeparator.h>
293 #include <Inventor/nodes/SoText2.h>
294 #include <Inventor/C/tidbits.h>
295
BOOST_AUTO_TEST_CASE(GlobalField)296 BOOST_AUTO_TEST_CASE(GlobalField)
297 {
298 SoDB::init();
299
300 //Store away the state before we mess with the global realTime field
301 SoSFTime * realtime = static_cast<SoSFTime *>(SoDB::getGlobalField("realTime"));
302 assert(realtime);
303 SbTime realTimeStorage = realtime->getValue();
304
305 static const char inlinescenegraph[] =
306 "#Inventor V2.1 ascii\n"
307 "\n"
308 "\n"
309 "Separator {\n"
310 "\n"
311 " Text2 { \n"
312 " string \"\" = GlobalField { \n"
313 " type \"SFTime\" realTime 0 \n"
314 "\n"
315 " } . realTime \n"
316 " }\n"
317 "}\n";
318
319 // read scene
320 SoInput in;
321 in.setBuffer((void *) inlinescenegraph, strlen(inlinescenegraph));
322 SoSeparator * top = SoDB::readAll(&in);
323 BOOST_REQUIRE(top);
324 top->ref();
325
326 // write scene
327 SoOutput out;
328 const int buffer_size = 1024;
329 char * buffer = (char *)malloc(buffer_size);
330 out.setBuffer(buffer, buffer_size, NULL);
331
332 SoWriteAction wa(&out);
333 wa.apply((SoNode *)top);
334
335 top->unref();
336 top = NULL;
337
338 // read scene again to check if realTime field was written
339 in.setBuffer((void *)buffer, strlen(buffer));
340 top = SoDB::readAll(&in);
341 BOOST_REQUIRE(top);
342 top->ref();
343
344 SoText2 * text = (SoText2 *)top->getChild(0);
345 BOOST_REQUIRE(text);
346
347 SoField * string = text->getField("string");
348 BOOST_REQUIRE(string);
349 BOOST_CHECK_MESSAGE(string->isConnected(), "String field not connected to realTime field in written scene graph");
350
351 free(buffer);
352
353 top->unref();
354
355 //Restore state
356 realtime->setValue(realTimeStorage);
357
358 }
359
360 #endif // COIN_TEST_SUITE
361