Files
Tomicz Engineering LLC deea7d2a56 Replace reflection with version-gated conditional compilation.
Addresses review feedback on the Unity 6.5 GetInstanceID migration:

- UnityObjectIdCompatExtensions: drop the reflective method lookup in
  favor of a simple #if UNITY_6000_5_OR_NEWER / #else split calling
  GetEntityId() or GetInstanceID() directly. Also wrap the class in
  the MCPForUnity.Runtime.Helpers namespace.
- UnityTypeConverters: remove the reflective EntityIdToObject probe and
  call EditorUtility.EntityIdToObject(EntityId) directly under the same
  version gate. Serialize entityID as EntityId.ToULong() rather than
  ToString(), since Unity's docs explicitly warn that the textual form
  is not a stable serialization contract.
- Drop #pragma warning disable 0619 from 22 files that no longer make
  any direct calls to obsolete APIs. The remaining 7 files still need
  it (FindObjectsOfType, InstanceIDToObject fallback) and are left as-is
  — those deprecations are out of scope for this PR.
- Add the MCPForUnity.Runtime.Helpers using to every file that calls
  GetInstanceIDCompat() now that the extension method lives in a
  namespace.
2026-04-11 22:55:45 +02:00

498 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using MCPForUnity.Editor.Helpers;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using MCPForUnity.Runtime.Helpers;
namespace MCPForUnity.Editor.Tools.Graphics
{
internal static class SkyboxOps
{
static Texture CustomReflectionTexture
{
get =>
#if UNITY_2022_1_OR_NEWER
RenderSettings.customReflectionTexture;
#else
RenderSettings.customReflection;
#endif
set {
#if UNITY_2022_1_OR_NEWER
RenderSettings.customReflectionTexture = value;
#else
RenderSettings.customReflection = value;
#endif
}
}
// ---------------------------------------------------------------
// skybox_get — read all environment settings
// ---------------------------------------------------------------
public static object GetEnvironment(JObject @params)
{
var skyMat = RenderSettings.skybox;
var sun = RenderSettings.sun;
object matInfo = null;
if (skyMat != null)
{
var props = new List<object>();
int count = skyMat.shader.GetPropertyCount();
for (int i = 0; i < count; i++)
{
string propName = skyMat.shader.GetPropertyName(i);
var propType = skyMat.shader.GetPropertyType(i);
object val = ReadMaterialProperty(skyMat, propName, propType);
props.Add(new { name = propName, type = propType.ToString(), value = val });
}
matInfo = new
{
name = skyMat.name,
shader = skyMat.shader.name,
path = AssetDatabase.GetAssetPath(skyMat),
properties = props
};
}
return new
{
success = true,
message = "Environment settings retrieved.",
data = new
{
skybox = matInfo,
ambient = new
{
mode = RenderSettings.ambientMode.ToString(),
skyColor = ColorToArray(RenderSettings.ambientSkyColor),
equatorColor = ColorToArray(RenderSettings.ambientEquatorColor),
groundColor = ColorToArray(RenderSettings.ambientGroundColor),
ambientLight = ColorToArray(RenderSettings.ambientLight),
intensity = RenderSettings.ambientIntensity
},
fog = new
{
enabled = RenderSettings.fog,
mode = RenderSettings.fogMode.ToString(),
color = ColorToArray(RenderSettings.fogColor),
density = RenderSettings.fogDensity,
startDistance = RenderSettings.fogStartDistance,
endDistance = RenderSettings.fogEndDistance
},
reflection = new
{
intensity = RenderSettings.reflectionIntensity,
bounces = RenderSettings.reflectionBounces,
mode = RenderSettings.defaultReflectionMode.ToString(),
resolution = RenderSettings.defaultReflectionResolution,
customCubemap = CustomReflectionTexture != null
? AssetDatabase.GetAssetPath(CustomReflectionTexture)
: null
},
sun = sun != null
? (object)new { name = sun.gameObject.name, instanceID = sun.gameObject.GetInstanceIDCompat() }
: null,
subtractiveShadowColor = ColorToArray(RenderSettings.subtractiveShadowColor)
}
};
}
// ---------------------------------------------------------------
// skybox_set_material — assign a skybox material
// ---------------------------------------------------------------
public static object SetMaterial(JObject @params)
{
var p = new ToolParams(@params);
string materialPath = p.Get("material") ?? p.Get("path") ?? p.Get("material_path");
if (string.IsNullOrEmpty(materialPath))
return new ErrorResponse("'material' (asset path) is required.");
var mat = AssetDatabase.LoadAssetAtPath<Material>(materialPath);
if (mat == null)
return new ErrorResponse($"Material not found at '{materialPath}'.");
RenderSettings.skybox = mat;
MarkSceneDirty();
return new
{
success = true,
message = $"Skybox set to '{mat.name}' (shader: {mat.shader.name}).",
data = new
{
material = mat.name,
shader = mat.shader.name,
path = materialPath
}
};
}
// ---------------------------------------------------------------
// skybox_set_properties — modify properties on the current skybox material
// ---------------------------------------------------------------
public static object SetMaterialProperties(JObject @params)
{
var p = new ToolParams(@params);
var skyMat = RenderSettings.skybox;
if (skyMat == null)
return new ErrorResponse("No skybox material is set.");
var propsRaw = p.GetRaw("properties") ?? p.GetRaw("parameters");
if (propsRaw == null || propsRaw.Type != JTokenType.Object)
return new ErrorResponse("'properties' dict is required.");
var set = new List<string>();
var failed = new List<string>();
foreach (var kvp in (JObject)propsRaw)
{
string propName = kvp.Key;
if (!skyMat.HasProperty(propName))
{
string altName = "_" + propName;
if (skyMat.HasProperty(altName))
propName = altName;
else
{
failed.Add(kvp.Key);
continue;
}
}
if (SetMaterialProperty(skyMat, propName, kvp.Value))
set.Add(kvp.Key);
else
failed.Add(kvp.Key);
}
EditorUtility.SetDirty(skyMat);
AssetDatabase.SaveAssets();
MarkSceneDirty();
return new
{
success = true,
message = $"Set {set.Count} property(ies) on skybox material '{skyMat.name}'.",
data = new { material = skyMat.name, set, failed }
};
}
// ---------------------------------------------------------------
// skybox_set_ambient — set ambient lighting mode and colors
// ---------------------------------------------------------------
public static object SetAmbient(JObject @params)
{
var p = new ToolParams(@params);
string modeStr = p.Get("ambient_mode") ?? p.Get("mode");
if (!string.IsNullOrEmpty(modeStr))
{
if (Enum.TryParse<AmbientMode>(modeStr, true, out var mode))
RenderSettings.ambientMode = mode;
else
return new ErrorResponse(
$"Invalid ambient mode '{modeStr}'. Valid: Skybox, Trilight, Flat, Custom.");
}
var skyColor = ParseColorToken(p.GetRaw("color") ?? p.GetRaw("sky_color"));
if (skyColor.HasValue)
RenderSettings.ambientSkyColor = skyColor.Value;
var equatorColor = ParseColorToken(p.GetRaw("equator_color"));
if (equatorColor.HasValue)
RenderSettings.ambientEquatorColor = equatorColor.Value;
var groundColor = ParseColorToken(p.GetRaw("ground_color"));
if (groundColor.HasValue)
RenderSettings.ambientGroundColor = groundColor.Value;
var intensity = p.GetFloat("intensity");
if (intensity.HasValue)
RenderSettings.ambientIntensity = intensity.Value;
MarkSceneDirty();
return new
{
success = true,
message = $"Ambient lighting updated (mode: {RenderSettings.ambientMode}).",
data = new
{
mode = RenderSettings.ambientMode.ToString(),
skyColor = ColorToArray(RenderSettings.ambientSkyColor),
equatorColor = ColorToArray(RenderSettings.ambientEquatorColor),
groundColor = ColorToArray(RenderSettings.ambientGroundColor),
intensity = RenderSettings.ambientIntensity
}
};
}
// ---------------------------------------------------------------
// skybox_set_fog — enable/configure fog
// ---------------------------------------------------------------
public static object SetFog(JObject @params)
{
var p = new ToolParams(@params);
var enabledToken = p.GetRaw("fog_enabled") ?? p.GetRaw("enabled");
if (enabledToken != null && enabledToken.Type != JTokenType.Null)
RenderSettings.fog = ParamCoercion.CoerceBool(enabledToken, RenderSettings.fog);
string modeStr = p.Get("fog_mode") ?? p.Get("mode");
if (!string.IsNullOrEmpty(modeStr))
{
if (Enum.TryParse<FogMode>(modeStr, true, out var fogMode))
RenderSettings.fogMode = fogMode;
else
return new ErrorResponse(
$"Invalid fog mode '{modeStr}'. Valid: Linear, Exponential, ExponentialSquared.");
}
var fogColor = ParseColorToken(p.GetRaw("fog_color") ?? p.GetRaw("color"));
if (fogColor.HasValue)
RenderSettings.fogColor = fogColor.Value;
var density = p.GetFloat("fog_density") ?? p.GetFloat("density");
if (density.HasValue)
RenderSettings.fogDensity = density.Value;
var start = p.GetFloat("fog_start") ?? p.GetFloat("start");
if (start.HasValue)
RenderSettings.fogStartDistance = start.Value;
var end = p.GetFloat("fog_end") ?? p.GetFloat("end");
if (end.HasValue)
RenderSettings.fogEndDistance = end.Value;
MarkSceneDirty();
return new
{
success = true,
message = $"Fog settings updated (enabled: {RenderSettings.fog}, mode: {RenderSettings.fogMode}).",
data = new
{
enabled = RenderSettings.fog,
mode = RenderSettings.fogMode.ToString(),
color = ColorToArray(RenderSettings.fogColor),
density = RenderSettings.fogDensity,
startDistance = RenderSettings.fogStartDistance,
endDistance = RenderSettings.fogEndDistance
}
};
}
// ---------------------------------------------------------------
// skybox_set_reflection — configure environment reflections
// ---------------------------------------------------------------
public static object SetReflection(JObject @params)
{
var p = new ToolParams(@params);
var intensity = p.GetFloat("intensity");
if (intensity.HasValue)
RenderSettings.reflectionIntensity = intensity.Value;
var bounces = p.GetInt("bounces");
if (bounces.HasValue)
RenderSettings.reflectionBounces = bounces.Value;
string modeStr = p.Get("reflection_mode") ?? p.Get("mode");
if (!string.IsNullOrEmpty(modeStr))
{
if (Enum.TryParse<DefaultReflectionMode>(modeStr, true, out var mode))
RenderSettings.defaultReflectionMode = mode;
else
return new ErrorResponse(
$"Invalid reflection mode '{modeStr}'. Valid: Skybox, Custom.");
}
var resolution = p.GetInt("resolution");
if (resolution.HasValue)
RenderSettings.defaultReflectionResolution = resolution.Value;
string cubemapPath = p.Get("path") ?? p.Get("cubemap_path");
if (!string.IsNullOrEmpty(cubemapPath))
{
var cubemap = AssetDatabase.LoadAssetAtPath<Texture>(cubemapPath);
if (cubemap != null)
CustomReflectionTexture = cubemap;
else
return new ErrorResponse($"Cubemap not found at '{cubemapPath}'.");
}
MarkSceneDirty();
return new
{
success = true,
message = $"Reflection settings updated (intensity: {RenderSettings.reflectionIntensity}, bounces: {RenderSettings.reflectionBounces}).",
data = new
{
intensity = RenderSettings.reflectionIntensity,
bounces = RenderSettings.reflectionBounces,
mode = RenderSettings.defaultReflectionMode.ToString(),
resolution = RenderSettings.defaultReflectionResolution,
customCubemap = CustomReflectionTexture != null
? AssetDatabase.GetAssetPath(CustomReflectionTexture)
: null
}
};
}
// ---------------------------------------------------------------
// skybox_set_sun — set the sun source light
// ---------------------------------------------------------------
public static object SetSun(JObject @params)
{
var p = new ToolParams(@params);
string target = p.Get("target") ?? p.Get("name");
if (string.IsNullOrEmpty(target))
return new ErrorResponse("'target' (light GameObject name or instance ID) is required.");
GameObject go = null;
if (int.TryParse(target, out int instanceId))
go = GameObjectLookup.ResolveInstanceID(instanceId) as GameObject;
if (go == null)
go = GameObject.Find(target);
if (go == null)
return new ErrorResponse($"GameObject '{target}' not found.");
var light = go.GetComponent<Light>();
if (light == null)
return new ErrorResponse($"'{go.name}' does not have a Light component.");
RenderSettings.sun = light;
MarkSceneDirty();
return new
{
success = true,
message = $"Sun source set to '{go.name}'.",
data = new
{
name = go.name,
instanceID = go.GetInstanceIDCompat(),
lightType = light.type.ToString()
}
};
}
// ---------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------
private static float[] ColorToArray(Color c)
{
return new[] { c.r, c.g, c.b, c.a };
}
private static Color ArrayToColor(float[] arr)
{
return new Color(
arr[0], arr[1], arr[2],
arr.Length >= 4 ? arr[3] : 1f);
}
private static Color? ParseColorToken(JToken token)
{
if (token == null || token.Type == JTokenType.Null) return null;
if (token is JArray arr && arr.Count >= 3)
{
return new Color(
(float)arr[0], (float)arr[1], (float)arr[2],
arr.Count >= 4 ? (float)arr[3] : 1f);
}
return null;
}
private static object ReadMaterialProperty(Material mat, string propName, ShaderPropertyType propType)
{
switch (propType)
{
case ShaderPropertyType.Color:
return ColorToArray(mat.GetColor(propName));
case ShaderPropertyType.Float:
case ShaderPropertyType.Range:
return mat.GetFloat(propName);
case ShaderPropertyType.Int:
return mat.GetInt(propName);
case ShaderPropertyType.Vector:
var v = mat.GetVector(propName);
return new[] { v.x, v.y, v.z, v.w };
case ShaderPropertyType.Texture:
var tex = mat.GetTexture(propName);
return tex != null ? AssetDatabase.GetAssetPath(tex) : null;
default:
return null;
}
}
private static bool SetMaterialProperty(Material mat, string propName, JToken value)
{
int propIdx = mat.shader.FindPropertyIndex(propName);
if (propIdx < 0) return false;
var propType = mat.shader.GetPropertyType(propIdx);
try
{
switch (propType)
{
case ShaderPropertyType.Color:
if (value is JArray colorArr && colorArr.Count >= 3)
{
mat.SetColor(propName, new Color(
(float)colorArr[0], (float)colorArr[1], (float)colorArr[2],
colorArr.Count >= 4 ? (float)colorArr[3] : 1f));
return true;
}
return false;
case ShaderPropertyType.Float:
case ShaderPropertyType.Range:
mat.SetFloat(propName, (float)value);
return true;
case ShaderPropertyType.Int:
mat.SetInt(propName, (int)value);
return true;
case ShaderPropertyType.Vector:
if (value is JArray vecArr && vecArr.Count >= 2)
{
mat.SetVector(propName, new Vector4(
(float)vecArr[0], (float)vecArr[1],
vecArr.Count >= 3 ? (float)vecArr[2] : 0f,
vecArr.Count >= 4 ? (float)vecArr[3] : 0f));
return true;
}
return false;
case ShaderPropertyType.Texture:
if (value.Type == JTokenType.String)
{
var tex = AssetDatabase.LoadAssetAtPath<Texture>(value.ToString());
if (tex != null) { mat.SetTexture(propName, tex); return true; }
}
else if (value.Type == JTokenType.Null)
{
mat.SetTexture(propName, null);
return true;
}
return false;
default:
return false;
}
}
catch
{
return false;
}
}
private static void MarkSceneDirty()
{
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(
UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
}
}
}