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 #include <dom/domLibrary_cameras.h> 21 #include <dom/domLibrary_lights.h> 22 #include <dae/domAny.h> 23 //#include <dom/domVisual_scene.h> 24 //#include <dom/domLibrary_visual_scenes.h> 25 26 #include <osgSim/MultiSwitch> 27 #include <osgAnimation/AnimationManagerBase> 28 #include <osgAnimation/UpdateMatrixTransform> 29 #include <osg/Sequence> 30 #include <osg/Billboard> 31 #include <osg/CameraView> 32 #include <osgDB/ConvertUTF> 33 34 35 using namespace osgDAE; 36 37 38 void daeWriter::writeAnimations( osg::Node &node ) 39 { 40 const std::string nodeNameUTF( _pluginOptions.namesUseCodepage ? osgDB::convertStringFromCurrentCodePageToUTF8(node.getName()) : node.getName() ); 41 osg::Callback* ncb = node.getUpdateCallback(); 42 if (ncb) 43 { 44 osgAnimation::AnimationManagerBase* am = dynamic_cast<osgAnimation::AnimationManagerBase*>(ncb); 45 if (am) 46 { 47 // Create library of animations if not existing yet 48 if (!_domLibraryAnimations) 49 { 50 _domLibraryAnimations = daeSafeCast<domLibrary_animations>( dom->add( COLLADA_ELEMENT_LIBRARY_ANIMATIONS ) ); 51 } 52 53 osgAnimation::AnimationList animationList = am->getAnimationList(); 54 for (size_t i = 0; i < animationList.size(); i++) 55 { 56 domAnimation* pDomAnimation = daeSafeCast< domAnimation >( _domLibraryAnimations->add( COLLADA_ELEMENT_ANIMATION ) ); 57 domAnimation* pMainDomAnimation = pDomAnimation; 58 59 osg::ref_ptr<osgAnimation::Animation> animation = animationList[i]; 60 std::string animationName( animation->getName() ); 61 if (animationName.empty()) 62 animationName = "animation"; 63 animationName = uniquify( animationName ); 64 pDomAnimation->setId(animationName.c_str()); 65 66 // Handle multiple channels within an animation 67 osgAnimation::ChannelList animationChannels = animation->getChannels(); 68 for (size_t j=0; j < animationChannels.size(); j++) 69 { 70 osgAnimation::Channel* channel = animationChannels[j].get(); 71 std::string channelName( channel->getName() ); 72 std::string channelNameUTF( _pluginOptions.namesUseCodepage ? osgDB::convertStringFromCurrentCodePageToUTF8(channelName) : channelName ); 73 74 // Wrap each animation channel into it's own child <animation> when more than 1 channel 75 if (animationChannels.size() > 1) 76 { 77 pDomAnimation = daeSafeCast< domAnimation >( pMainDomAnimation->add( COLLADA_ELEMENT_ANIMATION ) ); 78 79 if (channelName.empty()) 80 { 81 channelName = "channel"; 82 channelNameUTF = channelName; 83 } 84 animationName = uniquify( channelName ); 85 pDomAnimation->setId(channelNameUTF.c_str()); 86 } 87 88 std::string sourceName( channelNameUTF + "_sampler" ); 89 std::string inputSourceName( channelNameUTF + "_input" ); 90 std::string outputSourceName( channelNameUTF + "_output" ); 91 std::string interpolationSourceName( channelNameUTF + "_interpolation" ); 92 93 // Fill dom sources based on sampler 94 osgAnimation::Sampler* animationSampler = channel->getSampler(); 95 osgAnimation::KeyframeContainer* kfc = animationSampler->getKeyframeContainer(); 96 int size = kfc->size(); 97 98 float valueStride = 1; 99 100 domListOfFloats timeValues; 101 domListOfFloats frameValues; 102 domListOfNames interpolationValues; 103 104 osgAnimation::Vec3KeyframeContainer* v3kc = dynamic_cast<osgAnimation::Vec3KeyframeContainer*>(kfc); 105 if (v3kc) 106 { 107 valueStride = 3; 108 for (size_t i=0; i < v3kc->size(); i++) 109 { 110 timeValues.append((*v3kc)[i].getTime()); 111 osg::Vec3 vec = (*v3kc)[i].getValue(); 112 113 // This needs some serious cleanup 114 if (channelNameUTF.find("euler") != std::string::npos) 115 { 116 frameValues.append(osg::RadiansToDegrees(vec.x())); 117 frameValues.append(osg::RadiansToDegrees(vec.y())); 118 frameValues.append(osg::RadiansToDegrees(vec.z())); 119 } 120 else 121 { 122 frameValues.append(vec.x()); 123 frameValues.append(vec.y()); 124 frameValues.append(vec.z()); 125 } 126 interpolationValues.append("LINEAR"); 127 } 128 } 129 osgAnimation::FloatKeyframeContainer* fkc = dynamic_cast<osgAnimation::FloatKeyframeContainer*>(kfc); 130 if (fkc) 131 { 132 valueStride = 1; 133 for (size_t i=0; i < fkc->size(); i++) 134 { 135 timeValues.append((*fkc)[i].getTime()); 136 frameValues.append((*fkc)[i].getValue()); 137 interpolationValues.append("LINEAR"); 138 } 139 } 140 141 // time values 142 domSource* pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE)); 143 pDomSource->setId(inputSourceName.c_str()); 144 145 domFloat_array* pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); 146 std::string inputArrayName = inputSourceName + "_array"; 147 pDomFloatArray->setId(inputArrayName.c_str()); 148 pDomFloatArray->setCount(size); 149 pDomFloatArray->setValue(timeValues); 150 151 domSource::domTechnique_common* pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); 152 153 domAccessor* pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); 154 std::string url = "#" + inputArrayName; 155 pDomAccessor->setSource(url.c_str()); 156 pDomAccessor->setCount(size); 157 pDomAccessor->setStride(1); 158 159 domParam* pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); 160 pDomParam->setName("TIME"); 161 pDomParam->setType(COLLADA_TYPE_FLOAT); 162 163 // <source> interpolator type 164 pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE)); 165 pDomSource->setId(interpolationSourceName.c_str()); 166 167 domName_array* pDomNameArray = daeSafeCast< domName_array >(pDomSource->add(COLLADA_ELEMENT_NAME_ARRAY)); 168 std::string interpolationArrayName = interpolationSourceName + "_array"; 169 pDomNameArray->setId(interpolationArrayName.c_str()); 170 pDomNameArray->setCount(size); 171 pDomNameArray->setValue(interpolationValues); 172 173 // <technique_common> 174 pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); 175 176 // <accessor count=size stride="1"> 177 pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); 178 url = "#" + interpolationArrayName; 179 pDomAccessor->setSource(url.c_str()); 180 pDomAccessor->setCount(size); 181 pDomAccessor->setStride(1); 182 183 // <param name="INTERPOLATION" type="name"/> 184 pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); 185 pDomParam->setName("INTERPOLATION"); 186 pDomParam->setType(COLLADA_TYPE_NAME); 187 188 // Split up access to the euler float array into three sources, because we need to target three separate transforms 189 if (channelNameUTF.find("euler") != std::string::npos) 190 { 191 pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE)); 192 std::string outputSourceNameX = outputSourceName + "_X"; 193 pDomSource->setId(outputSourceNameX.c_str()); 194 195 pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); 196 std::string outputArrayNameX = outputSourceNameX + "_array"; 197 pDomFloatArray->setId(outputArrayNameX.c_str()); 198 199 // TODO, flexible handling of different keyframe types, see osg exporter for inspiration 200 pDomFloatArray->setCount(size * valueStride); 201 pDomFloatArray->setValue(frameValues); 202 203 pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); 204 205 pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); 206 url = "#" + outputArrayNameX; 207 pDomAccessor->setSource(url.c_str()); 208 pDomAccessor->setCount(size); 209 pDomAccessor->setStride(valueStride); 210 pDomAccessor->setOffset(0); 211 // <param name="ANGLE" type="float"/> 212 pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); 213 pDomParam->setName("ANGLE"); 214 pDomParam->setType(COLLADA_TYPE_FLOAT); 215 216 pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE)); 217 std::string outputSourceNameY = outputSourceName + "_Y"; 218 pDomSource->setId(outputSourceNameY.c_str()); 219 220 pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); 221 std::string outputArrayNameY = outputSourceNameY + "_array"; 222 pDomFloatArray->setId(outputArrayNameY.c_str()); 223 224 pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); 225 226 pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); 227 url = "#" + outputArrayNameY; 228 pDomAccessor->setSource(url.c_str()); 229 pDomAccessor->setCount(size); 230 pDomAccessor->setStride(valueStride); 231 pDomAccessor->setOffset(1); 232 // <param name="ANGLE" type="float"/> 233 pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); 234 pDomParam->setName("ANGLE"); 235 pDomParam->setType(COLLADA_TYPE_FLOAT); 236 237 pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE)); 238 std::string outputSourceNameZ = outputSourceName + "_Z"; 239 pDomSource->setId(outputSourceNameZ.c_str()); 240 241 pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); 242 std::string outputArrayNameZ = outputSourceNameZ + "_array"; 243 pDomFloatArray->setId(outputArrayNameZ.c_str()); 244 245 pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); 246 247 pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); 248 url = "#" + outputArrayNameZ; 249 pDomAccessor->setSource(url.c_str()); 250 pDomAccessor->setCount(size); 251 pDomAccessor->setStride(valueStride); 252 pDomAccessor->setOffset(2); 253 // <param name="ANGLE" type="float"/> 254 pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); 255 pDomParam->setName("ANGLE"); 256 pDomParam->setType(COLLADA_TYPE_FLOAT); 257 258 { 259 domSampler* pDomSampler = daeSafeCast< domSampler >(pDomAnimation->add(COLLADA_ELEMENT_SAMPLER)); 260 std::string sourceNameX = sourceName + "_X"; 261 pDomSampler->setId(sourceNameX.c_str()); 262 263 // Fill dom sampler based on common semantics 264 { 265 domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); 266 pDomInput->setSemantic(COMMON_PROFILE_INPUT_INPUT); 267 std::string url = "#" + inputSourceName; 268 pDomInput->setSource(url.c_str()); 269 270 pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); 271 pDomInput->setSemantic(COMMON_PROFILE_INPUT_OUTPUT); 272 url = "#" + outputSourceNameX; 273 pDomInput->setSource(url.c_str()); 274 275 pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); 276 pDomInput->setSemantic(COMMON_PROFILE_INPUT_INTERPOLATION); 277 url = "#" + interpolationSourceName; 278 pDomInput->setSource(url.c_str()); 279 } 280 281 // Set sampler as source 282 domChannel* pDomChannel = daeSafeCast< domChannel >(pDomAnimation->add(COLLADA_ELEMENT_CHANNEL)); 283 std::string url = "#" + sourceNameX; 284 pDomChannel->setSource(url.c_str()); 285 286 // targetName contains the name of the updateCallback 287 std::string targetName = channel->getTargetName(); 288 289 // based on the type of updateCallback we can determine the transform element to target eg. "nodeName/translation" 290 osg::Node* node = _animatedNodeCollector.getTargetNode(targetName); 291 if (node) 292 { 293 std::string domChannelTargetName = nodeNameUTF + "/rotateX.ANGLE"; 294 pDomChannel->setTarget(domChannelTargetName.c_str()); 295 } 296 else 297 { 298 OSG_WARN << "Could not find animation target '" << targetName << "'" << std::endl; 299 } 300 } 301 302 { 303 domSampler* pDomSampler = daeSafeCast< domSampler >(pDomAnimation->add(COLLADA_ELEMENT_SAMPLER)); 304 std::string sourceNameY = sourceName + "_Y"; 305 pDomSampler->setId(sourceNameY.c_str()); 306 307 // Fill dom sampler based on common semantics 308 { 309 domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); 310 pDomInput->setSemantic(COMMON_PROFILE_INPUT_INPUT); 311 std::string url = "#" + inputSourceName; 312 pDomInput->setSource(url.c_str()); 313 314 pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); 315 pDomInput->setSemantic(COMMON_PROFILE_INPUT_OUTPUT); 316 url = "#" + outputSourceNameY; 317 pDomInput->setSource(url.c_str()); 318 319 pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); 320 pDomInput->setSemantic(COMMON_PROFILE_INPUT_INTERPOLATION); 321 url = "#" + interpolationSourceName; 322 pDomInput->setSource(url.c_str()); 323 } 324 325 // Set sampler as source 326 domChannel* pDomChannel = daeSafeCast< domChannel >(pDomAnimation->add(COLLADA_ELEMENT_CHANNEL)); 327 std::string url = "#" + sourceNameY; 328 pDomChannel->setSource(url.c_str()); 329 330 // targetName contains the name of the updateCallback 331 std::string targetName = channel->getTargetName(); 332 333 // based on the type of updateCallback we can determine the transform element to target eg. "nodeName/translation" 334 osg::Node* node = _animatedNodeCollector.getTargetNode(targetName); 335 if (node) 336 { 337 std::string domChannelTargetName = nodeNameUTF + "/rotateY.ANGLE"; 338 pDomChannel->setTarget(domChannelTargetName.c_str()); 339 } 340 else 341 { 342 OSG_WARN << "Could not find animation target '" << targetName << "'" << std::endl; 343 } 344 } 345 346 { 347 domSampler* pDomSampler = daeSafeCast< domSampler >(pDomAnimation->add(COLLADA_ELEMENT_SAMPLER)); 348 std::string sourceNameZ = sourceName + "_Z"; 349 pDomSampler->setId(sourceNameZ.c_str()); 350 351 // Fill dom sampler based on common semantics 352 { 353 domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); 354 pDomInput->setSemantic(COMMON_PROFILE_INPUT_INPUT); 355 std::string url = "#" + inputSourceName; 356 pDomInput->setSource(url.c_str()); 357 358 pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); 359 pDomInput->setSemantic(COMMON_PROFILE_INPUT_OUTPUT); 360 url = "#" + outputSourceNameZ; 361 pDomInput->setSource(url.c_str()); 362 363 pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); 364 pDomInput->setSemantic(COMMON_PROFILE_INPUT_INTERPOLATION); 365 url = "#" + interpolationSourceName; 366 pDomInput->setSource(url.c_str()); 367 } 368 369 // Set sampler as source 370 domChannel* pDomChannel = daeSafeCast< domChannel >(pDomAnimation->add(COLLADA_ELEMENT_CHANNEL)); 371 std::string url = "#" + sourceNameZ; 372 pDomChannel->setSource(url.c_str()); 373 374 // targetName contains the name of the updateCallback 375 std::string targetName = channel->getTargetName(); 376 377 // based on the type of updateCallback we can determine the transform element to target eg. "nodeName/translation" 378 osg::Node* node = _animatedNodeCollector.getTargetNode(targetName); 379 if (node) 380 { 381 std::string domChannelTargetName = nodeNameUTF + "/rotateZ.ANGLE"; 382 pDomChannel->setTarget(domChannelTargetName.c_str()); 383 } 384 else 385 { 386 OSG_WARN << "Could not find animation target '" << targetName << "'" << std::endl; 387 } 388 } 389 } 390 else 391 { 392 // values in keyframecontainer 393 pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE)); 394 pDomSource->setId(outputSourceName.c_str()); 395 396 pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); 397 std::string outputArrayName = outputSourceName + "_array"; 398 pDomFloatArray->setId(outputArrayName.c_str()); 399 400 // TODO, flexible handling of different keyframe types, see osg exporter for inspiration 401 pDomFloatArray->setCount(size * valueStride); 402 pDomFloatArray->setValue(frameValues); 403 404 pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); 405 406 pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); 407 url = "#" + outputArrayName; 408 pDomAccessor->setSource(url.c_str()); 409 pDomAccessor->setCount(size); 410 pDomAccessor->setStride(valueStride); 411 412 if (v3kc) 413 { 414 // <param name="X" type="float"/> 415 pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); 416 pDomParam->setName("X"); 417 pDomParam->setType(COLLADA_TYPE_FLOAT); 418 // <param name="Y" type="float"/> 419 pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); 420 pDomParam->setName("Y"); 421 pDomParam->setType(COLLADA_TYPE_FLOAT); 422 // <param name="Z" type="float"/> 423 pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); 424 pDomParam->setName("Z"); 425 pDomParam->setType(COLLADA_TYPE_FLOAT); 426 } 427 if (fkc) 428 { 429 // <param type="float"/> 430 pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); 431 pDomParam->setType(COLLADA_TYPE_FLOAT); 432 } 433 434 domSampler* pDomSampler = daeSafeCast< domSampler >(pDomAnimation->add(COLLADA_ELEMENT_SAMPLER)); 435 pDomSampler->setId(sourceName.c_str()); 436 437 // Fill dom sampler based on common semantics 438 { 439 domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); 440 pDomInput->setSemantic(COMMON_PROFILE_INPUT_INPUT); 441 std::string url = "#" + inputSourceName; 442 pDomInput->setSource(url.c_str()); 443 444 pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); 445 pDomInput->setSemantic(COMMON_PROFILE_INPUT_OUTPUT); 446 url = "#" + outputSourceName; 447 pDomInput->setSource(url.c_str()); 448 449 pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); 450 pDomInput->setSemantic(COMMON_PROFILE_INPUT_INTERPOLATION); 451 url = "#" + interpolationSourceName; 452 pDomInput->setSource(url.c_str()); 453 } 454 455 // Set sampler as source 456 domChannel* pDomChannel = daeSafeCast< domChannel >(pDomAnimation->add(COLLADA_ELEMENT_CHANNEL)); 457 std::string url = "#" + sourceName; 458 pDomChannel->setSource(url.c_str()); 459 460 // targetName contains the name of the updateCallback 461 std::string targetName = channel->getTargetName(); 462 463 // based on the type of updateCallback we can determine the transform element to target eg. "nodeName/translation" 464 osg::Node* node = _animatedNodeCollector.getTargetNode(targetName); 465 if (node) 466 { 467 std::string domChannelTargetName = nodeNameUTF; 468 469 if (channelNameUTF.find("position") != std::string::npos) 470 { 471 domChannelTargetName += "/translate"; 472 } 473 else if (channelNameUTF.find("scale") != std::string::npos) 474 { 475 domChannelTargetName += "/scale"; 476 } 477 478 pDomChannel->setTarget(domChannelTargetName.c_str()); 479 } 480 else 481 { 482 OSG_WARN << "Could not find animation target '" << targetName << "'" << std::endl; 483 } 484 } 485 } 486 } 487 } 488 } 489 } 490 491