void AddConvexElemsToRigidActor_AssumesLocked() const { float ContactOffsetFactor, MaxContactOffset; GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset); for (int32 i = 0; i < BodySetup->AggGeom.ConvexElems.Num(); i++) { const FKConvexElem& ConvexElem = BodySetup->AggGeom.ConvexElems[i]; if (ConvexElem.ConvexMesh) { PxTransform PLocalPose; bool bUseNegX = CalcMeshNegScaleCompensation(Scale3D, PLocalPose); PxConvexMeshGeometry PConvexGeom; PConvexGeom.convexMesh = bUseNegX ? ConvexElem.ConvexMeshNegX : ConvexElem.ConvexMesh; PConvexGeom.scale.scale = U2PVector(Scale3DAbs * ConvexElem.GetTransform().GetScale3D().GetAbs()); FTransform ConvexTransform = ConvexElem.GetTransform(); if (ConvexTransform.GetScale3D().X < 0 || ConvexTransform.GetScale3D().Y < 0 || ConvexTransform.GetScale3D().Z < 0) { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has negative scale. Not currently supported"), *GetPathNameSafe(BodySetup->GetOuter()), i); } if (ConvexTransform.IsValid()) { PxTransform PElementTransform = U2PTransform(ConvexTransform * RelativeTM); PLocalPose.q *= PElementTransform.q; PLocalPose.p = PElementTransform.p; PLocalPose.p.x *= Scale3D.X; PLocalPose.p.y *= Scale3D.Y; PLocalPose.p.z *= Scale3D.Z; if (PConvexGeom.isValid()) { PxVec3 PBoundsExtents = PConvexGeom.convexMesh->getLocalBounds().getExtents(); ensure(PLocalPose.isValid()); { const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PBoundsExtents.minElement()); AttachShape_AssumesLocked(PConvexGeom, PLocalPose, ContactOffset); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] invalid"), *GetPathNameSafe(BodySetup->GetOuter()), i); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has invalid transform"), *GetPathNameSafe(BodySetup->GetOuter()), i); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: ConvexElem is missing ConvexMesh (%d: %s)"), i, *BodySetup->GetPathName()); } } }
void FAnimNode_CopyBone::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, const FBoneContainer & RequiredBones, FA2CSPose& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { check(OutBoneTransforms.Num() == 0); // Pass through if we're not doing anything. if( !bCopyTranslation && !bCopyRotation && !bCopyScale ) { return; } // Get component space transform for source and current bone. FTransform SourceBoneTM = MeshBases.GetComponentSpaceTransform(SourceBone.BoneIndex); FTransform CurrentBoneTM = MeshBases.GetComponentSpaceTransform(TargetBone.BoneIndex); // Copy individual components if (bCopyTranslation) { CurrentBoneTM.SetTranslation( SourceBoneTM.GetTranslation() ); } if (bCopyRotation) { CurrentBoneTM.SetRotation( SourceBoneTM.GetRotation() ); } if (bCopyScale) { CurrentBoneTM.SetScale3D( SourceBoneTM.GetScale3D() ); } // Output new transform for current bone. OutBoneTransforms.Add( FBoneTransform(TargetBone.BoneIndex, CurrentBoneTM) ); }
FVector FPhATEdPreviewViewportClient::GetWidgetLocation() const { if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit) /// BODY EDITING /// { // Don't draw widget if nothing selected. if (!SharedData->GetSelectedBody()) { return FVector::ZeroVector; } int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(SharedData->PhysicsAsset->SkeletalBodySetups[SharedData->GetSelectedBody()->Index]->BoneName); FTransform BoneTM = SharedData->EditorSkelComp->GetBoneTransform(BoneIndex); const float Scale = BoneTM.GetScale3D().GetAbsMax(); BoneTM.RemoveScaling(); return SharedData->EditorSkelComp->GetPrimitiveTransform(BoneTM, SharedData->GetSelectedBody()->Index, SharedData->GetSelectedBody()->PrimitiveType, SharedData->GetSelectedBody()->PrimitiveIndex, Scale).GetTranslation(); } else /// CONSTRAINT EDITING /// { if (!SharedData->GetSelectedConstraint()) { return FVector::ZeroVector; } return SharedData->GetConstraintMatrix(SharedData->GetSelectedConstraint()->Index, EConstraintFrame::Frame2, 1.f).GetTranslation(); } }
void UTerrainZoneComponent::SerializeInstancedMeshes(FBufferArchive& BinaryData) { int32 MeshCount = InstancedMeshMap.Num(); BinaryData << MeshCount; for (auto& Elem : InstancedMeshMap) { UHierarchicalInstancedStaticMeshComponent* InstancedStaticMeshComponent = Elem.Value; int32 MeshTypeId = Elem.Key; int32 MeshInstanceCount = InstancedStaticMeshComponent->GetInstanceCount(); BinaryData << MeshTypeId; BinaryData << MeshInstanceCount; for (int32 InstanceIdx = 0; InstanceIdx < MeshInstanceCount; InstanceIdx++) { FTransform InstanceTransform; InstancedStaticMeshComponent->GetInstanceTransform(InstanceIdx, InstanceTransform, true); float X = InstanceTransform.GetLocation().X; float Y = InstanceTransform.GetLocation().Y; float Z = InstanceTransform.GetLocation().Z; float Roll = InstanceTransform.Rotator().Roll; float Pitch = InstanceTransform.Rotator().Pitch; float Yaw = InstanceTransform.Rotator().Yaw; float ScaleX = InstanceTransform.GetScale3D().X; float ScaleY = InstanceTransform.GetScale3D().Y; float ScaleZ = InstanceTransform.GetScale3D().Z; BinaryData << X; BinaryData << Y; BinaryData << Z; BinaryData << Roll; BinaryData << Pitch; BinaryData << Yaw; BinaryData << ScaleX; BinaryData << ScaleY; BinaryData << ScaleZ; } } }
void FTransformCurve::UpdateOrAddKey(const FTransform& NewKey, float CurrentTime) { TranslationCurve.UpdateOrAddKey(NewKey.GetTranslation(), CurrentTime); // pitch, yaw, roll order - please check Evaluate function FVector RotationAsVector; FRotator Rotator = NewKey.GetRotation().Rotator(); RotationAsVector.X = Rotator.Roll; RotationAsVector.Y = Rotator.Pitch; RotationAsVector.Z = Rotator.Yaw; RotationCurve.UpdateOrAddKey(RotationAsVector, CurrentTime); ScaleCurve.UpdateOrAddKey(NewKey.GetScale3D(), CurrentTime); }
void FAnimNode_CopyBone::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { check(OutBoneTransforms.Num() == 0); // Pass through if we're not doing anything. if( !bCopyTranslation && !bCopyRotation && !bCopyScale ) { return; } // Get component space transform for source and current bone. const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer(); FCompactPoseBoneIndex SourceBoneIndex = SourceBone.GetCompactPoseIndex(BoneContainer); FCompactPoseBoneIndex TargetBoneIndex = TargetBone.GetCompactPoseIndex(BoneContainer); FTransform SourceBoneTM = MeshBases.GetComponentSpaceTransform(SourceBoneIndex); FTransform CurrentBoneTM = MeshBases.GetComponentSpaceTransform(TargetBoneIndex); if(ControlSpace != BCS_ComponentSpace) { // Convert out to selected space FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, SourceBoneTM, SourceBoneIndex, ControlSpace); FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, CurrentBoneTM, TargetBoneIndex, ControlSpace); } // Copy individual components if (bCopyTranslation) { CurrentBoneTM.SetTranslation( SourceBoneTM.GetTranslation() ); } if (bCopyRotation) { CurrentBoneTM.SetRotation( SourceBoneTM.GetRotation() ); } if (bCopyScale) { CurrentBoneTM.SetScale3D( SourceBoneTM.GetScale3D() ); } if(ControlSpace != BCS_ComponentSpace) { // Convert back out if we aren't operating in component space FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, CurrentBoneTM, TargetBoneIndex, ControlSpace); } // Output new transform for current bone. OutBoneTransforms.Add(FBoneTransform(TargetBoneIndex, CurrentBoneTM)); }
//============================================================================= static void TransformToSteamSpace(const FTransform& In, vr::HmdMatrix34_t& Out, float WorldToMeterScale) { const FRotator InRot = In.Rotator(); FRotator OutRot(InRot.Yaw, -InRot.Roll, -InRot.Pitch); const FVector InPos = In.GetTranslation(); FVector OutPos(InPos.Y, InPos.Z, -InPos.X); OutPos /= WorldToMeterScale; const FVector InScale = In.GetScale3D(); FVector OutScale(InScale.Y, InScale.Z, -InScale.X); OutScale /= WorldToMeterScale; Out = FSteamVRHMD::ToHmdMatrix34(FTransform(OutRot, OutPos, OutScale).ToMatrixNoScale()); }
void UBodySetup::AddShapesToRigidActor(PxRigidActor* PDestActor, FVector& Scale3D, const FTransform& RelativeTM /* = FTransform::Identity */, TArray<physx::PxShape*>* NewShapes /* = NULL */ ) { #if WITH_RUNTIME_PHYSICS_COOKING || WITH_EDITOR // in editor, there are a lot of things relying on body setup to create physics meshes CreatePhysicsMeshes(); #endif float MinScale; float MinScaleAbs; FVector Scale3DAbs; SetupNonUniformHelper(Scale3D, MinScale, MinScaleAbs, Scale3DAbs); { float MinScaleRelative; float MinScaleAbsRelative; FVector Scale3DAbsRelative; FVector Scale3DRelative = RelativeTM.GetScale3D(); SetupNonUniformHelper(Scale3DRelative, MinScaleRelative, MinScaleAbsRelative, Scale3DAbsRelative); MinScaleAbs *= MinScaleAbsRelative; Scale3DAbs.X *= Scale3DAbsRelative.X; Scale3DAbs.Y *= Scale3DAbsRelative.Y; Scale3DAbs.Z *= Scale3DAbsRelative.Z; } // Create shapes for simple collision if we do not want to use the complex collision mesh // for simple queries as well if (CollisionTraceFlag != ECollisionTraceFlag::CTF_UseComplexAsSimple) { AddSpheresToRigidActor(PDestActor, RelativeTM, MinScale, MinScaleAbs, NewShapes); AddBoxesToRigidActor(PDestActor, RelativeTM, Scale3D, Scale3DAbs, NewShapes); AddSphylsToRigidActor(PDestActor, RelativeTM, Scale3D, Scale3DAbs, NewShapes); AddConvexElemsToRigidActor(PDestActor, RelativeTM, Scale3D, Scale3DAbs, NewShapes); } // Create tri-mesh shape, when we are not using simple collision shapes for // complex queries as well if( CollisionTraceFlag != ECollisionTraceFlag::CTF_UseSimpleAsComplex ) { AddTriMeshToRigidActor(PDestActor, Scale3D, Scale3DAbs); } }
bool UPaperGroupedSpriteComponent::UpdateInstanceTransform(int32 InstanceIndex, const FTransform& NewInstanceTransform, bool bWorldSpace, bool bMarkRenderStateDirty) { if (!PerInstanceSpriteData.IsValidIndex(InstanceIndex)) { return false; } // Request navigation update UNavigationSystem::UpdateNavOctree(this); FSpriteInstanceData& InstanceData = PerInstanceSpriteData[InstanceIndex]; // Render data uses local transform of the instance FTransform LocalTransform = bWorldSpace ? NewInstanceTransform.GetRelativeTransform(ComponentToWorld) : NewInstanceTransform; InstanceData.Transform = LocalTransform.ToMatrixWithScale(); if (bPhysicsStateCreated) { // Physics uses world transform of the instance const FTransform WorldTransform = bWorldSpace ? NewInstanceTransform : (LocalTransform * ComponentToWorld); if (FBodyInstance* InstanceBodyInstance = InstanceBodies[InstanceIndex]) { // Update transform. InstanceBodyInstance->SetBodyTransform(WorldTransform, ETeleportType::None); InstanceBodyInstance->UpdateBodyScale(WorldTransform.GetScale3D()); } } // Request navigation update UNavigationSystem::UpdateNavOctree(this); if (bMarkRenderStateDirty) { MarkRenderStateDirty(); } return true; }
FBox FKAggregateGeom::CalcAABB(const FTransform& Transform) const { const FVector Scale3D = Transform.GetScale3D(); FTransform BoneTM = Transform; BoneTM.RemoveScaling(); FBox Box(0); // Instead of ignore if not uniform, I'm getting Min of the abs value // the reason for below function is for negative scale // say if you have scale of (-1, 2, -3), you'd like to get -1; const float ScaleFactor = SelectMinScale(Scale3D); for(int32 i=0; i<SphereElems.Num(); i++) { Box += SphereElems[i].CalcAABB(BoneTM, ScaleFactor); } for(int32 i=0; i<BoxElems.Num(); i++) { Box += BoxElems[i].CalcAABB(BoneTM, ScaleFactor); } for(int32 i=0; i<SphylElems.Num(); i++) { Box += SphylElems[i].CalcAABB(BoneTM, ScaleFactor); } // Accumulate convex element bounding boxes. for(int32 i=0; i<ConvexElems.Num(); i++) { Box += ConvexElems[i].CalcAABB(BoneTM, Scale3D); } return Box; }
void UAnimCompress_RemoveLinearKeys::ProcessAnimationTracks( UAnimSequence* AnimSeq, const TArray<FBoneData>& BoneData, TArray<FTranslationTrack>& PositionTracks, TArray<FRotationTrack>& RotationTracks, TArray<FScaleTrack>& ScaleTracks) { // extract all the data we'll need about the skeleton and animation sequence const int32 NumBones = BoneData.Num(); const int32 NumFrames = AnimSeq->NumFrames; const float SequenceLength = AnimSeq->SequenceLength; const int32 LastFrame = NumFrames-1; const float FrameRate = (float)(LastFrame) / SequenceLength; const float TimePerFrame = SequenceLength / (float)(LastFrame); const TArray<FTransform>& RefPose = AnimSeq->GetSkeleton()->GetRefLocalPoses(); const bool bHasScale = (ScaleTracks.Num() > 0); // make sure the parent key scale is properly bound to 1.0 or more ParentKeyScale = FMath::Max(ParentKeyScale, 1.0f); // generate the raw and compressed skeleton in world-space TArray<FTransform> RawWorldBones; TArray<FTransform> NewWorldBones; RawWorldBones.Empty(NumBones * NumFrames); NewWorldBones.Empty(NumBones * NumFrames); RawWorldBones.AddZeroed(NumBones * NumFrames); NewWorldBones.AddZeroed(NumBones * NumFrames); // generate an array to hold the indices of our end effectors TArray<int32> EndEffectors; EndEffectors.Empty(NumBones); // Create an array of FTransform to use as a workspace TArray<FTransform> BoneAtoms; // setup the raw bone transformation and find all end effectors for ( int32 BoneIndex = 0; BoneIndex < NumBones; ++BoneIndex ) { // get the raw world-atoms for this bone UpdateWorldBoneTransformTable( AnimSeq, BoneData, RefPose, BoneIndex, true, RawWorldBones); // also record all end-effectors we find const FBoneData& Bone = BoneData[BoneIndex]; if (Bone.IsEndEffector()) { EndEffectors.Add(BoneIndex); } } TArray<int32> TargetBoneIndices; // for each bone... for ( int32 BoneIndex = 0; BoneIndex < NumBones; ++BoneIndex ) { const FBoneData& Bone = BoneData[BoneIndex]; const int32 ParentBoneIndex = Bone.GetParent(); const int32 TrackIndex = AnimSeq->GetSkeleton()->GetAnimationTrackIndex(BoneIndex, AnimSeq); if (TrackIndex != INDEX_NONE) { // get the tracks we will be editing for this bone FRotationTrack& RotTrack = RotationTracks[TrackIndex]; FTranslationTrack& TransTrack = PositionTracks[TrackIndex]; const int32 NumRotKeys = RotTrack.RotKeys.Num(); const int32 NumPosKeys = TransTrack.PosKeys.Num(); const int32 NumScaleKeys = (bHasScale)? ScaleTracks[TrackIndex].ScaleKeys.Num() : 0; check( (NumPosKeys == 1) || (NumRotKeys == 1) || (NumPosKeys == NumRotKeys) ); // build an array of end effectors we need to monitor TargetBoneIndices.Reset(NumBones); int32 HighestTargetBoneIndex = BoneIndex; int32 FurthestTargetBoneIndex = BoneIndex; int32 ShortestChain = 0; float OffsetLength= -1.0f; for (int32 EffectorIndex=0; EffectorIndex < EndEffectors.Num(); ++EffectorIndex) { const int32 EffectorBoneIndex = EndEffectors[EffectorIndex]; const FBoneData& EffectorBoneData = BoneData[EffectorBoneIndex]; int32 RootIndex = EffectorBoneData.BonesToRoot.Find(BoneIndex); if (RootIndex != INDEX_NONE) { if (ShortestChain == 0 || (RootIndex+1) < ShortestChain) { ShortestChain = (RootIndex+1); } TargetBoneIndices.Add(EffectorBoneIndex); HighestTargetBoneIndex = FMath::Max(HighestTargetBoneIndex, EffectorBoneIndex); float ChainLength= 0.0f; for (long FamilyIndex=0; FamilyIndex < RootIndex; ++FamilyIndex) { const int32 NextParentBoneIndex= EffectorBoneData.BonesToRoot[FamilyIndex]; ChainLength += RefPose[NextParentBoneIndex].GetTranslation().Size(); } if (ChainLength > OffsetLength) { FurthestTargetBoneIndex = EffectorBoneIndex; OffsetLength = ChainLength; } } } // if requested, retarget the FBoneAtoms towards the target end effectors if (bRetarget) { if (NumScaleKeys > 0 && ParentBoneIndex != INDEX_NONE) { // update our bone table from the current bone through the last end effector we need to test UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, HighestTargetBoneIndex, false, NewWorldBones); FScaleTrack& ScaleTrack = ScaleTracks[TrackIndex]; // adjust all translation keys to align better with the destination for ( int32 KeyIndex = 0; KeyIndex < NumScaleKeys; ++KeyIndex ) { FVector& Key= ScaleTrack.ScaleKeys[KeyIndex]; const int32 FrameIndex= FMath::Clamp(KeyIndex, 0, LastFrame); const FTransform& NewWorldParent = NewWorldBones[(ParentBoneIndex*NumFrames) + FrameIndex]; const FTransform& RawWorldChild = RawWorldBones[(BoneIndex*NumFrames) + FrameIndex]; const FTransform& RelTM = (RawWorldChild.GetRelativeTransform(NewWorldParent)); const FTransform Delta = FTransform(RelTM); Key = Delta.GetScale3D(); } } if (NumRotKeys > 0 && ParentBoneIndex != INDEX_NONE) { if (HighestTargetBoneIndex == BoneIndex) { for ( int32 KeyIndex = 0; KeyIndex < NumRotKeys; ++KeyIndex ) { FQuat& Key = RotTrack.RotKeys[KeyIndex]; check(ParentBoneIndex != INDEX_NONE); const int32 FrameIndex = FMath::Clamp(KeyIndex, 0, LastFrame); FTransform NewWorldParent = NewWorldBones[(ParentBoneIndex*NumFrames) + FrameIndex]; FTransform RawWorldChild = RawWorldBones[(BoneIndex*NumFrames) + FrameIndex]; const FTransform& RelTM = (RawWorldChild.GetRelativeTransform(NewWorldParent)); FQuat Rot = FTransform(RelTM).GetRotation(); const FQuat& AlignedKey = EnforceShortestArc(Key, Rot); Key = AlignedKey; } } else { // update our bone table from the current bone through the last end effector we need to test UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, HighestTargetBoneIndex, false, NewWorldBones); // adjust all rotation keys towards the end effector target for ( int32 KeyIndex = 0; KeyIndex < NumRotKeys; ++KeyIndex ) { FQuat& Key = RotTrack.RotKeys[KeyIndex]; const int32 FrameIndex = FMath::Clamp(KeyIndex, 0, LastFrame); const FTransform& NewWorldTransform = NewWorldBones[(BoneIndex*NumFrames) + FrameIndex]; const FTransform& DesiredChildTransform = RawWorldBones[(FurthestTargetBoneIndex*NumFrames) + FrameIndex].GetRelativeTransform(NewWorldTransform); const FTransform& CurrentChildTransform = NewWorldBones[(FurthestTargetBoneIndex*NumFrames) + FrameIndex].GetRelativeTransform(NewWorldTransform); // find the two vectors which represent the angular error we are trying to correct const FVector& CurrentHeading = CurrentChildTransform.GetTranslation(); const FVector& DesiredHeading = DesiredChildTransform.GetTranslation(); // if these are valid, we can continue if (!CurrentHeading.IsNearlyZero() && !DesiredHeading.IsNearlyZero()) { const float DotResult = CurrentHeading.SafeNormal() | DesiredHeading.SafeNormal(); // limit the range we will retarget to something reasonable (~60 degrees) if (DotResult < 1.0f && DotResult > 0.5f) { FQuat Adjustment= FQuat::FindBetween(CurrentHeading, DesiredHeading); Adjustment.Normalize(); Adjustment= EnforceShortestArc(FQuat::Identity, Adjustment); const FVector Test = Adjustment.RotateVector(CurrentHeading); const float Delta = (Test - DesiredHeading).Size(); if (Delta < 0.001f) { FQuat NewKey = Adjustment * Key; NewKey.Normalize(); const FQuat& AlignedKey = EnforceShortestArc(Key, NewKey); Key = AlignedKey; } } } } } } if (NumPosKeys > 0 && ParentBoneIndex != INDEX_NONE) { // update our bone table from the current bone through the last end effector we need to test UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, HighestTargetBoneIndex, false, NewWorldBones); // adjust all translation keys to align better with the destination for ( int32 KeyIndex = 0; KeyIndex < NumPosKeys; ++KeyIndex ) { FVector& Key= TransTrack.PosKeys[KeyIndex]; const int32 FrameIndex= FMath::Clamp(KeyIndex, 0, LastFrame); FTransform NewWorldParent = NewWorldBones[(ParentBoneIndex*NumFrames) + FrameIndex]; FTransform RawWorldChild = RawWorldBones[(BoneIndex*NumFrames) + FrameIndex]; const FTransform& RelTM = RawWorldChild.GetRelativeTransform(NewWorldParent); const FTransform Delta = FTransform(RelTM); ensure (!Delta.ContainsNaN()); Key = Delta.GetTranslation(); } } } // look for a parent track to reference as a guide int32 GuideTrackIndex = INDEX_NONE; if (ParentKeyScale > 1.0f) { for (long FamilyIndex=0; (FamilyIndex < Bone.BonesToRoot.Num()) && (GuideTrackIndex == INDEX_NONE); ++FamilyIndex) { const int32 NextParentBoneIndex= Bone.BonesToRoot[FamilyIndex]; GuideTrackIndex = AnimSeq->GetSkeleton()->GetAnimationTrackIndex(NextParentBoneIndex, AnimSeq); } } // update our bone table from the current bone through the last end effector we need to test UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, HighestTargetBoneIndex, false, NewWorldBones); // rebuild the BoneAtoms table using the current set of keys UpdateBoneAtomList(AnimSeq, BoneIndex, TrackIndex, NumFrames, TimePerFrame, BoneAtoms); // determine the EndEffectorTolerance. // We use the Maximum value by default, and the Minimum value // as we approach the end effectors float EndEffectorTolerance = MaxEffectorDiff; if (ShortestChain <= 1) { EndEffectorTolerance = MinEffectorDiff; } // Determine if a guidance track should be used to aid in choosing keys to retain TArray<float>* GuidanceTrack = NULL; float GuidanceScale = 1.0f; if (GuideTrackIndex != INDEX_NONE) { FTranslationTrack& GuideTransTrack = PositionTracks[GuideTrackIndex]; GuidanceTrack = &GuideTransTrack.Times; GuidanceScale = ParentKeyScale; } // if the TargetBoneIndices array is empty, then this bone is an end effector. // so we add it to the list to maintain our tolerance checks if (TargetBoneIndices.Num() == 0) { TargetBoneIndices.Add(BoneIndex); } if (bActuallyFilterLinearKeys) { if (bHasScale) { FScaleTrack& ScaleTrack = ScaleTracks[TrackIndex]; // filter out translations we can approximate through interpolation FilterLinearKeysTemplate<FVector>( ScaleTrack.ScaleKeys, ScaleTrack.Times, BoneAtoms, GuidanceTrack, RawWorldBones, NewWorldBones, TargetBoneIndices, NumFrames, BoneIndex, ParentBoneIndex, GuidanceScale, MaxScaleDiff, EndEffectorTolerance, EffectorDiffSocket, BoneData); // update our bone table from the current bone through the last end effector we need to test UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, HighestTargetBoneIndex, false, NewWorldBones); // rebuild the BoneAtoms table using the current set of keys UpdateBoneAtomList(AnimSeq, BoneIndex, TrackIndex, NumFrames, TimePerFrame, BoneAtoms); } // filter out translations we can approximate through interpolation FilterLinearKeysTemplate<FVector>( TransTrack.PosKeys, TransTrack.Times, BoneAtoms, GuidanceTrack, RawWorldBones, NewWorldBones, TargetBoneIndices, NumFrames, BoneIndex, ParentBoneIndex, GuidanceScale, MaxPosDiff, EndEffectorTolerance, EffectorDiffSocket, BoneData); // update our bone table from the current bone through the last end effector we need to test UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, HighestTargetBoneIndex, false, NewWorldBones); // rebuild the BoneAtoms table using the current set of keys UpdateBoneAtomList(AnimSeq, BoneIndex, TrackIndex, NumFrames, TimePerFrame, BoneAtoms); // filter out rotations we can approximate through interpolation FilterLinearKeysTemplate<FQuat>( RotTrack.RotKeys, RotTrack.Times, BoneAtoms, GuidanceTrack, RawWorldBones, NewWorldBones, TargetBoneIndices, NumFrames, BoneIndex, ParentBoneIndex, GuidanceScale, MaxAngleDiff, EndEffectorTolerance, EffectorDiffSocket, BoneData); } } // make sure the final compressed keys are repesented in our NewWorldBones table UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, BoneIndex, false, NewWorldBones); } };
void UKismetMathLibrary::BreakTransform(const FTransform& InTransform, FVector& Translation, FRotator& Rotation, FVector& Scale) { Translation = InTransform.GetLocation(); Rotation = InTransform.Rotator(); Scale = InTransform.GetScale3D(); }
FVector UPoseableMeshComponent::GetBoneScaleByName(FName BoneName, EBoneSpaces::Type BoneSpace) { FTransform CurrentTransform = GetBoneTransformByName(BoneName, BoneSpace); return CurrentTransform.GetScale3D(); }
bool UnFbx::FFbxImporter::ImportAnimation(USkeleton* Skeleton, UAnimSequence * DestSeq, const FString& FileName, TArray<FbxNode*>& SortedLinks, TArray<FbxNode*>& NodeArray, FbxAnimStack* CurAnimStack, const int32 ResampleRate, const FbxTimeSpan AnimTimeSpan) { // @todo : the length might need to change w.r.t. sampling keys FbxTime SequenceLength = AnimTimeSpan.GetDuration(); float PreviousSequenceLength = DestSeq->SequenceLength; // if you have one pose(thus 0.f duration), it still contains animation, so we'll need to consider that as MINIMUM_ANIMATION_LENGTH time length DestSeq->SequenceLength = FGenericPlatformMath::Max<float>(SequenceLength.GetSecondDouble(), MINIMUM_ANIMATION_LENGTH); if(PreviousSequenceLength > MINIMUM_ANIMATION_LENGTH && DestSeq->RawCurveData.FloatCurves.Num() > 0) { // The sequence already existed when we began the import. We need to scale the key times for all curves to match the new // duration before importing over them. This is to catch any user-added curves float ScaleFactor = DestSeq->SequenceLength / PreviousSequenceLength; for(FFloatCurve& Curve : DestSeq->RawCurveData.FloatCurves) { Curve.FloatCurve.ScaleCurve(0.0f, ScaleFactor); } } if (ImportOptions->bDeleteExistingMorphTargetCurves) { for (int32 CurveIdx=0; CurveIdx<DestSeq->RawCurveData.FloatCurves.Num(); ++CurveIdx) { auto& Curve = DestSeq->RawCurveData.FloatCurves[CurveIdx]; if (Curve.GetCurveTypeFlag(ACF_DrivesMorphTarget)) { DestSeq->RawCurveData.FloatCurves.RemoveAt(CurveIdx, 1, false); --CurveIdx; } } DestSeq->RawCurveData.FloatCurves.Shrink(); } // // import blend shape curves // { GWarn->BeginSlowTask( LOCTEXT("BeginImportMorphTargetCurves", "Importing Morph Target Curves"), true); for ( int32 NodeIndex = 0; NodeIndex < NodeArray.Num(); NodeIndex++ ) { // consider blendshape animation curve FbxGeometry* Geometry = (FbxGeometry*)NodeArray[NodeIndex]->GetNodeAttribute(); if (Geometry) { int32 BlendShapeDeformerCount = Geometry->GetDeformerCount(FbxDeformer::eBlendShape); for(int32 BlendShapeIndex = 0; BlendShapeIndex<BlendShapeDeformerCount; ++BlendShapeIndex) { FbxBlendShape* BlendShape = (FbxBlendShape*)Geometry->GetDeformer(BlendShapeIndex, FbxDeformer::eBlendShape); const int32 BlendShapeChannelCount = BlendShape->GetBlendShapeChannelCount(); FString BlendShapeName = UTF8_TO_TCHAR(MakeName(BlendShape->GetName())); for(int32 ChannelIndex = 0; ChannelIndex<BlendShapeChannelCount; ++ChannelIndex) { FbxBlendShapeChannel* Channel = BlendShape->GetBlendShapeChannel(ChannelIndex); if(Channel) { FString ChannelName = UTF8_TO_TCHAR(MakeName(Channel->GetName())); // Maya adds the name of the blendshape and an underscore to the front of the channel name, so remove it if(ChannelName.StartsWith(BlendShapeName)) { ChannelName = ChannelName.Right(ChannelName.Len() - (BlendShapeName.Len()+1)); } FbxAnimCurve* Curve = Geometry->GetShapeChannel(BlendShapeIndex, ChannelIndex, (FbxAnimLayer*)CurAnimStack->GetMember(0)); if (Curve && Curve->KeyGetCount() > 0) { FFormatNamedArguments Args; Args.Add(TEXT("BlendShape"), FText::FromString(ChannelName)); const FText StatusUpate = FText::Format(LOCTEXT("ImportingMorphTargetCurvesDetail", "Importing Morph Target Curves [{BlendShape}]"), Args); GWarn->StatusUpdate(NodeIndex + 1, NodeArray.Num(), StatusUpate); // now see if we have one already exists. If so, just overwrite that. if not, add new one. ImportCurveToAnimSequence(DestSeq, *ChannelName, Curve, ACF_DrivesMorphTarget | ACF_TriggerEvent, AnimTimeSpan, 0.01f /** for some reason blend shape values are coming as 100 scaled **/); } } } } } } GWarn->EndSlowTask(); } // // importing custom attribute START // if (ImportOptions->bImportCustomAttribute) { GWarn->BeginSlowTask( LOCTEXT("BeginImportMorphTargetCurves", "Importing Custom Attirubte Curves"), true); const int32 TotalLinks = SortedLinks.Num(); int32 CurLinkIndex=0; for(auto Node: SortedLinks) { FbxProperty Property = Node->GetFirstProperty(); while (Property.IsValid()) { FbxAnimCurveNode* CurveNode = Property.GetCurveNode(); // do this if user defined and animated and leaf node if( CurveNode && Property.GetFlag(FbxPropertyAttr::eUserDefined) && CurveNode->IsAnimated() && IsSupportedCurveDataType(Property.GetPropertyDataType().GetType()) ) { FString CurveName = UTF8_TO_TCHAR(CurveNode->GetName()); UE_LOG(LogFbx, Log, TEXT("CurveName : %s"), *CurveName ); int32 TotalCount = CurveNode->GetChannelsCount(); for (int32 ChannelIndex=0; ChannelIndex<TotalCount; ++ChannelIndex) { FbxAnimCurve * AnimCurve = CurveNode->GetCurve(ChannelIndex); FString ChannelName = CurveNode->GetChannelName(ChannelIndex).Buffer(); if (AnimCurve) { FString FinalCurveName; if (TotalCount == 1) { FinalCurveName = CurveName; } else { FinalCurveName = CurveName + "_" + ChannelName; } FFormatNamedArguments Args; Args.Add(TEXT("CurveName"), FText::FromString(FinalCurveName)); const FText StatusUpate = FText::Format(LOCTEXT("ImportingCustomAttributeCurvesDetail", "Importing Custom Attribute [{CurveName}]"), Args); GWarn->StatusUpdate(CurLinkIndex + 1, TotalLinks, StatusUpate); ImportCurveToAnimSequence(DestSeq, FinalCurveName, AnimCurve, ACF_DefaultCurve, AnimTimeSpan); } } } Property = Node->GetNextProperty(Property); } CurLinkIndex++; } GWarn->EndSlowTask(); } // importing custom attribute END const bool bSourceDataExists = (DestSeq->SourceRawAnimationData.Num() > 0); TArray<AnimationTransformDebug::FAnimationTransformDebugData> TransformDebugData; int32 TotalNumKeys = 0; const FReferenceSkeleton& RefSkeleton = Skeleton->GetReferenceSkeleton(); // import animation { GWarn->BeginSlowTask( LOCTEXT("BeginImportAnimation", "Importing Animation"), true); TArray<struct FRawAnimSequenceTrack>& RawAnimationData = bSourceDataExists? DestSeq->SourceRawAnimationData : DestSeq->RawAnimationData; DestSeq->TrackToSkeletonMapTable.Empty(); DestSeq->AnimationTrackNames.Empty(); RawAnimationData.Empty(); TArray<FName> FbxRawBoneNames; FillAndVerifyBoneNames(Skeleton, SortedLinks, FbxRawBoneNames, FileName); UnFbx::FFbxImporter* FbxImporter = UnFbx::FFbxImporter::GetInstance(); const bool bPreserveLocalTransform = FbxImporter->GetImportOptions()->bPreserveLocalTransform; // Build additional transform matrix UFbxAnimSequenceImportData* TemplateData = Cast<UFbxAnimSequenceImportData>(DestSeq->AssetImportData); FbxAMatrix FbxAddedMatrix; BuildFbxMatrixForImportTransform(FbxAddedMatrix, TemplateData); FMatrix AddedMatrix = Converter.ConvertMatrix(FbxAddedMatrix); const int32 NumSamplingKeys = FMath::FloorToInt(AnimTimeSpan.GetDuration().GetSecondDouble() * ResampleRate); const FbxTime TimeIncrement = (NumSamplingKeys > 1)? AnimTimeSpan.GetDuration() / (NumSamplingKeys - 1) : AnimTimeSpan.GetDuration(); for(int32 SourceTrackIdx = 0; SourceTrackIdx < FbxRawBoneNames.Num(); ++SourceTrackIdx) { int32 NumKeysForTrack = 0; // see if it's found in Skeleton FName BoneName = FbxRawBoneNames[SourceTrackIdx]; int32 BoneTreeIndex = RefSkeleton.FindBoneIndex(BoneName); // update status FFormatNamedArguments Args; Args.Add(TEXT("TrackName"), FText::FromName(BoneName)); Args.Add(TEXT("TotalKey"), FText::AsNumber(NumSamplingKeys)); Args.Add(TEXT("TrackIndex"), FText::AsNumber(SourceTrackIdx+1)); Args.Add(TEXT("TotalTracks"), FText::AsNumber(FbxRawBoneNames.Num())); const FText StatusUpate = FText::Format(LOCTEXT("ImportingAnimTrackDetail", "Importing Animation Track [{TrackName}] ({TrackIndex}/{TotalTracks}) - TotalKey {TotalKey}"), Args); GWarn->StatusForceUpdate(SourceTrackIdx + 1, FbxRawBoneNames.Num(), StatusUpate); if (BoneTreeIndex!=INDEX_NONE) { bool bSuccess = true; FRawAnimSequenceTrack RawTrack; RawTrack.PosKeys.Empty(); RawTrack.RotKeys.Empty(); RawTrack.ScaleKeys.Empty(); AnimationTransformDebug::FAnimationTransformDebugData NewDebugData; FbxNode* Link = SortedLinks[SourceTrackIdx]; FbxNode * LinkParent = Link->GetParent(); for(FbxTime CurTime = AnimTimeSpan.GetStart(); CurTime <= AnimTimeSpan.GetStop(); CurTime += TimeIncrement) { // save global trasnform FbxAMatrix GlobalMatrix = Link->EvaluateGlobalTransform(CurTime); // we'd like to verify this before going to Transform. // currently transform has tons of NaN check, so it will crash there FMatrix GlobalUEMatrix = Converter.ConvertMatrix(GlobalMatrix); if (GlobalUEMatrix.ContainsNaN()) { bSuccess = false; AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_InvalidTransform", "Track {0} contains invalid transform. Could not import the track."), FText::FromName(BoneName))), FFbxErrors::Animation_TransformError); break; } FTransform GlobalTransform = Converter.ConvertTransform(GlobalMatrix); if (GlobalTransform.ContainsNaN()) { bSuccess = false; AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_InvalidUnrealTransform", "Track {0} did not yeild valid transform. Please report this to animation team."), FText::FromName(BoneName))), FFbxErrors::Animation_TransformError); break; } // debug data, including import transformation FTransform AddedTransform(AddedMatrix); NewDebugData.SourceGlobalTransform.Add(GlobalTransform * AddedTransform); FTransform LocalTransform; if( !bPreserveLocalTransform && LinkParent) { // I can't rely on LocalMatrix. I need to recalculate quaternion/scale based on global transform if Parent exists FbxAMatrix ParentGlobalMatrix = Link->GetParent()->EvaluateGlobalTransform(CurTime); FTransform ParentGlobalTransform = Converter.ConvertTransform(ParentGlobalMatrix); LocalTransform = GlobalTransform.GetRelativeTransform(ParentGlobalTransform); NewDebugData.SourceParentGlobalTransform.Add(ParentGlobalTransform); } else { FbxAMatrix& LocalMatrix = Link->EvaluateLocalTransform(CurTime); FbxVector4 NewLocalT = LocalMatrix.GetT(); FbxVector4 NewLocalS = LocalMatrix.GetS(); FbxQuaternion NewLocalQ = LocalMatrix.GetQ(); LocalTransform.SetTranslation(Converter.ConvertPos(NewLocalT)); LocalTransform.SetScale3D(Converter.ConvertScale(NewLocalS)); LocalTransform.SetRotation(Converter.ConvertRotToQuat(NewLocalQ)); NewDebugData.SourceParentGlobalTransform.Add(FTransform::Identity); } if(TemplateData && BoneTreeIndex == 0) { // If we found template data earlier, apply the import transform matrix to // the root track. LocalTransform.SetFromMatrix(LocalTransform.ToMatrixWithScale() * AddedMatrix); } if (LocalTransform.ContainsNaN()) { bSuccess = false; AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_InvalidUnrealLocalTransform", "Track {0} did not yeild valid local transform. Please report this to animation team."), FText::FromName(BoneName))), FFbxErrors::Animation_TransformError); break; } RawTrack.ScaleKeys.Add(LocalTransform.GetScale3D()); RawTrack.PosKeys.Add(LocalTransform.GetTranslation()); RawTrack.RotKeys.Add(LocalTransform.GetRotation()); NewDebugData.RecalculatedLocalTransform.Add(LocalTransform); ++NumKeysForTrack; } if (bSuccess) { //add new track int32 NewTrackIdx = RawAnimationData.Add(RawTrack); DestSeq->AnimationTrackNames.Add(BoneName); NewDebugData.SetTrackData(NewTrackIdx, BoneTreeIndex, BoneName); // add mapping to skeleton bone track DestSeq->TrackToSkeletonMapTable.Add(FTrackToSkeletonMap(BoneTreeIndex)); TransformDebugData.Add(NewDebugData); } } TotalNumKeys = FMath::Max( TotalNumKeys, NumKeysForTrack ); } DestSeq->NumFrames = TotalNumKeys; GWarn->EndSlowTask(); } // compress animation { GWarn->BeginSlowTask( LOCTEXT("BeginCompressAnimation", "Compress Animation"), true); GWarn->StatusForceUpdate(1, 1, LOCTEXT("CompressAnimation", "Compressing Animation")); // if source data exists, you should bake it to Raw to apply if(bSourceDataExists) { DestSeq->BakeTrackCurvesToRawAnimation(); } else { // otherwise just compress DestSeq->PostProcessSequence(); } // run debug mode AnimationTransformDebug::OutputAnimationTransformDebugData(TransformDebugData, TotalNumKeys, RefSkeleton); GWarn->EndSlowTask(); } return true; }
void UAnimBone::SetFromTransform(const FTransform& Transform) { Orientation = FRotator(Transform.GetRotation()); Position = Transform.GetTranslation(); Scale = Transform.GetScale3D(); }
void UBodySetup::AddConvexElemsToRigidActor(PxRigidActor* PDestActor, const FTransform& RelativeTM, const FVector& Scale3D, const FVector& Scale3DAbs, TArray<PxShape*>* NewShapes) const { float ContactOffsetFactor, MaxContactOffset; GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset); PxMaterial* PDefaultMat = GetDefaultPhysMaterial(); for (int32 i = 0; i < AggGeom.ConvexElems.Num(); i++) { const FKConvexElem& ConvexElem = AggGeom.ConvexElems[i]; if (ConvexElem.ConvexMesh) { PxTransform PLocalPose; bool bUseNegX = CalcMeshNegScaleCompensation(Scale3D, PLocalPose); PxConvexMeshGeometry PConvexGeom; PConvexGeom.convexMesh = bUseNegX ? ConvexElem.ConvexMeshNegX : ConvexElem.ConvexMesh; PConvexGeom.scale.scale = U2PVector(Scale3DAbs * ConvexElem.GetTransform().GetScale3D().GetAbs()); FTransform ConvexTransform = ConvexElem.GetTransform(); if (ConvexTransform.GetScale3D().X < 0 || ConvexTransform.GetScale3D().Y < 0 || ConvexTransform.GetScale3D().Z < 0) { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has negative scale. Not currently supported"), *GetPathNameSafe(GetOuter()), i); } if (ConvexTransform.IsValid()) { PxTransform PElementTransform = U2PTransform(ConvexTransform * RelativeTM); PLocalPose.q *= PElementTransform.q; PLocalPose.p = PElementTransform.p; PLocalPose.p.x *= Scale3D.X; PLocalPose.p.y *= Scale3D.Y; PLocalPose.p.z *= Scale3D.Z; if (PConvexGeom.isValid()) { PxVec3 PBoundsExtents = PConvexGeom.convexMesh->getLocalBounds().getExtents(); ensure(PLocalPose.isValid()); PxShape* NewShape = PDestActor->createShape(PConvexGeom, *PDefaultMat, PLocalPose); if (NewShapes) { NewShapes->Add(NewShape); } const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PBoundsExtents.minElement()); NewShape->setContactOffset(ContactOffset); } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] invalid"), *GetPathNameSafe(GetOuter()), i); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has invalid transform"), *GetPathNameSafe(GetOuter()), i); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: ConvexElem is missing ConvexMesh (%d: %s)"), i, *GetPathName()); } } }
void FKConvexElem::ScaleElem(FVector DeltaSize, float MinSize) { FTransform Transform = GetTransform(); Transform.SetScale3D(Transform.GetScale3D() + DeltaSize); SetTransform(Transform); }
void UPhATEdSkeletalMeshComponent::RenderAssetTools(const FSceneView* View, class FPrimitiveDrawInterface* PDI, bool bHitTest) { check(SharedData); UPhysicsAsset* const PhysicsAsset = GetPhysicsAsset(); check(PhysicsAsset); bool bHitTestAndBodyMode = bHitTest && SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit; bool bHitTestAndConstraintMode = bHitTest && SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit; FPhATSharedData::EPhATRenderMode CollisionViewMode = SharedData->GetCurrentCollisionViewMode(); #if DEBUG_CLICK_VIEWPORT PDI->DrawLine(SharedData->LastClickOrigin, SharedData->LastClickOrigin + SharedData->LastClickDirection * 5000.0f, FLinearColor(1, 1, 0, 1), SDPG_Foreground); PDI->DrawPoint(SharedData->LastClickOrigin, FLinearColor(1, 0, 0), 5, SDPG_Foreground); #endif // Draw bodies for (int32 i = 0; i <PhysicsAsset->BodySetup.Num(); ++i) { int32 BoneIndex = GetBoneIndex(PhysicsAsset->BodySetup[i]->BoneName); // If we found a bone for it, draw the collision. // The logic is as follows; always render in the ViewMode requested when not in hit mode - but if we are in hit mode and the right editing mode, render as solid if (BoneIndex != INDEX_NONE) { FTransform BoneTM = GetBoneTransform(BoneIndex); float Scale = BoneTM.GetScale3D().GetAbsMax(); FVector VectorScale(Scale); BoneTM.RemoveScaling(); FKAggregateGeom* AggGeom = &PhysicsAsset->BodySetup[i]->AggGeom; for (int32 j = 0; j <AggGeom->SphereElems.Num(); ++j) { if (bHitTest) { PDI->SetHitProxy(new HPhATEdBoneProxy(i, KPT_Sphere, j)); } FTransform ElemTM = GetPrimitiveTransform(BoneTM, i, KPT_Sphere, j, Scale); //solids are drawn if it's the ViewMode and we're not doing a hit, or if it's hitAndBodyMode if( (CollisionViewMode == FPhATSharedData::PRM_Solid && !bHitTest) || bHitTestAndBodyMode) { UMaterialInterface* PrimMaterial = GetPrimitiveMaterial(i, KPT_Sphere, j, bHitTestAndBodyMode); AggGeom->SphereElems[j].DrawElemSolid(PDI, ElemTM, VectorScale, PrimMaterial->GetRenderProxy(0)); } //wires are never used during hit if(!bHitTest) { if (CollisionViewMode == FPhATSharedData::PRM_Solid || CollisionViewMode == FPhATSharedData::PRM_Wireframe) { AggGeom->SphereElems[j].DrawElemWire(PDI, ElemTM, VectorScale, GetPrimitiveColor(i, KPT_Sphere, j)); } } if (bHitTest) { PDI->SetHitProxy(NULL); } } for (int32 j = 0; j <AggGeom->BoxElems.Num(); ++j) { if (bHitTest) { PDI->SetHitProxy(new HPhATEdBoneProxy(i, KPT_Box, j)); } FTransform ElemTM = GetPrimitiveTransform(BoneTM, i, KPT_Box, j, Scale); if ( (CollisionViewMode == FPhATSharedData::PRM_Solid && !bHitTest) || bHitTestAndBodyMode) { UMaterialInterface* PrimMaterial = GetPrimitiveMaterial(i, KPT_Box, j, bHitTestAndBodyMode); AggGeom->BoxElems[j].DrawElemSolid(PDI, ElemTM, VectorScale, PrimMaterial->GetRenderProxy(0)); } if(!bHitTest) { if (CollisionViewMode == FPhATSharedData::PRM_Solid || CollisionViewMode == FPhATSharedData::PRM_Wireframe) { AggGeom->BoxElems[j].DrawElemWire(PDI, ElemTM, VectorScale, GetPrimitiveColor(i, KPT_Box, j)); } } if (bHitTest) { PDI->SetHitProxy(NULL); } } for (int32 j = 0; j <AggGeom->SphylElems.Num(); ++j) { if (bHitTest) { PDI->SetHitProxy(new HPhATEdBoneProxy(i, KPT_Sphyl, j)); } FTransform ElemTM = GetPrimitiveTransform(BoneTM, i, KPT_Sphyl, j, Scale); if ( (CollisionViewMode == FPhATSharedData::PRM_Solid && !bHitTest) || bHitTestAndBodyMode) { UMaterialInterface* PrimMaterial = GetPrimitiveMaterial(i, KPT_Sphyl, j, bHitTestAndBodyMode); AggGeom->SphylElems[j].DrawElemSolid(PDI, ElemTM, VectorScale, PrimMaterial->GetRenderProxy(0)); } if(!bHitTest) { if (CollisionViewMode == FPhATSharedData::PRM_Solid || CollisionViewMode == FPhATSharedData::PRM_Wireframe) { AggGeom->SphylElems[j].DrawElemWire(PDI, ElemTM, VectorScale, GetPrimitiveColor(i, KPT_Sphyl, j)); } } if (bHitTest) { PDI->SetHitProxy(NULL); } } for (int32 j = 0; j <AggGeom->ConvexElems.Num(); ++j) { if (bHitTest) { PDI->SetHitProxy(new HPhATEdBoneProxy(i, KPT_Convex, j)); } FTransform ElemTM = GetPrimitiveTransform(BoneTM, i, KPT_Convex, j, Scale); //convex doesn't have solid draw so render lines if we're in hitTestAndBodyMode if(!bHitTest || bHitTestAndBodyMode) { if (CollisionViewMode == FPhATSharedData::PRM_Solid || CollisionViewMode == FPhATSharedData::PRM_Wireframe) { AggGeom->ConvexElems[j].DrawElemWire(PDI, ElemTM, Scale, GetPrimitiveColor(i, KPT_Convex, j)); } } if (bHitTest) { PDI->SetHitProxy(NULL); } } if (!bHitTest && SharedData->bShowCOM && Bodies.IsValidIndex(i)) { Bodies[i]->DrawCOMPosition(PDI, COMRenderSize, SharedData->COMRenderColor); } } } // Draw Constraints FPhATSharedData::EPhATConstraintViewMode ConstraintViewMode = SharedData->GetCurrentConstraintViewMode(); if (ConstraintViewMode != FPhATSharedData::PCV_None) { for (int32 i = 0; i <PhysicsAsset->ConstraintSetup.Num(); ++i) { int32 BoneIndex1 = GetBoneIndex(PhysicsAsset->ConstraintSetup[i]->DefaultInstance.ConstraintBone1); int32 BoneIndex2 = GetBoneIndex(PhysicsAsset->ConstraintSetup[i]->DefaultInstance.ConstraintBone2); // if bone doesn't exist, do not draw it. It crashes in random points when we try to manipulate. if (BoneIndex1 != INDEX_NONE && BoneIndex2 != INDEX_NONE) { if (bHitTest) { PDI->SetHitProxy(new HPhATEdConstraintProxy(i)); } if(bHitTestAndConstraintMode || !bHitTest) { DrawConstraint(i, View, PDI, SharedData->EditorSimOptions->bShowConstraintsAsPoints); } if (bHitTest) { PDI->SetHitProxy(NULL); } } } } if (!bHitTest && SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit && SharedData->bShowInfluences) { DrawCurrentInfluences(PDI); } // If desired, draw bone hierarchy. if (!bHitTest && SharedData->bShowHierarchy) { DrawHierarchy(PDI, false); } // If desired, draw animation skeleton. if (!bHitTest && SharedData->bShowAnimSkel) { DrawHierarchy(PDI, SharedData->bRunningSimulation); } }
void USkeletalMeshComponent::UpdateKinematicBonesToAnim(const TArray<FTransform>& InSpaceBases, ETeleportType Teleport, bool bNeedsSkinning) { SCOPE_CYCLE_COUNTER(STAT_UpdateRBBones); // This below code produces some interesting result here // - below codes update physics data, so if you don't update pose, the physics won't have the right result // - but if we just update physics bone without update current pose, it will have stale data // If desired, pass the animation data to the physics joints so they can be used by motors. // See if we are going to need to update kinematics const bool bUpdateKinematics = (KinematicBonesUpdateType != EKinematicBonesUpdateToPhysics::SkipAllBones); const bool bTeleport = Teleport == ETeleportType::TeleportPhysics; // If desired, update physics bodies associated with skeletal mesh component to match. if(!bUpdateKinematics && !(bTeleport && IsAnySimulatingPhysics())) { // nothing to do return; } // Get the scene, and do nothing if we can't get one. FPhysScene* PhysScene = nullptr; if (GetWorld() != nullptr) { PhysScene = GetWorld()->GetPhysicsScene(); } if(PhysScene == nullptr) { return; } const FTransform& CurrentLocalToWorld = ComponentToWorld; // Gracefully handle NaN if(CurrentLocalToWorld.ContainsNaN()) { return; } // If desired, draw the skeleton at the point where we pass it to the physics. if (bShowPrePhysBones && SkeletalMesh && InSpaceBases.Num() == SkeletalMesh->RefSkeleton.GetNum()) { for (int32 i = 1; i<InSpaceBases.Num(); i++) { FVector ThisPos = CurrentLocalToWorld.TransformPosition(InSpaceBases[i].GetLocation()); int32 ParentIndex = SkeletalMesh->RefSkeleton.GetParentIndex(i); FVector ParentPos = CurrentLocalToWorld.TransformPosition(InSpaceBases[ParentIndex].GetLocation()); GetWorld()->LineBatcher->DrawLine(ThisPos, ParentPos, AnimSkelDrawColor, SDPG_Foreground); } } // warn if it has non-uniform scale const FVector& MeshScale3D = CurrentLocalToWorld.GetScale3D(); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if( !MeshScale3D.IsUniform() ) { UE_LOG(LogPhysics, Log, TEXT("USkeletalMeshComponent::UpdateKinematicBonesToAnim : Non-uniform scale factor (%s) can cause physics to mismatch for %s SkelMesh: %s"), *MeshScale3D.ToString(), *GetFullName(), SkeletalMesh ? *SkeletalMesh->GetFullName() : TEXT("NULL")); } #endif if (bEnablePerPolyCollision == false) { const UPhysicsAsset* const PhysicsAsset = GetPhysicsAsset(); if (PhysicsAsset && SkeletalMesh && Bodies.Num() > 0) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (!ensure(PhysicsAsset->BodySetup.Num() == Bodies.Num())) { // related to TTP 280315 UE_LOG(LogPhysics, Warning, TEXT("Mesh (%s) has PhysicsAsset(%s), and BodySetup(%d) and Bodies(%d) don't match"), *SkeletalMesh->GetName(), *PhysicsAsset->GetName(), PhysicsAsset->BodySetup.Num(), Bodies.Num()); return; } #endif #if WITH_PHYSX // Lock the scenes we need (flags set in InitArticulated) if(bHasBodiesInSyncScene) { SCENE_LOCK_WRITE(PhysScene->GetPhysXScene(PST_Sync)) } if (bHasBodiesInAsyncScene) { SCENE_LOCK_WRITE(PhysScene->GetPhysXScene(PST_Async)) } #endif // Iterate over each body for (int32 i = 0; i < Bodies.Num(); i++) { // If we have a physics body, and its kinematic... FBodyInstance* BodyInst = Bodies[i]; check(BodyInst); if (bTeleport || (BodyInst->IsValidBodyInstance() && !BodyInst->IsInstanceSimulatingPhysics())) { const int32 BoneIndex = BodyInst->InstanceBoneIndex; // If we could not find it - warn. if (BoneIndex == INDEX_NONE || BoneIndex >= GetNumSpaceBases()) { const FName BodyName = PhysicsAsset->BodySetup[i]->BoneName; UE_LOG(LogPhysics, Log, TEXT("UpdateRBBones: WARNING: Failed to find bone '%s' need by PhysicsAsset '%s' in SkeletalMesh '%s'."), *BodyName.ToString(), *PhysicsAsset->GetName(), *SkeletalMesh->GetName()); } else { #if WITH_PHYSX // update bone transform to world const FTransform BoneTransform = InSpaceBases[BoneIndex] * CurrentLocalToWorld; if(BoneTransform.ContainsNaN()) { const FName BodyName = PhysicsAsset->BodySetup[i]->BoneName; UE_LOG(LogPhysics, Warning, TEXT("UpdateKinematicBonesToAnim: Trying to set transform with bad data %s on PhysicsAsset '%s' in SkeletalMesh '%s' for bone '%s'"), *BoneTransform.ToHumanReadableString(), *PhysicsAsset->GetName(), *SkeletalMesh->GetName(), *BodyName.ToString()); continue; } // If kinematic and not teleporting, set kinematic target PxRigidDynamic* PRigidDynamic = BodyInst->GetPxRigidDynamic_AssumesLocked(); if (!IsRigidBodyNonKinematic_AssumesLocked(PRigidDynamic) && !bTeleport) { PhysScene->SetKinematicTarget_AssumesLocked(BodyInst, BoneTransform, true); } // Otherwise, set global pose else { const PxTransform PNewPose = U2PTransform(BoneTransform); ensure(PNewPose.isValid()); PRigidDynamic->setGlobalPose(PNewPose); } #endif // now update scale // if uniform, we'll use BoneTranform if (MeshScale3D.IsUniform()) { // @todo UE4 should we update scale when it's simulated? BodyInst->UpdateBodyScale(BoneTransform.GetScale3D()); } else { // @note When you have non-uniform scale on mesh base, // hierarchical bone transform can update scale too often causing performance issue // So we just use mesh scale for all bodies when non-uniform // This means physics representation won't be accurate, but // it is performance friendly by preventing too frequent physics update BodyInst->UpdateBodyScale(MeshScale3D); } } } else { //make sure you have physics weight or blendphysics on, otherwise, you'll have inconsistent representation of bodies // @todo make this to be kismet log? But can be too intrusive if (!bBlendPhysics && BodyInst->PhysicsBlendWeight <= 0.f && BodyInst->BodySetup.IsValid()) { UE_LOG(LogPhysics, Warning, TEXT("%s(Mesh %s, PhysicsAsset %s, Bone %s) is simulating, but no blending. "), *GetName(), *GetNameSafe(SkeletalMesh), *GetNameSafe(PhysicsAsset), *BodyInst->BodySetup.Get()->BoneName.ToString()); } } } #if WITH_PHYSX // Unlock the scenes if (bHasBodiesInSyncScene) { SCENE_UNLOCK_WRITE(PhysScene->GetPhysXScene(PST_Sync)) } if (bHasBodiesInAsyncScene) { SCENE_UNLOCK_WRITE(PhysScene->GetPhysXScene(PST_Async)) } #endif } } else { //per poly update requires us to update all vertex positions if (MeshObject)
bool FEdMode::InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) { if(UsesPropertyWidgets()) { AActor* SelectedActor = GetFirstSelectedActorInstance(); if(SelectedActor != NULL && InViewportClient->GetCurrentWidgetAxis() != EAxisList::None) { GEditor->NoteActorMovement(); if (EditedPropertyName != TEXT("")) { FTransform LocalTM = FTransform::Identity; if(bEditedPropertyIsTransform) { LocalTM = GetPropertyValueByName<FTransform>(SelectedActor, EditedPropertyName, EditedPropertyIndex); } else { FVector LocalPos = GetPropertyValueByName<FVector>(SelectedActor, EditedPropertyName, EditedPropertyIndex); LocalTM = FTransform(LocalPos); } // Get actor transform (actor to world) FTransform ActorTM = SelectedActor->ActorToWorld(); // Calculate world transform FTransform WorldTM = LocalTM * ActorTM; // Calc delta specified by drag //FTransform DeltaTM(InRot.Quaternion(), InDrag); // Apply delta in world space WorldTM.SetTranslation(WorldTM.GetTranslation() + InDrag); WorldTM.SetRotation(InRot.Quaternion() * WorldTM.GetRotation()); // Convert new world transform back into local space LocalTM = WorldTM.GetRelativeTransform(ActorTM); // Apply delta scale LocalTM.SetScale3D(LocalTM.GetScale3D() + InScale); SelectedActor->PreEditChange(NULL); if(bEditedPropertyIsTransform) { SetPropertyValueByName<FTransform>(SelectedActor, EditedPropertyName, EditedPropertyIndex, LocalTM); } else { SetPropertyValueByName<FVector>(SelectedActor, EditedPropertyName, EditedPropertyIndex, LocalTM.GetLocation()); } SelectedActor->PostEditChange(); return true; } } } if( GetCurrentTool() ) { return GetCurrentTool()->InputDelta(InViewportClient,InViewport,InDrag,InRot,InScale); } return 0; }
FString UKismetStringLibrary::Conv_TransformToString(const FTransform& InTrans) { return FString::Printf(TEXT("Translation: %s Rotation: %s Scale %s"), *InTrans.GetTranslation().ToString(), *InTrans.Rotator().ToString(), *InTrans.GetScale3D().ToString()); }
UObject* USpriterImporterFactory::FactoryCreateText(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, const TCHAR* Type, const TCHAR*& Buffer, const TCHAR* BufferEnd, FFeedbackContext* Warn) { Flags |= RF_Transactional; FEditorDelegates::OnAssetPreImport.Broadcast(this, InClass, InParent, InName, Type); FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools"); bool bLoadedSuccessfully = true; const FString CurrentFilename = UFactory::GetCurrentFilename(); FString CurrentSourcePath; FString FilenameNoExtension; FString UnusedExtension; FPaths::Split(CurrentFilename, CurrentSourcePath, FilenameNoExtension, UnusedExtension); const FString LongPackagePath = FPackageName::GetLongPackagePath(InParent->GetOutermost()->GetPathName()); const FString NameForErrors(InName.ToString()); const FString FileContent(BufferEnd - Buffer, Buffer); TSharedPtr<FJsonObject> DescriptorObject = ParseJSON(FileContent, NameForErrors); UPaperSpriterImportData* Result = nullptr; // Parse the file FSpriterSCON DataModel; if (DescriptorObject.IsValid()) { DataModel.ParseFromJSON(DescriptorObject, NameForErrors, /*bSilent=*/ false, /*bPreParseOnly=*/ false); } // Create the new 'hub' asset and convert the data model over if (DataModel.IsValid()) { const bool bSilent = false; Result = NewObject<UPaperSpriterImportData>(InParent, InName, Flags); Result->Modify(); //@TODO: Do some things here maybe? Result->ImportedData = DataModel; // Import the assets in the folders for (const FSpriterFolder& Folder : DataModel.Folders) { for (const FSpriterFile& File : Folder.Files) { const FString RelativeFilename = File.Name.Replace(TEXT("\\"), TEXT("/"), ESearchCase::CaseSensitive); const FString SourceSpriterFilePath = FPaths::Combine(*CurrentSourcePath, *RelativeFilename); FString RelativeDestPath; FString JustFilename; FString JustExtension; FPaths::Split(RelativeFilename, /*out*/ RelativeDestPath, /*out*/ JustFilename, /*out*/ JustExtension); if (File.FileType == ESpriterFileType::Sprite) { const FString TargetTexturePath = LongPackagePath / TEXT("Textures") / RelativeDestPath; const FString TargetSpritePath = LongPackagePath / TEXT("Sprites") / RelativeDestPath; // Import the texture UTexture2D* ImportedTexture = ImportTexture(SourceSpriterFilePath, TargetTexturePath); if (ImportTexture == nullptr) { SPRITER_IMPORT_ERROR(TEXT("Failed to import texture '%s' while importing '%s'"), *SourceSpriterFilePath, *CurrentFilename); } // Create a sprite from it UPaperSprite* ImportedSprite = CastChecked<UPaperSprite>(CreateNewAsset(UPaperSprite::StaticClass(), TargetSpritePath, JustFilename, Flags)); const ESpritePivotMode::Type PivotMode = ConvertNormalizedPivotPointToPivotMode(File.PivotX, File.PivotY); const double PivotInPixelsX = File.Width * File.PivotX; const double PivotInPixelsY = File.Height * File.PivotY; ImportedSprite->SetPivotMode(PivotMode, FVector2D((float)PivotInPixelsX, (float)PivotInPixelsY)); FSpriteAssetInitParameters SpriteInitParams; SpriteInitParams.SetTextureAndFill(ImportedTexture); GetDefault<UPaperImporterSettings>()->ApplySettingsForSpriteInit(SpriteInitParams); SpriteInitParams.SetPixelsPerUnrealUnit(1.0f); ImportedSprite->InitializeSprite(SpriteInitParams); } else if (File.FileType == ESpriterFileType::Sound) { // Import the sound const FString TargetAssetPath = LongPackagePath / RelativeDestPath; UObject* ImportedSound = ImportAsset(SourceSpriterFilePath, TargetAssetPath); } else if (File.FileType != ESpriterFileType::INVALID) { ensureMsgf(false, TEXT("Importer was not updated when a new entry was added to ESpriterFileType")); } // TMap<FString, class UTexture2D*> ImportedTextures; // TMap<FString, class UPaperSprite> ImportedSprites; } } for (const FSpriterEntity& Entity : DataModel.Entities) { // Extract the common/shared skeleton FBoneHierarchyBuilder HierarchyBuilder; HierarchyBuilder.ProcessHierarchy(Entity); // Create the skeletal mesh const FString TargetMeshName = Entity.Name + TEXT("_SkelMesh"); const FString TargetMeshPath = LongPackagePath; USkeletalMesh* SkeletalMesh = CastChecked<USkeletalMesh>(CreateNewAsset(USkeletalMesh::StaticClass(), TargetMeshPath, TargetMeshName, Flags)); // Create the skeleton const FString TargetSkeletonName = Entity.Name + TEXT("_Skeleton"); const FString TargetSkeletonPath = LongPackagePath; USkeleton* EntitySkeleton = CastChecked<USkeleton>(CreateNewAsset(USkeleton::StaticClass(), TargetSkeletonPath, TargetSkeletonName, Flags)); // Initialize the mesh asset FSkeletalMeshResource* ImportedResource = SkeletalMesh->GetImportedResource(); check(ImportedResource->LODModels.Num() == 0); ImportedResource->LODModels.Empty(); FStaticLODModel& LODModel = *new (ImportedResource->LODModels) FStaticLODModel(); SkeletalMesh->LODInfo.Empty(); SkeletalMesh->LODInfo.AddZeroed(); SkeletalMesh->LODInfo[0].LODHysteresis = 0.02f; FSkeletalMeshOptimizationSettings Settings; // set default reduction settings values SkeletalMesh->LODInfo[0].ReductionSettings = Settings; // Create initial bounding box based on expanded version of reference pose for meshes without physics assets. Can be overridden by artist. // FBox BoundingBox(SkelMeshImportDataPtr->Points.GetData(), SkelMeshImportDataPtr->Points.Num()); // FBox Temp = BoundingBox; // FVector MidMesh = 0.5f*(Temp.Min + Temp.Max); // BoundingBox.Min = Temp.Min + 1.0f*(Temp.Min - MidMesh); // BoundingBox.Max = Temp.Max + 1.0f*(Temp.Max - MidMesh); // // Tuck up the bottom as this rarely extends lower than a reference pose's (e.g. having its feet on the floor). // // Maya has Y in the vertical, other packages have Z. // //BEN const int32 CoordToTuck = bAssumeMayaCoordinates ? 1 : 2; // //BEN BoundingBox.Min[CoordToTuck] = Temp.Min[CoordToTuck] + 0.1f*(Temp.Min[CoordToTuck] - MidMesh[CoordToTuck]); // BoundingBox.Min[2] = Temp.Min[2] + 0.1f*(Temp.Min[2] - MidMesh[2]); // SkeletalMesh->Bounds = FBoxSphereBounds(BoundingBox); // Store whether or not this mesh has vertex colors // SkeletalMesh->bHasVertexColors = SkelMeshImportDataPtr->bHasVertexColors; // Pass the number of texture coordinate sets to the LODModel. Ensure there is at least one UV coord LODModel.NumTexCoords = 1;// FMath::Max<uint32>(1, SkelMeshImportDataPtr->NumTexCoords); // Create the reference skeleton and update LOD0 FReferenceSkeleton& RefSkeleton = SkeletalMesh->RefSkeleton; HierarchyBuilder.CopyToRefSkeleton(RefSkeleton); SkeletalMesh->CalculateRequiredBones(LODModel, RefSkeleton, /*BonesToRemove=*/ nullptr); SkeletalMesh->CalculateInvRefMatrices(); // Initialize the skeleton asset EntitySkeleton->MergeAllBonesToBoneTree(SkeletalMesh); // Point the mesh and skeleton at each other SkeletalMesh->Skeleton = EntitySkeleton; EntitySkeleton->SetPreviewMesh(SkeletalMesh); // Create the animations for (const FSpriterAnimation& Animation : Entity.Animations) { //@TODO: That thing I said... const FString TargetAnimationName = Animation.Name; const FString TargetAnimationPath = LongPackagePath / TEXT("Animations"); UAnimSequence* AnimationAsset = CastChecked<UAnimSequence>(CreateNewAsset(UAnimSequence::StaticClass(), TargetAnimationPath, TargetAnimationName, Flags)); AnimationAsset->SetSkeleton(EntitySkeleton); // if you have one pose(thus 0.f duration), it still contains animation, so we'll need to consider that as MINIMUM_ANIMATION_LENGTH time length const float DurationInSeconds = Animation.LengthInMS * 0.001f; AnimationAsset->SequenceLength = FMath::Max<float>(DurationInSeconds, MINIMUM_ANIMATION_LENGTH); const bool bSourceDataExists = (AnimationAsset->SourceRawAnimationData.Num() > 0); TArray<struct FRawAnimSequenceTrack>& RawAnimationData = bSourceDataExists ? AnimationAsset->SourceRawAnimationData : AnimationAsset->RawAnimationData; int32 TotalNumKeys = 0; for (const FSpriterTimeline& Timeline : Animation.Timelines) { if (Timeline.ObjectType != ESpriterObjectType::Bone) { continue; } const FName BoneName = Entity.Objects[Timeline.ObjectIndex].ObjectName; const int32 RefBoneIndex = EntitySkeleton->GetReferenceSkeleton().FindBoneIndex(BoneName); check(RefBoneIndex != INDEX_NONE); FRawAnimSequenceTrack RawTrack; RawTrack.PosKeys.Empty(); RawTrack.RotKeys.Empty(); RawTrack.ScaleKeys.Empty(); int32 NumKeysForTrack = 0; //@TODO: Quick and dirty resampling code that needs to be replaced (totally ignores curve type, edge cases, etc...) const float ResampleFPS = 30.0f; int32 DesiredNumKeys = FMath::CeilToInt(ResampleFPS * DurationInSeconds); const float TimePerKey = 1.0f / ResampleFPS; float CurrentSampleTime = 0.0f; for (int32 FrameIndex = 0; FrameIndex < DesiredNumKeys; ++FrameIndex) { int32 LowerKeyIndex = 0; for (; LowerKeyIndex < Timeline.Keys.Num(); ++LowerKeyIndex) { if (Timeline.Keys[LowerKeyIndex].TimeInMS * 0.001f > CurrentSampleTime) { --LowerKeyIndex; break; } } if (LowerKeyIndex >= Timeline.Keys.Num()) { LowerKeyIndex = Timeline.Keys.Num() - 1; } int32 UpperKeyIndex = LowerKeyIndex + 1; float UpperKeyTime = 0.0f; if (UpperKeyIndex >= Timeline.Keys.Num()) { UpperKeyTime = DurationInSeconds; if (Animation.bIsLooping) { UpperKeyIndex = 0; } else { UpperKeyIndex = Timeline.Keys.Num() - 1; } } else { UpperKeyTime = Timeline.Keys[UpperKeyIndex].TimeInMS * 0.001f; } const FSpriterFatTimelineKey& TimelineKey0 = Timeline.Keys[LowerKeyIndex]; const FSpriterFatTimelineKey& TimelineKey1 = Timeline.Keys[UpperKeyIndex]; const float LowerKeyTime = TimelineKey0.TimeInMS * 0.001f; const FTransform LocalTransform0 = TimelineKey0.Info.ConvertToTransform(); const FTransform LocalTransform1 = TimelineKey1.Info.ConvertToTransform(); FTransform LocalTransform = LocalTransform0; if (LowerKeyIndex != UpperKeyIndex) { const float Alpha = (CurrentSampleTime - LowerKeyTime) / (UpperKeyTime - LowerKeyTime); LocalTransform.Blend(LocalTransform0, LocalTransform1, Alpha); } RawTrack.ScaleKeys.Add(LocalTransform.GetScale3D()); RawTrack.PosKeys.Add(LocalTransform.GetTranslation()); RawTrack.RotKeys.Add(LocalTransform.GetRotation()); ++NumKeysForTrack; CurrentSampleTime += TimePerKey; } // // for (const FSpriterFatTimelineKey& TimelineKey : Timeline.Keys) // { // //@TODO: Ignoring TimeInMS // const FTransform LocalTransform = TimelineKey.Info.ConvertToTransform(); // // RawTrack.ScaleKeys.Add(LocalTransform.GetScale3D()); // RawTrack.PosKeys.Add(LocalTransform.GetTranslation()); // RawTrack.RotKeys.Add(LocalTransform.GetRotation()); // // ++NumKeysForTrack; // } // RawAnimationData.Add(RawTrack); AnimationAsset->AnimationTrackNames.Add(BoneName); // add mapping to skeleton bone track AnimationAsset->TrackToSkeletonMapTable.Add(FTrackToSkeletonMap(RefBoneIndex)); TotalNumKeys = FMath::Max(TotalNumKeys, NumKeysForTrack); } AnimationAsset->NumFrames = TotalNumKeys; AnimationAsset->MarkRawDataAsModified(); // compress animation { GWarn->BeginSlowTask(LOCTEXT("BeginCompressAnimation", "Compress Animation"), true); GWarn->StatusForceUpdate(1, 1, LOCTEXT("CompressAnimation", "Compressing Animation")); // if source data exists, you should bake it to Raw to apply if (bSourceDataExists) { AnimationAsset->BakeTrackCurvesToRawAnimation(); } else { // otherwise just compress AnimationAsset->PostProcessSequence(); } // run debug mode GWarn->EndSlowTask(); } // NewAnimation = FFbxImporter->ImportAnimations(Skeleton, Outer, SortedLinks, AnimName, TemplateImportData, FBXMeshNodeArray); // // if (NewAnimation) // { // // since to know full path, reimport will need to do same // UFbxAnimSequenceImportData* ImportData = UFbxAnimSequenceImportData::GetImportDataForAnimSequence(NewAnimation, TemplateImportData); // ImportData->SourceFilePath = FReimportManager::SanitizeImportFilename(UFactory::CurrentFilename, NewAnimation); // ImportData->SourceFileTimestamp = IFileManager::Get().GetTimeStamp(*UFactory::CurrentFilename).ToString(); // } } } Result->PostEditChange(); } else { // Failed to parse the JSON bLoadedSuccessfully = false; } if (Result != nullptr) { //@TODO: Need to do this // Store the current file path and timestamp for re-import purposes // UAssetImportData* ImportData = UTileMapAssetImportData::GetImportDataForTileMap(Result); // ImportData->SourceFilePath = FReimportManager::SanitizeImportFilename(CurrentFilename, Result); // ImportData->SourceFileTimestamp = IFileManager::Get().GetTimeStamp(*CurrentFilename).ToString(); } FEditorDelegates::OnAssetPostImport.Broadcast(this, Result); return Result; }
FBoxSphereBounds UBillboardComponent::CalcBounds(const FTransform& LocalToWorld) const { const float NewScale = LocalToWorld.GetScale3D().GetMax() * (Sprite ? (float)FMath::Max(Sprite->GetSizeX(),Sprite->GetSizeY()) : 1.0f); return FBoxSphereBounds(LocalToWorld.GetLocation(),FVector(NewScale,NewScale,NewScale),FMath::Sqrt(3.0f * FMath::Square(NewScale))); }
void FAnimNode_CopyBoneDelta::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { if(!bCopyTranslation && !bCopyRotation && !bCopyScale) { return; } const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer(); FCompactPoseBoneIndex SourceBoneIndex = SourceBone.GetCompactPoseIndex(BoneContainer); FCompactPoseBoneIndex TargetBoneIndex = TargetBone.GetCompactPoseIndex(BoneContainer); FTransform SourceTM = MeshBases.GetComponentSpaceTransform(SourceBoneIndex); FTransform TargetTM = MeshBases.GetComponentSpaceTransform(TargetBoneIndex); // Convert to parent space FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, SourceTM, SourceBoneIndex, BCS_ParentBoneSpace); FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, TargetTM, TargetBoneIndex, BCS_ParentBoneSpace); // Ref pose transform FTransform RefLSTransform = SkelComp->SkeletalMesh->RefSkeleton.GetRefBonePose()[SourceBone.GetMeshPoseIndex().GetInt()]; // Get transform relative to ref pose SourceTM.SetToRelativeTransform(RefLSTransform); if(CopyMode == CopyBoneDeltaMode::Accumulate) { if(bCopyTranslation) { TargetTM.AddToTranslation(SourceTM.GetTranslation() * TranslationMultiplier); } if(bCopyRotation) { FVector Axis; float Angle; SourceTM.GetRotation().ToAxisAndAngle(Axis, Angle); TargetTM.SetRotation(FQuat(Axis, Angle * RotationMultiplier) * TargetTM.GetRotation()); } if(bCopyScale) { TargetTM.SetScale3D(TargetTM.GetScale3D() * (SourceTM.GetScale3D() * ScaleMultiplier)); } } else //CopyMode = CopyBoneDeltaMode::Copy { if(bCopyTranslation) { TargetTM.SetTranslation(SourceTM.GetTranslation() * TranslationMultiplier); } if(bCopyRotation) { FVector Axis; float Angle; SourceTM.GetRotation().ToAxisAndAngle(Axis, Angle); TargetTM.SetRotation(FQuat(Axis, Angle * RotationMultiplier)); } if(bCopyScale) { TargetTM.SetScale3D(SourceTM.GetScale3D() * ScaleMultiplier); } } // Back out to component space FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, TargetTM, TargetBoneIndex, BCS_ParentBoneSpace); OutBoneTransforms.Add(FBoneTransform(TargetBoneIndex, TargetTM)); }
bool FPhATEdPreviewViewportClient::InputWidgetDelta( FViewport* InViewport, EAxisList::Type CurrentAxis, FVector& Drag, FRotator& Rot, FVector& Scale ) { bool bHandled = false; TArray<FPhATSharedData::FSelection> & SelectedObjects = SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit ? SharedData->SelectedBodies : SharedData->SelectedConstraints; for(int32 i=0; i<SelectedObjects.Num(); ++i) { FPhATSharedData::FSelection & SelectedObject = SelectedObjects[i]; if( SharedData->bManipulating ) { float BoneScale = 1.f; if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit) /// BODY EDITING /// { int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(SharedData->PhysicsAsset->SkeletalBodySetups[SelectedObject.Index]->BoneName); FTransform BoneTM = SharedData->EditorSkelComp->GetBoneTransform(BoneIndex); BoneScale = BoneTM.GetScale3D().GetAbsMax(); BoneTM.RemoveScaling(); SelectedObject.WidgetTM = SharedData->EditorSkelComp->GetPrimitiveTransform(BoneTM, SelectedObject.Index, SelectedObject.PrimitiveType, SelectedObject.PrimitiveIndex, BoneScale); } else /// CONSTRAINT EDITING /// { SelectedObject.WidgetTM = SharedData->GetConstraintMatrix(SelectedObject.Index, EConstraintFrame::Frame2, 1.f); } if ( GetWidgetMode() == FWidget::WM_Translate ) { FVector Dir = SelectedObject.WidgetTM.InverseTransformVector( Drag.GetSafeNormal() ); FVector DragVec = Dir * Drag.Size() / BoneScale; SelectedObject.ManipulateTM.AddToTranslation( DragVec ); } else if ( GetWidgetMode() == FWidget::WM_Rotate ) { FVector Axis; float Angle; Rot.Quaternion().ToAxisAndAngle(Axis, Angle); Axis = SelectedObject.WidgetTM.InverseTransformVectorNoScale( Axis ); const FQuat Start = SelectedObject.ManipulateTM.GetRotation(); const FQuat Delta = FQuat( Axis, Angle ); const FQuat Result = Delta * Start; SelectedObject.ManipulateTM = FTransform( Result ); } else if ( GetWidgetMode() == FWidget::WM_Scale && SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit) // Scaling only valid for bodies. { ModifyPrimitiveSize(SelectedObject.Index, SelectedObject.PrimitiveType, SelectedObject.PrimitiveIndex, Scale ); } if (SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit) { UPhysicsConstraintTemplate* ConstraintSetup = SharedData->PhysicsAsset->ConstraintSetup[SelectedObject.Index]; ConstraintSetup->DefaultInstance.SetRefFrame(EConstraintFrame::Frame2, SelectedObject.ManipulateTM * StartManParentConTM); //Rotation by default only rotates one frame, but translation by default moves both bool bMultiFrame = (IsAltPressed() && GetWidgetMode() == FWidget::WM_Rotate) || (!IsAltPressed() && GetWidgetMode() == FWidget::WM_Translate); if (bMultiFrame) { SharedData->SetSelectedConstraintRelTM(StartManRelConTM); } else { ConstraintSetup->DefaultInstance.SetRefFrame(EConstraintFrame::Frame1, FTransform(StartManChildConTM)); } } bHandled = true; } } return bHandled; }
void FAnimNode_ModifyFinger::EvaluateOneBoneTransforms(FBoneReference* BoneToModify, FRotator* Rotation, const FBoneContainer BoneContainer, FCSPose<FCompactPose> &MeshBases, USkeletalMeshComponent* SkelComp, TArray<FBoneTransform> &OutBoneTransforms) { FCompactPoseBoneIndex CompactPoseBoneToModify = BoneToModify->GetCompactPoseIndex(BoneContainer); FTransform NewBoneTM = MeshBases.GetComponentSpaceTransform(CompactPoseBoneToModify); if (RotationMode != BMM_Ignore) { // Convert to Bone Space. FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, RotationSpace); const FQuat BoneQuat(*Rotation); if (RotationMode == BMM_Additive) { NewBoneTM.SetRotation(BoneQuat * NewBoneTM.GetRotation()); } else { NewBoneTM.SetRotation(BoneQuat); } // Convert back to Component Space. FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, RotationSpace); } if (ScaleMode != BMM_Ignore) { // Convert to Bone Space. FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, ScaleSpace); if (ScaleMode == BMM_Additive) { NewBoneTM.SetScale3D(NewBoneTM.GetScale3D() * Scale); } else { NewBoneTM.SetScale3D(Scale); } // Convert back to Component Space. FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, ScaleSpace); } if (TranslationMode != BMM_Ignore) { // Convert to Bone Space. FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, TranslationSpace); if (TranslationMode == BMM_Additive) { NewBoneTM.AddToTranslation(Translation1); } else { NewBoneTM.SetTranslation(Translation1); } // Convert back to Component Space. FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, TranslationSpace); } OutBoneTransforms.Add(FBoneTransform(BoneToModify->GetCompactPoseIndex(BoneContainer), NewBoneTM)); const float BlendWeight = FMath::Clamp<float>(Alpha, 0.f, 1.f); MeshBases.LocalBlendCSBoneTransforms(OutBoneTransforms, BlendWeight); OutBoneTransforms.Empty(); }
void FAnimNode_ModifyBone::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { check(OutBoneTransforms.Num() == 0); // the way we apply transform is same as FMatrix or FTransform // we apply scale first, and rotation, and translation // if you'd like to translate first, you'll need two nodes that first node does translate and second nodes to rotate. const FBoneContainer BoneContainer = MeshBases.GetPose().GetBoneContainer(); FCompactPoseBoneIndex CompactPoseBoneToModify = BoneToModify.GetCompactPoseIndex(BoneContainer); FTransform NewBoneTM = MeshBases.GetComponentSpaceTransform(CompactPoseBoneToModify); if (ScaleMode != BMM_Ignore) { // Convert to Bone Space. FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, ScaleSpace); if (ScaleMode == BMM_Additive) { NewBoneTM.SetScale3D(NewBoneTM.GetScale3D() * Scale); } else { NewBoneTM.SetScale3D(Scale); } // Convert back to Component Space. FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, ScaleSpace); } if (RotationMode != BMM_Ignore) { // Convert to Bone Space. FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, RotationSpace); const FQuat BoneQuat(Rotation); if (RotationMode == BMM_Additive) { NewBoneTM.SetRotation(BoneQuat * NewBoneTM.GetRotation()); } else { NewBoneTM.SetRotation(BoneQuat); } // Convert back to Component Space. FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, RotationSpace); } if (TranslationMode != BMM_Ignore) { // Convert to Bone Space. FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, TranslationSpace); if (TranslationMode == BMM_Additive) { NewBoneTM.AddToTranslation(Translation); } else { NewBoneTM.SetTranslation(Translation); } // Convert back to Component Space. FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, NewBoneTM, CompactPoseBoneToModify, TranslationSpace); } OutBoneTransforms.Add( FBoneTransform(BoneToModify.GetCompactPoseIndex(BoneContainer), NewBoneTM) ); }