void hkDemoDatabase::rebuildDatabase() { m_demos.clear(); // // Normalize and collect all demo register entries // into a temporary array // hkArray<hkDemoEntry*> demos; hkDemoEntryRegister* e = s_demoList; while(e) { // Convert the file path to Havok path format hkArray<char> buffer; const char* tmp = hkNativeFileSystem::platformToHavokConvertPath( e->m_demoPath, buffer ); hkString resourcePath( hkDemoFileSystem::removeDemoBasePath( tmp, buffer ) ); int lastSlash = resourcePath.lastIndexOf('/'); HK_ASSERT2(0x6e7879d4, lastSlash >= 0, "probably a metrowerks build with no 'pragma fullpath_file on': " << resourcePath ); // Copy as the menu path hkString menuPath( resourcePath.substr(0, lastSlash) ); // remove the demo filename for the menu // If we have a variant, append the variant name if( e->m_variantName ) { menuPath += "/"; menuPath += e->m_variantName; } #ifdef USING_DEMO_FILTERS { hkString name(menuPath); bool valid = false; for (int f=0; f < HK_COUNT_OF(demoFilters); f++) { valid |= name.beginsWith(demoFilters[f]); } if (!valid) { e = e->m_next; continue; } } #endif // Store the entry hkDemoEntry* entry = new hkDemoEntry(); entry->m_func = e->m_func; entry->m_demoTypeFlags = e->m_demoTypeFlags; entry->m_menuPath = menuPath; entry->m_demoPath = resourcePath; entry->m_variantId = e->m_variantId; entry->m_help = e->m_help; entry->m_details = e->m_details; if ( e->m_createEntryFunc ) // now we have a create other demos function { e->m_createEntryFunc(*entry, demos); delete entry; } else { demos.pushBack(entry); } e = e->m_next; } HK_ASSERT2(0x4467bbc3, demos.getSize() != 0, "No demos registered!"); // find a common root for the demos, if any m_prefix = findCommonRootPath( demos ); // strip the common root plus from the names int prefixLen = m_prefix.getLength(); for(int i = 0; i < demos.getSize(); ++i) { // prune the prefix from each name hkString& n = demos[i]->m_menuPath; if( n[0] == '*' ) // this comes from unit tests, */means "put me at the demos level"??? { n.setAsSubstr(2); } else if( m_prefix.getLength() > 0 ) { n.setAsSubstr( prefixLen, n.getLength() - prefixLen ); } } // // Sort the array alphabetically and by ID in the same variant group // hkSort( demos.begin(), demos.getSize(), demoEntryPathAndVariantIdLess() ); // Collect the menu order files into an array extern const char* DemoOrder[]; // Build the member demos, sorting according to the menu order for (int i = 0; DemoOrder[i] != 0; i++) { // the demos are already sorted alphabetically int j=0; // look for the first matching path.. while( j < demos.getSize() && !( demos[j]!=HK_NULL && demos[j]->m_menuPath.beginsWith(DemoOrder[i]) ) ) ++j; // ..and add it together with all the following matching ones while( j < demos.getSize() && demos[j]!=HK_NULL && (demos[j]->m_menuPath.beginsWith(DemoOrder[i])) ) { m_demos.pushBack( *(demos[j]) ); delete demos[j]; demos[j] = HK_NULL; ++j; } } // Store any leftover demos for (int j = 0; j < demos.getSize(); j++) { if(demos[j] != HK_NULL) { m_demos.pushBack( *(demos[j]) ); } delete demos[j]; } // cleanup demos.clear(); /* // // Write the demo list to a file // hkOstream stream(HK_DEMO_LIST_FILENAME); if ( stream.isOk() ) { stream << ";Havok '" << productCode << "' demo list:" << hkendl << hkendl; for (int j = 0; j < m_demos.getSize(); j++) { stream << m_demos[j].m_menuPath << hkendl; } } */ }
void FbxToHkxConverter::extractKeyFramesAndAnnotations(hkxScene *scene, FbxNode* fbxChildNode, hkxNode* newChildNode, int animStackIndex) { FbxAMatrix bindPoseMatrix; FbxAnimStack* lAnimStack = NULL; int numAnimLayers = 0; FbxTimeSpan animTimeSpan; if (animStackIndex >= 0) { lAnimStack = m_curFbxScene->GetSrcObject<FbxAnimStack>(animStackIndex); numAnimLayers = lAnimStack->GetMemberCount<FbxAnimLayer>(); animTimeSpan = lAnimStack->GetLocalTimeSpan(); } // Find the time offset (in the "time space" of the FBX file) of the first animation frame FbxTime timePerFrame; timePerFrame.SetTime(0, 0, 0, 1, 0, m_curFbxScene->GetGlobalSettings().GetTimeMode()); const FbxTime startTime = animTimeSpan.GetStart(); const FbxTime endTime = animTimeSpan.GetStop(); const hkReal startTimeSeconds = static_cast<hkReal>(startTime.GetSecondDouble()); const hkReal endTimeSeconds = static_cast<hkReal>(endTime.GetSecondDouble()); int numFrames = 0; bool staticNode = true; if (scene->m_sceneLength == 0) { bindPoseMatrix = fbxChildNode->EvaluateLocalTransform(startTime); } else { hkArray<hkStringOld> annotationStrings; hkArray<hkReal> annotationTimes; HK_ASSERT(0x0, newChildNode->m_keyFrames.getSize() == 0); // Sample each animation frame for (FbxTime time = startTime, priorSampleTime = endTime; time < endTime; priorSampleTime = time, time += timePerFrame, ++numFrames) { FbxAMatrix frameMatrix = fbxChildNode->EvaluateLocalTransform(time); staticNode = staticNode && (frameMatrix == bindPoseMatrix); hkMatrix4 mat; // Extract this frame's transform convertFbxXMatrixToMatrix4(frameMatrix, mat); newChildNode->m_keyFrames.pushBack(mat); // Extract all annotation strings for this frame using the deprecated // pipeline (new annotations are extracted when sampling attributes) if (m_options.m_exportAnnotations && numAnimLayers > 0) { FbxProperty prop = fbxChildNode->GetFirstProperty(); while(prop.IsValid()) { FbxString propName = prop.GetName(); FbxDataType lDataType = prop.GetPropertyDataType(); hkStringOld name(propName.Buffer(), (int) propName.GetLen()); if (name.asUpperCase().beginsWith("HK") && lDataType.GetType() == eFbxEnum) { FbxAnimLayer* lAnimLayer = lAnimStack->GetMember<FbxAnimLayer>(0); FbxAnimCurve* lAnimCurve = prop.GetCurve(lAnimLayer); int currentKeyIndex; const int keyIndex = (int) lAnimCurve->KeyFind(time, ¤tKeyIndex); const int priorKeyIndex = (int) lAnimCurve->KeyFind(priorSampleTime); // Only store annotations on frames where they're explicitly keyframed, or if this is the first keyframe if (priorKeyIndex != keyIndex) { const int currentEnumValueIndex = keyIndex < 0 ? (int) lAnimCurve->Evaluate(priorSampleTime) : (int) lAnimCurve->Evaluate(time); HK_ASSERT(0x0, currentEnumValueIndex < prop.GetEnumCount()); const char* enumValue = prop.GetEnumValue(currentEnumValueIndex); hkxNode::AnnotationData& annotation = newChildNode->m_annotations.expandOne(); annotation.m_time = (hkReal) (time - startTime).GetSecondDouble(); annotation.m_description = (name + hkStringOld(enumValue, hkString::strLen(enumValue))).cString(); } } prop = fbxChildNode->GetNextProperty(prop); } } } } // Replace animation key data for static nodes with just 1 or 2 frames of bind pose data if (staticNode) { // Static nodes in animated scene data are exported with two keys const bool exportTwoFramesForStaticNodes = (numFrames > 1); // replace transform newChildNode->m_keyFrames.setSize(exportTwoFramesForStaticNodes ? 2: 1); newChildNode->m_keyFrames.optimizeCapacity(0, true); // convert the bind pose transform to Havok format convertFbxXMatrixToMatrix4(bindPoseMatrix, newChildNode->m_keyFrames[0]); if (exportTwoFramesForStaticNodes) { newChildNode->m_keyFrames[1] = newChildNode->m_keyFrames[0]; } } // Extract all times of actual keyframes for the current node... this can be used by Vision if ( m_options.m_storeKeyframeSamplePoints && newChildNode->m_keyFrames.getSize() > 2 && numAnimLayers > 0 ) { FbxAnimLayer* lAnimLayer = lAnimStack->GetMember<FbxAnimLayer>(0); extractKeyTimes(fbxChildNode, lAnimLayer, FBXSDK_CURVENODE_TRANSLATION, newChildNode, startTimeSeconds, endTimeSeconds); extractKeyTimes(fbxChildNode, lAnimLayer, FBXSDK_CURVENODE_ROTATION, newChildNode, startTimeSeconds, endTimeSeconds); extractKeyTimes(fbxChildNode, lAnimLayer, FBXSDK_CURVENODE_SCALING, newChildNode, startTimeSeconds, endTimeSeconds); extractKeyTimes(fbxChildNode, lAnimLayer, FBXSDK_CURVENODE_COMPONENT_X, newChildNode, startTimeSeconds, endTimeSeconds); extractKeyTimes(fbxChildNode, lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, newChildNode, startTimeSeconds, endTimeSeconds); extractKeyTimes(fbxChildNode, lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, newChildNode, startTimeSeconds, endTimeSeconds); if (newChildNode->m_linearKeyFrameHints.getSize() > 1) { hkSort(newChildNode->m_linearKeyFrameHints.begin(), newChildNode->m_linearKeyFrameHints.getSize()); } } }