// PROCESSBIPNODERECURSE // When we find a Biped-controlled node in our hierarchy, we need to find one non-biped // child and promote it to the place of the biped node in the hierarchy. The siblings // of the promoted node will become its children, as will the original children from the // biped node. void ProcessBipedNodeRecurse(INode *bipNode, INode *parent, Interface *theInterface) { int numChildren = bipNode->NumberOfChildren(); char *bipName = bipNode ? bipNode->GetName() : nil; INode *replacement = nil; for (int i = 0; i < numChildren; i++) { INode *child = bipNode->GetChildNode(i); char *childName = child ? child->GetName() : nil; if( ! HasBipController(child) ) { replacement = child; // this child is going to be our replacement for this bipnode // sample the animation (into global space) plSampleVec *samples = SampleNodeMotion(replacement, bipNode, 1, theInterface); // detach from the parent (this blows away the animation) replacement->Detach(0); // attach the node to the biped's parent. parent->AttachChild(replacement); ReapplyAnimation(child, samples); FreeMotionSamples(samples); // we only need one replacement for the bip node break; } } if(replacement) { // reparent the siblings to the newly promoted replacement node numChildren = bipNode->NumberOfChildren(); for (int i = 0; i < numChildren; i++) { INode *child = bipNode->GetChildNode(i); if( HasBipController(child) ) { ProcessBipedNodeRecurse(child, replacement, theInterface); } else { child->Detach(0); // remove the (non-bip) child from the bip node replacement->AttachChild(child); // attach it to the non-bip parent ProcessNonBipedNodeRecurse(child, replacement, theInterface); } } } else { // this is an error condition: we've got a bip node that has no non-bip child for us to promote char buf[256]; sprintf(buf, "Couldn't find non-bip node to transfer motion to for bip node %s\n", bipNode->GetName()); hsStatusMessage(buf); } }
void NifImporter::ImportBones(NiNodeRef node, bool recurse) { try { if (uncontrolledDummies) BuildControllerRefList(node, ctrlCount); string name = node->GetName(); vector<NiAVObjectRef> children = node->GetChildren(); vector<NiNodeRef> childNodes = DynamicCast<NiNode>(children); NiAVObject::CollisionType cType = node->GetCollisionMode(); if (children.empty() && name == "Bounding Box") return; // Do all node manipulations here NiNodeRef parent = node->GetParent(); string parentname = (parent ? parent->GetName() : ""); Matrix44 m4 = node->GetWorldTransform(); // Check for Prn strings and change parent if necessary if (supportPrnStrings) { list<NiStringExtraDataRef> strings = DynamicCast<NiStringExtraData>(node->GetExtraData()); for (list<NiStringExtraDataRef>::iterator itr = strings.begin(); itr != strings.end(); ++itr){ if (strmatch((*itr)->GetName(), "Prn")) { parentname = (*itr)->GetData(); if (INode *pn = gi->GetINodeByName(parentname.c_str())){ // Apparently Heads tend to need to be rotated 90 degrees on import for if (!rotate90Degrees.empty() && wildmatch(rotate90Degrees, parentname)) { m4 *= TOMATRIX4(RotateYMatrix(TORAD(90))); } m4 *= TOMATRIX4(pn->GetObjTMAfterWSM(0, NULL)); } } } } float len = node->GetLocalTranslation().Magnitude(); // Remove NonAccum nodes and merge into primary bone if (mergeNonAccum && wildmatch("* NonAccum", name) && parent) { string realname = name.substr(0, name.length() - 9); if (strmatch(realname, parent->GetName())) { Matrix44 tm = parent->GetLocalTransform() * node->GetLocalTransform(); name = realname; len += tm.GetTranslation().Magnitude(); parent = parent->GetParent(); } } PosRotScale prs = prsDefault; Vector3 pos; Matrix33 rot; float scale; m4.Decompose(pos, rot, scale); Matrix3 im = TOMATRIX3(m4); Point3 p = im.GetTrans(); Quat q(im); //q.Normalize(); Vector3 ppos; Point3 zAxis(0,0,0); bool hasChildren = !children.empty(); if (hasChildren) { float len = 0.0f; for (vector<NiAVObjectRef>::iterator itr=children.begin(), end = children.end(); itr != end; ++itr) { len += GetObjectLength(*itr); } len /= float(children.size()); ppos = pos + Vector3(len, 0.0f, 0.0f); // just really need magnitude as rotation will take care of positioning } else if (parent) { ppos = pos + Vector3(len/3.0f, 0.0f, 0.0f); } Point3 pp(ppos.x, ppos.y, ppos.z); Point3 qp = TORAD(TOEULER(im)); INode *bone = NULL; if (!doNotReuseExistingBones) // Games like BC3 reuse the same bone names { bone = FindNode(node); if (bone == NULL) bone = gi->GetINodeByName(name.c_str()); } if (bone) { // Is there a better way of "Affect Pivot Only" behaviors? INode *pinode = bone->GetParentNode(); if (pinode) bone->Detach(0,1); PosRotScaleNode(bone, p, q, scale, prs); if (pinode) pinode->AttachChild(bone, 1); } else { bool isDummy = ( (uncontrolledDummies && !HasControllerRef(ctrlCount, name)) || (!dummyNodeMatches.empty() && wildmatch(dummyNodeMatches, name)) || (convertBillboardsToDummyNodes && node->IsDerivedType(NiBillboardNode::TYPE)) ); if (wildmatch("Camera*", name)) { if (enableCameras) { if (bone = CreateCamera(name)) { PosRotScaleNode(bone, p, q, scale, prs); bone->Hide(node->GetVisibility() ? FALSE : TRUE); } } }else if (isDummy && createNubsForBones) bone = CreateHelper(name, p); else if (bone = CreateBone(name, p, pp, zAxis)) { PosRotScaleNode(bone, p, q, scale, prs); bone->Hide(node->GetVisibility() ? FALSE : TRUE); } if (bone) { if (!parentname.empty()) { if (mergeNonAccum && wildmatch("* NonAccum", parentname)) { parentname = parentname.substr(0, parentname.length() - 9); } if (INode *pn = gi->GetINodeByName(parentname.c_str())) pn->AttachChild(bone, 1); } RegisterNode(node, bone); } } // Import UPB if (bone) ImportUPB(bone, node); // Import Havok Collision Data surrounding node, // unfortunately this causes double import of collision so I'm disabling it for now. if (enableCollision && node->GetParent()) { ImportCollision(node); } if (bone && recurse) { ImportBones(childNodes); } } catch( exception & e ) { e=e; } catch( ... ) { } }
void NifImporter::AlignBiped(IBipMaster* master, NiNodeRef node) { #ifdef USE_BIPED NiNodeRef parent = node->GetParent(); string name = node->GetName(); vector<NiAVObjectRef> children = node->GetChildren(); vector<NiNodeRef> childNodes = DynamicCast<NiNode>(children); TSTR s1 = FormatText("Processing %s:", name.c_str()); TSTR s2 = FormatText("Processing %s:", name.c_str()); INode *bone = GetNode(node); if (bone != NULL) { if (uncontrolledDummies) BuildControllerRefList(node, ctrlCount); Matrix44 m4 = node->GetWorldTransform(); Vector3 pos; Matrix33 rot; float scale; m4.Decompose(pos, rot, scale); Matrix3 m = TOMATRIX3(m4); Point3 p = m.GetTrans(); Quat q(m); s1 += FormatText(" ( %s)", PrintMatrix3(m).data()); if (strmatch(name, master->GetRootName())) { // Align COM //PosRotScaleNode(bone, p, q, 1.0f, prsPos); PosRotScaleBiped(master, bone, p, q, 1.0f, prsPos); } else if (INode *pnode = bone->GetParentNode()) { // Reparent if necessary if (!strmatch(parent->GetName(), pnode->GetName())) { if (pnode = FindNode(parent)) { bone->Detach(0); pnode->AttachChild(bone); } } // Hack to scale the object until it fits for (int i=0; i<10; ++i) { float s = CalcScale(bone, node, childNodes); if (fabs(s-1.0f) < (FLT_EPSILON*100.0f)) break; s1 += FormatText(" (%g)", s); master->SetBipedScale(TRUE, ScaleValue(Point3(s,s,s)), 0, bone); } PosRotScale prs = prsDefault; PosRotScaleBiped(master, bone, p, q, scale, prs); // Rotation with Clavicle is useless in Figure Mode using the standard interface // I was tring unsuccessfully to correct for it //if (wildcmpi("Bip?? ? Clavicle", name.c_str())) { // AngAxis a1 = CalcTransform(bone, node, childNodes); // Matrix3 tm1 = GenerateRotMatrix(a1); // Quat nq = TransformQuat(tm1, q); // PosRotScaleNode(bone, p, nq, scale, prsRot); //} } s2 += FormatText(" ( %s)", PrintMatrix3(bone->GetNodeTM(0)).data()); } else { ImportBones(node, false); } for (char *p = s1; *p != 0; ++p) if (isspace(*p)) *p = ' '; for (char *p = s2; *p != 0; ++p) if (isspace(*p)) *p = ' '; OutputDebugString(s1 + "\n"); OutputDebugString(s2 + "\n"); for (vector<NiNodeRef>::iterator itr = childNodes.begin(), end = childNodes.end(); itr != end; ++itr){ AlignBiped(master, *itr); } #endif }