556 lines
25 KiB
C#
556 lines
25 KiB
C#
using UnityEngine;
|
|
using UnityEditor;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using VRM;
|
|
using MagicaCloth2;
|
|
using MagicaClothType = MagicaCloth2.MagicaCloth;
|
|
using UnityEngine.Animations;
|
|
|
|
public class AvatarComponetCopier : EditorWindow
|
|
{
|
|
GameObject sourcePrefab;
|
|
GameObject destinationPrefab;
|
|
|
|
[MenuItem("Tools/Avatar Component Mover (SpringBone+MagicaCloth2)")]
|
|
static void ShowWindow()
|
|
{
|
|
GetWindow<AvatarComponetCopier>("Avatar Component Mover");
|
|
}
|
|
|
|
void OnGUI()
|
|
{
|
|
GUILayout.Label("Move VRMSpringBone/MagicaCloth2 & Collider Components", EditorStyles.boldLabel);
|
|
|
|
sourcePrefab = (GameObject)EditorGUILayout.ObjectField("Source Object", sourcePrefab, typeof(GameObject), true);
|
|
destinationPrefab = (GameObject)EditorGUILayout.ObjectField("Target Object", destinationPrefab, typeof(GameObject), true);
|
|
|
|
if (GUILayout.Button("Spring본콜라이더 복사"))
|
|
{
|
|
if (sourcePrefab == null || destinationPrefab == null)
|
|
{
|
|
EditorUtility.DisplayDialog("Error", "Source and Target Objects must be set.", "OK");
|
|
return;
|
|
}
|
|
CopySpringBoneAndMagicaColliders();
|
|
}
|
|
|
|
if (GUILayout.Button("Spring본/Cloth 옮기기"))
|
|
{
|
|
if (sourcePrefab == null || destinationPrefab == null)
|
|
{
|
|
EditorUtility.DisplayDialog("Error", "Source and Target Objects must be set.", "OK");
|
|
return;
|
|
}
|
|
MoveSpringBonesAndMagicaCloth();
|
|
}
|
|
|
|
if (GUILayout.Button("Constraint 값 복사"))
|
|
{
|
|
if (sourcePrefab == null || destinationPrefab == null)
|
|
{
|
|
EditorUtility.DisplayDialog("Error", "Source and Target Objects must be set.", "OK");
|
|
return;
|
|
}
|
|
CopyConstraints();
|
|
}
|
|
|
|
if (GUILayout.Button("BlendShapeCopy"))
|
|
{
|
|
if (sourcePrefab == null || destinationPrefab == null)
|
|
{
|
|
EditorUtility.DisplayDialog("Error", "Source and Target Objects must be set.", "OK");
|
|
return;
|
|
}
|
|
CopyBlendShapes();
|
|
}
|
|
|
|
if (GUILayout.Button("ActiveStateCopy"))
|
|
{
|
|
if (sourcePrefab == null || destinationPrefab == null)
|
|
{
|
|
EditorUtility.DisplayDialog("Error", "Source and Target Objects must be set.", "OK");
|
|
return;
|
|
}
|
|
CopyActiveStates();
|
|
}
|
|
}
|
|
|
|
void CopySpringBoneAndMagicaColliders()
|
|
{
|
|
// VRMSpringBoneColliderGroup
|
|
var srcColliders = sourcePrefab.GetComponentsInChildren<VRMSpringBoneColliderGroup>(true)
|
|
.OrderBy(c => GetTransformPath(c.transform, sourcePrefab.transform)).ToArray();
|
|
foreach (var srcCollider in srcColliders)
|
|
{
|
|
string path = GetTransformPath(srcCollider.transform, sourcePrefab.transform);
|
|
var tgtTransform = destinationPrefab.transform.Find(path);
|
|
if (tgtTransform == null) continue;
|
|
if (tgtTransform.GetComponent<VRMSpringBoneColliderGroup>() != null) continue;
|
|
var tgtCollider = tgtTransform.gameObject.AddComponent<VRMSpringBoneColliderGroup>();
|
|
CopyColliderGroupParameters(srcCollider, tgtCollider);
|
|
}
|
|
// MagicaCloth2 Colliders
|
|
CopyMagicaCollider<MagicaSphereCollider>();
|
|
CopyMagicaCollider<MagicaCapsuleCollider>();
|
|
CopyMagicaCollider<MagicaPlaneCollider>();
|
|
Debug.Log("VRMSpringBoneColliderGroup & MagicaCloth2 Collider 복사가 완료되었습니다.");
|
|
}
|
|
|
|
void CopyMagicaCollider<T>() where T : Component
|
|
{
|
|
var srcColliders = sourcePrefab.GetComponentsInChildren<T>(true)
|
|
.OrderBy(c => GetTransformPath(((Component)c).transform, sourcePrefab.transform)).ToArray();
|
|
foreach (var srcCollider in srcColliders)
|
|
{
|
|
var srcTr = ((Component)srcCollider).transform;
|
|
string path = GetTransformPath(srcTr, sourcePrefab.transform);
|
|
var tgtTransform = destinationPrefab.transform.Find(path);
|
|
if (tgtTransform == null) continue;
|
|
if (tgtTransform.GetComponent<T>() != null) continue;
|
|
var tgtCollider = tgtTransform.gameObject.AddComponent<T>();
|
|
CopyMagicaColliderComponents(srcCollider, tgtCollider);
|
|
}
|
|
}
|
|
|
|
void CopyMagicaColliderComponents(Component src, Component tgt)
|
|
{
|
|
var fields = src.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
foreach (var field in fields)
|
|
{
|
|
var value = field.GetValue(src);
|
|
if (typeof(UnityEngine.Object).IsAssignableFrom(field.FieldType))
|
|
{
|
|
Object refObj = value as Object;
|
|
if (refObj == null) { field.SetValue(tgt, null); continue; }
|
|
if (refObj is Transform tr)
|
|
{
|
|
string refPath = GetTransformPath(tr, sourcePrefab.transform);
|
|
var tgtTr = destinationPrefab.transform.Find(refPath);
|
|
if (tgtTr != null)
|
|
field.SetValue(tgt, tgtTr);
|
|
}
|
|
else if (refObj is GameObject go)
|
|
{
|
|
string refPath = GetTransformPath(go.transform, sourcePrefab.transform);
|
|
var tgtGo = destinationPrefab.transform.Find(refPath);
|
|
if (tgtGo != null)
|
|
field.SetValue(tgt, tgtGo.gameObject);
|
|
}
|
|
else if (refObj is Component comp)
|
|
{
|
|
string refPath = GetTransformPath(comp.gameObject.transform, sourcePrefab.transform);
|
|
var tgtGo = destinationPrefab.transform.Find(refPath);
|
|
if (tgtGo != null)
|
|
{
|
|
var tgtComps = tgtGo.GetComponents(comp.GetType());
|
|
int compIdx = comp.gameObject.GetComponents(comp.GetType()).ToList().IndexOf(comp);
|
|
if (compIdx >= 0 && compIdx < tgtComps.Length)
|
|
field.SetValue(tgt, tgtComps[compIdx]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 기타 UnityEngine.Object 타입은 그대로 복사
|
|
field.SetValue(tgt, refObj);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 값 타입, enum, string 등은 그대로 복사
|
|
field.SetValue(tgt, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MoveSpringBonesAndMagicaCloth()
|
|
{
|
|
// VRMSpringBone
|
|
var srcSpringBones = sourcePrefab.GetComponentsInChildren<VRMSpringBone>(true)
|
|
.OrderBy(s => GetTransformPath(s.transform, sourcePrefab.transform)).ToArray();
|
|
foreach (var srcSpringBone in srcSpringBones)
|
|
{
|
|
string path = GetTransformPath(srcSpringBone.transform, sourcePrefab.transform);
|
|
var tgtTransform = destinationPrefab.transform.Find(path);
|
|
if (tgtTransform == null) continue;
|
|
var tgtSpringBone = tgtTransform.gameObject.AddComponent<VRMSpringBone>();
|
|
CopyVRMSpringBoneComponents(srcSpringBone, tgtSpringBone);
|
|
DestroyImmediate(srcSpringBone);
|
|
}
|
|
// MagicaCloth
|
|
var srcMagicaCloths = sourcePrefab.GetComponentsInChildren<MagicaClothType>(true)
|
|
.OrderBy(s => GetTransformPath(s.transform, sourcePrefab.transform)).ToArray();
|
|
foreach (var srcCloth in srcMagicaCloths)
|
|
{
|
|
string path = GetTransformPath(srcCloth.transform, sourcePrefab.transform);
|
|
var tgtTransform = destinationPrefab.transform.Find(path);
|
|
if (tgtTransform == null) continue;
|
|
var tgtCloth = tgtTransform.gameObject.AddComponent<MagicaClothType>();
|
|
CopyMagicaClothComponents(srcCloth, tgtCloth);
|
|
DestroyImmediate(srcCloth);
|
|
}
|
|
Debug.Log("VRMSpringBone & MagicaCloth 옮기기가 완료되었습니다.");
|
|
}
|
|
|
|
void CopyColliderGroupParameters(VRMSpringBoneColliderGroup source, VRMSpringBoneColliderGroup target)
|
|
{
|
|
target.Colliders = source.Colliders.Select(c => new VRMSpringBoneColliderGroup.SphereCollider
|
|
{
|
|
Offset = c.Offset,
|
|
Radius = c.Radius
|
|
}).ToArray();
|
|
}
|
|
|
|
void CopyVRMSpringBoneComponents(VRMSpringBone original, VRMSpringBone copy)
|
|
{
|
|
copy.m_comment = original.m_comment;
|
|
copy.m_stiffnessForce = original.m_stiffnessForce;
|
|
copy.m_gravityPower = original.m_gravityPower;
|
|
copy.m_gravityDir = original.m_gravityDir;
|
|
copy.m_dragForce = original.m_dragForce;
|
|
copy.m_center = FindCorrespondingTransform(original.m_center, destinationPrefab.transform);
|
|
copy.m_hitRadius = original.m_hitRadius;
|
|
copy.m_updateType = original.m_updateType;
|
|
|
|
copy.RootBones = original.RootBones
|
|
.Select(bone => FindCorrespondingTransform(bone, destinationPrefab.transform))
|
|
.Where(t => t != null)
|
|
.ToList();
|
|
|
|
copy.ColliderGroups = original.ColliderGroups
|
|
.Select(colliderGroup => FindCorrespondingColliderGroup(colliderGroup, destinationPrefab.transform))
|
|
.Where(cg => cg != null)
|
|
.ToArray();
|
|
}
|
|
|
|
Transform FindCorrespondingTransform(Transform original, Transform searchRoot)
|
|
{
|
|
if (original == null) return null;
|
|
var path = GetTransformPath(original, sourcePrefab.transform);
|
|
return searchRoot.Find(path);
|
|
}
|
|
|
|
string GetTransformPath(Transform current, Transform root)
|
|
{
|
|
if (current == root) return "";
|
|
var path = current.name;
|
|
while (current.parent != null && current.parent != root)
|
|
{
|
|
current = current.parent;
|
|
path = current.name + "/" + path;
|
|
}
|
|
return path;
|
|
}
|
|
|
|
VRMSpringBoneColliderGroup FindCorrespondingColliderGroup(VRMSpringBoneColliderGroup original, Transform searchRoot)
|
|
{
|
|
if (original == null) return null;
|
|
var path = GetTransformPath(original.transform, sourcePrefab.transform);
|
|
var correspondingTransform = searchRoot.Find(path);
|
|
return correspondingTransform != null ? correspondingTransform.GetComponent<VRMSpringBoneColliderGroup>() : null;
|
|
}
|
|
|
|
void CopyMagicaClothComponents(MagicaClothType src, MagicaClothType tgt)
|
|
{
|
|
var fields = typeof(MagicaClothType).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
foreach (var field in fields)
|
|
{
|
|
var value = field.GetValue(src);
|
|
// ClothType 우선 복사 및 분기 처리
|
|
if (field.Name == "serializeData" && value is MagicaCloth2.ClothSerializeData sdata)
|
|
{
|
|
var tdata = new MagicaCloth2.ClothSerializeData();
|
|
// ClothType을 먼저 복사
|
|
tdata.clothType = sdata.clothType;
|
|
// ClothType에 따라 분기
|
|
if (sdata.clothType == MagicaCloth2.ClothProcess.ClothType.MeshCloth)
|
|
{
|
|
// sourceRenderers 복사
|
|
tdata.sourceRenderers = sdata.sourceRenderers
|
|
.Select(r => {
|
|
if (r == null) return null;
|
|
string path = GetTransformPath(r.transform, sourcePrefab.transform);
|
|
var tgtTr = destinationPrefab.transform.Find(path);
|
|
return tgtTr ? tgtTr.GetComponent<Renderer>() : null;
|
|
})
|
|
.Where(r => r != null)
|
|
.ToList();
|
|
}
|
|
else if (sdata.clothType == MagicaCloth2.ClothProcess.ClothType.BoneCloth || sdata.clothType == MagicaCloth2.ClothProcess.ClothType.BoneSpring)
|
|
{
|
|
// rootBones 복사
|
|
tdata.rootBones = sdata.rootBones
|
|
.Select(bone => FindCorrespondingTransform(bone, destinationPrefab.transform))
|
|
.Where(t => t != null)
|
|
.ToList();
|
|
}
|
|
// colliderCollisionConstraint 복사
|
|
if (sdata.colliderCollisionConstraint != null)
|
|
{
|
|
var srcCol = sdata.colliderCollisionConstraint;
|
|
var tgtCol = new MagicaCloth2.ColliderCollisionConstraint.SerializeData();
|
|
tgtCol.mode = srcCol.mode;
|
|
tgtCol.friction = srcCol.friction;
|
|
tgtCol.limitDistance = srcCol.limitDistance;
|
|
// colliderList 변환
|
|
tgtCol.colliderList = srcCol.colliderList
|
|
.Select(c => {
|
|
if (c == null) return null;
|
|
string path = GetTransformPath(c.transform, sourcePrefab.transform);
|
|
var tgtTr = destinationPrefab.transform.Find(path);
|
|
return tgtTr ? tgtTr.GetComponent<MagicaCloth2.ColliderComponent>() : null;
|
|
})
|
|
.Where(c => c != null)
|
|
.ToList();
|
|
// collisionBones 변환
|
|
tgtCol.collisionBones = srcCol.collisionBones
|
|
.Select(bone => FindCorrespondingTransform(bone, destinationPrefab.transform))
|
|
.Where(t => t != null)
|
|
.ToList();
|
|
tdata.colliderCollisionConstraint = tgtCol;
|
|
}
|
|
// 공통 필드 복사
|
|
tdata.paintMaps = new List<Texture2D>(sdata.paintMaps);
|
|
tdata.paintMode = sdata.paintMode;
|
|
tdata.connectionMode = sdata.connectionMode;
|
|
tdata.rotationalInterpolation = sdata.rotationalInterpolation;
|
|
tdata.rootRotation = sdata.rootRotation;
|
|
tdata.updateMode = sdata.updateMode;
|
|
tdata.animationPoseRatio = sdata.animationPoseRatio;
|
|
tdata.reductionSetting = sdata.reductionSetting;
|
|
tdata.customSkinningSetting = sdata.customSkinningSetting;
|
|
tdata.normalAlignmentSetting = sdata.normalAlignmentSetting;
|
|
tdata.cullingSettings = sdata.cullingSettings;
|
|
tdata.normalAxis = sdata.normalAxis;
|
|
tdata.gravity = sdata.gravity;
|
|
tdata.gravityDirection = sdata.gravityDirection;
|
|
tdata.gravityFalloff = sdata.gravityFalloff;
|
|
tdata.stablizationTimeAfterReset = sdata.stablizationTimeAfterReset;
|
|
tdata.blendWeight = sdata.blendWeight;
|
|
tdata.damping = sdata.damping;
|
|
tdata.radius = sdata.radius;
|
|
// 기타 값들도 필요시 추가 복사
|
|
field.SetValue(tgt, tdata);
|
|
continue;
|
|
}
|
|
if (field.Name == "serializeData2" && value is MagicaCloth2.ClothSerializeData2 sdata2)
|
|
{
|
|
var tdata2 = new MagicaCloth2.ClothSerializeData2();
|
|
// boneAttributeDict 변환
|
|
tdata2.boneAttributeDict = sdata2.boneAttributeDict.ToDictionary(
|
|
kvp => FindCorrespondingTransform(kvp.Key, destinationPrefab.transform),
|
|
kvp => kvp.Value
|
|
);
|
|
// selectionData 등 나머지 값은 그대로 복사
|
|
tdata2.selectionData = sdata2.selectionData;
|
|
tdata2.preBuildData = sdata2.preBuildData;
|
|
field.SetValue(tgt, tdata2);
|
|
continue;
|
|
}
|
|
// customSkinningSetting.skinningBones 변환
|
|
if (field.Name == "customSkinningSetting" && value != null)
|
|
{
|
|
var srcSkin = value;
|
|
var skinField = value.GetType().GetField("skinningBones");
|
|
if (skinField != null)
|
|
{
|
|
var srcList = skinField.GetValue(srcSkin) as List<Transform>;
|
|
if (srcList != null)
|
|
{
|
|
var tgtList = srcList
|
|
.Select(bone => FindCorrespondingTransform(bone, destinationPrefab.transform))
|
|
.Where(t => t != null)
|
|
.ToList();
|
|
skinField.SetValue(srcSkin, tgtList);
|
|
}
|
|
}
|
|
field.SetValue(tgt, srcSkin);
|
|
continue;
|
|
}
|
|
// 이하 기존 로직
|
|
if (typeof(UnityEngine.Object).IsAssignableFrom(field.FieldType))
|
|
{
|
|
Object refObj = value as Object;
|
|
if (refObj == null) { field.SetValue(tgt, null); continue; }
|
|
if (refObj is Transform tr)
|
|
{
|
|
string refPath = GetTransformPath(tr, sourcePrefab.transform);
|
|
var tgtTr = destinationPrefab.transform.Find(refPath);
|
|
if (tgtTr != null)
|
|
field.SetValue(tgt, tgtTr);
|
|
}
|
|
else if (refObj is GameObject go)
|
|
{
|
|
string refPath = GetTransformPath(go.transform, sourcePrefab.transform);
|
|
var tgtGo = destinationPrefab.transform.Find(refPath);
|
|
if (tgtGo != null)
|
|
field.SetValue(tgt, tgtGo.gameObject);
|
|
}
|
|
else if (refObj is Component comp)
|
|
{
|
|
string refPath = GetTransformPath(comp.gameObject.transform, sourcePrefab.transform);
|
|
var tgtGo = destinationPrefab.transform.Find(refPath);
|
|
if (tgtGo != null)
|
|
{
|
|
var tgtComps = tgtGo.GetComponents(comp.GetType());
|
|
int compIdx = comp.gameObject.GetComponents(comp.GetType()).ToList().IndexOf(comp);
|
|
if (compIdx >= 0 && compIdx < tgtComps.Length)
|
|
field.SetValue(tgt, tgtComps[compIdx]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 기타 UnityEngine.Object 타입은 그대로 복사
|
|
field.SetValue(tgt, refObj);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 값 타입, enum, string 등은 그대로 복사
|
|
field.SetValue(tgt, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CopyConstraints()
|
|
{
|
|
var srcTransforms = sourcePrefab.GetComponentsInChildren<Transform>(true);
|
|
foreach (var srcTr in srcTransforms)
|
|
{
|
|
string path = GetTransformPath(srcTr, sourcePrefab.transform);
|
|
var tgtTr = destinationPrefab.transform.Find(path);
|
|
if (tgtTr == null) continue;
|
|
|
|
// PositionConstraint
|
|
var srcPos = srcTr.GetComponent<PositionConstraint>();
|
|
if (srcPos != null)
|
|
CopyConstraintComponent<PositionConstraint>(srcPos, tgtTr);
|
|
|
|
// RotationConstraint
|
|
var srcRot = srcTr.GetComponent<RotationConstraint>();
|
|
if (srcRot != null)
|
|
CopyConstraintComponent<RotationConstraint>(srcRot, tgtTr);
|
|
|
|
// ParentConstraint
|
|
var srcParent = srcTr.GetComponent<ParentConstraint>();
|
|
if (srcParent != null)
|
|
CopyConstraintComponent<ParentConstraint>(srcParent, tgtTr);
|
|
}
|
|
Debug.Log("Constraint 값 복사가 완료되었습니다.");
|
|
}
|
|
|
|
void CopyConstraintComponent<T>(T srcConstraint, Transform tgtTr) where T : Behaviour
|
|
{
|
|
// 이미 있으면 삭제 후 새로 추가
|
|
var tgtConstraint = tgtTr.GetComponent<T>();
|
|
if (tgtConstraint != null)
|
|
DestroyImmediate(tgtConstraint);
|
|
tgtConstraint = tgtTr.gameObject.AddComponent<T>();
|
|
|
|
if (srcConstraint is PositionConstraint srcPos && tgtConstraint is PositionConstraint tgtPos)
|
|
{
|
|
tgtPos.weight = srcPos.weight;
|
|
tgtPos.constraintActive = srcPos.constraintActive;
|
|
tgtPos.locked = srcPos.locked;
|
|
for (int i = 0; i < srcPos.sourceCount; i++)
|
|
{
|
|
var src = srcPos.GetSource(i);
|
|
var srcTr = src.sourceTransform;
|
|
if (srcTr == null) continue;
|
|
string srcPath = GetTransformPath(srcTr, sourcePrefab.transform);
|
|
var tgtSourceTr = destinationPrefab.transform.Find(srcPath);
|
|
if (tgtSourceTr == null) continue;
|
|
var newSource = src;
|
|
newSource.sourceTransform = tgtSourceTr;
|
|
tgtPos.AddSource(newSource);
|
|
}
|
|
tgtPos.translationAtRest = srcPos.translationAtRest;
|
|
tgtPos.translationOffset = srcPos.translationOffset;
|
|
tgtPos.translationAxis = srcPos.translationAxis;
|
|
}
|
|
else if (srcConstraint is RotationConstraint srcRot && tgtConstraint is RotationConstraint tgtRot)
|
|
{
|
|
tgtRot.weight = srcRot.weight;
|
|
tgtRot.constraintActive = srcRot.constraintActive;
|
|
tgtRot.locked = srcRot.locked;
|
|
for (int i = 0; i < srcRot.sourceCount; i++)
|
|
{
|
|
var src = srcRot.GetSource(i);
|
|
var srcTr = src.sourceTransform;
|
|
if (srcTr == null) continue;
|
|
string srcPath = GetTransformPath(srcTr, sourcePrefab.transform);
|
|
var tgtSourceTr = destinationPrefab.transform.Find(srcPath);
|
|
if (tgtSourceTr == null) continue;
|
|
var newSource = src;
|
|
newSource.sourceTransform = tgtSourceTr;
|
|
tgtRot.AddSource(newSource);
|
|
}
|
|
tgtRot.rotationAtRest = srcRot.rotationAtRest;
|
|
tgtRot.rotationOffset = srcRot.rotationOffset;
|
|
tgtRot.rotationAxis = srcRot.rotationAxis;
|
|
}
|
|
else if (srcConstraint is ParentConstraint srcParent && tgtConstraint is ParentConstraint tgtParent)
|
|
{
|
|
tgtParent.weight = srcParent.weight;
|
|
tgtParent.constraintActive = srcParent.constraintActive;
|
|
tgtParent.locked = srcParent.locked;
|
|
for (int i = 0; i < srcParent.sourceCount; i++)
|
|
{
|
|
var src = srcParent.GetSource(i);
|
|
var srcTr = src.sourceTransform;
|
|
if (srcTr == null) continue;
|
|
string srcPath = GetTransformPath(srcTr, sourcePrefab.transform);
|
|
var tgtSourceTr = destinationPrefab.transform.Find(srcPath);
|
|
if (tgtSourceTr == null) continue;
|
|
var newSource = src;
|
|
newSource.sourceTransform = tgtSourceTr;
|
|
tgtParent.AddSource(newSource);
|
|
// Offset도 복사
|
|
tgtParent.SetTranslationOffset(i, srcParent.GetTranslationOffset(i));
|
|
tgtParent.SetRotationOffset(i, srcParent.GetRotationOffset(i));
|
|
}
|
|
tgtParent.translationAtRest = srcParent.translationAtRest;
|
|
tgtParent.rotationAtRest = srcParent.rotationAtRest;
|
|
tgtParent.translationAxis = srcParent.translationAxis;
|
|
tgtParent.rotationAxis = srcParent.rotationAxis;
|
|
}
|
|
}
|
|
|
|
void CopyBlendShapes()
|
|
{
|
|
var srcRenderers = sourcePrefab.GetComponentsInChildren<SkinnedMeshRenderer>(true);
|
|
foreach (var srcRenderer in srcRenderers)
|
|
{
|
|
string path = GetTransformPath(srcRenderer.transform, sourcePrefab.transform);
|
|
var tgtTransform = destinationPrefab.transform.Find(path);
|
|
if (tgtTransform == null) continue;
|
|
var tgtRenderer = tgtTransform.GetComponent<SkinnedMeshRenderer>();
|
|
if (tgtRenderer == null) continue;
|
|
int blendShapeCount = srcRenderer.sharedMesh != null ? srcRenderer.sharedMesh.blendShapeCount : 0;
|
|
for (int i = 0; i < blendShapeCount; i++)
|
|
{
|
|
string shapeName = srcRenderer.sharedMesh.GetBlendShapeName(i);
|
|
int tgtIndex = tgtRenderer.sharedMesh != null ? tgtRenderer.sharedMesh.GetBlendShapeIndex(shapeName) : -1;
|
|
if (tgtIndex >= 0)
|
|
{
|
|
float value = srcRenderer.GetBlendShapeWeight(i);
|
|
tgtRenderer.SetBlendShapeWeight(tgtIndex, value);
|
|
}
|
|
}
|
|
}
|
|
Debug.Log("BlendShape 값 복사가 완료되었습니다.");
|
|
}
|
|
|
|
void CopyActiveStates()
|
|
{
|
|
var srcTransforms = sourcePrefab.GetComponentsInChildren<Transform>(true);
|
|
foreach (var srcTr in srcTransforms)
|
|
{
|
|
string path = GetTransformPath(srcTr, sourcePrefab.transform);
|
|
var tgtTr = destinationPrefab.transform.Find(path);
|
|
if (tgtTr == null) continue;
|
|
tgtTr.gameObject.SetActive(srcTr.gameObject.activeSelf);
|
|
}
|
|
Debug.Log("ActiveState 복사가 완료되었습니다.");
|
|
}
|
|
} |