void ColladaShapeLoader::processAnimation(const domAnimation* anim, F32& maxEndTime, F32& minFrameTime) { const char* sRGBANames[] = { ".R", ".G", ".B", ".A", "" }; const char* sXYZNames[] = { ".X", ".Y", ".Z", "" }; const char* sXYZANames[] = { ".X", ".Y", ".Z", ".ANGLE" }; const char* sLOOKATNames[] = { ".POSITIONX", ".POSITIONY", ".POSITIONZ", ".TARGETX", ".TARGETY", ".TARGETZ", ".UPX", ".UPY", ".UPZ", "" }; const char* sSKEWNames[] = { ".ROTATEX", ".ROTATEY", ".ROTATEZ", ".AROUNDX", ".AROUNDY", ".AROUNDZ", ".ANGLE", "" }; const char* sNullNames[] = { "" }; for (S32 iChannel = 0; iChannel < anim->getChannel_array().getCount(); iChannel++) { // Get the animation elements: <channel>, <sampler> domChannel* channel = anim->getChannel_array()[iChannel]; domSampler* sampler = daeSafeCast<domSampler>(channel->getSource().getElement()); if (!sampler) continue; // Find the animation channel target daeSIDResolver resolver(channel, channel->getTarget()); daeElement* target = resolver.getElement(); if (!target) { daeErrorHandler::get()->handleWarning(avar("Failed to resolve animation " "target: %s", channel->getTarget())); continue; } /* // If the target is a <source>, point it at the array instead // @todo:Only support targeting float arrays for now... if (target->getElementType() == COLLADA_TYPE::SOURCE) { domSource* source = daeSafeCast<domSource>(target); if (source->getFloat_array()) target = source->getFloat_array(); } */ // Get the target's animation channels (create them if not already) if (!AnimData::getAnimChannels(target)) { animations.push_back(new AnimChannels(target)); } AnimChannels* targetChannels = AnimData::getAnimChannels(target); // Add a new animation channel to the target targetChannels->push_back(new AnimData()); channel->setUserData(targetChannels->last()); AnimData& data = *targetChannels->last(); for (S32 iInput = 0; iInput < sampler->getInput_array().getCount(); iInput++) { const domInputLocal* input = sampler->getInput_array()[iInput]; const domSource* source = daeSafeCast<domSource>(input->getSource().getElement()); if (!source) continue; // @todo:don't care about the input param names for now. Could // validate against the target type.... if (dStrEqual(input->getSemantic(), "INPUT")) { data.input.initFromSource(source); // Adjust the maximum sequence end time maxEndTime = getMax(maxEndTime, data.input.getFloatValue((S32)data.input.size()-1)); // Detect the frame rate (minimum time between keyframes) for (S32 iFrame = 1; iFrame < data.input.size(); iFrame++) { F32 delta = data.input.getFloatValue( iFrame ) - data.input.getFloatValue( iFrame-1 ); if ( delta < 0 ) { daeErrorHandler::get()->handleError(avar("<animation> INPUT '%s' " "has non-monotonic keys. Animation is unlikely to be imported correctly.", source->getID())); break; } minFrameTime = getMin( minFrameTime, delta ); } } else if (dStrEqual(input->getSemantic(), "OUTPUT")) data.output.initFromSource(source); else if (dStrEqual(input->getSemantic(), "IN_TANGENT")) data.inTangent.initFromSource(source); else if (dStrEqual(input->getSemantic(), "OUT_TANGENT")) data.outTangent.initFromSource(source); else if (dStrEqual(input->getSemantic(), "INTERPOLATION")) data.interpolation.initFromSource(source); } // Set initial value for visibility targets that were added automatically (in colladaUtils.cpp if (dStrEqual(target->getElementName(), "visibility")) { domAny* visTarget = daeSafeCast<domAny>(target); if (visTarget && dStrEqual(visTarget->getValue(), "")) visTarget->setValue(avar("%g", data.output.getFloatValue(0))); } // Ignore empty animations if (data.input.size() == 0) { channel->setUserData(0); delete targetChannels->last(); targetChannels->pop_back(); continue; } // Determine the number and offset the elements of the target value // targeted by this animation switch (target->getElementType()) { case COLLADA_TYPE::COLOR: data.parseTargetString(channel->getTarget(), 4, sRGBANames); break; case COLLADA_TYPE::TRANSLATE: data.parseTargetString(channel->getTarget(), 3, sXYZNames); break; case COLLADA_TYPE::ROTATE: data.parseTargetString(channel->getTarget(), 4, sXYZANames); break; case COLLADA_TYPE::SCALE: data.parseTargetString(channel->getTarget(), 3, sXYZNames); break; case COLLADA_TYPE::LOOKAT: data.parseTargetString(channel->getTarget(), 3, sLOOKATNames); break; case COLLADA_TYPE::SKEW: data.parseTargetString(channel->getTarget(), 3, sSKEWNames); break; case COLLADA_TYPE::MATRIX: data.parseTargetString(channel->getTarget(), 16, sNullNames); break; case COLLADA_TYPE::FLOAT_ARRAY: data.parseTargetString(channel->getTarget(), daeSafeCast<domFloat_array>(target)->getCount(), sNullNames); break; default: data.parseTargetString(channel->getTarget(), 1, sNullNames); break; } } // Process child animations for (S32 iAnim = 0; iAnim < anim->getAnimation_array().getCount(); iAnim++) processAnimation(anim->getAnimation_array()[iAnim], maxEndTime, minFrameTime); }
S32 TorqueMain( S32 argc, const char **argv ) { S32 failed = 0; // Initialize the subsystems. StandardMainLoop::init(); Con::setVariable( "Con::Prompt", "" ); WindowsConsole->enable( true ); // install all drives for now until we have everything using the volume stuff Platform::FS::InstallFileSystems(); Platform::FS::MountDefaults(); bool compatMode = false; bool diffuseNames = false; bool verbose = false; bool saveDTS = true; bool saveDSQ = false; bool genMaterials = false; Torque::Path cfgPath, srcPath, destPath; // Parse arguments S32 i; for ( i = 1; i < argc-1; i++ ) { if ( dStrEqual( argv[i], "--config" ) ) cfgPath = makeFullPath( argv[++i] ); else if ( dStrEqual( argv[i], "--output" ) ) destPath = makeFullPath( argv[++i] ); else if ( dStrEqual( argv[i], "--dsq" ) ) saveDSQ = true; else if ( dStrEqual( argv[i], "--dsq-only" ) ) { saveDTS = false; saveDSQ = true; } else if ( dStrEqual( argv[i], "--compat" ) ) compatMode = true; else if ( dStrEqual( argv[i], "--diffuse" ) ) diffuseNames = true; else if ( dStrEqual( argv[i], "--materials" ) ) genMaterials = true; else if ( dStrEqual( argv[i], "--verbose" ) ) verbose = true; } if ( ( i >= argc ) || ( !dStrEndsWith(argv[i], ".dae") && !dStrEndsWith(argv[i], ".kmz" ) ) ) { Con::errorf( "Error: no DAE file specified.\n" ); printUsage(); return -1; } srcPath = makeFullPath( argv[i] ); if ( destPath.isEmpty() ) { destPath = srcPath; destPath.setExtension( "dts" ); } if ( !cfgPath.isEmpty() ) Con::printf( "Configuration files not yet supported.\n" ); // Define script callbacks if ( verbose ) Con::evaluate( "function UpdateTSShapeLoadProgress(%progress, %msg) { echo(%msg); }" ); else Con::evaluate( "function UpdateTSShapeLoadProgress(%progress, %msg) { }" ); if ( verbose ) Con::printf( "Parsing configuration file...\n" ); // Set import options ColladaUtils::getOptions().reset(); ColladaUtils::getOptions().forceUpdateMaterials = genMaterials; ColladaUtils::getOptions().useDiffuseNames = diffuseNames; if ( verbose ) Con::printf( "Reading dae file...\n" ); // Attempt to load the DAE file Resource<TSShape> shape = ResourceManager::get().load( srcPath ); if ( !shape ) { Con::errorf( "Failed to convert DAE file: %s\n", srcPath.getFullPath() ); failed = 1; } else { if ( compatMode && !shape->canWriteOldFormat() ) { Con::errorf( "Warning: Attempting to save to DTS v24 but the shape " "contains v26 features. Resulting DTS file may not be valid." ); } FileStream outStream; if ( saveDSQ ) { Torque::Path dsqPath( destPath ); dsqPath.setExtension( "dsq" ); for ( S32 i = 0; i < shape->sequences.size(); i++ ) { const String& seqName = shape->getName( shape->sequences[i].nameIndex ); if ( verbose ) Con::printf( "Writing DSQ file for sequence '%s'...\n", seqName.c_str() ); dsqPath.setFileName( destPath.getFileName() + "_" + seqName ); if ( outStream.open( dsqPath, Torque::FS::File::Write ) ) { shape->exportSequence( &outStream, shape->sequences[i], compatMode ); outStream.close(); } else { Con::errorf( "Failed to save sequence to %s\n", dsqPath.getFullPath().c_str() ); failed = 1; } } } if ( saveDTS ) { if ( verbose ) Con::printf( "Writing DTS file...\n" ); if ( outStream.open( destPath, Torque::FS::File::Write ) ) { if ( saveDSQ ) shape->sequences.setSize(0); shape->write( &outStream, compatMode ); outStream.close(); } else { Con::errorf( "Failed to save shape to %s\n", destPath.getFullPath().c_str() ); failed = 1; } } } // Clean everything up. StandardMainLoop::shutdown(); // Do we need to restart? if( StandardMainLoop::requiresRestart() ) Platform::restartInstance(); return failed; }
void TSShapeLoader::recurseSubshape(AppNode* appNode, S32 parentIndex, bool recurseChildren) { // Ignore local bounds nodes if (appNode->isBounds()) return; S32 subShapeNum = shape->subShapeFirstNode.size()-1; Subshape* subshape = subshapes[subShapeNum]; // Check if we should collapse this node S32 myIndex; if (ignoreNode(appNode->getName())) { myIndex = parentIndex; } else { // Check that adding this node will not exceed the maximum node count if (shape->nodes.size() >= MAX_TS_SET_SIZE) return; myIndex = shape->nodes.size(); String nodeName = getUniqueName(appNode->getName(), cmpShapeName, shape->names); // Create the 3space node shape->nodes.increment(); shape->nodes.last().nameIndex = shape->addName(nodeName); shape->nodes.last().parentIndex = parentIndex; shape->nodes.last().firstObject = -1; shape->nodes.last().firstChild = -1; shape->nodes.last().nextSibling = -1; // Add the AppNode to a matching list (so AppNodes can be accessed using 3space // node indices) appNodes.push_back(appNode); appNodes.last()->mParentIndex = parentIndex; // Check for NULL detail or AutoBillboard nodes (no children or geometry) if ((appNode->getNumChildNodes() == 0) && (appNode->getNumMesh() == 0)) { S32 size = 0x7FFFFFFF; String dname(String::GetTrailingNumber(appNode->getName(), size)); if (dStrEqual(dname, "nulldetail") && (size != 0x7FFFFFFF)) { shape->addDetail("detail", size, subShapeNum); } else if (appNode->isBillboard() && (size != 0x7FFFFFFF)) { // AutoBillboard detail S32 numEquatorSteps = 4; S32 numPolarSteps = 0; F32 polarAngle = 0.0f; S32 dl = 0; S32 dim = 64; bool includePoles = true; appNode->getInt("BB::EQUATOR_STEPS", numEquatorSteps); appNode->getInt("BB::POLAR_STEPS", numPolarSteps); appNode->getFloat("BB::POLAR_ANGLE", polarAngle); appNode->getInt("BB::DL", dl); appNode->getInt("BB::DIM", dim); appNode->getBool("BB::INCLUDE_POLES", includePoles); S32 detIndex = shape->addDetail( "bbDetail", size, -1 ); shape->details[detIndex].bbEquatorSteps = numEquatorSteps; shape->details[detIndex].bbPolarSteps = numPolarSteps; shape->details[detIndex].bbDetailLevel = dl; shape->details[detIndex].bbDimension = dim; shape->details[detIndex].bbIncludePoles = includePoles; shape->details[detIndex].bbPolarAngle = polarAngle; } } } // Collect geometry for (U32 iMesh = 0; iMesh < appNode->getNumMesh(); iMesh++) { AppMesh* mesh = appNode->getMesh(iMesh); if (!ignoreMesh(mesh->getName())) { subshape->objMeshes.push_back(mesh); subshape->objNodes.push_back(mesh->isSkin() ? -1 : myIndex); } } // Create children if (recurseChildren) { for (int iChild = 0; iChild < appNode->getNumChildNodes(); iChild++) recurseSubshape(appNode->getChildNode(iChild), myIndex, true); } }
// Recurse through the <visual_scene> adding nodes and geometry to the GuiTreeView control static void processNode(GuiTreeViewCtrl* tree, domNode* node, S32 parentID, SceneStats& stats) { stats.numNodes++; S32 nodeID = tree->insertItem(parentID, _GetNameOrId(node), "node", "", 0, 0); // Update mesh and poly counts for (int i = 0; i < node->getContents().getCount(); i++) { domGeometry* geom = 0; const char* elemName = ""; daeElement* child = node->getContents()[i]; switch (child->getElementType()) { case COLLADA_TYPE::INSTANCE_GEOMETRY: { domInstance_geometry* instgeom = daeSafeCast<domInstance_geometry>(child); if (instgeom) { geom = daeSafeCast<domGeometry>(instgeom->getUrl().getElement()); elemName = _GetNameOrId(geom); } break; } case COLLADA_TYPE::INSTANCE_CONTROLLER: { domInstance_controller* instctrl = daeSafeCast<domInstance_controller>(child); if (instctrl) { domController* ctrl = daeSafeCast<domController>(instctrl->getUrl().getElement()); elemName = _GetNameOrId(ctrl); if (ctrl && ctrl->getSkin()) geom = daeSafeCast<domGeometry>(ctrl->getSkin()->getSource().getElement()); else if (ctrl && ctrl->getMorph()) geom = daeSafeCast<domGeometry>(ctrl->getMorph()->getSource().getElement()); } break; } case COLLADA_TYPE::INSTANCE_LIGHT: stats.numLights++; tree->insertItem(nodeID, _GetNameOrId(node), "light", "", 0, 0); break; } if (geom && geom->getMesh()) { const char* name = _GetNameOrId(node); if ( dStrEqual( name, "null" ) || dStrEndsWith( name, "PIVOT" ) ) name = _GetNameOrId( daeSafeCast<domNode>(node->getParent()) ); stats.numMeshes++; tree->insertItem(nodeID, name, "mesh", "", 0, 0); for (S32 j = 0; j < geom->getMesh()->getTriangles_array().getCount(); j++) stats.numPolygons += geom->getMesh()->getTriangles_array()[j]->getCount(); for (S32 j = 0; j < geom->getMesh()->getTristrips_array().getCount(); j++) stats.numPolygons += geom->getMesh()->getTristrips_array()[j]->getCount(); for (S32 j = 0; j < geom->getMesh()->getTrifans_array().getCount(); j++) stats.numPolygons += geom->getMesh()->getTrifans_array()[j]->getCount(); for (S32 j = 0; j < geom->getMesh()->getPolygons_array().getCount(); j++) stats.numPolygons += geom->getMesh()->getPolygons_array()[j]->getCount(); for (S32 j = 0; j < geom->getMesh()->getPolylist_array().getCount(); j++) stats.numPolygons += geom->getMesh()->getPolylist_array()[j]->getCount(); } } // Recurse into child nodes for (S32 i = 0; i < node->getNode_array().getCount(); i++) processNode(tree, node->getNode_array()[i], nodeID, stats); for (S32 i = 0; i < node->getInstance_node_array().getCount(); i++) { domInstance_node* instnode = node->getInstance_node_array()[i]; domNode* node = daeSafeCast<domNode>(instnode->getUrl().getElement()); if (node) processNode(tree, node, nodeID, stats); } }