916 lines
30 KiB
C++
916 lines
30 KiB
C++
#include "VrmSpringBone.h"
|
|
|
|
#include "VrmAssetListObject.h"
|
|
#include "Engine/World.h"
|
|
|
|
VrmSpringBone::VrmSpringBone()
|
|
{
|
|
}
|
|
|
|
VrmSpringBone::~VrmSpringBone()
|
|
{
|
|
}
|
|
|
|
namespace VRMSpringBone {
|
|
|
|
void VRMSpring::Update(const FAnimNode_VrmSpringBone* animNode, float DeltaTime, FTransform ComponentToLocal,
|
|
const TArray<VRMSpringColliderGroup>& colliderGroup,
|
|
FComponentSpacePoseContext& Output) {
|
|
|
|
if (skeletalMesh == nullptr) {
|
|
return;
|
|
}
|
|
|
|
const auto RefSkeleton = Output.AnimInstanceProxy->GetSkeleton()->GetReferenceSkeleton();
|
|
const auto& RefSkeletonTransform = Output.Pose.GetPose().GetBoneContainer().GetRefPoseArray();
|
|
|
|
const FTransform ComponentTransform = Output.AnimInstanceProxy->GetComponentTransform();
|
|
// モデルローカル座標
|
|
ComponentToLocal = ComponentTransform.Inverse();
|
|
|
|
//
|
|
// x10 adjust?
|
|
FVector ue4grav(-gravityDir.X, gravityDir.Z, gravityDir.Y);
|
|
|
|
const int MAX_LOOP = FMath::Max(1, animNode->loopc);
|
|
for (int i = 0; i < MAX_LOOP; ++i) {
|
|
//const float stiffnessForce = stiffness * DeltaTime * 10.f * animNode->stiffnessScale + animNode->stiffinessAdd;
|
|
//FVector external = ComponentToLocal.TransformVector(ue4grav) * (gravityPower * DeltaTime) * animNode->gravityScale + ComponentToLocal.TransformVector(animNode->gravityAdd) * DeltaTime;
|
|
//external *= 100.f; // to unreal scale
|
|
|
|
float CurrentDeltaTime = DeltaTime / (float)MAX_LOOP;
|
|
|
|
const float stiffnessForce = stiffness * CurrentDeltaTime * 10.f * animNode->stiffnessScale + animNode->stiffnessAdd;
|
|
FVector external = ComponentToLocal.TransformVector(ue4grav) * (gravityPower * CurrentDeltaTime) * animNode->gravityScale + ComponentToLocal.TransformVector(animNode->gravityAdd) * CurrentDeltaTime;
|
|
|
|
//wind
|
|
if (animNode->bIgnoreWindDirectionalSource == false) {
|
|
const USkeletalMeshComponent* SkelComp = Output.AnimInstanceProxy->GetSkelMeshComponent();
|
|
//InAnimInstance->GetSkelMeshComponent();
|
|
|
|
const UWorld* World = nullptr;
|
|
if (SkelComp) {
|
|
World = SkelComp->GetWorld();
|
|
}
|
|
FSceneInterface* Scene = nullptr;
|
|
if (World) {
|
|
Scene = World->Scene;
|
|
}
|
|
|
|
if (Scene)
|
|
{
|
|
|
|
// Unused by our simulation but needed for the call to GetWindParameters below
|
|
float WindMinGust;
|
|
float WindMaxGust;
|
|
FVector WindDirection;
|
|
float WindSpeed;
|
|
|
|
|
|
// Setup wind data
|
|
//Body->bWindEnabled = true;
|
|
//Scene->GetWindParameters_GameThread(SkelComp->GetComponentTransform().TransformPosition(Body->Pose.Position), Body->WindData.WindDirection, Body->WindData.WindSpeed, WindMinGust, WindMaxGust);
|
|
Scene->GetWindParameters_GameThread(SkelComp->GetComponentTransform().GetLocation(), WindDirection, WindSpeed, WindMinGust, WindMaxGust);
|
|
|
|
WindDirection = SkelComp->GetComponentTransform().Inverse().TransformVector(WindDirection);
|
|
|
|
// from AnimPhysicsSolver
|
|
const float WindUnitScale = 0.5f * 250.0f * FMath::FRandRange(1.f - animNode->randomWindRange, 1.f + animNode->randomWindRange) * animNode->windScale;
|
|
//
|
|
|
|
// Wind velocity in body space
|
|
FVector WindVelocity = WindDirection * WindSpeed * WindUnitScale;// *BodyWindScale;
|
|
WindVelocity *= CurrentDeltaTime;
|
|
|
|
//Body->WindData.WindDirection = SkelComp->GetComponentTransform().Inverse().TransformVector(Body->WindData.WindDirection);
|
|
//Body->WindData.WindAdaption = FMath::FRandRange(0.0f, 2.0f);
|
|
//Body->WindData.BodyWindScale = WindScale;
|
|
|
|
//if (CVarEnableWind.GetValueOnAnyThread() == 1 && bEnableWind)
|
|
|
|
external += WindVelocity / 100.f;
|
|
}
|
|
}// wind end
|
|
|
|
|
|
external *= 100.f; // to unreal scale
|
|
|
|
FVector external_noAdd = ComponentToLocal.TransformVector(ue4grav) * (gravityPower * CurrentDeltaTime) * animNode->gravityScale;
|
|
external_noAdd *= 100.f; // to unreal scale
|
|
|
|
|
|
const auto WorldContext = Output.AnimInstanceProxy->GetSkelMeshComponent();
|
|
|
|
for (int springCount = 0; springCount < SpringDataChain.Num(); ++springCount) {
|
|
|
|
auto& ChainRoot = SpringDataChain[springCount];
|
|
FTransform currentTransform = FTransform::Identity;
|
|
|
|
bool bSkipGravAdd = false;
|
|
|
|
for (int chainCount = 0; chainCount < ChainRoot.Num(); ++chainCount) {
|
|
|
|
auto& sData = ChainRoot[chainCount];
|
|
|
|
FVector currentTail = ComponentToLocal.TransformPosition(sData.m_currentTail);
|
|
FVector prevTail = ComponentToLocal.TransformPosition(sData.m_prevTail);
|
|
|
|
int myBoneIndex = RefSkeleton.FindBoneIndex(sData.boneName);
|
|
if (myBoneIndex < 0) {
|
|
continue;
|
|
}
|
|
|
|
if (animNode->NoWindBoneNameList.Contains(sData.boneName)) {
|
|
bSkipGravAdd = true;
|
|
}
|
|
|
|
|
|
int myParentBoneIndex = RefSkeleton.GetParentIndex(myBoneIndex);
|
|
|
|
//currentTransform = FTransform::Identity;
|
|
FQuat ParentRotation = FQuat::Identity;
|
|
if (chainCount == 0) {
|
|
FCompactPoseBoneIndex uu = Output.Pose.GetPose().GetBoneContainer().GetCompactPoseIndexFromSkeletonIndex(myBoneIndex);
|
|
//FCompactPoseBoneIndex uu(myBoneIndex);
|
|
|
|
if (Output.Pose.GetPose().IsValidIndex(uu) == false) {
|
|
continue;
|
|
}
|
|
|
|
FTransform NewBoneTM = Output.Pose.GetComponentSpaceTransform(uu);
|
|
ParentRotation = NewBoneTM.GetRotation();
|
|
|
|
currentTransform = NewBoneTM;
|
|
}
|
|
else {
|
|
auto c = RefSkeletonTransform[sData.boneIndex];
|
|
auto t = c * currentTransform;
|
|
|
|
ParentRotation = t.GetRotation();
|
|
currentTransform = t;
|
|
}
|
|
FQuat m_localRotation = FQuat::Identity;
|
|
|
|
|
|
// verlet積分で次の位置を計算
|
|
FVector nextTail = currentTail
|
|
+ (currentTail - prevTail) * (1.0f - dragForce) // 前フレームの移動を継続する(減衰もあるよ)
|
|
+ ParentRotation * m_localRotation * sData.m_boneAxis * stiffnessForce // 親の回転による子ボーンの移動目標
|
|
;
|
|
if (bSkipGravAdd) {
|
|
nextTail += external_noAdd; // 外力による移動量
|
|
}
|
|
else {
|
|
nextTail += external; // 外力による移動量
|
|
}
|
|
|
|
|
|
// 長さをboneLengthに強制
|
|
//nextTail = sData.m_transform.GetLocation() + (nextTail - sData.m_transform.GetLocation()).GetSafeNormal() * sData.m_length;
|
|
nextTail = currentTransform.GetLocation() + (nextTail - currentTransform.GetLocation()).GetSafeNormal() * sData.m_length;
|
|
|
|
// Collisionで移動
|
|
|
|
// vrm <-> physics collision
|
|
if (animNode->bIgnorePhysicsCollision == false) {
|
|
const int ColCount = animNode->collisionCheckLoopCount;
|
|
for (int colc = 0; colc < ColCount; ++colc) {
|
|
FVector Start = ComponentToLocal.InverseTransformPosition(nextTail);
|
|
FVector End = Start + FVector(0.001f);
|
|
//* WorldContextObject
|
|
float Radius = hitRadius * 100.f;
|
|
ETraceTypeQuery TraceChannel = ETraceTypeQuery::TraceTypeQuery1;
|
|
bool bTraceComplex = false;
|
|
|
|
TArray<AActor*> ActorsToIgnore;
|
|
EDrawDebugTrace::Type DrawDebugType = EDrawDebugTrace::None;
|
|
TArray<FHitResult> OutHits;
|
|
bool bIgnoreSelf = true;
|
|
FLinearColor TraceColor(EForceInit::ForceInit);
|
|
FLinearColor TraceHitColor(EForceInit::ForceInit);
|
|
float DrawTime = 0.f;
|
|
|
|
bool b = UKismetSystemLibrary::SphereTraceMulti(WorldContext, Start, End, hitRadius * 100.f,
|
|
TraceChannel, false, ActorsToIgnore,
|
|
DrawDebugType,
|
|
OutHits, bIgnoreSelf, TraceColor, TraceHitColor, DrawTime);
|
|
if (b == false) {
|
|
break;
|
|
}
|
|
for (auto hit : OutHits) {
|
|
{
|
|
//WorldContext->GetOwner
|
|
//hit.BoneName
|
|
|
|
}
|
|
float r = hit.PenetrationDepth; // hitRadius * 100.f + hit.Distance;
|
|
auto normal = hit.Normal;
|
|
auto posFromCollider = nextTail + normal * r; // (float)ColCount;
|
|
// 長さをboneLengthに強制
|
|
nextTail = currentTransform.GetLocation() + (posFromCollider - currentTransform.GetLocation()).GetSafeNormal() * sData.m_length;
|
|
}
|
|
}
|
|
}
|
|
|
|
// vrm <-> vrm collision
|
|
if (animNode->bIgnoreVRMCollision == false) {
|
|
for (auto ind : ColliderGroupIndexArray) {
|
|
if (ind >= colliderGroup.Num()) {
|
|
continue;
|
|
}
|
|
const auto& cg = colliderGroup[ind];
|
|
|
|
int ii = RefSkeleton.FindBoneIndex(cg.node_name);
|
|
if (ii < 0) {
|
|
continue;
|
|
}
|
|
|
|
FCompactPoseBoneIndex uu = Output.Pose.GetPose().GetBoneContainer().GetCompactPoseIndexFromSkeletonIndex(ii);
|
|
//FCompactPoseBoneIndex uu(ii);
|
|
if (uu == INDEX_NONE) {
|
|
continue;
|
|
}
|
|
FTransform collisionBoneTrans = Output.Pose.GetComponentSpaceTransform(uu);
|
|
|
|
for (auto c : cg.colliders) {
|
|
|
|
|
|
float r = (hitRadius + c.radius) * 100.f;
|
|
//FVector v = collisionBoneTrans.TransformPosition(c.offset*100);
|
|
auto offs = c.offset;
|
|
//offs.Set(offs.X, -offs.Z, offs.Y); // 本来はこれが正しいが、VRM0の座標が間違っている
|
|
offs.Set(-offs.X, offs.Z, offs.Y); // VRM0の仕様としては これ
|
|
offs *= 100;
|
|
FVector v = collisionBoneTrans.TransformPosition(offs);
|
|
|
|
if ((v - nextTail).SizeSquared() > r * r) {
|
|
continue;
|
|
}
|
|
|
|
// ヒット。Colliderの半径方向に押し出す
|
|
auto normal = (nextTail - v).GetSafeNormal();
|
|
auto posFromCollider = v + normal * (r);
|
|
// 長さをboneLengthに強制
|
|
nextTail = currentTransform.GetLocation() + (posFromCollider - currentTransform.GetLocation()).GetSafeNormal() * sData.m_length;
|
|
}
|
|
}
|
|
}
|
|
|
|
sData.m_prevTail = ComponentToLocal.InverseTransformPosition(currentTail);
|
|
sData.m_currentTail = ComponentToLocal.InverseTransformPosition(nextTail);
|
|
|
|
FQuat rotation = ParentRotation * m_localRotation;
|
|
|
|
sData.m_resultQuat = FQuat::FindBetween((rotation * sData.m_boneAxis).GetSafeNormal(),
|
|
(nextTail - currentTransform.GetLocation()).GetSafeNormal()) * rotation;
|
|
|
|
currentTransform.SetRotation(sData.m_resultQuat);
|
|
}
|
|
}// chain loop
|
|
}// delta time loop
|
|
}
|
|
|
|
|
|
void VRMSpringManager::reset() {
|
|
spring.Empty();
|
|
bInit = false;
|
|
}
|
|
|
|
void VRMSpringManager::init(const UVrmMetaObject* meta, FComponentSpacePoseContext& Output) {
|
|
if (meta == nullptr) return;
|
|
if (bInit) return;
|
|
|
|
if (meta->GetVRMVersion() == 1) return;
|
|
if (meta->VrmAssetListObject == nullptr) return;
|
|
|
|
skeletalMesh = VRMGetSkinnedAsset(Output.AnimInstanceProxy->GetSkelMeshComponent());
|
|
//skeletalMesh = meta->SkeletalMesh;
|
|
const FReferenceSkeleton& RefSkeleton = VRMGetRefSkeleton(skeletalMesh);
|
|
const auto& RefSkeletonTransform = Output.Pose.GetPose().GetBoneContainer().GetRefPoseArray();
|
|
|
|
spring.SetNum(meta->VRMSpringMeta.Num());
|
|
|
|
for (int i = 0; i < spring.Num(); ++i) {
|
|
auto& s = spring[i];
|
|
const auto& metaS = meta->VRMSpringMeta[i];
|
|
|
|
s.skeletalMesh = skeletalMesh;// meta->SkeletalMesh;
|
|
|
|
s.stiffness = metaS.stiffness;
|
|
s.gravityPower = metaS.gravityPower;
|
|
s.gravityDir = meta->VrmAssetListObject->model_root_transform.TransformVector(metaS.gravityDir);
|
|
s.dragForce = metaS.dragForce;
|
|
s.hitRadius = metaS.hitRadius;
|
|
|
|
s.RootSpringData.SetNum(metaS.bones.Num());
|
|
for (int scount = 0; scount < s.RootSpringData.Num(); ++scount) {
|
|
s.RootSpringData[scount].boneName = *metaS.boneNames[scount];
|
|
s.RootSpringData[scount].boneIndex = RefSkeleton.FindBoneIndex(*metaS.boneNames[scount]);
|
|
}
|
|
// TODO add child
|
|
|
|
s.SpringDataChain.SetNum(metaS.bones.Num());
|
|
for (int scount = 0; scount < s.RootSpringData.Num(); ++scount) {
|
|
auto& chain = s.SpringDataChain[scount];
|
|
|
|
//root
|
|
{
|
|
auto index = VRMGetRefSkeleton(skeletalMesh).FindBoneIndex(*metaS.boneNames[scount]);
|
|
if (index == INDEX_NONE) {
|
|
continue;
|
|
}
|
|
|
|
auto& sData = chain.AddDefaulted_GetRef();
|
|
sData.boneName = *metaS.boneNames[scount];
|
|
sData.boneIndex = index;
|
|
|
|
TArray<int32> Children;
|
|
VRMUtil::GetDirectChildBones(VRMGetRefSkeleton(skeletalMesh), sData.boneIndex, Children);
|
|
if (Children.Num() > 0) {
|
|
sData.m_boneAxis = RefSkeletonTransform[Children[0]].GetLocation();
|
|
}
|
|
else {
|
|
sData.m_boneAxis = RefSkeletonTransform[sData.boneIndex].GetLocation() * 0.7f;
|
|
}
|
|
}
|
|
|
|
// child
|
|
if (1) {
|
|
for (int chainCount = 0; chainCount < 100; ++chainCount) {
|
|
bool bLast = false;
|
|
TArray<int32> Children;
|
|
|
|
VRMUtil::GetDirectChildBones(VRMGetRefSkeleton(skeletalMesh), chain[chainCount].boneIndex, Children);
|
|
if (Children.Num() <= 0) {
|
|
break;
|
|
}
|
|
|
|
auto& sData = chain.AddDefaulted_GetRef();
|
|
|
|
sData.boneIndex = Children[0];
|
|
sData.boneName = *RefSkeleton.GetBoneName(sData.boneIndex).ToString();
|
|
|
|
VRMUtil::GetDirectChildBones(VRMGetRefSkeleton(skeletalMesh), sData.boneIndex, Children);
|
|
if (Children.Num() > 0) {
|
|
sData.m_boneAxis = RefSkeletonTransform[Children[0]].GetLocation();
|
|
}
|
|
else {
|
|
sData.m_boneAxis = RefSkeletonTransform[sData.boneIndex].GetLocation() * 0.7f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
s.ColliderGroupIndexArray.SetNum(metaS.ColliderIndexArray.Num());
|
|
for (int c = 0; c < s.ColliderGroupIndexArray.Num(); ++c) {
|
|
s.ColliderGroupIndexArray[c] = metaS.ColliderIndexArray[c];
|
|
}
|
|
|
|
}
|
|
|
|
// init default transform
|
|
{
|
|
const FTransform ComponentTransform = Output.AnimInstanceProxy->GetComponentTransform();
|
|
for (auto& s : spring) {
|
|
for (auto& root : s.SpringDataChain) {
|
|
FTransform currentTransform = FTransform::Identity;
|
|
|
|
for (int ChildCount = 0; ChildCount < root.Num(); ++ChildCount) {
|
|
//for (auto& sData : root) {
|
|
auto& sData = root[ChildCount];
|
|
sData.m_length = sData.m_boneAxis.Size();
|
|
int myBoneIndex = RefSkeleton.FindBoneIndex(sData.boneName);
|
|
|
|
#if UE_VERSION_OLDER_THAN(5,0,0)
|
|
if (sData.boneIndex >= Output.Pose.GetPose().GetBoneContainer().GetSkeletonToPoseBoneIndexArray().Num()) {
|
|
continue;
|
|
}
|
|
#else
|
|
if (Output.Pose.GetPose().GetBoneContainer().GetMeshPoseIndexFromSkeletonPoseIndex(FSkeletonPoseBoneIndex(sData.boneIndex)) == FMeshPoseBoneIndex(INDEX_NONE)) {
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
{
|
|
FQuat ParentRotation = FQuat::Identity;
|
|
if (ChildCount == 0) {
|
|
FCompactPoseBoneIndex uu = Output.Pose.GetPose().GetBoneContainer().GetCompactPoseIndexFromSkeletonIndex(myBoneIndex);
|
|
|
|
if (Output.Pose.GetPose().IsValidIndex(uu) == false) {
|
|
continue;
|
|
}
|
|
|
|
FTransform NewBoneTM = Output.Pose.GetComponentSpaceTransform(uu);
|
|
ParentRotation = NewBoneTM.GetRotation();
|
|
|
|
currentTransform = NewBoneTM;
|
|
}
|
|
else {
|
|
auto c = RefSkeletonTransform[sData.boneIndex];
|
|
auto t = c * currentTransform;
|
|
|
|
ParentRotation = t.GetRotation();
|
|
currentTransform = t;
|
|
}
|
|
}
|
|
FVector v = currentTransform.GetLocation() + sData.m_boneAxis;
|
|
sData.m_currentTail = sData.m_prevTail = ComponentTransform.TransformPosition(v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// collider
|
|
colliderGroup.SetNum(meta->VRMColliderMeta.Num());
|
|
for (int i = 0; i < colliderGroup.Num(); ++i) {
|
|
auto& cg = colliderGroup[i];
|
|
const auto& cmeta = meta->VRMColliderMeta[i];
|
|
|
|
cg.node = cmeta.bone;
|
|
cg.node_name = *cmeta.boneName;
|
|
|
|
cg.colliders.SetNum(cmeta.collider.Num());
|
|
for (int c = 0; c < cg.colliders.Num(); ++c) {
|
|
cg.colliders[c].offset = cmeta.collider[c].offset;
|
|
cg.colliders[c].radius = cmeta.collider[c].radius;
|
|
}
|
|
|
|
TArray<VRMSpringCollider> colliders;
|
|
}
|
|
|
|
|
|
bInit = true;
|
|
}
|
|
void VRMSpringManager::update(const FAnimNode_VrmSpringBone* animNode, float DeltaTime, FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms) {
|
|
for (int i = 0; i < spring.Num(); ++i) {
|
|
FTransform c;
|
|
//c = Output.AnimInstanceProxy->GetComponentTransform();
|
|
c = Output.AnimInstanceProxy->GetActorTransform();
|
|
|
|
spring[i].Update(animNode, DeltaTime, c, colliderGroup, Output);
|
|
}
|
|
}
|
|
void VRMSpringManager::applyToComponent(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms) {
|
|
|
|
const auto RefSkeleton = Output.AnimInstanceProxy->GetSkeleton()->GetReferenceSkeleton();
|
|
const auto& RefSkeletonTransform = Output.Pose.GetPose().GetBoneContainer().GetRefPoseArray();
|
|
|
|
|
|
VRMSpringManager* SpringManager = this;
|
|
|
|
for (auto& springRoot : SpringManager->spring) {
|
|
for (auto& sChain : springRoot.SpringDataChain) {
|
|
int BoneChain = 0;
|
|
|
|
FTransform CurrentTransForm = FTransform::Identity;
|
|
for (auto& sData : sChain) {
|
|
|
|
|
|
//FCompactPoseBoneIndex uu = Output.Pose.GetPose().GetBoneContainer().GetCompactPoseIndexFromSkeletonIndex(sData.boneIndex);
|
|
FCompactPoseBoneIndex uu(sData.boneIndex);
|
|
|
|
if (Output.Pose.GetPose().IsValidIndex(uu) == false) {
|
|
continue;
|
|
}
|
|
|
|
FTransform NewBoneTM;
|
|
|
|
if (BoneChain == 0) {
|
|
NewBoneTM = Output.Pose.GetComponentSpaceTransform(uu);
|
|
NewBoneTM.SetRotation(sData.m_resultQuat);
|
|
|
|
CurrentTransForm = NewBoneTM;
|
|
}
|
|
else {
|
|
|
|
NewBoneTM = CurrentTransForm;
|
|
|
|
auto c = RefSkeletonTransform[sData.boneIndex];
|
|
NewBoneTM = c * NewBoneTM;
|
|
NewBoneTM.SetRotation(sData.m_resultQuat);
|
|
|
|
|
|
//const FTransform ComponentTransform = Output.AnimInstanceProxy->GetComponentTransform();
|
|
//NewBoneTM.SetLocation(ComponentTransform.TransformPosition(sData.m_currentTail));
|
|
|
|
CurrentTransForm = NewBoneTM;
|
|
}
|
|
|
|
FBoneTransform a(uu, NewBoneTM);
|
|
|
|
bool bFirst = true;
|
|
for (auto& t : OutBoneTransforms) {
|
|
if (t.BoneIndex == a.BoneIndex) {
|
|
bFirst = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bFirst) {
|
|
OutBoneTransforms.Add(a);
|
|
}
|
|
BoneChain++;
|
|
}
|
|
}
|
|
|
|
}
|
|
OutBoneTransforms.Sort(FCompareBoneTransformIndex());
|
|
}
|
|
|
|
}
|
|
|
|
////////////////////////////
|
|
namespace VRM1Spring {
|
|
|
|
void VRM1SpringManager::reset() {
|
|
for (auto &j : JointStateMap) {
|
|
j.Value.currentTail = j.Value.prevTail = j.Value.initialTail;
|
|
}
|
|
}
|
|
|
|
void VRM1SpringManager::init(const UVrmMetaObject* meta, FComponentSpacePoseContext& Output) {
|
|
|
|
if (meta == nullptr) return;
|
|
|
|
const FTransform ComponentTransform = Output.AnimInstanceProxy->GetComponentTransform();
|
|
FTransform ComponentToLocal = ComponentTransform.Inverse();
|
|
|
|
vrmMetaObject = meta;
|
|
skeletalMesh = VRMGetSkinnedAsset(Output.AnimInstanceProxy->GetSkelMeshComponent());
|
|
const FReferenceSkeleton& RefSkeleton = VRMGetRefSkeleton(skeletalMesh);
|
|
//const auto& RefSkeletonTransform = Output.Pose.GetPose().GetBoneContainer().GetRefPoseArray();
|
|
const auto& RefSkeletonTransform = RefSkeleton.GetRefBonePose();
|
|
|
|
|
|
for (auto& s : vrmMetaObject->VRM1SpringBoneMeta.Springs) {
|
|
for (int jointNo = 0; jointNo < s.joints.Num(); jointNo++) {
|
|
|
|
auto &j1 = s.joints[jointNo];
|
|
|
|
if (j1.boneNo == INDEX_NONE) {
|
|
continue;
|
|
}
|
|
|
|
int parentBoneIndex = RefSkeleton.GetParentIndex(j1.boneNo);
|
|
if (parentBoneIndex < 0) continue;
|
|
FCompactPoseBoneIndex uu = Output.Pose.GetPose().GetBoneContainer().GetCompactPoseIndexFromSkeletonIndex(parentBoneIndex);
|
|
if (Output.Pose.GetPose().IsValidIndex(uu) == false) {
|
|
continue;
|
|
}
|
|
FTransform parentTransform = Output.Pose.GetComponentSpaceTransform(uu);
|
|
|
|
auto& state = JointStateMap.FindOrAdd(j1.boneNo);
|
|
|
|
if (RefSkeletonTransform.IsValidIndex(j1.boneNo) == false) {
|
|
continue;
|
|
}
|
|
|
|
state.initialLocalMatrix = RefSkeletonTransform[j1.boneNo];
|
|
state.initialLocalRotation = RefSkeletonTransform[j1.boneNo].GetRotation();
|
|
|
|
#if UE_VERSION_OLDER_THAN(5,0,0)
|
|
state.boneLength = RefSkeletonTransform[parentBoneIndex].GetLocation().Size();
|
|
#else
|
|
state.boneLength = RefSkeletonTransform[j1.boneNo].GetLocation().Length();
|
|
#endif
|
|
state.boneAxis = RefSkeletonTransform[j1.boneNo].TransformPosition(FVector::ZeroVector).GetSafeNormal();
|
|
|
|
{
|
|
FCompactPoseBoneIndex u = Output.Pose.GetPose().GetBoneContainer().GetCompactPoseIndexFromSkeletonIndex(j1.boneNo);
|
|
auto t = Output.Pose.GetComponentSpaceTransform(u);
|
|
|
|
state.prevTail =
|
|
state.currentTail =
|
|
state.initialTail = ComponentToLocal.InverseTransformPosition(t.GetLocation());
|
|
|
|
state.resultQuat = t.GetRotation();
|
|
}
|
|
}
|
|
}
|
|
bInit = true;
|
|
}
|
|
|
|
void VRM1SpringManager::update(const FAnimNode_VrmSpringBone* animNode, float DeltaTime, FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms) {
|
|
|
|
if (skeletalMesh == nullptr) {
|
|
return;
|
|
}
|
|
if (FMath::IsNearlyZero(DeltaTime)) {
|
|
return;
|
|
}
|
|
|
|
const auto RefSkeleton = Output.AnimInstanceProxy->GetSkeleton()->GetReferenceSkeleton();
|
|
//const auto& RefSkeletonTransform = Output.Pose.GetPose().GetBoneContainer().GetRefPoseArray();
|
|
const auto& RefSkeletonTransform = RefSkeleton.GetRefBonePose();
|
|
|
|
const FTransform ComponentTransform = Output.AnimInstanceProxy->GetComponentTransform();
|
|
// モデルローカル座標
|
|
// FTransform c;
|
|
//c = Output.AnimInstanceProxy->GetComponentTransform();
|
|
//c = Output.AnimInstanceProxy->GetActorTransform();
|
|
|
|
const FTransform ComponentToLocal = ComponentTransform.Inverse();
|
|
|
|
// 計算した揺れ骨、または揺れ骨の親
|
|
TMap<int, FTransform> calcTransform;
|
|
|
|
// 揺れ骨一覧
|
|
TArray<int> springBoneNoList;
|
|
{
|
|
for (auto& s : vrmMetaObject->VRM1SpringBoneMeta.Springs) {
|
|
for (auto& j : s.joints) {
|
|
springBoneNoList.AddUnique(j.boneNo);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto& s : vrmMetaObject->VRM1SpringBoneMeta.Springs) {
|
|
|
|
for (int jointNo = 0; jointNo < s.joints.Num(); ++jointNo) {
|
|
FTransform parentTransform = FTransform::Identity;
|
|
FTransform currentTransform = FTransform::Identity;
|
|
|
|
auto& j1 = s.joints[jointNo];
|
|
|
|
auto* state = JointStateMap.Find(j1.boneNo);
|
|
if (state == nullptr) {
|
|
init(vrmMetaObject, Output);
|
|
continue;
|
|
}
|
|
|
|
if (RefSkeletonTransform.IsValidIndex(j1.boneNo) == false) {
|
|
continue;
|
|
}
|
|
|
|
int parentBoneIndex = RefSkeleton.GetParentIndex(j1.boneNo);
|
|
if (parentBoneIndex < 0) {
|
|
continue;
|
|
}
|
|
|
|
if (springBoneNoList.Find(parentBoneIndex) < 0) {
|
|
// 親が揺れ骨ではない。通常骨から参照
|
|
|
|
// 親
|
|
{
|
|
FCompactPoseBoneIndex uu = Output.Pose.GetPose().GetBoneContainer().GetCompactPoseIndexFromSkeletonIndex(parentBoneIndex);
|
|
if (Output.Pose.GetPose().IsValidIndex(uu) == false) {
|
|
continue;
|
|
}
|
|
FTransform NewBoneTM = Output.Pose.GetComponentSpaceTransform(uu);
|
|
parentTransform = NewBoneTM;
|
|
}
|
|
|
|
{
|
|
// 自分
|
|
FCompactPoseBoneIndex uu = Output.Pose.GetPose().GetBoneContainer().GetCompactPoseIndexFromSkeletonIndex(j1.boneNo);
|
|
if (Output.Pose.GetPose().IsValidIndex(uu) == false) {
|
|
continue;
|
|
}
|
|
FTransform NewBoneTM = Output.Pose.GetComponentSpaceTransform(uu);
|
|
currentTransform = NewBoneTM;
|
|
}
|
|
} else {
|
|
// 親が揺れ骨。揺れ骨計算結果から参照
|
|
|
|
// 親
|
|
//parentTransform = currentTransform;
|
|
auto* p = calcTransform.Find(parentBoneIndex);
|
|
if (p) {
|
|
parentTransform = *p;
|
|
}
|
|
|
|
auto c = RefSkeletonTransform[j1.boneNo];
|
|
auto t = c * parentTransform;
|
|
|
|
// 自分
|
|
currentTransform = t;
|
|
}
|
|
|
|
FQuat m_localRotation = state->initialLocalMatrix.GetRotation();
|
|
|
|
const FVector currentTail = ComponentToLocal.TransformPosition(state->currentTail);
|
|
const FVector prevTail = ComponentToLocal.TransformPosition(state->prevTail);
|
|
|
|
const FVector inertia = (currentTail - prevTail) * (1.0f - j1.dragForce);
|
|
const FVector stiffness = currentTransform.GetRotation() * state->boneAxis * 1.f * DeltaTime
|
|
* 100.f * j1.stiffness * animNode->stiffnessScale + animNode->stiffnessAdd;
|
|
|
|
FVector ue4grav(-j1.gravityDir.X, j1.gravityDir.Z, j1.gravityDir.Y);
|
|
ue4grav = vrmMetaObject->VrmAssetListObject->model_root_transform.Inverse().TransformVector(ue4grav);
|
|
FVector external = ComponentToLocal.TransformVector(ue4grav) * (j1.gravityPower * DeltaTime) * animNode->gravityScale
|
|
+ ComponentToLocal.TransformVector(animNode->gravityAdd) * DeltaTime;
|
|
|
|
|
|
FVector nextTailTarget = currentTail + inertia + stiffness + external;
|
|
//FVector nextTailTarget = currentTail + stiffness * 10;
|
|
|
|
// 長さをboneLengthに強制
|
|
FVector nextTailDirection = (nextTailTarget - currentTransform.GetLocation()).GetSafeNormal();
|
|
FVector nextTailPosition = currentTransform.GetLocation() + nextTailDirection * state->boneLength;
|
|
|
|
|
|
// vrm <-> vrm collision
|
|
if (animNode->bIgnoreVRMCollision == false) {
|
|
|
|
// このSpringが参照するコライダのインデックス
|
|
TArray<int> checkcolliderIndexArray;
|
|
for (auto colg : s.colliderGroups) {
|
|
checkcolliderIndexArray.Append(vrmMetaObject->VRM1SpringBoneMeta.ColliderGroups[colg].colliders);
|
|
}
|
|
|
|
// 全てのコライダ
|
|
auto& AllColliderArray = vrmMetaObject->VRM1SpringBoneMeta.Colliders;
|
|
|
|
for (auto colNo : checkcolliderIndexArray){
|
|
if (colNo >= AllColliderArray.Num()) {
|
|
continue;
|
|
}
|
|
const auto& collider = AllColliderArray[colNo];
|
|
|
|
FTransform collisionBoneTrans;
|
|
{
|
|
int boneNo = RefSkeleton.FindBoneIndex(*collider.boneName);
|
|
if (boneNo < 0) {
|
|
continue;
|
|
}
|
|
|
|
FCompactPoseBoneIndex uu = Output.Pose.GetPose().GetBoneContainer().GetCompactPoseIndexFromSkeletonIndex(boneNo);
|
|
//FCompactPoseBoneIndex uu(ii);
|
|
if (uu == INDEX_NONE) {
|
|
continue;
|
|
}
|
|
collisionBoneTrans = Output.Pose.GetComponentSpaceTransform(uu);
|
|
}
|
|
|
|
auto offs = collider.offset;
|
|
offs.Set(offs.X, -offs.Z, offs.Y);
|
|
offs *= 100;
|
|
//offs = collisionBoneTrans.TransformVector(offs);
|
|
|
|
auto tail = collider.tail;
|
|
tail.Set(tail.X, -tail.Z, tail.Y);
|
|
tail *= 100;
|
|
//tail = collisionBoneTrans.TransformVector(tail);
|
|
|
|
float r = (j1.hitRadius + collider.radius) * 100.f;
|
|
|
|
if (collider.shapeType == TEXT("sphere")) {
|
|
FVector v = collisionBoneTrans.TransformPosition(offs);
|
|
|
|
if ((v - nextTailPosition).SizeSquared() > r * r) {
|
|
continue;
|
|
}
|
|
// ヒット。Colliderの半径方向に押し出す
|
|
auto normal = (nextTailPosition - v).GetSafeNormal();
|
|
auto posFromCollider = v + normal * r;
|
|
// 長さをboneLengthに強制
|
|
nextTailPosition = currentTransform.GetLocation() + (posFromCollider - currentTransform.GetLocation()).GetSafeNormal() * state->boneLength;
|
|
nextTailDirection = (posFromCollider - currentTransform.GetLocation()).GetSafeNormal();
|
|
}
|
|
else {
|
|
|
|
FTransform t1 = collisionBoneTrans;
|
|
auto v1 = t1.TransformPosition(offs);
|
|
|
|
FTransform t2 = collisionBoneTrans;
|
|
auto v2 = t2.TransformPosition(tail);
|
|
|
|
FVector nearestPoint = FMath::ClosestPointOnSegment(nextTailPosition, v1, v2);
|
|
|
|
float dif = (nearestPoint - nextTailPosition).SizeSquared();
|
|
if (dif > r * r) {
|
|
continue;
|
|
}
|
|
|
|
auto normal = (nextTailPosition - nearestPoint).GetSafeNormal();
|
|
|
|
auto posFromCollider = nearestPoint + normal * r;
|
|
// 長さをboneLengthに強制
|
|
nextTailPosition = currentTransform.GetLocation() + (posFromCollider - currentTransform.GetLocation()).GetSafeNormal() * state->boneLength;
|
|
nextTailDirection = (posFromCollider - currentTransform.GetLocation()).GetSafeNormal();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
state->prevTail = ComponentToLocal.InverseTransformPosition(currentTail);
|
|
state->currentTail = ComponentToLocal.InverseTransformPosition(nextTailPosition);
|
|
|
|
{
|
|
FVector from = currentTransform.TransformVector(state->boneAxis).GetSafeNormal();
|
|
FVector to = nextTailDirection;
|
|
|
|
state->resultQuat = FQuat::FindBetween(from, to) * parentTransform.GetRotation() * state->initialLocalMatrix.GetRotation();
|
|
}
|
|
|
|
// initial rot
|
|
{
|
|
//state->resultQuat = currentTransform.GetRotation();
|
|
}
|
|
{
|
|
//state->resultQuat = (state->initialLocalMatrix * parentTransform).GetRotation();
|
|
}
|
|
{
|
|
//state->resultQuat = parentTransform.GetRotation() * state->initialLocalRotation;
|
|
}
|
|
|
|
currentTransform.SetRotation(state->resultQuat);
|
|
|
|
// 揺れ骨計算結果を保持。これの子の揺れ骨のため。
|
|
calcTransform.Add(j1.boneNo, currentTransform);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VRM1SpringManager::applyToComponent(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms) {
|
|
|
|
const auto RefSkeleton = Output.AnimInstanceProxy->GetSkeleton()->GetReferenceSkeleton();
|
|
const auto& RefSkeletonTransform = Output.Pose.GetPose().GetBoneContainer().GetRefPoseArray();
|
|
|
|
// 計算した揺れ骨、または揺れ骨の親
|
|
TMap<int, FTransform> calcTransform;
|
|
|
|
// 揺れ骨一覧
|
|
TArray<int> springBoneNoList;
|
|
{
|
|
for (auto& s : vrmMetaObject->VRM1SpringBoneMeta.Springs) {
|
|
for (auto& j : s.joints) {
|
|
springBoneNoList.AddUnique(j.boneNo);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto& s : vrmMetaObject->VRM1SpringBoneMeta.Springs) {
|
|
|
|
FTransform CurrentTransForm;
|
|
|
|
for (int jointNo = 0; jointNo < s.joints.Num(); ++jointNo) {
|
|
auto& j = s.joints[jointNo];
|
|
|
|
auto* state = JointStateMap.Find(j.boneNo);
|
|
if (state == nullptr) continue;
|
|
|
|
FCompactPoseBoneIndex uu = Output.Pose.GetPose().GetBoneContainer().GetCompactPoseIndexFromSkeletonIndex(j.boneNo);
|
|
//FCompactPoseBoneIndex uu(j.boneNo);
|
|
|
|
if (Output.Pose.GetPose().IsValidIndex(uu) == false) {
|
|
continue;
|
|
}
|
|
|
|
FTransform NewBoneTM;
|
|
|
|
int parentBoneIndex = RefSkeleton.GetParentIndex(j.boneNo);
|
|
if (springBoneNoList.Find(parentBoneIndex) < 0) {
|
|
// 親は揺れ骨でない。
|
|
|
|
// 現在値
|
|
NewBoneTM = Output.Pose.GetComponentSpaceTransform(uu);
|
|
|
|
// 差分
|
|
NewBoneTM.SetRotation(state->resultQuat);
|
|
|
|
CurrentTransForm = NewBoneTM;
|
|
}
|
|
else {
|
|
// 親は揺れ骨。計算結果から参照する
|
|
|
|
auto* p = calcTransform.Find(parentBoneIndex);
|
|
if (p) {
|
|
NewBoneTM = *p;
|
|
}
|
|
|
|
auto c = RefSkeletonTransform[j.boneNo];
|
|
NewBoneTM = c * NewBoneTM;
|
|
NewBoneTM.SetRotation(state->resultQuat);
|
|
|
|
CurrentTransForm = NewBoneTM;
|
|
}
|
|
|
|
FBoneTransform a(uu, NewBoneTM);
|
|
|
|
bool bFirst = true;
|
|
for (auto& t : OutBoneTransforms) {
|
|
if (t.BoneIndex == a.BoneIndex) {
|
|
bFirst = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bFirst) {
|
|
OutBoneTransforms.Add(a);
|
|
}
|
|
|
|
|
|
calcTransform.Add(j.boneNo, CurrentTransForm);
|
|
// update rotation
|
|
//FVector to = (state->currentTail * (node.parent.worldMatrix * state->initialLocalMatrix).inverse).normalized;
|
|
//node.rotation = initialLocalRotation * Quaternion.fromToQuaternion(boneAxis, to);
|
|
}
|
|
}
|
|
OutBoneTransforms.Sort(FCompareBoneTransformIndex());
|
|
|
|
}
|
|
} //spring1
|