1 /*
2 * Copyright 2006 Sony Computer Entertainment Inc.
3 *
4 * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this
5 * file except in compliance with the License. You may obtain a copy of the License at:
6 * http://research.scea.com/scea_shared_source_license.html
7 *
8 * Unless required by applicable law or agreed to in writing, software distributed under the License
9 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
10 * implied. See the License for the specific language governing permissions and limitations under the
11 * License.
12 */
13
14 #include "daeWriter.h"
15
16 #include <dom/domCOLLADA.h>
17
18 #include <dom/domNode.h>
19 #include <dom/domConstants.h>
20
21 #include <sstream>
22 #include <osgDB/ConvertUTF>
23
24
25 namespace osgDAE {
26
ArrayNIndices(osg::Array * valArray,osg::IndexArray * ind)27 daeWriter::ArrayNIndices::ArrayNIndices( osg::Array* valArray, osg::IndexArray* ind ) :
28 vec2(0), vec3(0), vec4(0),
29 vec2d(0), vec3d(0), vec4d(0),
30 vec4ub(0),
31 valArray(valArray),
32 inds( ind ), mode(NONE)
33 {
34 if ( valArray != NULL )
35 {
36 switch( valArray->getType() )
37 {
38 case osg::Array::Vec2ArrayType:
39 mode = VEC2F;
40 vec2 = (osg::Vec2Array*)valArray;
41 break;
42 case osg::Array::Vec3ArrayType:
43 mode = VEC3F;
44 vec3 = (osg::Vec3Array*)valArray;
45 break;
46 case osg::Array::Vec4ArrayType:
47 mode = VEC4F;
48 vec4 = (osg::Vec4Array*)valArray;
49 break;
50 case osg::Array::Vec2dArrayType:
51 mode = VEC2D;
52 vec2d = (osg::Vec2dArray*)valArray;
53 break;
54 case osg::Array::Vec3dArrayType:
55 mode = VEC3D;
56 vec3d = (osg::Vec3dArray*)valArray;
57 break;
58 case osg::Array::Vec4dArrayType:
59 mode = VEC4D;
60 vec4d = (osg::Vec4dArray*)valArray;
61 break;
62 case osg::Array::Vec4ubArrayType:
63 mode = VEC4_UB;
64 vec4ub = (osg::Vec4ubArray*)valArray;
65 break;
66 default:
67 OSG_WARN << "Array is unsupported vector type" << std::endl;
68 break;
69 }
70 }
71 }
72
73
toString(const osg::Vec3f & value)74 std::string toString(const osg::Vec3f& value)
75 {
76 std::stringstream str;
77 str << value.x() << " " << value.y() << " " << value.z();
78 return str.str();
79 }
80
toString(const osg::Vec3d & value)81 std::string toString(const osg::Vec3d& value)
82 {
83 std::stringstream str;
84 str << value.x() << " " << value.y() << " " << value.z();
85 return str.str();
86 }
87
toString(const osg::Matrix & value)88 std::string toString(const osg::Matrix& value)
89 {
90 std::stringstream str;
91 str << value(0,0) << " " << value(1,0) << " " << value(2,0) << " " << value(3,0) << " "
92 << value(0,1) << " " << value(1,1) << " " << value(2,1) << " " << value(3,1) << " "
93 << value(0,2) << " " << value(1,2) << " " << value(2,2) << " " << value(3,2) << " "
94 << value(0,3) << " " << value(1,3) << " " << value(2,3) << " " << value(3,3);
95 return str.str();
96 }
97
98
Options()99 daeWriter::Options::Options()
100 : usePolygons(false),
101 googleMode(false),
102 writeExtras(true),
103 earthTex(false),
104 linkOrignialTextures(false),
105 forceTexture(false),
106 namesUseCodepage(false),
107 relativiseImagesPathNbUpDirs(0)
108 {}
109
daeWriter(DAE * dae_,const std::string & fileURI,const std::string & directory,const std::string & srcDirectory,const osgDB::ReaderWriter::Options * options,TraversalMode tm,const Options * pluginOptions)110 daeWriter::daeWriter( DAE *dae_, const std::string & fileURI, const std::string & directory, const std::string & srcDirectory, const osgDB::ReaderWriter::Options * options, TraversalMode tm, const Options * pluginOptions) : osg::NodeVisitor( tm ),
111 dae(dae_),
112 _domLibraryAnimations(NULL),
113 rootName(*dae_),
114 m_CurrentRenderingHint(osg::StateSet::DEFAULT_BIN),
115 _options(options),
116 _pluginOptions(pluginOptions ? *pluginOptions : Options()),
117 _externalWriter(srcDirectory, directory, true, pluginOptions ? pluginOptions->relativiseImagesPathNbUpDirs : 0)
118 {
119 success = true;
120
121 dae->setDatabase( NULL );
122 dae->setIOPlugin( NULL );
123 //create document
124 dae->getDatabase()->createDocument( fileURI.c_str(), &doc );
125 dom = (domCOLLADA*)doc->getDomRoot();
126 //create scene and instance visual scene
127 domCOLLADA::domScene *scene = daeSafeCast< domCOLLADA::domScene >( dom->add( COLLADA_ELEMENT_SCENE ) );
128 domInstanceWithExtra *ivs = daeSafeCast< domInstanceWithExtra >( scene->add( COLLADA_ELEMENT_INSTANCE_VISUAL_SCENE ) );
129 ivs->setUrl( "#defaultScene" );
130 //create library visual scenes and a visual scene and the root node
131 lib_vis_scenes = daeSafeCast<domLibrary_visual_scenes>( dom->add( COLLADA_ELEMENT_LIBRARY_VISUAL_SCENES ) );
132 vs = daeSafeCast< domVisual_scene >( lib_vis_scenes->add( COLLADA_ELEMENT_VISUAL_SCENE ) );
133 vs->setId( "defaultScene" );
134 currentNode = daeSafeCast< domNode >( vs->add( COLLADA_ELEMENT_NODE ) );
135 currentNode->setId( "sceneRoot" );
136
137 //create Asset
138 //createAssetTag(m_ZUpAxis); // we now call this in the set root node
139
140 lib_cameras = NULL;
141 lib_effects = NULL;
142 lib_controllers = NULL;
143 lib_geoms = NULL;
144 lib_lights = NULL;
145 lib_mats = NULL;
146
147 lastDepth = 0;
148
149 // Clean up caches
150 uniqueNames.clear();
151
152 currentStateSet = new osg::StateSet();
153 }
154
~daeWriter()155 daeWriter::~daeWriter()
156 {
157 }
158
debugPrint(osg::Node & node)159 void daeWriter::debugPrint( osg::Node &node )
160 {
161 #ifdef _DEBUG
162 std::string indent = "";
163 for ( unsigned int i = 0; i < _nodePath.size(); i++ )
164 {
165 indent += " ";
166 }
167 OSG_INFO << indent << node.className() << std::endl;
168 #endif
169 }
170
171
setRootNode(const osg::Node & node)172 void daeWriter::setRootNode( const osg::Node &node )
173 {
174 std::string fname = osgDB::findDataFile( node.getName() );
175 //create Asset with root node providing meta data
176 createAssetTag( node );
177
178 const_cast<osg::Node*>(&node)->accept( _animatedNodeCollector );
179 }
180
181 //### provide a name to node
getNodeName(const osg::Node & node,const std::string & defaultName)182 std::string daeWriter::getNodeName(const osg::Node & node,const std::string & defaultName)
183 {
184 std::string nodeName;
185 if (node.getName().empty())
186 nodeName=uniquify(defaultName);
187 else
188 nodeName=uniquify(node.getName());
189 return nodeName;
190 }
191
192 //NODE
apply(osg::Node & node)193 void daeWriter::apply( osg::Node &node )
194 {
195 debugPrint( node );
196
197 writeNodeExtra(node);
198
199 traverse( node );
200 }
201
updateCurrentDaeNode()202 void daeWriter::updateCurrentDaeNode()
203 {
204 while ( lastDepth >= _nodePath.size() )
205 {
206 //We are not a child of previous node
207 currentNode = daeSafeCast< domNode >( currentNode->getParentElement() );
208 --lastDepth;
209 }
210 }
211
uniquify(const std::string & _name)212 std::string daeWriter::uniquify( const std::string &_name )
213 {
214 const std::string name( _pluginOptions.namesUseCodepage ? osgDB::convertStringFromCurrentCodePageToUTF8(_name) : _name );
215 std::map< std::string, int >::iterator iter = uniqueNames.find( name );
216 if ( iter != uniqueNames.end() )
217 {
218 iter->second++;
219 std::ostringstream num;
220 num << std::dec << iter->second;
221 return name + "_" + num.str();
222 }
223 else
224 {
225 uniqueNames.insert( std::make_pair( name, 0 ) );
226 return name;
227 }
228 }
229
createAssetTag(bool isZUpAxis)230 void daeWriter::createAssetTag( bool isZUpAxis )
231 {
232 domAsset *asset = daeSafeCast< domAsset >(dom->add( COLLADA_ELEMENT_ASSET ) );
233 domAsset::domCreated *c = daeSafeCast< domAsset::domCreated >(asset->add(COLLADA_ELEMENT_CREATED));
234 domAsset::domModified *m = daeSafeCast< domAsset::domModified >(asset->add(COLLADA_ELEMENT_MODIFIED));
235 domAsset::domUnit *u = daeSafeCast< domAsset::domUnit >(asset->add(COLLADA_ELEMENT_UNIT));
236 domAsset::domUp_axis *up = daeSafeCast< domAsset::domUp_axis >(asset->add(COLLADA_ELEMENT_UP_AXIS));
237 up->setValue(UPAXISTYPE_Z_UP);
238
239 //TODO : set date and time
240 c->setValue( "2006-07-25T00:00:00Z" );
241 m->setValue( "2006-07-25T00:00:00Z" );
242
243 u->setName( "meter" );
244 u->setMeter( 1 );
245 }
246
247
248 // Overloaded version of createAssetTag that provides the ability to specify
249 // user defined values for child elements within asset tags
createAssetTag(const osg::Node & node)250 void daeWriter::createAssetTag(const osg::Node &node)
251 {
252 domAsset *asset = daeSafeCast< domAsset >(dom->add( COLLADA_ELEMENT_ASSET ) );
253 domAsset::domCreated *c = daeSafeCast< domAsset::domCreated >(asset->add("created" ));
254 domAsset::domModified *m = daeSafeCast< domAsset::domModified >(asset->add("modified" ));
255 domAsset::domUnit *u = daeSafeCast< domAsset::domUnit >(asset->add("unit"));
256 domAsset::domUp_axis *up_axis = daeSafeCast< domAsset::domUp_axis >(asset->add("up_axis"));
257
258 domAsset::domContributor *contributor = daeSafeCast< domAsset::domContributor >(asset->add("contributor" ));
259
260 // set date and time
261 // Generate the date like this
262 // "YYYY-mm-ddTHH:MM:SSZ" ISO 8601 Date Time format
263 const size_t bufSize = 1024;
264 static char dateStamp[bufSize];
265 time_t rawTime = time(NULL);
266 struct tm* timeInfo = localtime(&rawTime);
267 strftime(dateStamp, sizeof(dateStamp), "%Y-%m-%dT%H:%M:%SZ", timeInfo);
268
269
270 // set up fallback defaults
271 c->setValue( dateStamp );
272 m->setValue( dateStamp );
273 u->setName( "meter" ); // NOTE: SketchUp incorrectly sets this to "meters" but it does not really matter.
274 // Also note that since the default is: <unit meter="1.0" name="meter"/> this is equivalent to <unit/>
275 u->setMeter( 1.0 ); // this is the important units setting as it tells consuming apps how to convert to meters.
276 up_axis->setValue(UPAXISTYPE_Z_UP);
277
278
279
280 // get description info as name value pairs
281 if (node.getDescriptions().size()%2 == 0)
282 {
283 for(osg::Node::DescriptionList::const_iterator ditr=node.getDescriptions().begin();
284 ditr!=node.getDescriptions().end();
285 ++ditr)
286 {
287 std::string attrName( *ditr ); ++ditr;
288 std::string attrValue( *ditr );
289
290 if (attrName=="collada_created" && !attrValue.empty())
291 {
292 c->setValue( attrValue.c_str() );
293 }
294 else if (attrName=="collada_modified" && !attrValue.empty())
295 {
296 m->setValue( attrValue.c_str() );
297 }
298 else if (attrName=="collada_keywords" && !attrValue.empty())
299 {
300 domAsset::domKeywords *keywords = daeSafeCast< domAsset::domKeywords >(asset->add("keywords" ));
301 keywords->setValue( attrValue.c_str() );
302 }
303 else if (attrName=="collada_revision" && !attrValue.empty())
304 {
305 domAsset::domRevision *revision = daeSafeCast< domAsset::domRevision >(asset->add("revision" ));
306 revision->setValue( attrValue.c_str() );
307 }
308 else if (attrName=="collada_subject" && !attrValue.empty())
309 {
310 domAsset::domSubject *subject = daeSafeCast< domAsset::domSubject >(asset->add("subject" ));
311 subject->setValue( attrValue.c_str() );
312 }
313 else if (attrName=="collada_title" && !attrValue.empty())
314 {
315 domAsset::domTitle *title = daeSafeCast< domAsset::domTitle >(asset->add("title" ));
316 title->setValue( attrValue.c_str() );
317 }
318 else if (attrName=="collada_up_axis" && !attrValue.empty())
319 {
320 if (attrValue=="X_UP")
321 {
322 up_axis->setValue( UPAXISTYPE_X_UP );
323 }
324 else if (attrValue=="Y_UP")
325 {
326 up_axis->setValue( UPAXISTYPE_Y_UP );
327 }
328 else
329 {
330 up_axis->setValue( UPAXISTYPE_Z_UP ); // default
331 }
332 }
333 else if (attrName=="collada_unit_name" && !attrValue.empty())
334 {
335 u->setName( attrValue.c_str() );
336 }
337 else if (attrName=="collada_unit_meter_length" && !attrValue.empty())
338 {
339 double fValFromStr(1.0);
340 try {
341 std::istringstream sConversion(attrValue);
342 sConversion >> fValFromStr;
343 u->setMeter((domFloat)fValFromStr);
344 }
345 catch (...)
346 {
347 // TODO: handle error
348 u->setMeter((domFloat)fValFromStr);
349 continue;
350 }
351 }
352 else if (attrName=="collada_contributor{0}.author" && !attrValue.empty())
353 {
354 domAsset::domContributor::domAuthor *author =
355 daeSafeCast< domAsset::domContributor::domAuthor >(contributor->add("author" ));
356 author->setValue( attrValue.c_str() );
357 }
358 else if (attrName=="collada_contributor{0}.authoring_tool" && !attrValue.empty())
359 {
360 domAsset::domContributor::domAuthoring_tool *authoring_tool =
361 daeSafeCast< domAsset::domContributor::domAuthoring_tool >(contributor->add("authoring_tool" ));
362 authoring_tool->setValue( attrValue.c_str() );
363 }
364 else if (attrName=="collada_contributor{0}.comments" && !attrValue.empty())
365 {
366 domAsset::domContributor::domComments *comments =
367 daeSafeCast< domAsset::domContributor::domComments >(contributor->add("comments" ));
368 comments->setValue( attrValue.c_str() );
369 }
370 else if (attrName=="collada_contributor{0}.source_data" && !attrValue.empty())
371 {
372 domAsset::domContributor::domSource_data *source_data =
373 daeSafeCast< domAsset::domContributor::domSource_data >(contributor->add("source_data" ));
374 source_data->setValue( attrValue.c_str() );
375 }
376 else if (attrName=="collada_contributor{0}.copyright" && !attrValue.empty())
377 {
378 domAsset::domContributor::domCopyright *copyright =
379 daeSafeCast< domAsset::domContributor::domCopyright >(contributor->add("copyright" ));
380 copyright->setValue( attrValue.c_str() );
381 }
382
383 // TODO: handle array of contributor data rather that just the first.
384 // also there is probably a better way to pass attribute data as DescriptionList is a bit fragile
385
386 }
387 }
388 }
389
traverse(osg::Node & node)390 void daeWriter::traverse (osg::Node &node)
391 {
392 pushStateSet(node.getStateSet());
393
394 osg::NodeVisitor::traverse( node );
395
396 popStateSet(node.getStateSet());
397 }
398
pushStateSet(osg::StateSet * ss)399 void daeWriter::pushStateSet(osg::StateSet* ss)
400 {
401 if (NULL!=ss) {
402 // Save our current stateset
403 stateSetStack.push(currentStateSet.get());
404
405 // merge with node stateset
406 currentStateSet = static_cast<osg::StateSet*>(currentStateSet->clone(osg::CopyOp::SHALLOW_COPY));
407 currentStateSet->merge(*ss);
408 }
409 }
410
411
popStateSet(osg::StateSet * ss)412 void daeWriter::popStateSet(osg::StateSet* ss)
413 {
414 if (NULL!=ss) {
415 // restore the previous stateset
416 currentStateSet = stateSetStack.top();
417 stateSetStack.pop();
418 }
419 }
420
421 } // namespace osgdae
422