Refactor : LimbWeightController, FingerShapedController를 Serializable 모듈로 전환
- LimbWeightController: MonoBehaviour → [Serializable] 모듈, CRS.limbWeight로 통합 - FingerShapedController: MonoBehaviour → [Serializable] 모듈, CRS.fingerShaped로 통합 - GetHand()를 FindObjectsOfType<LimbWeightController> → FindObjectsByType<CustomRetargetingScript>로 리팩토링 - HumanPoseHandler 라이프사이클을 Initialize/Cleanup 패턴으로 전환 - RetargetingControlWindow: 모든 GetComponent 호출을 CRS SO의 중첩 프로퍼티 경로로 변경 - RetargetingRemoteController: 직접 script.limbWeight/fingerShaped 접근으로 변경 - LimbWeightControllerEditor, FingerShapedControllerEditor 삭제 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
62a5a9bbb5
commit
e4ca30b98a
@ -104,6 +104,12 @@ namespace KindRetargeting
|
||||
[Header("프랍 부착")]
|
||||
[SerializeField] public PropLocationController propLocation = new PropLocationController();
|
||||
|
||||
[Header("사지 가중치")]
|
||||
[SerializeField] public LimbWeightController limbWeight = new LimbWeightController();
|
||||
|
||||
[Header("손가락 셰이핑")]
|
||||
[SerializeField] public FingerShapedController fingerShaped = new FingerShapedController();
|
||||
|
||||
[Header("아바타 크기 조정")]
|
||||
[SerializeField, Range(0.1f, 3f)] private float avatarScale = 1f;
|
||||
private float previousScale = 1f;
|
||||
@ -337,6 +343,13 @@ namespace KindRetargeting
|
||||
// 프랍 부착 모듈 초기화
|
||||
if (targetAnimator != null)
|
||||
propLocation.Initialize(targetAnimator);
|
||||
|
||||
// 사지 가중치 모듈 초기화
|
||||
limbWeight.Initialize(ikSolver, this, transform);
|
||||
|
||||
// 손가락 셰이핑 모듈 초기화
|
||||
if (targetAnimator != null)
|
||||
fingerShaped.Initialize(targetAnimator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -534,8 +547,7 @@ namespace KindRetargeting
|
||||
}
|
||||
|
||||
// LimbWeightController에서 의자 높이 오프셋 가져오기
|
||||
var limbController = GetComponent<LimbWeightController>();
|
||||
float chairOffset = limbController != null ? limbController.chairSeatHeightOffset : 0.05f;
|
||||
float chairOffset = limbWeight.chairSeatHeightOffset;
|
||||
|
||||
var settings = new RetargetingSettings
|
||||
{
|
||||
@ -613,11 +625,7 @@ namespace KindRetargeting
|
||||
headScale = settings.headScale;
|
||||
|
||||
// LimbWeightController에 의자 높이 오프셋 적용
|
||||
var limbController = GetComponent<LimbWeightController>();
|
||||
if (limbController != null)
|
||||
{
|
||||
limbController.chairSeatHeightOffset = settings.chairSeatHeightOffset;
|
||||
}
|
||||
limbWeight.chairSeatHeightOffset = settings.chairSeatHeightOffset;
|
||||
|
||||
// 머리 회전 오프셋 로드
|
||||
headRotationOffsetX = settings.headRotationOffsetX;
|
||||
@ -834,9 +842,15 @@ namespace KindRetargeting
|
||||
break;
|
||||
}
|
||||
|
||||
// 손가락 셰이핑 (기존 ExecutionOrder 2)
|
||||
fingerShaped.OnUpdate();
|
||||
|
||||
// 어깨 보정 (기존 ExecutionOrder 3)
|
||||
shoulderCorrection.OnUpdate();
|
||||
|
||||
// 사지 가중치 (기존 ExecutionOrder 4)
|
||||
limbWeight.OnUpdate();
|
||||
|
||||
// 발 접지 Pre-IK (기존 ExecutionOrder 5)
|
||||
footGrounding.OnUpdate();
|
||||
|
||||
@ -1404,6 +1418,7 @@ namespace KindRetargeting
|
||||
{
|
||||
sourcePoseHandler?.Dispose();
|
||||
targetPoseHandler?.Dispose();
|
||||
fingerShaped.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -1,193 +0,0 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.UIElements;
|
||||
|
||||
[CustomEditor(typeof(FingerShapedController))]
|
||||
public class FingerShapedControllerEditor : Editor
|
||||
{
|
||||
private const string CommonUssPath = "Assets/Scripts/Streamingle/StreamingleControl/Editor/UXML/StreamingleCommon.uss";
|
||||
private static readonly string[] fingerPropNames = { "ThumbCurl", "IndexCurl", "MiddleCurl", "RingCurl", "PinkyCurl" };
|
||||
private static readonly string[] fingerKoreanNames = { "엄지", "검지", "중지", "약지", "새끼" };
|
||||
|
||||
private FingerShapedController controller;
|
||||
|
||||
public override VisualElement CreateInspectorGUI()
|
||||
{
|
||||
controller = target as FingerShapedController;
|
||||
if (controller == null) return new VisualElement();
|
||||
var root = new VisualElement();
|
||||
|
||||
var commonUss = AssetDatabase.LoadAssetAtPath<StyleSheet>(CommonUssPath);
|
||||
if (commonUss != null) root.styleSheets.Add(commonUss);
|
||||
|
||||
// 활성화 토글
|
||||
var enabledProp = serializedObject.FindProperty("m_Enabled");
|
||||
var enableToggle = new PropertyField(enabledProp, "손가락 제어 활성화");
|
||||
root.Add(enableToggle);
|
||||
|
||||
// 왼손
|
||||
var leftFoldout = new Foldout { text = "왼손", value = true };
|
||||
leftFoldout.Add(BuildHandControls("left"));
|
||||
root.Add(leftFoldout);
|
||||
|
||||
// 오른손
|
||||
var rightFoldout = new Foldout { text = "오른손", value = true };
|
||||
rightFoldout.Add(BuildHandControls("right"));
|
||||
root.Add(rightFoldout);
|
||||
|
||||
// 프리셋 버튼
|
||||
root.Add(BuildPresetButtons());
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private VisualElement BuildHandControls(string prefix)
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.style.backgroundColor = new Color(0, 0, 0, 0.08f);
|
||||
container.style.borderTopLeftRadius = container.style.borderTopRightRadius =
|
||||
container.style.borderBottomLeftRadius = container.style.borderBottomRightRadius = 4;
|
||||
container.style.paddingTop = container.style.paddingBottom =
|
||||
container.style.paddingLeft = container.style.paddingRight = 6;
|
||||
|
||||
// 헤더: 활성화 토글 + 초기화 버튼
|
||||
var header = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center, marginBottom = 4 } };
|
||||
var handEnabledProp = serializedObject.FindProperty($"{prefix}HandEnabled");
|
||||
header.Add(new PropertyField(handEnabledProp, "제어 활성화") { style = { flexGrow = 1 } });
|
||||
var resetBtn = new Button(() => ResetHandValues(prefix)) { text = "초기화" };
|
||||
resetBtn.style.width = 60;
|
||||
header.Add(resetBtn);
|
||||
container.Add(header);
|
||||
|
||||
// 손가락 슬라이더 (가로 배치)
|
||||
var slidersRow = new VisualElement { style = { flexDirection = FlexDirection.Row, justifyContent = Justify.Center, marginTop = 4 } };
|
||||
|
||||
for (int i = 0; i < fingerPropNames.Length; i++)
|
||||
{
|
||||
var fingerProp = serializedObject.FindProperty($"{prefix}{fingerPropNames[i]}");
|
||||
slidersRow.Add(BuildVerticalSlider(fingerKoreanNames[i], fingerProp));
|
||||
}
|
||||
container.Add(slidersRow);
|
||||
|
||||
// 벌리기 슬라이더
|
||||
var spreadProp = serializedObject.FindProperty($"{prefix}SpreadFingers");
|
||||
var spreadSlider = new Slider("벌리기", -1f, 1f) { showInputField = true };
|
||||
spreadSlider.BindProperty(spreadProp);
|
||||
spreadSlider.style.marginTop = 8;
|
||||
container.Add(spreadSlider);
|
||||
|
||||
// 비활성 시 숨기기
|
||||
container.TrackPropertyValue(handEnabledProp, prop =>
|
||||
{
|
||||
slidersRow.style.display = prop.boolValue ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
spreadSlider.style.display = prop.boolValue ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
});
|
||||
slidersRow.style.display = handEnabledProp.boolValue ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
spreadSlider.style.display = handEnabledProp.boolValue ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement BuildVerticalSlider(string label, SerializedProperty prop)
|
||||
{
|
||||
var column = new VisualElement { style = { alignItems = Align.Center, width = 45, marginLeft = 2, marginRight = 2 } };
|
||||
|
||||
column.Add(new Label(label) { style = { fontSize = 10, color = new Color(0.6f, 0.6f, 0.6f) } });
|
||||
|
||||
var valueLabel = new Label(prop.floatValue.ToString("F1")) { style = { fontSize = 10, color = new Color(0.6f, 0.6f, 0.6f) } };
|
||||
column.Add(valueLabel);
|
||||
|
||||
// UI Toolkit Slider in vertical mode (direction: Vertical 대신 세로용 Slider 사용)
|
||||
// SliderDirection.Vertical 사용
|
||||
var slider = new Slider(-1f, 1f) { direction = SliderDirection.Vertical };
|
||||
slider.style.height = 100;
|
||||
slider.style.width = 25;
|
||||
slider.BindProperty(prop);
|
||||
slider.RegisterValueChangedCallback(evt => valueLabel.text = evt.newValue.ToString("F1"));
|
||||
|
||||
// 초기값 동기화
|
||||
column.TrackPropertyValue(prop, p => valueLabel.text = p.floatValue.ToString("F1"));
|
||||
|
||||
column.Add(slider);
|
||||
return column;
|
||||
}
|
||||
|
||||
private VisualElement BuildPresetButtons()
|
||||
{
|
||||
var container = new VisualElement { style = { marginTop = 10 } };
|
||||
container.Add(new Label("손 모양 프리셋") { style = { unityFontStyleAndWeight = FontStyle.Bold, marginBottom = 4 } });
|
||||
|
||||
string[,] presets = {
|
||||
{ "가위", "바위", "보" },
|
||||
{ "브이", "검지", "초기화" }
|
||||
};
|
||||
|
||||
for (int row = 0; row < 2; row++)
|
||||
{
|
||||
var btnRow = new VisualElement { style = { flexDirection = FlexDirection.Row, justifyContent = Justify.Center, marginBottom = 4 } };
|
||||
for (int col = 0; col < 3; col++)
|
||||
{
|
||||
string presetName = presets[row, col];
|
||||
var btn = new Button(() => ApplyPreset(presetName)) { text = presetName };
|
||||
btn.style.height = 30;
|
||||
btn.style.width = 100;
|
||||
btn.style.marginLeft = btn.style.marginRight = 4;
|
||||
btnRow.Add(btn);
|
||||
}
|
||||
container.Add(btnRow);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private void ResetHandValues(string prefix)
|
||||
{
|
||||
serializedObject.Update();
|
||||
string[] props = { "ThumbCurl", "IndexCurl", "MiddleCurl", "RingCurl", "PinkyCurl", "SpreadFingers" };
|
||||
foreach (var prop in props)
|
||||
serializedObject.FindProperty($"{prefix}{prop}").floatValue = 0f;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void ApplyPreset(string presetName)
|
||||
{
|
||||
if (!controller.enabled)
|
||||
controller.enabled = true;
|
||||
|
||||
switch (presetName)
|
||||
{
|
||||
case "가위": SetPreset(1f, 1f, -1f, -1f, -1f, 0.3f); break;
|
||||
case "바위": SetPreset(-1f, -1f, -1f, -1f, -1f, 0f); break;
|
||||
case "보": SetPreset(1f, 1f, 1f, 1f, 1f, 1f); break;
|
||||
case "브이": SetPreset(-1f, 1f, 1f, -1f, -1f, 1f); break;
|
||||
case "검지": SetPreset(-1f, 1f, -1f, -1f, -1f, 0f); break;
|
||||
case "초기화": SetPreset(0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPreset(float thumb, float index, float middle, float ring, float pinky, float spread)
|
||||
{
|
||||
if (controller.leftHandEnabled)
|
||||
{
|
||||
controller.leftThumbCurl = thumb;
|
||||
controller.leftIndexCurl = index;
|
||||
controller.leftMiddleCurl = middle;
|
||||
controller.leftRingCurl = ring;
|
||||
controller.leftPinkyCurl = pinky;
|
||||
controller.leftSpreadFingers = spread;
|
||||
}
|
||||
|
||||
if (controller.rightHandEnabled)
|
||||
{
|
||||
controller.rightThumbCurl = thumb;
|
||||
controller.rightIndexCurl = index;
|
||||
controller.rightMiddleCurl = middle;
|
||||
controller.rightRingCurl = ring;
|
||||
controller.rightPinkyCurl = pinky;
|
||||
controller.rightSpreadFingers = spread;
|
||||
}
|
||||
|
||||
EditorUtility.SetDirty(controller);
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 291a583b9a953e041a119ba6c332d187
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,185 +0,0 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.UIElements;
|
||||
|
||||
namespace KindRetargeting
|
||||
{
|
||||
[CustomEditor(typeof(LimbWeightController))]
|
||||
public class LimbWeightControllerEditor : BaseRetargetingEditor
|
||||
{
|
||||
private const string CommonUssPath = "Assets/Scripts/Streamingle/StreamingleControl/Editor/UXML/StreamingleCommon.uss";
|
||||
|
||||
SerializedProperty maxDistance;
|
||||
SerializedProperty minDistance;
|
||||
SerializedProperty weightSmoothSpeed;
|
||||
SerializedProperty middleWeightMultiplier;
|
||||
SerializedProperty hipsMinDistance;
|
||||
SerializedProperty hipsMaxDistance;
|
||||
SerializedProperty props;
|
||||
SerializedProperty characterRoot;
|
||||
SerializedProperty groundHipsMinHeight;
|
||||
SerializedProperty groundHipsMaxHeight;
|
||||
SerializedProperty footHeightMinThreshold;
|
||||
SerializedProperty footHeightMaxThreshold;
|
||||
SerializedProperty enableLeftArmIK;
|
||||
SerializedProperty enableRightArmIK;
|
||||
SerializedProperty chairSeatHeightOffset;
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
if (serializedObject == null) return;
|
||||
|
||||
maxDistance = serializedObject.FindProperty("maxDistance");
|
||||
minDistance = serializedObject.FindProperty("minDistance");
|
||||
weightSmoothSpeed = serializedObject.FindProperty("weightSmoothSpeed");
|
||||
middleWeightMultiplier = serializedObject.FindProperty("middleWeightMultiplier");
|
||||
hipsMinDistance = serializedObject.FindProperty("hipsMinDistance");
|
||||
hipsMaxDistance = serializedObject.FindProperty("hipsMaxDistance");
|
||||
props = serializedObject.FindProperty("props");
|
||||
characterRoot = serializedObject.FindProperty("characterRoot");
|
||||
groundHipsMinHeight = serializedObject.FindProperty("groundHipsMinHeight");
|
||||
groundHipsMaxHeight = serializedObject.FindProperty("groundHipsMaxHeight");
|
||||
footHeightMinThreshold = serializedObject.FindProperty("footHeightMinThreshold");
|
||||
footHeightMaxThreshold = serializedObject.FindProperty("footHeightMaxThreshold");
|
||||
enableLeftArmIK = serializedObject.FindProperty("enableLeftArmIK");
|
||||
enableRightArmIK = serializedObject.FindProperty("enableRightArmIK");
|
||||
chairSeatHeightOffset = serializedObject.FindProperty("chairSeatHeightOffset");
|
||||
}
|
||||
|
||||
public override VisualElement CreateInspectorGUI()
|
||||
{
|
||||
if (serializedObject == null || target == null)
|
||||
return new VisualElement();
|
||||
|
||||
var root = new VisualElement();
|
||||
|
||||
var commonUss = AssetDatabase.LoadAssetAtPath<StyleSheet>(CommonUssPath);
|
||||
if (commonUss != null) root.styleSheets.Add(commonUss);
|
||||
|
||||
// IK 활성화 설정
|
||||
var ikFoldout = new Foldout { text = "IK 활성화 설정", value = true };
|
||||
ikFoldout.Add(new PropertyField(enableLeftArmIK, "왼팔 IK 활성화"));
|
||||
ikFoldout.Add(new PropertyField(enableRightArmIK, "오른팔 IK 활성화"));
|
||||
root.Add(ikFoldout);
|
||||
|
||||
// 거리 기반 가중치 설정
|
||||
var distFoldout = new Foldout { text = "거리 기반 가중치 설정", value = true };
|
||||
distFoldout.Add(BuildMinMaxRange("거리 범위 (가중치 1 → 0)", minDistance, maxDistance, 0f, 1f));
|
||||
root.Add(distFoldout);
|
||||
|
||||
// 가중치 변화 설정
|
||||
var weightFoldout = new Foldout { text = "가중치 변화 설정", value = true };
|
||||
weightFoldout.Add(new PropertyField(weightSmoothSpeed, "가중치 변화 속도"));
|
||||
root.Add(weightFoldout);
|
||||
|
||||
// 허리 가중치 설정
|
||||
var hipsFoldout = new Foldout { text = "허리 가중치 설정", value = true };
|
||||
hipsFoldout.Add(BuildMinMaxRange("허리 거리 범위 (가중치 1 → 0)", hipsMinDistance, hipsMaxDistance, 0f, 1f));
|
||||
root.Add(hipsFoldout);
|
||||
|
||||
// 바닥 기준 히프 보정 설정
|
||||
var groundFoldout = new Foldout { text = "바닥 기준 히프 보정 설정", value = true };
|
||||
groundFoldout.Add(BuildMinMaxRange("바닥 기준 히프 높이 범위 (가중치 0 → 1)", groundHipsMinHeight, groundHipsMaxHeight, 0f, 2f));
|
||||
root.Add(groundFoldout);
|
||||
|
||||
// 발 높이 기반 가중치 설정
|
||||
var footFoldout = new Foldout { text = "발 높이 기반 가중치 설정", value = true };
|
||||
footFoldout.Add(BuildMinMaxRange("발 높이 범위 (가중치 1 → 0)", footHeightMinThreshold, footHeightMaxThreshold, 0.1f, 1f));
|
||||
root.Add(footFoldout);
|
||||
|
||||
// 의자 앉기 높이 설정
|
||||
var chairFoldout = new Foldout { text = "의자 앉기 높이 설정", value = true };
|
||||
chairFoldout.Add(new PropertyField(chairSeatHeightOffset, "좌석 높이 오프셋"));
|
||||
root.Add(chairFoldout);
|
||||
|
||||
// 참조 설정
|
||||
var refFoldout = new Foldout { text = "참조 설정", value = true };
|
||||
refFoldout.Add(new PropertyField(props, "프랍 오브젝트"));
|
||||
refFoldout.Add(new PropertyField(characterRoot, "캐릭터 루트"));
|
||||
root.Add(refFoldout);
|
||||
|
||||
// 변경 감지
|
||||
root.TrackSerializedObjectValue(serializedObject, so =>
|
||||
{
|
||||
if (target == null) return;
|
||||
MarkDirty();
|
||||
var script = (LimbWeightController)target;
|
||||
var retargeting = script.GetComponent<CustomRetargetingScript>();
|
||||
if (retargeting != null)
|
||||
EditorUtility.SetDirty(retargeting);
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private VisualElement BuildMinMaxRange(string label, SerializedProperty minProp, SerializedProperty maxProp, float limitMin, float limitMax)
|
||||
{
|
||||
var container = new VisualElement { style = { marginBottom = 4 } };
|
||||
if (minProp == null || maxProp == null)
|
||||
{
|
||||
container.Add(new HelpBox($"'{label}' 프로퍼티를 찾을 수 없습니다.", HelpBoxMessageType.Warning));
|
||||
return container;
|
||||
}
|
||||
container.Add(new Label(label) { style = { marginBottom = 2 } });
|
||||
|
||||
var row = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center } };
|
||||
|
||||
var minField = new FloatField { value = minProp.floatValue, style = { width = 50 } };
|
||||
var slider = new MinMaxSlider(minProp.floatValue, maxProp.floatValue, limitMin, limitMax);
|
||||
slider.style.flexGrow = 1;
|
||||
slider.style.marginLeft = slider.style.marginRight = 4;
|
||||
var maxField = new FloatField { value = maxProp.floatValue, style = { width = 50 } };
|
||||
|
||||
// MinMaxSlider → sync fields + properties
|
||||
slider.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
minProp.floatValue = evt.newValue.x;
|
||||
maxProp.floatValue = evt.newValue.y;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
minField.SetValueWithoutNotify(evt.newValue.x);
|
||||
maxField.SetValueWithoutNotify(evt.newValue.y);
|
||||
});
|
||||
|
||||
// FloatField min → sync slider + property
|
||||
minField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
float clamped = Mathf.Clamp(evt.newValue, limitMin, maxProp.floatValue);
|
||||
minProp.floatValue = clamped;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
slider.SetValueWithoutNotify(new Vector2(clamped, maxProp.floatValue));
|
||||
minField.SetValueWithoutNotify(clamped);
|
||||
});
|
||||
|
||||
// FloatField max → sync slider + property
|
||||
maxField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
float clamped = Mathf.Clamp(evt.newValue, minProp.floatValue, limitMax);
|
||||
maxProp.floatValue = clamped;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
slider.SetValueWithoutNotify(new Vector2(minProp.floatValue, clamped));
|
||||
maxField.SetValueWithoutNotify(clamped);
|
||||
});
|
||||
|
||||
// Track external changes (undo, etc.)
|
||||
container.TrackPropertyValue(minProp, p =>
|
||||
{
|
||||
minField.SetValueWithoutNotify(p.floatValue);
|
||||
slider.SetValueWithoutNotify(new Vector2(p.floatValue, maxProp.floatValue));
|
||||
});
|
||||
container.TrackPropertyValue(maxProp, p =>
|
||||
{
|
||||
maxField.SetValueWithoutNotify(p.floatValue);
|
||||
slider.SetValueWithoutNotify(new Vector2(minProp.floatValue, p.floatValue));
|
||||
});
|
||||
|
||||
row.Add(minField);
|
||||
row.Add(slider);
|
||||
row.Add(maxField);
|
||||
container.Add(row);
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 199539b34f08aac41a86f4767bc49def
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -144,11 +144,7 @@ public class RetargetingControlWindow : EditorWindow
|
||||
foreach (var s in retargetingScripts)
|
||||
{
|
||||
if (s == null) continue;
|
||||
if (s.GetComponent<LimbWeightController>() == null)
|
||||
s.gameObject.AddComponent<LimbWeightController>();
|
||||
if (s.GetComponent<FingerShapedController>() == null)
|
||||
s.gameObject.AddComponent<FingerShapedController>();
|
||||
// PropLocationController는 CRS 내부 모듈로 이동됨
|
||||
// 모든 컴포넌트는 CRS 내부 모듈로 이동됨
|
||||
EditorUtility.SetDirty(s.gameObject);
|
||||
}
|
||||
|
||||
@ -197,7 +193,7 @@ public class RetargetingControlWindow : EditorWindow
|
||||
panel.Add(BuildHeader(script));
|
||||
|
||||
// 가중치 설정
|
||||
panel.Add(BuildWeightSection(script));
|
||||
panel.Add(BuildWeightSection(script, so));
|
||||
|
||||
// 힙 위치 보정
|
||||
panel.Add(BuildHipsSection(script, so));
|
||||
@ -229,7 +225,7 @@ public class RetargetingControlWindow : EditorWindow
|
||||
panel.Add(footFoldout);
|
||||
|
||||
// 손가락 제어 설정
|
||||
panel.Add(BuildFingerControlSection(script));
|
||||
panel.Add(BuildFingerControlSection(script, so));
|
||||
|
||||
// 손가락 복제 설정
|
||||
panel.Add(BuildFingerCopySection(script, so));
|
||||
@ -303,27 +299,22 @@ public class RetargetingControlWindow : EditorWindow
|
||||
|
||||
// ========== Weight Settings ==========
|
||||
|
||||
private VisualElement BuildWeightSection(CustomRetargetingScript script)
|
||||
private VisualElement BuildWeightSection(CustomRetargetingScript script, SerializedObject so)
|
||||
{
|
||||
var foldout = new Foldout { text = "가중치 설정", value = false };
|
||||
var limb = script.GetComponent<LimbWeightController>();
|
||||
if (limb == null) { foldout.Add(new HelpBox("LimbWeightController가 없습니다.", HelpBoxMessageType.Warning)); return foldout; }
|
||||
|
||||
var limbSO = CreateTrackedSO(limb);
|
||||
var container = new VisualElement();
|
||||
|
||||
container.Add(BuildMinMaxRange("손과 프랍과의 범위 (가중치 1 → 0)",
|
||||
limbSO.FindProperty("minDistance"), limbSO.FindProperty("maxDistance"), 0f, 1f, limbSO));
|
||||
so.FindProperty("limbWeight.minDistance"), so.FindProperty("limbWeight.maxDistance"), 0f, 1f, so));
|
||||
container.Add(BuildMinMaxRange("의자와 허리 거리 범위 (가중치 1 → 0)",
|
||||
limbSO.FindProperty("hipsMinDistance"), limbSO.FindProperty("hipsMaxDistance"), 0f, 1f, limbSO));
|
||||
so.FindProperty("limbWeight.hipsMinDistance"), so.FindProperty("limbWeight.hipsMaxDistance"), 0f, 1f, so));
|
||||
container.Add(BuildMinMaxRange("바닥과 허리 높이에 의한 블렌딩 (가중치 0 → 1)",
|
||||
limbSO.FindProperty("groundHipsMinHeight"), limbSO.FindProperty("groundHipsMaxHeight"), 0f, 2f, limbSO));
|
||||
so.FindProperty("limbWeight.groundHipsMinHeight"), so.FindProperty("limbWeight.groundHipsMaxHeight"), 0f, 2f, so));
|
||||
container.Add(BuildMinMaxRange("지면으로부터 발의 범위에 의한 IK 블렌딩 (가중치 1 → 0)",
|
||||
limbSO.FindProperty("footHeightMinThreshold"), limbSO.FindProperty("footHeightMaxThreshold"), 0.1f, 1f, limbSO));
|
||||
so.FindProperty("limbWeight.footHeightMinThreshold"), so.FindProperty("limbWeight.footHeightMaxThreshold"), 0.1f, 1f, so));
|
||||
|
||||
var smoothField = new PropertyField(limbSO.FindProperty("weightSmoothSpeed"), "가중치 변화 속도");
|
||||
var smoothField = new PropertyField(so.FindProperty("limbWeight.weightSmoothSpeed"), "가중치 변화 속도");
|
||||
container.Add(smoothField);
|
||||
container.Bind(limbSO);
|
||||
|
||||
foldout.Add(container);
|
||||
return foldout;
|
||||
@ -366,15 +357,9 @@ public class RetargetingControlWindow : EditorWindow
|
||||
container.Add(hz);
|
||||
|
||||
// 의자 앉기 높이
|
||||
var limb = script.GetComponent<LimbWeightController>();
|
||||
if (limb != null)
|
||||
{
|
||||
var limbSO = CreateTrackedSO(limb);
|
||||
var chairSlider = new Slider("의자 앉기 높이", -1f, 1f) { showInputField = true, tooltip = "의자에 앉을 때 엉덩이 높이 조정 (월드 Y 기준)" };
|
||||
chairSlider.BindProperty(limbSO.FindProperty("chairSeatHeightOffset"));
|
||||
chairSlider.BindProperty(so.FindProperty("limbWeight.chairSeatHeightOffset"));
|
||||
container.Add(chairSlider);
|
||||
chairSlider.Bind(limbSO);
|
||||
}
|
||||
|
||||
// 다리 길이 자동 보정 버튼
|
||||
var autoHipsBtn = new Button(() =>
|
||||
@ -402,48 +387,28 @@ public class RetargetingControlWindow : EditorWindow
|
||||
|
||||
// ========== Finger Control ==========
|
||||
|
||||
private VisualElement BuildFingerControlSection(CustomRetargetingScript script)
|
||||
private VisualElement BuildFingerControlSection(CustomRetargetingScript script, SerializedObject so)
|
||||
{
|
||||
var foldout = new Foldout { text = "손가락 제어 설정", value = false };
|
||||
var fingerController = script.GetComponent<FingerShapedController>();
|
||||
if (fingerController == null)
|
||||
{
|
||||
foldout.Add(new HelpBox("FingerShapedController가 없습니다.", HelpBoxMessageType.Warning));
|
||||
var addBtn = new Button(() =>
|
||||
{
|
||||
script.gameObject.AddComponent<FingerShapedController>();
|
||||
EditorUtility.SetDirty(script.gameObject);
|
||||
RebuildCharacterPanels();
|
||||
}) { text = "FingerShapedController 추가" };
|
||||
foldout.Add(addBtn);
|
||||
return foldout;
|
||||
}
|
||||
|
||||
var fso = CreateTrackedSO(fingerController);
|
||||
var container = new VisualElement();
|
||||
|
||||
// 활성화 토글
|
||||
var enableToggle = new Toggle("손가락 제어 활성화") { value = fingerController.enabled };
|
||||
enableToggle.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
fingerController.enabled = evt.newValue;
|
||||
EditorUtility.SetDirty(fingerController);
|
||||
});
|
||||
var enabledProp = so.FindProperty("fingerShaped.enabled");
|
||||
var enableToggle = new PropertyField(enabledProp, "손가락 제어 활성화");
|
||||
container.Add(enableToggle);
|
||||
|
||||
// 왼손
|
||||
container.Add(BuildHandSection("왼손", "left", fso, fingerController));
|
||||
container.Add(BuildHandSection("왼손", "left", so, script.fingerShaped));
|
||||
// 오른손
|
||||
container.Add(BuildHandSection("오른손", "right", fso, fingerController));
|
||||
container.Add(BuildHandSection("오른손", "right", so, script.fingerShaped));
|
||||
// 프리셋
|
||||
container.Add(BuildFingerPresets(fingerController));
|
||||
container.Add(BuildFingerPresets(script, script.fingerShaped));
|
||||
|
||||
container.Bind(fso);
|
||||
foldout.Add(container);
|
||||
return foldout;
|
||||
}
|
||||
|
||||
private VisualElement BuildHandSection(string label, string prefix, SerializedObject fso, FingerShapedController fc)
|
||||
private VisualElement BuildHandSection(string label, string prefix, SerializedObject so, FingerShapedController fc)
|
||||
{
|
||||
var box = new VisualElement();
|
||||
box.style.backgroundColor = new Color(0, 0, 0, 0.08f);
|
||||
@ -455,13 +420,13 @@ public class RetargetingControlWindow : EditorWindow
|
||||
|
||||
var header = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center } };
|
||||
var handFoldout = new Foldout { text = label, value = false };
|
||||
var handEnabledProp = fso.FindProperty($"{prefix}HandEnabled");
|
||||
var handEnabledProp = so.FindProperty($"fingerShaped.{prefix}HandEnabled");
|
||||
header.Add(new PropertyField(handEnabledProp, "활성화") { style = { flexGrow = 1 } });
|
||||
var resetBtn = new Button(() =>
|
||||
{
|
||||
string[] props = { "ThumbCurl", "IndexCurl", "MiddleCurl", "RingCurl", "PinkyCurl", "SpreadFingers" };
|
||||
foreach (var p in props) fso.FindProperty($"{prefix}{p}").floatValue = 0f;
|
||||
fso.ApplyModifiedProperties();
|
||||
foreach (var p in props) so.FindProperty($"fingerShaped.{prefix}{p}").floatValue = 0f;
|
||||
so.ApplyModifiedProperties();
|
||||
}) { text = "초기화" };
|
||||
resetBtn.style.width = 60;
|
||||
header.Add(resetBtn);
|
||||
@ -477,7 +442,7 @@ public class RetargetingControlWindow : EditorWindow
|
||||
var col = new VisualElement { style = { alignItems = Align.Center, width = 45, marginLeft = 2, marginRight = 2 } };
|
||||
col.Add(new Label(korNames[i]) { style = { fontSize = 10, color = new Color(0.6f, 0.6f, 0.6f) } });
|
||||
|
||||
var prop = fso.FindProperty($"{prefix}{fingerNames[i]}Curl");
|
||||
var prop = so.FindProperty($"fingerShaped.{prefix}{fingerNames[i]}Curl");
|
||||
var valLabel = new Label(prop.floatValue.ToString("F1")) { style = { fontSize = 10, color = new Color(0.6f, 0.6f, 0.6f) } };
|
||||
col.Add(valLabel);
|
||||
|
||||
@ -494,16 +459,18 @@ public class RetargetingControlWindow : EditorWindow
|
||||
|
||||
// 벌리기
|
||||
var spreadSlider = new Slider("벌리기", -1f, 1f) { showInputField = true };
|
||||
spreadSlider.BindProperty(fso.FindProperty($"{prefix}SpreadFingers"));
|
||||
spreadSlider.BindProperty(so.FindProperty($"fingerShaped.{prefix}SpreadFingers"));
|
||||
handFoldout.Add(spreadSlider);
|
||||
|
||||
// 비활성 시 숨김
|
||||
handFoldout.schedule.Execute(() =>
|
||||
{
|
||||
try { if (fso == null || fso.targetObject == null) return; }
|
||||
try { if (so == null || so.targetObject == null) return; }
|
||||
catch (System.Exception) { return; }
|
||||
fso.Update();
|
||||
bool enabled = fso.FindProperty($"{prefix}HandEnabled").boolValue;
|
||||
so.Update();
|
||||
var enabledProp = so.FindProperty($"fingerShaped.{prefix}HandEnabled");
|
||||
if (enabledProp == null) return;
|
||||
bool enabled = enabledProp.boolValue;
|
||||
slidersRow.style.display = enabled ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
spreadSlider.style.display = enabled ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}).Every(300);
|
||||
@ -512,7 +479,7 @@ public class RetargetingControlWindow : EditorWindow
|
||||
return box;
|
||||
}
|
||||
|
||||
private VisualElement BuildFingerPresets(FingerShapedController controller)
|
||||
private VisualElement BuildFingerPresets(CustomRetargetingScript script, FingerShapedController controller)
|
||||
{
|
||||
var container = new VisualElement { style = { marginTop = 6 } };
|
||||
container.Add(new Label("손 모양 프리셋") { style = { unityFontStyleAndWeight = FontStyle.Bold, marginBottom = 4 } });
|
||||
@ -524,7 +491,7 @@ public class RetargetingControlWindow : EditorWindow
|
||||
for (int col = 0; col < 3; col++)
|
||||
{
|
||||
string name = presets[row, col];
|
||||
var btn = new Button(() => ApplyFingerPreset(controller, name)) { text = name };
|
||||
var btn = new Button(() => ApplyFingerPreset(script, controller, name)) { text = name };
|
||||
btn.style.height = 30; btn.style.width = 100; btn.style.marginLeft = btn.style.marginRight = 4;
|
||||
btnRow.Add(btn);
|
||||
}
|
||||
@ -835,7 +802,7 @@ public class RetargetingControlWindow : EditorWindow
|
||||
return container;
|
||||
}
|
||||
|
||||
private void ApplyFingerPreset(FingerShapedController controller, string presetName)
|
||||
private void ApplyFingerPreset(CustomRetargetingScript script, FingerShapedController controller, string presetName)
|
||||
{
|
||||
if (!controller.enabled) controller.enabled = true;
|
||||
|
||||
@ -860,7 +827,7 @@ public class RetargetingControlWindow : EditorWindow
|
||||
controller.rightThumbCurl = t; controller.rightIndexCurl = i; controller.rightMiddleCurl = m;
|
||||
controller.rightRingCurl = r; controller.rightPinkyCurl = p; controller.rightSpreadFingers = s;
|
||||
}
|
||||
EditorUtility.SetDirty(controller);
|
||||
EditorUtility.SetDirty(script);
|
||||
}
|
||||
|
||||
// ========== Head Calibration ==========
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[DefaultExecutionOrder(2)]
|
||||
public class FingerShapedController : MonoBehaviour
|
||||
namespace KindRetargeting
|
||||
{
|
||||
[System.Serializable]
|
||||
public class FingerShapedController
|
||||
{
|
||||
private Animator animator;
|
||||
private HumanPoseHandler humanPoseHandler;
|
||||
@ -40,6 +42,8 @@ public class FingerShapedController : MonoBehaviour
|
||||
HumanBodyBones.Jaw
|
||||
};
|
||||
|
||||
public bool enabled = false;
|
||||
|
||||
[Header("왼손 제어 값")]
|
||||
[Range(-1, 1)] public float leftPinkyCurl; // 새끼손가락 구부리기
|
||||
[Range(-1, 1)] public float leftRingCurl; // 약지 구부리기
|
||||
@ -59,30 +63,30 @@ public class FingerShapedController : MonoBehaviour
|
||||
public bool leftHandEnabled = false; // 왼손 제어 활성화 상태
|
||||
public bool rightHandEnabled = false; // 오른손 제어 활성화 상태
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
// 컴포넌트가 처음 추가될 때 자동으로 비활성화
|
||||
enabled = false;
|
||||
leftHandEnabled = false;
|
||||
rightHandEnabled = false;
|
||||
}
|
||||
private bool isInitialized;
|
||||
|
||||
private void Awake()
|
||||
public void Initialize(Animator targetAnimator)
|
||||
{
|
||||
animator = GetComponent<Animator>();
|
||||
animator = targetAnimator;
|
||||
if (animator == null || !animator.isHuman) return;
|
||||
|
||||
humanPoseHandler = new HumanPoseHandler(animator.avatar, animator.transform);
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
public void Cleanup()
|
||||
{
|
||||
if (humanPoseHandler != null)
|
||||
{
|
||||
humanPoseHandler.Dispose();
|
||||
humanPoseHandler = null;
|
||||
}
|
||||
isInitialized = false;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
public void OnUpdate()
|
||||
{
|
||||
if (!isInitialized || !enabled) return;
|
||||
UpdateMuscleValues();
|
||||
}
|
||||
|
||||
@ -166,3 +170,4 @@ public class FingerShapedController : MonoBehaviour
|
||||
if (baseOffset + 19 < muscleCount) humanPose.muscles[baseOffset + 19] = pinky; // Little 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,8 +4,8 @@ using UnityEngine;
|
||||
|
||||
namespace KindRetargeting
|
||||
{
|
||||
[DefaultExecutionOrder(4)]
|
||||
public class LimbWeightController : MonoBehaviour
|
||||
[System.Serializable]
|
||||
public class LimbWeightController
|
||||
{
|
||||
[Header("거리 기반 가중치 설정")]
|
||||
[SerializeField, Range(0.3f, 1f)] public float maxDistance = 0.5f; // 가중치가 0이 되는 최대 거리
|
||||
@ -36,9 +36,8 @@ namespace KindRetargeting
|
||||
public float chairSeatHeightOffset = 0.05f;
|
||||
|
||||
private TwoBoneIKSolver ikSolver;
|
||||
|
||||
private CustomRetargetingScript crs;
|
||||
private Dictionary<string, Dictionary<int, float>> weightLayers = new Dictionary<string, Dictionary<int, float>>();
|
||||
private Transform characterRoot;
|
||||
|
||||
List<float> leftArmEndWeights = new List<float>();
|
||||
List<float> rightArmEndWeights = new List<float>();
|
||||
@ -57,8 +56,6 @@ namespace KindRetargeting
|
||||
|
||||
public List<Transform> props = new List<Transform>();
|
||||
|
||||
public Transform characterRoot;
|
||||
|
||||
// 힙스 가중치 리스트 추가
|
||||
List<float> hipsWeights = new List<float>();
|
||||
private float MasterHipsWeight = 1f;
|
||||
@ -67,8 +64,63 @@ namespace KindRetargeting
|
||||
private float currentChairSeatOffset = 0f;
|
||||
private float targetChairSeatOffset = 0f;
|
||||
|
||||
void Update()
|
||||
private bool isInitialized;
|
||||
|
||||
public void Initialize(TwoBoneIKSolver ikSolver, CustomRetargetingScript crs, Transform characterRoot)
|
||||
{
|
||||
this.ikSolver = ikSolver;
|
||||
this.crs = crs;
|
||||
this.characterRoot = characterRoot;
|
||||
|
||||
if (ikSolver == null || crs == null) return;
|
||||
|
||||
InitWeightLayers();
|
||||
|
||||
//프랍 오브젝트 찾기
|
||||
props = Object.FindObjectsByType<PropTypeController>(FindObjectsSortMode.None).Select(controller => controller.transform).ToList();
|
||||
|
||||
// 다른 캐릭터의 손을 props에 추가
|
||||
GetHand();
|
||||
|
||||
//HandDistances()에서 사용을 위한 리스트 추가
|
||||
//손 거리에 따른 웨이트 업데이트 인덱스 0번
|
||||
leftArmEndWeights.Add(0);
|
||||
rightArmEndWeights.Add(0);
|
||||
|
||||
// 프랍과의 거리에 따른 웨이트 업데이트 인덱스 1번
|
||||
leftArmEndWeights.Add(0);
|
||||
rightArmEndWeights.Add(0);
|
||||
|
||||
// 앉아있을 때 다리와의 거리에 따른 가중치 적용 인덱스 0번
|
||||
leftLegEndWeights.Add(0);
|
||||
rightLegEndWeights.Add(0);
|
||||
|
||||
// 다리 골 가중치 초기화
|
||||
leftLegBendWeights.Add(1f); // 기본 가중치
|
||||
rightLegBendWeights.Add(1f); // 기본 가중치
|
||||
|
||||
if (this.characterRoot == null)
|
||||
{
|
||||
this.characterRoot = crs.transform;
|
||||
}
|
||||
|
||||
// 힙스 가중치 초기화 인덱스 0번
|
||||
hipsWeights.Add(1f); // 의자 거리 기반 가중치
|
||||
|
||||
// 지면 높이 기반 가중치 초기화 인덱스 1번
|
||||
hipsWeights.Add(1f); // 지면 높이 기반 가중치
|
||||
|
||||
// 발 높이 기반 가중치 초기화 인덱스 1번
|
||||
leftLegEndWeights.Add(1f);
|
||||
rightLegEndWeights.Add(1f);
|
||||
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
public void OnUpdate()
|
||||
{
|
||||
if (!isInitialized) return;
|
||||
|
||||
//손의 거리를 기반으로한 가중치 적용
|
||||
HandDistances();
|
||||
|
||||
@ -94,68 +146,15 @@ namespace KindRetargeting
|
||||
ApplyWeightsToFBIK();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
crs = GetComponent<CustomRetargetingScript>();
|
||||
if (crs != null) ikSolver = crs.ikSolver;
|
||||
|
||||
InitWeightLayers();
|
||||
|
||||
//프랍 오브젝트 찾기
|
||||
props = FindObjectsByType<PropTypeController>(FindObjectsSortMode.None).Select(controller => controller.transform).ToList();
|
||||
|
||||
// 프랍 오브젝트 찾기
|
||||
GetHand();
|
||||
|
||||
//HandDistances();에서 사용을 위한 리스트 추가
|
||||
//손 거리에 따른 웨이트 업데이트 인덱스 0번
|
||||
leftArmEndWeights.Add(0);
|
||||
rightArmEndWeights.Add(0);
|
||||
|
||||
// 프랍과의 거리에 따른 웨이트 업데이트 인덱스 1번
|
||||
leftArmEndWeights.Add(0);
|
||||
rightArmEndWeights.Add(0);
|
||||
|
||||
// 앉아있을 때 다리와의 거리에 따른 가중치 적용 인덱스 0번
|
||||
leftLegEndWeights.Add(0);
|
||||
rightLegEndWeights.Add(0);
|
||||
|
||||
// 다리 골 가중치 초기화
|
||||
leftLegBendWeights.Add(1f); // 기본 가중치
|
||||
rightLegBendWeights.Add(1f); // 기본 가중치
|
||||
|
||||
// CharacterController가 있는 루트 오브젝트 찾기
|
||||
|
||||
|
||||
if (characterRoot == null)
|
||||
{
|
||||
characterRoot = transform;
|
||||
}
|
||||
|
||||
// 힙스 가중치 초기화 인덱스 0번
|
||||
hipsWeights.Add(1f); // 의자 거리 기반 가중치
|
||||
|
||||
// 지면 높이 기반 가중치 초기화 인덱스 1번
|
||||
hipsWeights.Add(1f); // 지면 높이 기반 가중치
|
||||
|
||||
// 발 높이 기반 가중치 초기화 인덱스 1번
|
||||
leftLegEndWeights.Add(1f);
|
||||
rightLegEndWeights.Add(1f);
|
||||
}
|
||||
|
||||
private void GetHand()
|
||||
{
|
||||
// 모든 LimbWeightController 찾기
|
||||
LimbWeightController[] allControllers = FindObjectsOfType<LimbWeightController>();
|
||||
// 모든 CustomRetargetingScript 찾기 (다른 캐릭터의 손을 props에 추가)
|
||||
CustomRetargetingScript[] allCrs = Object.FindObjectsByType<CustomRetargetingScript>(FindObjectsSortMode.None);
|
||||
|
||||
foreach (LimbWeightController controller in allControllers)
|
||||
foreach (CustomRetargetingScript otherCrs in allCrs)
|
||||
{
|
||||
// 자기 자신은 제외
|
||||
if (controller == this) continue;
|
||||
|
||||
// CustomRetargetingScript 가져오기
|
||||
CustomRetargetingScript otherCrs = controller.GetComponent<CustomRetargetingScript>();
|
||||
if (otherCrs == null) continue;
|
||||
if (otherCrs == crs) continue;
|
||||
|
||||
// 왼손과 오른손 Transform 가져오기
|
||||
Transform leftHand = otherCrs?.sourceAnimator?.GetBoneTransform(HumanBodyBones.LeftHand);
|
||||
|
||||
@ -215,9 +215,6 @@ namespace KindRetargeting.Remote
|
||||
return;
|
||||
}
|
||||
|
||||
var limbWeight = script.GetComponent<LimbWeightController>();
|
||||
var handPose = script.GetComponent<FingerShapedController>();
|
||||
|
||||
var data = new Dictionary<string, object>
|
||||
{
|
||||
// 힙 위치 보정 (로컬)
|
||||
@ -251,37 +248,25 @@ namespace KindRetargeting.Remote
|
||||
{ "fingerCopyMode", (int)GetPrivateField<EnumsList.FingerCopyMode>(script, "fingerCopyMode") },
|
||||
|
||||
// 캘리브레이션 상태
|
||||
{ "hasCalibrationData", script.HasCachedSettings() }
|
||||
};
|
||||
{ "hasCalibrationData", script.HasCachedSettings() },
|
||||
|
||||
// LimbWeightController 데이터
|
||||
if (limbWeight != null)
|
||||
{
|
||||
data["limbMinDistance"] = limbWeight.minDistance;
|
||||
data["limbMaxDistance"] = limbWeight.maxDistance;
|
||||
data["weightSmoothSpeed"] = limbWeight.weightSmoothSpeed;
|
||||
data["hipsMinDistance"] = limbWeight.hipsMinDistance;
|
||||
data["hipsMaxDistance"] = limbWeight.hipsMaxDistance;
|
||||
data["groundHipsMinHeight"] = limbWeight.groundHipsMinHeight;
|
||||
data["groundHipsMaxHeight"] = limbWeight.groundHipsMaxHeight;
|
||||
data["footHeightMinThreshold"] = limbWeight.footHeightMinThreshold;
|
||||
data["footHeightMaxThreshold"] = limbWeight.footHeightMaxThreshold;
|
||||
data["chairSeatHeightOffset"] = limbWeight.chairSeatHeightOffset;
|
||||
}
|
||||
{ "limbMinDistance", script.limbWeight.minDistance },
|
||||
{ "limbMaxDistance", script.limbWeight.maxDistance },
|
||||
{ "weightSmoothSpeed", script.limbWeight.weightSmoothSpeed },
|
||||
{ "hipsMinDistance", script.limbWeight.hipsMinDistance },
|
||||
{ "hipsMaxDistance", script.limbWeight.hipsMaxDistance },
|
||||
{ "groundHipsMinHeight", script.limbWeight.groundHipsMinHeight },
|
||||
{ "groundHipsMaxHeight", script.limbWeight.groundHipsMaxHeight },
|
||||
{ "footHeightMinThreshold", script.limbWeight.footHeightMinThreshold },
|
||||
{ "footHeightMaxThreshold", script.limbWeight.footHeightMaxThreshold },
|
||||
{ "chairSeatHeightOffset", script.limbWeight.chairSeatHeightOffset },
|
||||
};
|
||||
|
||||
// FingerShapedController 데이터
|
||||
if (handPose != null)
|
||||
{
|
||||
data["handPoseEnabled"] = handPose.enabled;
|
||||
data["leftHandEnabled"] = handPose.leftHandEnabled;
|
||||
data["rightHandEnabled"] = handPose.rightHandEnabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
data["handPoseEnabled"] = false;
|
||||
data["leftHandEnabled"] = false;
|
||||
data["rightHandEnabled"] = false;
|
||||
}
|
||||
data["handPoseEnabled"] = script.fingerShaped.enabled;
|
||||
data["leftHandEnabled"] = script.fingerShaped.leftHandEnabled;
|
||||
data["rightHandEnabled"] = script.fingerShaped.rightHandEnabled;
|
||||
|
||||
var response = new
|
||||
{
|
||||
@ -298,9 +283,6 @@ namespace KindRetargeting.Remote
|
||||
var script = FindCharacter(characterId);
|
||||
if (script == null) return;
|
||||
|
||||
var limbWeight = script.GetComponent<LimbWeightController>();
|
||||
var handPose = script.GetComponent<FingerShapedController>();
|
||||
|
||||
switch (property)
|
||||
{
|
||||
// 힙 위치 보정
|
||||
@ -363,56 +345,49 @@ namespace KindRetargeting.Remote
|
||||
|
||||
// LimbWeightController 속성
|
||||
case "limbMinDistance":
|
||||
if (limbWeight != null) limbWeight.minDistance = value;
|
||||
script.limbWeight.minDistance = value;
|
||||
break;
|
||||
case "limbMaxDistance":
|
||||
if (limbWeight != null) limbWeight.maxDistance = value;
|
||||
script.limbWeight.maxDistance = value;
|
||||
break;
|
||||
case "weightSmoothSpeed":
|
||||
if (limbWeight != null) limbWeight.weightSmoothSpeed = value;
|
||||
script.limbWeight.weightSmoothSpeed = value;
|
||||
break;
|
||||
case "hipsMinDistance":
|
||||
if (limbWeight != null) limbWeight.hipsMinDistance = value;
|
||||
script.limbWeight.hipsMinDistance = value;
|
||||
break;
|
||||
case "hipsMaxDistance":
|
||||
if (limbWeight != null) limbWeight.hipsMaxDistance = value;
|
||||
script.limbWeight.hipsMaxDistance = value;
|
||||
break;
|
||||
case "groundHipsMinHeight":
|
||||
if (limbWeight != null) limbWeight.groundHipsMinHeight = value;
|
||||
script.limbWeight.groundHipsMinHeight = value;
|
||||
break;
|
||||
case "groundHipsMaxHeight":
|
||||
if (limbWeight != null) limbWeight.groundHipsMaxHeight = value;
|
||||
script.limbWeight.groundHipsMaxHeight = value;
|
||||
break;
|
||||
case "footHeightMinThreshold":
|
||||
if (limbWeight != null) limbWeight.footHeightMinThreshold = value;
|
||||
script.limbWeight.footHeightMinThreshold = value;
|
||||
break;
|
||||
case "footHeightMaxThreshold":
|
||||
if (limbWeight != null) limbWeight.footHeightMaxThreshold = value;
|
||||
script.limbWeight.footHeightMaxThreshold = value;
|
||||
break;
|
||||
case "chairSeatHeightOffset":
|
||||
if (limbWeight != null) limbWeight.chairSeatHeightOffset = value;
|
||||
script.limbWeight.chairSeatHeightOffset = value;
|
||||
break;
|
||||
|
||||
// FingerShapedController 속성
|
||||
case "handPoseEnabled":
|
||||
if (handPose != null)
|
||||
handPose.enabled = value > 0.5f;
|
||||
script.fingerShaped.enabled = value > 0.5f;
|
||||
break;
|
||||
case "leftHandEnabled":
|
||||
if (handPose != null)
|
||||
{
|
||||
handPose.leftHandEnabled = value > 0.5f;
|
||||
if (handPose.leftHandEnabled)
|
||||
handPose.enabled = true;
|
||||
}
|
||||
script.fingerShaped.leftHandEnabled = value > 0.5f;
|
||||
if (script.fingerShaped.leftHandEnabled)
|
||||
script.fingerShaped.enabled = true;
|
||||
break;
|
||||
case "rightHandEnabled":
|
||||
if (handPose != null)
|
||||
{
|
||||
handPose.rightHandEnabled = value > 0.5f;
|
||||
if (handPose.rightHandEnabled)
|
||||
handPose.enabled = true;
|
||||
}
|
||||
script.fingerShaped.rightHandEnabled = value > 0.5f;
|
||||
if (script.fingerShaped.rightHandEnabled)
|
||||
script.fingerShaped.enabled = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -426,8 +401,7 @@ namespace KindRetargeting.Remote
|
||||
var script = FindCharacter(characterId);
|
||||
if (script == null) return;
|
||||
|
||||
var handPose = script.GetComponent<FingerShapedController>();
|
||||
if (handPose == null) return;
|
||||
var handPose = script.fingerShaped;
|
||||
|
||||
// 스크립트 자동 활성화
|
||||
handPose.enabled = true;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user