Example #1
0
// 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;
}
Example #2
0
// 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;
}
Example #3
0
//-----------------------------------------------
// 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);
}
Example #5
0
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;
}
Example #6
0
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;
}
Example #7
0
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;
}
Example #8
0
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;
}
Example #9
0
//-----------------------------------------------
// 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 //
Example #10
0
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

}