// function is similar to part of CSkelMeshInstance::SetMesh() static void BuildSkeleton(TArray<CCoords> &Coords, const TArray<RJoint> &Bones, const TArray<AnalogTrack> &Anim) { guard(BuildSkeleton); int numBones = Anim.Num(); Coords.Empty(numBones); Coords.AddZeroed(numBones); for (int i = 0; i < numBones; i++) { const AnalogTrack &A = Anim[i]; const RJoint &B = Bones[i]; CCoords &BC = Coords[i]; // compute reference bone coords CVec3 BP; CQuat BO; // get default pose BP = CVT(A.KeyPos[0]); BO = CVT(A.KeyQuat[0]); if (!i) BO.Conjugate(); BC.origin = BP; BO.ToAxis(BC.axis); // move bone position to global coordinate space if (i) // do not rotate root bone { assert(B.parent >= 0); Coords[B.parent].UnTransformCoords(BC, BC); } } unguard; }
// function is similar to part of CSkelMeshInstance::SetMesh() and Rune mesh loader static void BuildSkeleton(TArray<CCoords> &Coords, const TArray<CSkelMeshBone> &Bones) { guard(BuildSkeleton); int numBones = Bones.Num(); Coords.Empty(numBones); Coords.AddZeroed(numBones); for (int i = 0; i < numBones; i++) { const CSkelMeshBone &B = Bones[i]; CCoords &BC = Coords[i]; // compute reference bone coords CVec3 BP; CQuat BO; // get default pose BP = B.Position; BO = B.Orientation; #if MIRROR_MESH BP[1] *= -1; // y BO.y *= -1; BO.w *= -1; #endif if (!i) BO.Conjugate(); // root bone BC.origin = BP; BO.ToAxis(BC.axis); // move bone position to global coordinate space if (i) // do not rotate root bone { assert(B.ParentIndex < i); Coords[B.ParentIndex].UnTransformCoords(BC, BC); } #if 0 //!! if (i == 32) { appNotify("Bone %d (%8.3f %8.3f %8.3f) - (%8.3f %8.3f %8.3f %8.3f)", i, VECTOR_ARG(BP), QUAT_ARG(BO)); #define C BC appNotify("REF : o=%8.3f %8.3f %8.3f", VECTOR_ARG(C.origin )); appNotify(" 0=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[0])); appNotify(" 1=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[1])); appNotify(" 2=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[2])); #undef C } #endif } unguard; }
//----------------------------------------------- // qStart is the quaterion at t=0, // qEnd is the quaterion at t=TAnimEnd, // time is between 0 and 1. (1=> TAnimEnd) //----------------------------------------------- CQuat applyRotationFactor(CQuat in, float rotFactor, CQuat qStart, CQuat qEnd, float time) { H_AUTO ( RZ_Client_Apply_Rotation_Factor ) CQuat qRotTotal1, qRotTotal2; qStart.invert(); qRotTotal1= qEnd*qStart; // qRotTotal2.makeClosest(CQuat::Identity); qRotTotal2= CQuat::slerp(CQuat::Identity, qRotTotal1, rotFactor); // apply animation factor // qRotTotal1.makeClosest(CQuat::Identity); // qRotTotal2.makeClosest(CQuat::Identity); CQuat qRotDelta1= CQuat::slerp(CQuat::Identity, qRotTotal1, time); CQuat qRotDelta2= CQuat::slerp(CQuat::Identity, qRotTotal2, time); // remove normal rotation, and apply rotFactor-ed one. qRotDelta1.invert(); return qRotDelta2 * qRotDelta1 * in; }// applyRotationFactor //
//sub force calculation types... void CVXS_Bond::CalcLinForce() //get bond forces given positions, angles, and stiffnesses... { Vec3D<double> CurXRelPos(pVox2->GetCurPosHighAccuracy() - pVox1->GetCurPosHighAccuracy()); //digit truncation happens here... CQuat<double> CurXAng1(pVox1->GetCurAngleHighAccuracy()); CQuat<double> CurXAng2(pVox2->GetCurAngleHighAccuracy()); ToXDirBond(&CurXRelPos); ToXDirBond(&CurXAng1); ToXDirBond(&CurXAng2); Vec3D<double> Ang1AlignedRelPos(CurXAng1.RotateVec3DInv(CurXRelPos)); //undo current voxel rotation to put in line with original bond according to Angle 1 CQuat<double> NewAng2(CurXAng1.Conjugate()*CurXAng2); bool ChangedSaState = false; vfloat SmallTurn = (abs(Ang1AlignedRelPos.z)+abs(Ang1AlignedRelPos.y))/Ang1AlignedRelPos.x; if (!SmallAngle && NewAng2.IsSmallAngle() && SmallTurn < SA_BOND_BEND_RAD){ SmallAngle = true; ChangedSaState=true;} else if (SmallAngle && !NewAng2.IsSmallishAngle() && SmallTurn > VEC3D_HYSTERESIS_FACTOR*SA_BOND_BEND_RAD){SmallAngle = false; ChangedSaState=true;} double NomDistance = (pVox1->GetCurScale().x + pVox2->GetCurScale().x)*0.5; //nominal distance between voxels CQuat<> TotalRot; if (SmallAngle) { //Align so Angle1 is all zeros _Angle1 = Vec3D<>(0,0,0); _Angle2 = NewAng2.ToRotationVector(); Ang1AlignedRelPos.x -= NomDistance; //only valid for small angles _Pos2 = Ang1AlignedRelPos; TotalRot = CurXAng1.Conjugate(); } else { //Large angle. Align so that Pos2.y, Pos2.z are zero. CQuat<double> Pos2AlignedRotAng; Pos2AlignedRotAng.FromAngleToPosX(Ang1AlignedRelPos); //get the angle to align this with the X axis TotalRot = Pos2AlignedRotAng * CurXAng1.Conjugate(); vfloat Length = CurXRelPos.Length(); //Ang1AlignedRelPos.x<0 ? -Ang1AlignedRelPos.Length() : Ang1AlignedRelPos.Length(); _Pos2 = Vec3D<>(Length - NomDistance, 0, 0); //Small angle optimization target!! // Vec3D<> Pos2a = Pos2AlignedRotAng.RotateVec3D(Ang1AlignedRelPos); //high performance (but slow version) for special cases. should never crop up. (i.e. 1dof sim dragging voxel past each other) // _Pos2 = Vec3D<>(Pos2a.x - NomDistance, 0, 0); _Angle1 = Pos2AlignedRotAng.ToRotationVector(); //these are currently a source of error of up to 1e-6 _Angle2 = (TotalRot * CurXAng2).ToRotationVector(); } UpdateBondStrain(_Pos2.x/L.x); //updates the bond parameters (yielded, broken, stress...) based on the current Strain //Beam equations! (all terms here, even though some are sero for small angle and large angle (negligible perfoprmance penalty) Force1 = Vec3D<> ( -CurStress*L.y*L.z, -b1z*_Pos2.y + b2z*(_Angle1.z + _Angle2.z), -b1y*_Pos2.z - b2y*(_Angle1.y + _Angle2.y)); //Use Curstress instead of -a1*Pos2.x to account for non-linear deformation Force2 = -Force1; Moment1 = Vec3D<> ( a2*(_Angle1.x - _Angle2.x), b2z*_Pos2.z + b3y*(2*_Angle1.y + _Angle2.y), -b2y*_Pos2.y + b3z*(2*_Angle1.z + _Angle2.z)); Moment2 = Vec3D<> ( a2*(_Angle2.x - _Angle1.x), b2z*_Pos2.z + b3y*(_Angle1.y + 2*_Angle2.y), -b2y*_Pos2.y + b3z*(_Angle1.z + 2*_Angle2.z)); if (p_Sim->StatToCalc & CALCSTAT_STRAINE) StrainEnergy = CalcStrainEnergy(); //depends on Force1, Force2, Moment1, Moment2 being set! if (!ChangedSaState) AddDampForces(); //Unrotate back to global coordinate system Force1 = TotalRot.RotateVec3DInv(Force1); if (!HomogenousBond) Force2 = TotalRot.RotateVec3DInv(Force2); //could reduce for homogenous... Moment1 = TotalRot.RotateVec3DInv(Moment1); Moment2 = TotalRot.RotateVec3DInv(Moment2); ToOrigDirBond(&Force1); if (HomogenousBond) Force2 = -Force1; else ToOrigDirBond(&Force2); //Added ToOrigDirBond(&Moment1); ToOrigDirBond(&Moment2); }
void ExportMd5Mesh(const CSkeletalMesh *Mesh) { guard(ExportMd5Mesh); int i; UObject *OriginalMesh = Mesh->OriginalMesh; if (!Mesh->Lods.Num()) { appNotify("Mesh %s has 0 lods", OriginalMesh->Name); return; } FArchive *Ar = CreateExportArchive(OriginalMesh, "%s.md5mesh", OriginalMesh->Name); if (!Ar) return; const CSkelMeshLod &Lod = Mesh->Lods[0]; Ar->Printf( "MD5Version 10\n" "commandline \"Created with UE Viewer\"\n" "\n" "numJoints %d\n" "numMeshes %d\n" "\n", Mesh->RefSkeleton.Num(), Lod.Sections.Num() ); // compute skeleton TArray<CCoords> BoneCoords; BuildSkeleton(BoneCoords, Mesh->RefSkeleton); // write joints Ar->Printf("joints {\n"); for (i = 0; i < Mesh->RefSkeleton.Num(); i++) { const CSkelMeshBone &B = Mesh->RefSkeleton[i]; const CCoords &BC = BoneCoords[i]; CVec3 BP; CQuat BO; BP = BC.origin; BO.FromAxis(BC.axis); if (BO.w < 0) BO.Negate(); // W-component of quaternion will be removed ... Ar->Printf( "\t\"%s\"\t%d ( %f %f %f ) ( %.10f %.10f %.10f )\n", *B.Name, (i == 0) ? -1 : B.ParentIndex, VECTOR_ARG(BP), BO.x, BO.y, BO.z ); #if 0 //!! if (i == 32 || i == 34) { CCoords BC; BC.origin = BP; BO.ToAxis(BC.axis); appNotify("Bone %d (%8.3f %8.3f %8.3f) - (%8.3f %8.3f %8.3f %8.3f)", i, VECTOR_ARG(BP), QUAT_ARG(BO)); #define C BC appNotify("INV : o=%8.3f %8.3f %8.3f", VECTOR_ARG(C.origin )); appNotify(" 0=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[0])); appNotify(" 1=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[1])); appNotify(" 2=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[2])); #undef C // BO.Negate(); BO.w *= -1; BO.ToAxis(BC.axis); #define C BC appNotify("INV2 : o=%8.3f %8.3f %8.3f", VECTOR_ARG(C.origin )); appNotify(" 0=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[0])); appNotify(" 1=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[1])); appNotify(" 2=%8.3f %8.3f %8.3f", VECTOR_ARG(C.axis[2])); #undef C } #endif } Ar->Printf("}\n\n"); // collect weights information TArray<VertInfluences> Weights; // Point -> Influences Weights.AddZeroed(Lod.NumVerts); for (i = 0; i < Lod.NumVerts; i++) { const CSkelMeshVertex &V = Lod.Verts[i]; CVec4 UnpackedWeights; V.UnpackWeights(UnpackedWeights); for (int j = 0; j < NUM_INFLUENCES; j++) { if (V.Bone[j] < 0) break; VertInfluence *I = new (Weights[i].Inf) VertInfluence; I->Bone = V.Bone[j]; I->Weight = UnpackedWeights[j]; } } CIndexBuffer::IndexAccessor_t Index = Lod.Indices.GetAccessor(); // write meshes // we are using some terms here: // - "mesh vertex" is a vertex in Lod.Verts[] array, global for whole mesh // - "surcace vertex" is a vertex from the mesh stripped to only one (current) section for (int m = 0; m < Lod.Sections.Num(); m++) { const CMeshSection &Sec = Lod.Sections[m]; TArray<int> MeshVerts; // surface vertex -> mesh vertex TArray<int> BackWedge; // mesh vertex -> surface vertex TArray<bool> UsedVerts; // mesh vertex -> surface: used of not TArray<int> MeshWeights; // mesh vertex -> weight index MeshVerts.Empty(Lod.NumVerts); UsedVerts.AddZeroed(Lod.NumVerts); BackWedge.AddZeroed(Lod.NumVerts); MeshWeights.AddZeroed(Lod.NumVerts); // find verts and triangles for current material for (i = 0; i < Sec.NumFaces * 3; i++) { int idx = Index(i + Sec.FirstIndex); if (UsedVerts[idx]) continue; // vertex is already used in previous triangle UsedVerts[idx] = true; int locWedge = MeshVerts.Add(idx); BackWedge[idx] = locWedge; } // find influences int WeightIndex = 0; for (i = 0; i < Lod.NumVerts; i++) { if (!UsedVerts[i]) continue; MeshWeights[i] = WeightIndex; WeightIndex += Weights[i].Inf.Num(); } // mesh header const UUnrealMaterial *Tex = Sec.Material; if (Tex) { Ar->Printf( "mesh {\n" "\tshader \"%s\"\n\n", Tex->Name ); ExportObject(Tex); } else { Ar->Printf( "mesh {\n" "\tshader \"material_%d\"\n\n", m ); } // verts Ar->Printf("\tnumverts %d\n", MeshVerts.Num()); for (i = 0; i < MeshVerts.Num(); i++) { int iPoint = MeshVerts[i]; const CSkelMeshVertex &V = Lod.Verts[iPoint]; Ar->Printf("\tvert %d ( %f %f ) %d %d\n", i, V.UV.U, V.UV.V, MeshWeights[iPoint], Weights[iPoint].Inf.Num()); } // triangles Ar->Printf("\n\tnumtris %d\n", Sec.NumFaces); for (i = 0; i < Sec.NumFaces; i++) { Ar->Printf("\ttri %d", i); #if MIRROR_MESH for (int j = 2; j >= 0; j--) #else for (int j = 0; j < 3; j++) #endif Ar->Printf(" %d", BackWedge[Index(Sec.FirstIndex + i * 3 + j)]); Ar->Printf("\n"); } // weights Ar->Printf("\n\tnumweights %d\n", WeightIndex); int saveWeightIndex = WeightIndex; WeightIndex = 0; for (i = 0; i < Lod.NumVerts; i++) { if (!UsedVerts[i]) continue; for (int j = 0; j < Weights[i].Inf.Num(); j++) { const VertInfluence &I = Weights[i].Inf[j]; CVec3 v; v = Lod.Verts[i].Position; #if MIRROR_MESH v[1] *= -1; // y #endif BoneCoords[I.Bone].TransformPoint(v, v); Ar->Printf( "\tweight %d %d %f ( %f %f %f )\n", WeightIndex, I.Bone, I.Weight, VECTOR_ARG(v) ); WeightIndex++; } } assert(saveWeightIndex == WeightIndex); // mesh footer Ar->Printf("}\n"); } delete Ar; unguard; }
void ExportMd5Anim(const CAnimSet *Anim) { guard(ExportMd5Anim); int numBones = Anim->TrackBoneNames.Num(); UObject *OriginalAnim = Anim->OriginalAnim; for (int AnimIndex = 0; AnimIndex < Anim->Sequences.Num(); AnimIndex++) { int i; const CAnimSequence &S = Anim->Sequences[AnimIndex]; FArchive *Ar = CreateExportArchive(OriginalAnim, "%s/%s.md5anim", OriginalAnim->Name, *S.Name); if (!Ar) continue; Ar->Printf( "MD5Version 10\n" "commandline \"Created with UE Viewer\"\n" "\n" "numFrames %d\n" "numJoints %d\n" "frameRate %g\n" "numAnimatedComponents %d\n" "\n", S.NumFrames, numBones, S.Rate, numBones * 6 ); // skeleton Ar->Printf("hierarchy {\n"); for (i = 0; i < numBones; i++) { Ar->Printf("\t\"%s\" %d %d %d\n", *Anim->TrackBoneNames[i], (i == 0) ? -1 : 0, 63, i * 6); // ParentIndex is unknown for UAnimSet, so always write "0" // here: 6 is number of components per frame, 63 = (1<<6)-1 -- flags "all components are used" } // bounds Ar->Printf("}\n\nbounds {\n"); for (i = 0; i < S.NumFrames; i++) Ar->Printf("\t( -100 -100 -100 ) ( 100 100 100 )\n"); //!! dummy Ar->Printf("}\n\n"); // baseframe and frames for (int Frame = -1; Frame < S.NumFrames; Frame++) { int t = Frame; if (Frame == -1) { Ar->Printf("baseframe {\n"); t = 0; } else Ar->Printf("frame %d {\n", Frame); for (int b = 0; b < numBones; b++) { CVec3 BP; CQuat BO; S.Tracks[b].GetBonePosition(t, S.NumFrames, false, BP, BO); if (!b) BO.Conjugate(); // root bone #if MIRROR_MESH BO.y *= -1; BO.w *= -1; BP[1] *= -1; // y #endif if (BO.w < 0) BO.Negate(); // W-component of quaternion will be removed ... if (Frame < 0) Ar->Printf("\t( %f %f %f ) ( %.10f %.10f %.10f )\n", VECTOR_ARG(BP), BO.x, BO.y, BO.z); else Ar->Printf("\t%f %f %f %.10f %.10f %.10f\n", VECTOR_ARG(BP), BO.x, BO.y, BO.z); } Ar->Printf("}\n\n"); } delete Ar; } unguard; }
static void ExportAnimations(ExportContext& Context, FArchive& Ar) { guard(ExportAnimations); const CAnimSet* Anim = Context.SkelMesh->Anim; int NumBones = Context.SkelMesh->RefSkeleton.Num(); // Build mesh to anim bone map TArray<int> BoneMap; BoneMap.Init(-1, NumBones); TArray<int> AnimBones; AnimBones.Empty(NumBones); for (int i = 0; i < NumBones; i++) { const CSkelMeshBone &B = Context.SkelMesh->RefSkeleton[i]; for (int j = 0; j < Anim->TrackBoneNames.Num(); j++) { if (!stricmp(B.Name, Anim->TrackBoneNames[j])) { BoneMap[i] = j; // lookup CAnimSet bone by mesh bone index AnimBones.Add(i); // indicate that the bone has animation break; } } } Ar.Printf( " \"animations\" : [\n" ); int FirstDataIndex = Context.Data.Num(); // Iterate over all animations for (int SeqIndex = 0; SeqIndex < Anim->Sequences.Num(); SeqIndex++) { const CAnimSequence &Seq = *Anim->Sequences[SeqIndex]; Ar.Printf( " {\n" " \"name\" : \"%s\",\n", *Seq.Name ); struct AnimSampler { enum ChannelType { TRANSLATION, ROTATION }; int BoneNodeIndex; ChannelType Type; const CAnimTrack* Track; }; TArray<AnimSampler> Samplers; Samplers.Empty(AnimBones.Num() * 2); //!! Optimization: //!! 1. there will be missing tracks (AnimRotationOnly etc) - drop such samplers //!! 2. store all time tracks in a single BufferView, all rotation tracks in another, and all position track in 3rd one - this //!! will reduce amount of BufferViews in json text (combine them by data type) // Prepare channels array Ar.Printf(" \"channels\" : [\n"); for (int BoneIndex = 0; BoneIndex < AnimBones.Num(); BoneIndex++) { int MeshBoneIndex = AnimBones[BoneIndex]; int AnimBoneIndex = BoneMap[MeshBoneIndex]; const CAnimTrack* Track = Seq.Tracks[AnimBoneIndex]; int TranslationSamplerIndex = Samplers.Num(); AnimSampler* Sampler = new (Samplers) AnimSampler; Sampler->Type = AnimSampler::TRANSLATION; Sampler->BoneNodeIndex = MeshBoneIndex + FIRST_BONE_NODE; Sampler->Track = Track; int RotationSamplerIndex = Samplers.Num(); Sampler = new (Samplers) AnimSampler; Sampler->Type = AnimSampler::ROTATION; Sampler->BoneNodeIndex = MeshBoneIndex + FIRST_BONE_NODE; Sampler->Track = Track; // Print glTF information. Not using usual formatting here to make output a little bit more compact. Ar.Printf( " { \"sampler\" : %d, \"target\" : { \"node\" : %d, \"path\" : \"%s\" } },\n", TranslationSamplerIndex, MeshBoneIndex + FIRST_BONE_NODE, "translation" ); Ar.Printf( " { \"sampler\" : %d, \"target\" : { \"node\" : %d, \"path\" : \"%s\" } }%s\n", RotationSamplerIndex, MeshBoneIndex + FIRST_BONE_NODE, "rotation", BoneIndex == AnimBones.Num()-1 ? "" : "," ); } Ar.Printf(" ],\n"); // Prepare samplers Ar.Printf(" \"samplers\" : [\n"); for (int SamplerIndex = 0; SamplerIndex < Samplers.Num(); SamplerIndex++) { const AnimSampler& Sampler = Samplers[SamplerIndex]; // Prepare time array const TArray<float>* TimeArray = (Sampler.Type == AnimSampler::TRANSLATION) ? &Sampler.Track->KeyPosTime : &Sampler.Track->KeyQuatTime; if (TimeArray->Num() == 0) { // For this situation, use track's time array TimeArray = &Sampler.Track->KeyTime; } int NumKeys = Sampler.Type == (AnimSampler::TRANSLATION) ? Sampler.Track->KeyPos.Num() : Sampler.Track->KeyQuat.Num(); int TimeBufIndex = Context.Data.AddZeroed(); BufferData& TimeBuf = Context.Data[TimeBufIndex]; TimeBuf.Setup(NumKeys, "SCALAR", BufferData::FLOAT, sizeof(float)); float RateScale = 1.0f / Seq.Rate; float LastFrameTime = 0; if (TimeArray->Num() == 0 || NumKeys == 1) { // Fill with equally spaced values for (int i = 0; i < NumKeys; i++) { TimeBuf.Put(i * RateScale); } LastFrameTime = NumKeys-1; } else { for (int i = 0; i < TimeArray->Num(); i++) { TimeBuf.Put((*TimeArray)[i] * RateScale); } LastFrameTime = (*TimeArray)[TimeArray->Num()-1]; } // Prepare min/max values for time track, it's required by glTF standard TimeBuf.BoundsMin = "[ 0 ]"; char buf[64]; appSprintf(ARRAY_ARG(buf), "[ %g ]", LastFrameTime * RateScale); TimeBuf.BoundsMax = buf; // Try to reuse TimeBuf from previous tracks TimeBufIndex = Context.GetFinalIndexForLastBlock(FirstDataIndex); // Prepare data int DataBufIndex = Context.Data.AddZeroed(); BufferData& DataBuf = Context.Data[DataBufIndex]; if (Sampler.Type == AnimSampler::TRANSLATION) { // Translation track DataBuf.Setup(NumKeys, "VEC3", BufferData::FLOAT, sizeof(CVec3)); for (int i = 0; i < NumKeys; i++) { CVec3 Pos = Sampler.Track->KeyPos[i]; TransformPosition(Pos); DataBuf.Put(Pos); } } else { // Rotation track DataBuf.Setup(NumKeys, "VEC4", BufferData::FLOAT, sizeof(CQuat)); for (int i = 0; i < NumKeys; i++) { CQuat Rot = Sampler.Track->KeyQuat[i]; TransformRotation(Rot); if (Sampler.BoneNodeIndex - FIRST_BONE_NODE == 0) { Rot.Conjugate(); } DataBuf.Put(Rot); } } // Try to reuse data block as well DataBufIndex = Context.GetFinalIndexForLastBlock(FirstDataIndex); // Write glTF info Ar.Printf( " { \"input\" : %d, \"output\" : %d }%s\n", TimeBufIndex, DataBufIndex, SamplerIndex == Samplers.Num()-1 ? "" : "," ); } Ar.Printf(" ]\n"); Ar.Printf(" }%s\n", SeqIndex == Anim->Sequences.Num()-1 ? "" : ","); } Ar.Printf(" ],\n"); unguard; }
static void ExportSkinData(ExportContext& Context, const CSkelMeshLod& Lod, FArchive& Ar) { guard(ExportSkinData); int numBones = Context.SkelMesh->RefSkeleton.Num(); int MatrixBufIndex = Context.Data.AddZeroed(); BufferData& MatrixBuf = Context.Data[MatrixBufIndex]; MatrixBuf.Setup(numBones, "MAT4", BufferData::FLOAT, sizeof(CMat4)); Ar.Printf( " \"nodes\" : [\n" " {\n" " \"name\" : \"%s\",\n" " \"mesh\" : 0,\n" " \"skin\" : 0,\n" " \"children\" : [ 1 ]\n" " },\n", Context.MeshName); TArray<CCoords> BoneCoords; BoneCoords.AddZeroed(numBones); for (int boneIndex = 0; boneIndex < numBones; boneIndex++) { const CSkelMeshBone& B = Context.SkelMesh->RefSkeleton[boneIndex]; // Find all children TStaticArray<int, 32> children; for (int j = 0; j < numBones; j++) { if (boneIndex == j) continue; const CSkelMeshBone& B2 = Context.SkelMesh->RefSkeleton[j]; if (B2.ParentIndex == boneIndex) { children.Add(j); } } Ar.Printf( " {\n" " \"name\" : \"%s\",\n", *B.Name ); // Write children if (children.Num()) { Ar.Printf(" \"children\" : [ %d", children[0]+FIRST_BONE_NODE); for (int j = 1; j < children.Num(); j++) { Ar.Printf(", %d", children[j]+FIRST_BONE_NODE); } Ar.Printf(" ],\n"); } // Bone transform CVec3 bonePos = B.Position; CQuat boneRot = B.Orientation; if (boneIndex == 0) { boneRot.Conjugate(); } TransformPosition(bonePos); TransformRotation(boneRot); Ar.Printf( " \"translation\" : [ %g, %g, %g ],\n" " \"rotation\" : [ %g, %g, %g, %g ]\n", bonePos[0], bonePos[1], bonePos[2], boneRot.x, boneRot.y, boneRot.z, boneRot.w ); boneRot.w *= -1; CCoords& BC = BoneCoords[boneIndex]; BC.origin = bonePos; boneRot.ToAxis(BC.axis); if (boneIndex) { // World coordinate BoneCoords[B.ParentIndex].UnTransformCoords(BC, BC); } CCoords InvCoords; InvertCoords(BC, InvCoords); CMat4 BC4x4(InvCoords); MatrixBuf.Put(BC4x4); // Closing brace Ar.Printf( " }%s\n", boneIndex == (numBones-1) ? "" : "," ); } // Close "nodes" array Ar.Printf(" ],\n"); // Make "skins" Ar.Printf( " \"skins\" : [\n" " {\n" " \"inverseBindMatrices\" : %d,\n" " \"skeleton\" : 1,\n" " \"joints\" : [", MatrixBufIndex ); for (int i = 0; i < numBones; i++) { if ((i & 31) == 0) Ar.Printf("\n "); Ar.Printf("%d%s", i+FIRST_BONE_NODE, (i == numBones-1) ? "" : ","); } Ar.Printf( "\n" " ]\n" " }\n" " ],\n" ); unguard; }
//----------------------------------------------- // init //----------------------------------------------- void CAnimationSet::init(CAnimationSetSheet *sheet, NL3D::UAnimationSet *animationSet) { _Sheet = sheet; _AnimationStates.resize(_Sheet->AnimationStates.size()); for (uint32 i = 0; i < _AnimationStates.size(); ++i) { _AnimationStates[i].init(&_Sheet->AnimationStates[i], animationSet); } const CAnimationState *animState = getAnimationState (CAnimationStateSheet::Walk); if (animState) { // Compute the distance to break the idle (done according to the walk state). CAnimation::TAnimId walkId = animState->chooseAnim(0, EGSPD::CPeople::Unknown, GSGENDER::male); // Check the animation Id. if(walkId != CAnimation::UnknownAnim) { // Get the animation const CAnimation *walkAnim = animState->getAnimation(walkId); // Compute the dist made by the walk animation. CVector mov; if(CAnimationMisc::getAnimationMove(animationSet, walkAnim->id(), mov)) { _WalkDist = fabs(mov.y); _MaxDist = ClientCfg.MinDistFactor*_WalkDist; } _WalkLength = CAnimationMisc::getAnimationLength(animationSet, walkAnim->id()); animState = getAnimationState (CAnimationStateSheet::Run); if (animState) { // Get the run animation ID. CAnimation::TAnimId runId = animState->chooseAnim(0, EGSPD::CPeople::Unknown, GSGENDER::male); if(runId != CAnimation::UnknownAnim) { // Get the animation const CAnimation *runAnim = animState->getAnimation(runId); CVector mov; if(CAnimationMisc::getAnimationMove(animationSet, runAnim->id(), mov)) _RunDist = fabs(mov.y); _RunLength = CAnimationMisc::getAnimationLength(animationSet, runAnim->id()); // Get the walk average speed. double aveWalk = CAnimationMisc::getAnimationAverageSpeed(animationSet, walkAnim->id()); // Get the run average speed. double aveRun = CAnimationMisc::getAnimationAverageSpeed(animationSet, runAnim->id()); pushInfoStr(NLMISC::toString("Walk speed(%f).", aveWalk)); pushInfoStr(NLMISC::toString("Run speed(%f).", aveRun)); // Check animations average speed for walk and run. if(aveRun<aveWalk) pushDebugStr(NLMISC::toString("Walk speed(%f) > Run speed(%f) !", aveWalk, aveRun)); // Average Speed. double ave = (aveWalk+aveRun)/2.0; // Compute the min speed to run when walking. _SpeedToRun = (ave + aveRun)/2.0; // Compute the max speed to walk when running. _SpeedToWalk = (ave + aveWalk)/2.0; } // No animation found to run. else if(_Sheet->IsRunEssential) pushDebugStr("No animation found to run: speedToRun and speedToWalk will return the default value."); } } // No animation found to walk. else if(_Sheet->IsWalkEssential) pushDebugStr("No animation found to walk: maxDist, speedToRun and speedToWalk will return the default value."); } // Compute the angle after the one the character should turn (left/or right). animState = getAnimationState (CAnimationStateSheet::TurnLeft); if (animState) { CAnimation::TAnimId turnLeftId = animState->chooseAnim(0, EGSPD::CPeople::Unknown, GSGENDER::male); // Check the animation Id. if(turnLeftId != CAnimation::UnknownAnim) { // Get the animation const CAnimation *anim = animState->getAnimation(turnLeftId); if (anim) { CQuat currentAnimRotStart, currentAnimRotEnd; if(CAnimationMisc::interpolate(animationSet, anim->id(), 0.0, currentAnimRotStart)) { double animLength = CAnimationMisc::getAnimationLength(animationSet, turnLeftId); if(CAnimationMisc::interpolate(animationSet, anim->id(), animLength, currentAnimRotEnd)) { currentAnimRotStart.invert(); CQuat currentAnimRot = currentAnimRotStart * currentAnimRotEnd; _Angle = 0.75 * fabs(currentAnimRot.getAngle()); } } } } } }// init //
void CVXS_SimGLView::DrawForce(void) { CVXS_Voxel* pVox; float PrevLineWidth; glGetFloatv(GL_LINE_WIDTH, &PrevLineWidth); glLineWidth(1.0); glDisable(GL_LIGHTING); vfloat MaxForce = 0; int iT = pSim->NumBond(); int NumVox = pSim->NumVox(); if (NumVox <=0) return; vfloat VSize = pSim->VoxArray[0].GetNominalSize(); for (int i = 0; i<iT; i++){ //go through all the bonds... vfloat ThisMag = pSim->BondArrayInternal[i].GetForce1().Length(); if (ThisMag > MaxForce) MaxForce = ThisMag; } vfloat ForceScale = 0.3*VSize/MaxForce; //Vox size / max Force // int x, y, z; glBegin(GL_LINES); glLoadName (-1); //to disable picking for (int i = 0; i<NumVox; i++) //go through all the voxels... { //pSim->pEnv->pObj->GetXYZNom(&x, &y, &z, pSim->StoXIndexMap[i]); //if (ViewSection && z>SectionLayer) continue; //exit if obscured in a section view! pVox = &pSim->VoxArray[i]; Vec3D<> Center = pVox->GetCurPos(); CQuat<> Angle = pVox->GetCurAngle(); Vec3D<> PointToDrawFrom; for (int i=0; i<6; i++) //for each potential bond { switch (i){ case BD_PX: PointToDrawFrom = Center + Angle.RotateVec3D(Vec3D<>(0.2*VSize, 0, 0)); break; case BD_NX: PointToDrawFrom = Center + Angle.RotateVec3D(Vec3D<>(-0.2*VSize, 0, 0)); break; case BD_PY: PointToDrawFrom = Center + Angle.RotateVec3D(Vec3D<>(0, 0.2*VSize, 0)); break; case BD_NY: PointToDrawFrom = Center + Angle.RotateVec3D(Vec3D<>(0, -0.2*VSize, 0)); break; case BD_PZ: PointToDrawFrom = Center + Angle.RotateVec3D(Vec3D<>(0, 0, 0.2*VSize)); break; case BD_NZ: PointToDrawFrom = Center + Angle.RotateVec3D(Vec3D<>(0, 0, -0.2*VSize)); break; }; CVXS_BondInternal* pBond = pVox->GetpInternalBond((BondDir)i); if (pBond){ glColor4d(1.0, 0.0, 0.0, 1.0); //red Vec3D<> PointToDrawTo; //Total Force if (pVox->IAmInternalVox2(i)) PointToDrawTo = PointToDrawFrom+ForceScale*pBond->GetForce2(); else PointToDrawTo = PointToDrawFrom+ForceScale*pBond->GetForce1(); CGL_Utils::DrawLineArrowD(PointToDrawFrom, PointToDrawTo, 1.0, CColor(1, 0, 0)); //Red //Axial Force if (pVox->IAmInternalVox2(i)) PointToDrawTo = PointToDrawFrom+ForceScale*pBond->AxialForce2; else PointToDrawTo = PointToDrawFrom+ForceScale*pBond->AxialForce1; CGL_Utils::DrawLineArrowD(PointToDrawFrom, PointToDrawTo, 1.0, CColor(0.2, 0.2, 0.7)); //Blue //Bending Force if (pVox->IAmInternalVox2(i)) PointToDrawTo = PointToDrawFrom+ForceScale*pBond->BendingForce2; else PointToDrawTo = PointToDrawFrom+ForceScale*pBond->BendingForce1; CGL_Utils::DrawLineArrowD(PointToDrawFrom, PointToDrawTo, 1.0, CColor(0.2, 0.7, 0.2)); //Green //Shear Force if (pVox->IAmInternalVox2(i)) PointToDrawTo = PointToDrawFrom+ForceScale*pBond->ShearForce2; else PointToDrawTo = PointToDrawFrom+ForceScale*pBond->ShearForce1; CGL_Utils::DrawLineArrowD(PointToDrawFrom, PointToDrawTo, 1.0, CColor(0.7, 0.2, 0.7)); //Purple } } } glEnd(); glLineWidth(PrevLineWidth); glEnable(GL_LIGHTING); }
//sub force calculation types... void CVXS_BondInternal::CalcLinForce() //get bond forces given positions, angles, and stiffnesses... { Vec3D<double> CurXRelPos(pVox2->GetCurPosHighAccuracy() - pVox1->GetCurPosHighAccuracy()); //digit truncation happens here... CQuat<double> CurXAng1(pVox1->GetCurAngleHighAccuracy()); CQuat<double> CurXAng2(pVox2->GetCurAngleHighAccuracy()); ToXDirBond(&CurXRelPos); ToXDirBond(&CurXAng1); ToXDirBond(&CurXAng2); Vec3D<double> Ang1AlignedRelPos(CurXAng1.RotateVec3DInv(CurXRelPos)); //undo current voxel rotation to put in line with original bond according to Angle 1 CQuat<double> NewAng2(CurXAng1.Conjugate()*CurXAng2); //todo: lump into stress calculations double NomDistance; if (p_Sim->IsFeatureEnabled(VXSFEAT_VOLUME_EFFECTS)) NomDistance = L.x; else NomDistance = (pVox1->GetCurScale() + pVox2->GetCurScale())*0.5; //nominal distance between voxels bool ChangedSaState = false; vfloat SmallTurn = (abs(Ang1AlignedRelPos.z)+abs(Ang1AlignedRelPos.y))/Ang1AlignedRelPos.x; vfloat ExtendPerc = Ang1AlignedRelPos.x/NomDistance; if (!SmallAngle && NewAng2.IsSmallAngle() && SmallTurn < SA_BOND_BEND_RAD && ExtendPerc < SA_BOND_EXT_PERC){ SmallAngle = true; ChangedSaState=true;} else if (SmallAngle && (!NewAng2.IsSmallishAngle() || SmallTurn > VEC3D_HYSTERESIS_FACTOR*SA_BOND_BEND_RAD || ExtendPerc > VEC3D_HYSTERESIS_FACTOR*SA_BOND_EXT_PERC)){SmallAngle = false; ChangedSaState=true;} CQuat<> TotalRot; //Shear stuff! vfloat ShearStrainY=0; vfloat ShearStrainZ=0; if (SmallAngle) { //Align so Angle1 is all zeros _Angle1 = Vec3D<>(0,0,0); _Angle2 = NewAng2.ToRotationVector(); Ang1AlignedRelPos.x -= NomDistance; //only valid for small angles _Pos2 = Ang1AlignedRelPos; TotalRot = CurXAng1.Conjugate(); ShearStrainY =-_Pos2.y/L.x; //delta Y/l ShearStrainZ =-_Pos2.z/L.x; //delta Z/l //vfloat Phi = 12*E*I/(G*A*L*L); //ShearStrainY = -(_Pos2.y/L.x + _Angle2.z*Phi/2); } else { //Large angle. Align so that Pos2.y, Pos2.z are zero. CQuat<double> Pos2AlignedRotAng; Pos2AlignedRotAng.FromAngleToPosX(Ang1AlignedRelPos); //get the angle to align this with the X axis TotalRot = Pos2AlignedRotAng * CurXAng1.Conjugate(); vfloat Length = CurXRelPos.Length(); //Ang1AlignedRelPos.x<0 ? -Ang1AlignedRelPos.Length() : Ang1AlignedRelPos.Length(); _Pos2 = Vec3D<>(Length - NomDistance, 0, 0); //Small angle optimization target!! // Vec3D<> Pos2a = Pos2AlignedRotAng.RotateVec3D(Ang1AlignedRelPos); //high performance (but slow version) for special cases. should never crop up. (i.e. 1dof sim dragging voxel past each other) // _Pos2 = Vec3D<>(Pos2a.x - NomDistance, 0, 0); _Angle1 = Pos2AlignedRotAng.ToRotationVector(); //these are currently a source of error of up to 1e-6 _Angle2 = (TotalRot * CurXAng2).ToRotationVector(); // ShearStrainY = tan(_Angle2.z-_Angle1.z); //tan theta // ShearStrainZ = -tan(_Angle2.y-_Angle1.y); //tan theta ShearStrainY = -tan(_Angle1.z); //tan theta ShearStrainZ = tan(_Angle1.y); //tan theta } UpdateBondStrain(_Pos2.x/L.x); //updates the bond parameters (yielded, broken, stress...) based on the current Strain //Beam equations! (all terms here, even though some are zero for small angle and large angle (negligible performance penalty) // if (p_Sim->IsFeatureEnabled(VXSFEAT_VOLUME_EFFECTS)) // Force1 = Vec3D<> ( -CurStress*(CSArea1+CSArea2)/2, -b1z*_Pos2.y + b2z*(_Angle1.z + _Angle2.z) - G*A*ShearStrainY, -b1y*_Pos2.z - b2y*(_Angle1.y + _Angle2.y)/*+ G*A*ShearStrainZ*/); //Use Curstress instead of -a1*Pos2.x to account for non-linear deformation // else /// Force1 = Vec3D<> ( -CurStress*(CSArea1+CSArea2)/2, -b1z*_Pos2.y + b2z*(_Angle1.z + _Angle2.z), -b1y*_Pos2.z - b2y*(_Angle1.y + _Angle2.y)); //Use Curstress instead of -a1*Pos2.x to account for non-linear deformation Force1 = Vec3D<> ( CurStress*(CSArea1+CSArea2)/2, b1z*_Pos2.y - b2z*(_Angle1.z + _Angle2.z), b1y*_Pos2.z + b2y*(_Angle1.y + _Angle2.y)); //Use Curstress instead of -a1*Pos2.x to account for non-linear deformation Force2 = -Force1; #ifdef DEBUG AxialForce1 = Vec3D<>(Force1.x, 0, 0); AxialForce2 = Vec3D<>(Force2.x, 0, 0); BendingForce1 = Vec3D<>(0, Force1.y, Force1.z); BendingForce2 = Vec3D<>(0, Force2.y, Force2.z); ShearForce1 = ShearForce2 = Vec3D<>(0,0,0); if (p_Sim->IsFeatureEnabled(VXSFEAT_VOLUME_EFFECTS)){ vfloat CA = G*(CSArea1+CSArea2)/2; //vfloat CA = G*A; Force1.y += CA*ShearStrainY; Force2.y -= CA*ShearStrainY; Force1.z += CA*ShearStrainZ; Force2.z -= CA*ShearStrainZ; ShearForce1 = Vec3D<>(0, CA*ShearStrainY, CA*ShearStrainZ); ShearForce2 = Vec3D<>(0, -CA*ShearStrainY, -CA*ShearStrainZ); } #endif Moment1 = Vec3D<> ( a2*(_Angle1.x - _Angle2.x), b2z*_Pos2.z + b3y*(2*_Angle1.y + _Angle2.y), -b2y*_Pos2.y + b3z*(2*_Angle1.z + _Angle2.z)); Moment2 = Vec3D<> ( a2*(_Angle2.x - _Angle1.x), b2z*_Pos2.z + b3y*(_Angle1.y + 2*_Angle2.y), -b2y*_Pos2.y + b3z*(_Angle1.z + 2*_Angle2.z)); if (p_Sim->StatToCalc & CALCSTAT_STRAINE) StrainEnergy = CalcStrainEnergy(); //depends on Force1, Force2, Moment1, Moment2 being set! if (!ChangedSaState) AddDampForces(); //Unrotate back to global coordinate system: //!!possible optimization: Do this after summing forces for a voxel! Force1 = TotalRot.RotateVec3DInv(Force1); if (!HomogenousBond) Force2 = TotalRot.RotateVec3DInv(Force2); Moment1 = TotalRot.RotateVec3DInv(Moment1); Moment2 = TotalRot.RotateVec3DInv(Moment2); ToOrigDirBond(&Force1); if (HomogenousBond) Force2 = -Force1; else ToOrigDirBond(&Force2); //Added ToOrigDirBond(&Moment1); ToOrigDirBond(&Moment2); #ifdef DEBUG AxialForce1 = TotalRot.RotateVec3DInv(AxialForce1); AxialForce2 = TotalRot.RotateVec3DInv(AxialForce2); ShearForce1 = TotalRot.RotateVec3DInv(ShearForce1); ShearForce2 = TotalRot.RotateVec3DInv(ShearForce2); BendingForce1 = TotalRot.RotateVec3DInv(BendingForce1); BendingForce2 = TotalRot.RotateVec3DInv(BendingForce2); ToOrigDirBond(&AxialForce1); ToOrigDirBond(&AxialForce2); ToOrigDirBond(&ShearForce1); ToOrigDirBond(&ShearForce2); ToOrigDirBond(&BendingForce1); ToOrigDirBond(&BendingForce2); #endif }