chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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 CameraControl
|
||||
{
|
||||
internal static object ListCameras(JObject @params)
|
||||
{
|
||||
var unityCameras = UnityFindObjectsCompat.FindAll<UnityEngine.Camera>();
|
||||
var cameraList = new List<object>();
|
||||
var unityCamList = new List<object>();
|
||||
|
||||
// Cinemachine cameras
|
||||
if (CameraHelpers.HasCinemachine)
|
||||
{
|
||||
var cmType = CameraHelpers.CinemachineCameraType;
|
||||
var allCm = UnityFindObjectsCompat.FindAll(cmType);
|
||||
foreach (Component cm in allCm)
|
||||
{
|
||||
var follow = CameraHelpers.GetReflectionProperty(cm, "Follow") as Transform;
|
||||
var lookAt = CameraHelpers.GetReflectionProperty(cm, "LookAt") as Transform;
|
||||
var isLive = CameraHelpers.GetReflectionProperty(cm, "IsLive");
|
||||
var priority = CameraHelpers.ReadCinemachinePriority(cm);
|
||||
|
||||
var body = CameraHelpers.GetPipelineComponent(cm, "Body");
|
||||
var aim = CameraHelpers.GetPipelineComponent(cm, "Aim");
|
||||
var noise = CameraHelpers.GetPipelineComponent(cm, "Noise");
|
||||
|
||||
// Collect extensions
|
||||
var extensions = new List<string>();
|
||||
var cmExtBaseType = cm.GetType().Assembly.GetType("Unity.Cinemachine.CinemachineExtension");
|
||||
if (cmExtBaseType != null)
|
||||
{
|
||||
foreach (var comp in cm.gameObject.GetComponents(cmExtBaseType))
|
||||
{
|
||||
if (comp != null)
|
||||
extensions.Add(comp.GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
cameraList.Add(new
|
||||
{
|
||||
instanceID = cm.gameObject.GetInstanceIDCompat(),
|
||||
name = cm.gameObject.name,
|
||||
isLive = isLive is bool b && b,
|
||||
priority,
|
||||
follow = follow != null ? new { name = follow.gameObject.name, instanceID = follow.gameObject.GetInstanceIDCompat() } : null,
|
||||
lookAt = lookAt != null ? new { name = lookAt.gameObject.name, instanceID = lookAt.gameObject.GetInstanceIDCompat() } : null,
|
||||
body = body?.GetType().Name,
|
||||
aim = aim?.GetType().Name,
|
||||
noise = noise?.GetType().Name,
|
||||
extensions
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Unity cameras
|
||||
foreach (var cam in unityCameras)
|
||||
{
|
||||
bool hasBrain = CameraHelpers.HasCinemachine &&
|
||||
cam.gameObject.GetComponent(CameraHelpers.CinemachineBrainType) != null;
|
||||
unityCamList.Add(new
|
||||
{
|
||||
instanceID = cam.gameObject.GetInstanceIDCompat(),
|
||||
name = cam.gameObject.name,
|
||||
depth = cam.depth,
|
||||
fieldOfView = cam.fieldOfView,
|
||||
hasBrain
|
||||
});
|
||||
}
|
||||
|
||||
// Brain info
|
||||
object brainInfo = null;
|
||||
if (CameraHelpers.HasCinemachine)
|
||||
{
|
||||
var brain = CameraHelpers.FindBrain();
|
||||
if (brain != null)
|
||||
{
|
||||
var activeCam = CameraHelpers.GetReflectionProperty(brain, "ActiveVirtualCamera");
|
||||
var isBlending = CameraHelpers.GetReflectionProperty(brain, "IsBlending");
|
||||
|
||||
string activeName = null;
|
||||
int? activeID = null;
|
||||
if (activeCam != null)
|
||||
{
|
||||
var nameProp = activeCam.GetType().GetProperty("Name");
|
||||
activeName = nameProp?.GetValue(activeCam) as string;
|
||||
|
||||
if (activeCam is Component activeComp)
|
||||
activeID = activeComp.gameObject.GetInstanceIDCompat();
|
||||
}
|
||||
|
||||
brainInfo = new
|
||||
{
|
||||
exists = true,
|
||||
gameObject = brain.gameObject.name,
|
||||
instanceID = brain.gameObject.GetInstanceIDCompat(),
|
||||
activeCameraName = activeName,
|
||||
activeCameraID = activeID,
|
||||
isBlending = isBlending is bool bl && bl
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
data = new
|
||||
{
|
||||
brain = brainInfo,
|
||||
cinemachineCameras = cameraList,
|
||||
unityCameras = unityCamList,
|
||||
cinemachineInstalled = CameraHelpers.HasCinemachine
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
internal static object GetBrainStatus(JObject @params)
|
||||
{
|
||||
var brain = CameraHelpers.FindBrain();
|
||||
if (brain == null)
|
||||
return new ErrorResponse("No CinemachineBrain found in the scene.");
|
||||
|
||||
var activeCam = CameraHelpers.GetReflectionProperty(brain, "ActiveVirtualCamera");
|
||||
var isBlending = CameraHelpers.GetReflectionProperty(brain, "IsBlending");
|
||||
var activeBlend = CameraHelpers.GetReflectionProperty(brain, "ActiveBlend");
|
||||
|
||||
string activeName = null;
|
||||
int? activeID = null;
|
||||
if (activeCam != null)
|
||||
{
|
||||
var nameProp = activeCam.GetType().GetProperty("Name");
|
||||
activeName = nameProp?.GetValue(activeCam) as string;
|
||||
if (activeCam is Component comp)
|
||||
activeID = comp.gameObject.GetInstanceIDCompat();
|
||||
}
|
||||
|
||||
string blendDesc = null;
|
||||
if (activeBlend != null)
|
||||
{
|
||||
var descProp = activeBlend.GetType().GetProperty("Description");
|
||||
blendDesc = descProp?.GetValue(activeBlend) as string;
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
data = new
|
||||
{
|
||||
gameObject = brain.gameObject.name,
|
||||
instanceID = brain.gameObject.GetInstanceIDCompat(),
|
||||
activeCameraName = activeName,
|
||||
activeCameraID = activeID,
|
||||
isBlending = isBlending is bool b && b,
|
||||
blendDescription = blendDesc
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
internal static object SetBlend(JObject @params)
|
||||
{
|
||||
var brain = CameraHelpers.FindBrain();
|
||||
if (brain == null)
|
||||
return new ErrorResponse("No CinemachineBrain found. Use 'ensure_brain' first.");
|
||||
|
||||
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
||||
Undo.RecordObject(brain, "Set Camera Blend");
|
||||
|
||||
using var so = new SerializedObject(brain);
|
||||
var defaultBlendProp = so.FindProperty("DefaultBlend") ?? so.FindProperty("m_DefaultBlend");
|
||||
if (defaultBlendProp == null)
|
||||
return new ErrorResponse("Could not find DefaultBlend property on CinemachineBrain.");
|
||||
|
||||
string style = ParamCoercion.CoerceString(props["style"], null);
|
||||
if (style != null)
|
||||
{
|
||||
var styleProp = defaultBlendProp.FindPropertyRelative("Style")
|
||||
?? defaultBlendProp.FindPropertyRelative("m_Style");
|
||||
if (styleProp != null && styleProp.propertyType == SerializedPropertyType.Enum)
|
||||
{
|
||||
// Try to parse the style enum
|
||||
var enumNames = styleProp.enumNames;
|
||||
int idx = Array.FindIndex(enumNames, n => n.Equals(style, StringComparison.OrdinalIgnoreCase));
|
||||
if (idx >= 0)
|
||||
styleProp.enumValueIndex = idx;
|
||||
}
|
||||
}
|
||||
|
||||
float duration = ParamCoercion.CoerceFloat(props["duration"], -1f);
|
||||
if (duration >= 0)
|
||||
{
|
||||
var timeProp = defaultBlendProp.FindPropertyRelative("Time")
|
||||
?? defaultBlendProp.FindPropertyRelative("m_Time");
|
||||
if (timeProp != null)
|
||||
timeProp.floatValue = duration;
|
||||
}
|
||||
|
||||
so.ApplyModifiedProperties();
|
||||
CameraHelpers.MarkDirty(brain.gameObject);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = "Default blend configured on CinemachineBrain.",
|
||||
data = new { instanceID = brain.gameObject.GetInstanceIDCompat() }
|
||||
};
|
||||
}
|
||||
|
||||
private static int _overrideId = -1;
|
||||
|
||||
internal static object ForceCamera(JObject @params)
|
||||
{
|
||||
var brain = CameraHelpers.FindBrain();
|
||||
if (brain == null)
|
||||
return new ErrorResponse("No CinemachineBrain found. Use 'ensure_brain' first.");
|
||||
|
||||
var cmCamera = CameraHelpers.FindCinemachineCamera(@params);
|
||||
if (cmCamera == null)
|
||||
return new ErrorResponse("Target CinemachineCamera not found.");
|
||||
|
||||
// Use SetCameraOverride via reflection
|
||||
var brainType = brain.GetType();
|
||||
var method = brainType.GetMethod("SetCameraOverride",
|
||||
BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
if (method == null)
|
||||
{
|
||||
// Fallback: just set high priority
|
||||
CameraHelpers.SetReflectionProperty(cmCamera, "Priority", 999);
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Set high priority on '{cmCamera.gameObject.name}' (SetCameraOverride not available).",
|
||||
data = new { instanceID = cmCamera.gameObject.GetInstanceIDCompat(), method = "priority" }
|
||||
};
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// CM3 signature: SetCameraOverride(int overrideId, int priority,
|
||||
// ICinemachineCamera camA, ICinemachineCamera camB, float weightB, float deltaTime)
|
||||
// -1 for overrideId creates a new override; same cam for A+B with weight=1 = instant switch
|
||||
_overrideId = (int)method.Invoke(brain, new object[]
|
||||
{
|
||||
_overrideId >= 0 ? _overrideId : -1,
|
||||
999, // high priority to win over all others
|
||||
cmCamera, // camA (at weight=0)
|
||||
cmCamera, // camB (at weight=1) — same camera = no blend
|
||||
1f, // weightB = fully on camB
|
||||
-1f // deltaTime = use default
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Fallback
|
||||
CameraHelpers.SetReflectionProperty(cmCamera, "Priority", 999);
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Forced via priority (override failed: {ex.Message}).",
|
||||
data = new { instanceID = cmCamera.gameObject.GetInstanceIDCompat(), method = "priority" }
|
||||
};
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Camera overridden to '{cmCamera.gameObject.name}'.",
|
||||
data = new
|
||||
{
|
||||
instanceID = cmCamera.gameObject.GetInstanceIDCompat(),
|
||||
overrideId = _overrideId,
|
||||
method = "override"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
internal static object ReleaseOverride(JObject @params)
|
||||
{
|
||||
var brain = CameraHelpers.FindBrain();
|
||||
if (brain == null)
|
||||
return new ErrorResponse("No CinemachineBrain found.");
|
||||
|
||||
if (_overrideId < 0)
|
||||
return new { success = true, message = "No active camera override to release." };
|
||||
|
||||
var method = brain.GetType().GetMethod("ReleaseCameraOverride",
|
||||
BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
if (method != null)
|
||||
{
|
||||
method.Invoke(brain, new object[] { _overrideId });
|
||||
int releasedId = _overrideId;
|
||||
_overrideId = -1;
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = "Camera override released.",
|
||||
data = new { releasedOverrideId = releasedId }
|
||||
};
|
||||
}
|
||||
|
||||
_overrideId = -1;
|
||||
return new { success = true, message = "Override state cleared." };
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user