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