chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,412 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using MCPForUnity.Editor.Helpers;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using MCPForUnity.Runtime.Helpers;
|
||||
|
||||
namespace MCPForUnity.Editor.Tools.Cameras
|
||||
{
|
||||
internal static class CameraConfigure
|
||||
{
|
||||
#region Tier 1 — Basic Camera
|
||||
|
||||
internal static object SetBasicCameraTarget(JObject @params)
|
||||
{
|
||||
var go = CameraHelpers.FindTargetGameObject(@params);
|
||||
if (go == null) return new ErrorResponse("Target Camera not found.");
|
||||
|
||||
var cam = go.GetComponent<UnityEngine.Camera>();
|
||||
if (cam == null) return new ErrorResponse($"No Camera component on '{go.name}'.");
|
||||
|
||||
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
||||
var lookAtToken = props["lookAt"] ?? props["look_at"] ?? props["follow"];
|
||||
if (lookAtToken == null)
|
||||
return new ErrorResponse("'follow' or 'lookAt' property is required.");
|
||||
|
||||
var target = CameraHelpers.ResolveGameObjectRef(lookAtToken);
|
||||
if (target == null)
|
||||
return new ErrorResponse($"Target '{lookAtToken}' not found.");
|
||||
|
||||
Undo.RecordObject(go.transform, "Set Camera Target");
|
||||
go.transform.LookAt(target.transform);
|
||||
CameraHelpers.MarkDirty(go);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Camera '{go.name}' now looking at '{target.name}'.",
|
||||
data = new { instanceID = go.GetInstanceIDCompat() }
|
||||
};
|
||||
}
|
||||
|
||||
internal static object SetBasicCameraLens(JObject @params)
|
||||
{
|
||||
var go = CameraHelpers.FindTargetGameObject(@params);
|
||||
if (go == null) return new ErrorResponse("Target Camera not found.");
|
||||
|
||||
var cam = go.GetComponent<UnityEngine.Camera>();
|
||||
if (cam == null) return new ErrorResponse($"No Camera component on '{go.name}'.");
|
||||
|
||||
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
||||
Undo.RecordObject(cam, "Set Camera Lens");
|
||||
|
||||
if (props["fieldOfView"] != null)
|
||||
cam.fieldOfView = ParamCoercion.CoerceFloat(props["fieldOfView"], cam.fieldOfView);
|
||||
if (props["nearClipPlane"] != null)
|
||||
cam.nearClipPlane = ParamCoercion.CoerceFloat(props["nearClipPlane"], cam.nearClipPlane);
|
||||
if (props["farClipPlane"] != null)
|
||||
cam.farClipPlane = ParamCoercion.CoerceFloat(props["farClipPlane"], cam.farClipPlane);
|
||||
if (props["orthographicSize"] != null)
|
||||
cam.orthographicSize = ParamCoercion.CoerceFloat(props["orthographicSize"], cam.orthographicSize);
|
||||
|
||||
CameraHelpers.MarkDirty(go);
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Lens properties set on Camera '{go.name}'.",
|
||||
data = new { instanceID = go.GetInstanceIDCompat() }
|
||||
};
|
||||
}
|
||||
|
||||
internal static object SetBasicCameraPriority(JObject @params)
|
||||
{
|
||||
var go = CameraHelpers.FindTargetGameObject(@params);
|
||||
if (go == null) return new ErrorResponse("Target Camera not found.");
|
||||
|
||||
var cam = go.GetComponent<UnityEngine.Camera>();
|
||||
if (cam == null) return new ErrorResponse($"No Camera component on '{go.name}'.");
|
||||
|
||||
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
||||
float depth = ParamCoercion.CoerceFloat(props["priority"], cam.depth);
|
||||
|
||||
Undo.RecordObject(cam, "Set Camera Depth");
|
||||
cam.depth = depth;
|
||||
CameraHelpers.MarkDirty(go);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Camera '{go.name}' depth set to {depth}.",
|
||||
data = new { instanceID = go.GetInstanceIDCompat(), depth }
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tier 2 — Cinemachine
|
||||
|
||||
internal static object SetCinemachineTarget(JObject @params)
|
||||
{
|
||||
var cmCamera = CameraHelpers.FindCinemachineCamera(@params);
|
||||
if (cmCamera == null) return new ErrorResponse("Target CinemachineCamera not found.");
|
||||
|
||||
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
||||
|
||||
Undo.RecordObject(cmCamera, "Set Cinemachine Target");
|
||||
|
||||
if (props.ContainsKey("follow"))
|
||||
CameraHelpers.SetTransformTarget(cmCamera, "Follow", props["follow"]);
|
||||
if (props.ContainsKey("lookAt") || props.ContainsKey("look_at"))
|
||||
CameraHelpers.SetTransformTarget(cmCamera, "LookAt", props["lookAt"] ?? props["look_at"]);
|
||||
|
||||
CameraHelpers.MarkDirty(cmCamera.gameObject);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Targets set on CinemachineCamera '{cmCamera.gameObject.name}'.",
|
||||
data = new { instanceID = cmCamera.gameObject.GetInstanceIDCompat() }
|
||||
};
|
||||
}
|
||||
|
||||
internal static object SetCinemachineLens(JObject @params)
|
||||
{
|
||||
var cmCamera = CameraHelpers.FindCinemachineCamera(@params);
|
||||
if (cmCamera == null) return new ErrorResponse("Target CinemachineCamera not found.");
|
||||
|
||||
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
||||
Undo.RecordObject(cmCamera, "Set Cinemachine Lens");
|
||||
|
||||
// Lens is a struct field — use SerializedProperty for reliable setting
|
||||
using var so = new SerializedObject(cmCamera);
|
||||
var lensProp = so.FindProperty("Lens") ?? so.FindProperty("m_Lens");
|
||||
if (lensProp == null)
|
||||
return new ErrorResponse("Could not find Lens property on CinemachineCamera.");
|
||||
|
||||
SetFloatSubProp(lensProp, "FieldOfView", props["fieldOfView"]);
|
||||
SetFloatSubProp(lensProp, "NearClipPlane", props["nearClipPlane"]);
|
||||
SetFloatSubProp(lensProp, "FarClipPlane", props["farClipPlane"]);
|
||||
SetFloatSubProp(lensProp, "OrthographicSize", props["orthographicSize"]);
|
||||
SetFloatSubProp(lensProp, "Dutch", props["dutch"]);
|
||||
|
||||
so.ApplyModifiedProperties();
|
||||
CameraHelpers.MarkDirty(cmCamera.gameObject);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Lens properties set on CinemachineCamera '{cmCamera.gameObject.name}'.",
|
||||
data = new { instanceID = cmCamera.gameObject.GetInstanceIDCompat() }
|
||||
};
|
||||
}
|
||||
|
||||
internal static object SetCinemachinePriority(JObject @params)
|
||||
{
|
||||
var cmCamera = CameraHelpers.FindCinemachineCamera(@params);
|
||||
if (cmCamera == null) return new ErrorResponse("Target CinemachineCamera not found.");
|
||||
|
||||
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
||||
int priority = ParamCoercion.CoerceInt(props["priority"], 10);
|
||||
|
||||
// PrioritySettings is a struct with Enabled + m_Value — use SerializedProperty
|
||||
using var so = new SerializedObject(cmCamera);
|
||||
var priorityProp = so.FindProperty("Priority");
|
||||
if (priorityProp != null)
|
||||
{
|
||||
var enabledProp = priorityProp.FindPropertyRelative("Enabled");
|
||||
var valueProp = priorityProp.FindPropertyRelative("m_Value");
|
||||
if (enabledProp != null) enabledProp.boolValue = true;
|
||||
if (valueProp != null) valueProp.intValue = priority;
|
||||
so.ApplyModifiedProperties();
|
||||
}
|
||||
else
|
||||
{
|
||||
Undo.RecordObject(cmCamera, "Set Cinemachine Priority");
|
||||
CameraHelpers.SetReflectionProperty(cmCamera, "Priority", priority);
|
||||
}
|
||||
CameraHelpers.MarkDirty(cmCamera.gameObject);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Priority set to {priority} on CinemachineCamera '{cmCamera.gameObject.name}'.",
|
||||
data = new { instanceID = cmCamera.gameObject.GetInstanceIDCompat(), priority }
|
||||
};
|
||||
}
|
||||
|
||||
internal static object SetBody(JObject @params)
|
||||
{
|
||||
var cmCamera = CameraHelpers.FindCinemachineCamera(@params);
|
||||
if (cmCamera == null) return new ErrorResponse("Target CinemachineCamera not found.");
|
||||
|
||||
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
||||
var go = cmCamera.gameObject;
|
||||
|
||||
// Optionally swap body component
|
||||
string bodyTypeName = ParamCoercion.CoerceString(props["bodyType"] ?? props["body_type"], null);
|
||||
Component bodyComponent;
|
||||
|
||||
if (bodyTypeName != null)
|
||||
{
|
||||
bodyComponent = SwapPipelineComponent(go, "Body", bodyTypeName);
|
||||
if (bodyComponent == null)
|
||||
return new ErrorResponse($"Could not resolve body component type '{bodyTypeName}'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyComponent = CameraHelpers.GetPipelineComponent(cmCamera, "Body");
|
||||
if (bodyComponent == null)
|
||||
return new ErrorResponse("No Body component found on this CinemachineCamera. Provide 'bodyType' to add one.");
|
||||
}
|
||||
|
||||
// Set properties on body component
|
||||
SetComponentProperties(bodyComponent, props, new[] { "bodyType", "body_type" });
|
||||
CameraHelpers.MarkDirty(go);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Body configured on CinemachineCamera '{go.name}'.",
|
||||
data = new { instanceID = go.GetInstanceIDCompat(), body = bodyComponent.GetType().Name }
|
||||
};
|
||||
}
|
||||
|
||||
internal static object SetAim(JObject @params)
|
||||
{
|
||||
var cmCamera = CameraHelpers.FindCinemachineCamera(@params);
|
||||
if (cmCamera == null) return new ErrorResponse("Target CinemachineCamera not found.");
|
||||
|
||||
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
||||
var go = cmCamera.gameObject;
|
||||
|
||||
string aimTypeName = ParamCoercion.CoerceString(props["aimType"] ?? props["aim_type"], null);
|
||||
Component aimComponent;
|
||||
|
||||
if (aimTypeName != null)
|
||||
{
|
||||
aimComponent = SwapPipelineComponent(go, "Aim", aimTypeName);
|
||||
if (aimComponent == null)
|
||||
return new ErrorResponse($"Could not resolve aim component type '{aimTypeName}'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
aimComponent = CameraHelpers.GetPipelineComponent(cmCamera, "Aim");
|
||||
if (aimComponent == null)
|
||||
return new ErrorResponse("No Aim component found. Provide 'aimType' to add one.");
|
||||
}
|
||||
|
||||
SetComponentProperties(aimComponent, props, new[] { "aimType", "aim_type" });
|
||||
CameraHelpers.MarkDirty(go);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Aim configured on CinemachineCamera '{go.name}'.",
|
||||
data = new { instanceID = go.GetInstanceIDCompat(), aim = aimComponent.GetType().Name }
|
||||
};
|
||||
}
|
||||
|
||||
internal static object SetNoise(JObject @params)
|
||||
{
|
||||
var cmCamera = CameraHelpers.FindCinemachineCamera(@params);
|
||||
if (cmCamera == null) return new ErrorResponse("Target CinemachineCamera not found.");
|
||||
|
||||
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
||||
var go = cmCamera.gameObject;
|
||||
|
||||
// Get or add noise component
|
||||
var noiseType = CameraHelpers.ResolveComponentType("CinemachineBasicMultiChannelPerlin");
|
||||
if (noiseType == null)
|
||||
return new ErrorResponse("CinemachineBasicMultiChannelPerlin type not found.");
|
||||
|
||||
var noiseComponent = go.GetComponent(noiseType);
|
||||
bool added = false;
|
||||
if (noiseComponent == null)
|
||||
{
|
||||
noiseComponent = Undo.AddComponent(go, noiseType);
|
||||
added = true;
|
||||
}
|
||||
|
||||
Undo.RecordObject(noiseComponent, "Set Cinemachine Noise");
|
||||
SetComponentProperties(noiseComponent, props, Array.Empty<string>());
|
||||
CameraHelpers.MarkDirty(go);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = added
|
||||
? $"Added noise to CinemachineCamera '{go.name}'."
|
||||
: $"Noise configured on CinemachineCamera '{go.name}'.",
|
||||
data = new { instanceID = go.GetInstanceIDCompat(), added }
|
||||
};
|
||||
}
|
||||
|
||||
internal static object AddExtension(JObject @params)
|
||||
{
|
||||
var cmCamera = CameraHelpers.FindCinemachineCamera(@params);
|
||||
if (cmCamera == null) return new ErrorResponse("Target CinemachineCamera not found.");
|
||||
|
||||
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
||||
string extTypeName = ParamCoercion.CoerceString(
|
||||
props["extensionType"] ?? props["extension_type"], null);
|
||||
if (string.IsNullOrEmpty(extTypeName))
|
||||
return new ErrorResponse("'extensionType' property is required.");
|
||||
|
||||
var extType = CameraHelpers.ResolveComponentType(extTypeName);
|
||||
if (extType == null)
|
||||
return new ErrorResponse($"Extension type '{extTypeName}' not found.");
|
||||
|
||||
var go = cmCamera.gameObject;
|
||||
var existing = go.GetComponent(extType);
|
||||
if (existing != null)
|
||||
return new { success = true, message = $"Extension '{extTypeName}' already exists on '{go.name}'." };
|
||||
|
||||
var ext = Undo.AddComponent(go, extType);
|
||||
SetComponentProperties(ext, props, new[] { "extensionType", "extension_type" });
|
||||
CameraHelpers.MarkDirty(go);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Extension '{extTypeName}' added to CinemachineCamera '{go.name}'.",
|
||||
data = new { instanceID = go.GetInstanceIDCompat(), extensionType = extTypeName }
|
||||
};
|
||||
}
|
||||
|
||||
internal static object RemoveExtension(JObject @params)
|
||||
{
|
||||
var cmCamera = CameraHelpers.FindCinemachineCamera(@params);
|
||||
if (cmCamera == null) return new ErrorResponse("Target CinemachineCamera not found.");
|
||||
|
||||
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
||||
string extTypeName = ParamCoercion.CoerceString(
|
||||
props["extensionType"] ?? props["extension_type"], null);
|
||||
if (string.IsNullOrEmpty(extTypeName))
|
||||
return new ErrorResponse("'extensionType' property is required.");
|
||||
|
||||
var extType = CameraHelpers.ResolveComponentType(extTypeName);
|
||||
if (extType == null)
|
||||
return new ErrorResponse($"Extension type '{extTypeName}' not found.");
|
||||
|
||||
var go = cmCamera.gameObject;
|
||||
var ext = go.GetComponent(extType);
|
||||
if (ext == null)
|
||||
return new ErrorResponse($"Extension '{extTypeName}' not found on '{go.name}'.");
|
||||
|
||||
Undo.DestroyObjectImmediate(ext);
|
||||
CameraHelpers.MarkDirty(go);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Extension '{extTypeName}' removed from CinemachineCamera '{go.name}'.",
|
||||
data = new { instanceID = go.GetInstanceIDCompat() }
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
private static void SetFloatSubProp(SerializedProperty parent, string subPropName, JToken value)
|
||||
{
|
||||
if (value == null || value.Type == JTokenType.Null) return;
|
||||
var sub = parent.FindPropertyRelative(subPropName)
|
||||
?? parent.FindPropertyRelative("m_" + subPropName);
|
||||
if (sub != null && sub.propertyType == SerializedPropertyType.Float)
|
||||
sub.floatValue = ParamCoercion.CoerceFloat(value, sub.floatValue);
|
||||
}
|
||||
|
||||
private static Component SwapPipelineComponent(GameObject go, string stage, string newTypeName)
|
||||
{
|
||||
var newType = CameraHelpers.ResolveComponentType(newTypeName);
|
||||
if (newType == null) return null;
|
||||
|
||||
// Remove existing component of same pipeline stage
|
||||
var cmCamera = go.GetComponent(CameraHelpers.CinemachineCameraType);
|
||||
if (cmCamera != null)
|
||||
{
|
||||
var existing = CameraHelpers.GetPipelineComponent(cmCamera, stage);
|
||||
if (existing != null && existing.GetType() != newType)
|
||||
Undo.DestroyObjectImmediate(existing);
|
||||
}
|
||||
|
||||
// Add new component if not already present
|
||||
var comp = go.GetComponent(newType);
|
||||
if (comp == null)
|
||||
comp = Undo.AddComponent(go, newType);
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
private static void SetComponentProperties(Component component, JObject props, string[] skipKeys)
|
||||
{
|
||||
if (component == null || props == null) return;
|
||||
|
||||
var skipSet = new System.Collections.Generic.HashSet<string>(
|
||||
skipKeys, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
Undo.RecordObject(component, $"Configure {component.GetType().Name}");
|
||||
|
||||
foreach (var kv in props)
|
||||
{
|
||||
if (skipSet.Contains(kv.Key)) continue;
|
||||
ComponentOps.SetProperty(component, kv.Key, kv.Value, out _);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user