Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions MCPForUnity/Editor/Helpers/RenderPipelineUtility.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;

namespace MCPForUnity.Editor.Helpers
{
Expand All @@ -14,6 +16,15 @@ internal enum PipelineKind
Custom
}

internal enum VFXComponentType
{
ParticleSystem,
LineRenderer,
TrailRenderer
}

private static Dictionary<string, Material> s_DefaultVFXMaterials = new Dictionary<string, Material>();
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field 's_DefaultVFXMaterials' can be 'readonly'.

Suggested change
private static Dictionary<string, Material> s_DefaultVFXMaterials = new Dictionary<string, Material>();
private static readonly Dictionary<string, Material> s_DefaultVFXMaterials = new Dictionary<string, Material>();

Copilot uses AI. Check for mistakes.

private static readonly string[] BuiltInLitShaders = { "Standard", "Legacy Shaders/Diffuse" };
private static readonly string[] BuiltInUnlitShaders = { "Unlit/Color", "Unlit/Texture" };
private static readonly string[] UrpLitShaders = { "Universal Render Pipeline/Lit", "Universal Render Pipeline/Simple Lit" };
Expand Down Expand Up @@ -192,5 +203,82 @@ private static void WarnIfPipelineMismatch(string shaderName, PipelineKind activ
break;
}
}

internal static Material GetOrCreateDefaultVFXMaterial(VFXComponentType componentType)
{
var pipeline = GetActivePipeline();
string cacheKey = $"{pipeline}_{componentType}";

if (s_DefaultVFXMaterials.TryGetValue(cacheKey, out Material cachedMaterial) && cachedMaterial != null)
{
return cachedMaterial;
}

Material material = null;

if (pipeline == PipelineKind.BuiltIn)
{
string builtinPath = componentType == VFXComponentType.ParticleSystem
? "Default-Particle.mat"
: "Default-Line.mat";

material = AssetDatabase.GetBuiltinExtraResource<Material>(builtinPath);
}

if (material == null)
{
Shader shader = ResolveDefaultUnlitShader(pipeline);
if (shader == null)
{
shader = Shader.Find("Unlit/Color");
}

if (shader != null)
{
material = new Material(shader);
material.name = $"Auto_Default_{componentType}_{pipeline}";

// Set default color (white is standard for VFX)
if (material.HasProperty("_Color"))
{
material.SetColor("_Color", Color.white);
}
if (material.HasProperty("_BaseColor"))
{
material.SetColor("_BaseColor", Color.white);
}

if (componentType == VFXComponentType.ParticleSystem)
{
material.renderQueue = 3000;
if (material.HasProperty("_Mode"))
{
material.SetFloat("_Mode", 2);
}
if (material.HasProperty("_SrcBlend"))
{
material.SetFloat("_SrcBlend", (float)UnityEngine.Rendering.BlendMode.SrcAlpha);
}
if (material.HasProperty("_DstBlend"))
{
material.SetFloat("_DstBlend", (float)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
}
if (material.HasProperty("_ZWrite"))
{
material.SetFloat("_ZWrite", 0);
}
}

McpLog.Info($"[RenderPipelineUtility] Created default VFX material for {componentType} using {shader.name}");
}
}

if (material != null)
{
s_DefaultVFXMaterials[cacheKey] = material;
}

return material;
}
}
}
77 changes: 75 additions & 2 deletions MCPForUnity/Editor/Helpers/RendererHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,43 @@ namespace MCPForUnity.Editor.Helpers
/// </summary>
public static class RendererHelpers
{
/// <summary>
/// Ensures a renderer has a material assigned. If not, auto-assigns a default material
/// based on the render pipeline and component type.
/// </summary>
/// <param name="renderer">The renderer to check</param>
public static void EnsureMaterial(Renderer renderer)
{
if (renderer == null || renderer.sharedMaterial != null)
{
return;
}

RenderPipelineUtility.VFXComponentType? componentType = null;
if (renderer is ParticleSystemRenderer)
{
componentType = RenderPipelineUtility.VFXComponentType.ParticleSystem;
}
else if (renderer is LineRenderer)
{
componentType = RenderPipelineUtility.VFXComponentType.LineRenderer;
}
else if (renderer is TrailRenderer)
{
componentType = RenderPipelineUtility.VFXComponentType.TrailRenderer;
}

if (componentType.HasValue)
{
Material defaultMat = RenderPipelineUtility.GetOrCreateDefaultVFXMaterial(componentType.Value);
if (defaultMat != null)
{
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EnsureMaterial mutates renderer.sharedMaterial without recording an Undo step or marking the object dirty. Since many call sites invoke EnsureMaterial(...) before Undo.RecordObject(...), the auto-assigned material change won’t be undoable and may not persist reliably in editor workflows. Consider either (a) moving EnsureMaterial calls to after Undo.RecordObject, or (b) updating EnsureMaterial to handle Undo/SetDirty (e.g., accept an optional undo label and call Undo.RecordObject / EditorUtility.SetDirty when it assigns a material).

Suggested change
{
{
Undo.RecordObject(renderer, "Assign default VFX material");
EditorUtility.SetDirty(renderer);

Copilot uses AI. Check for mistakes.
Undo.RecordObject(renderer, "Assign default VFX material");
EditorUtility.SetDirty(renderer);
renderer.sharedMaterial = defaultMat;
}
}
}

/// <summary>
/// Applies common Renderer properties (shadows, lighting, probes, sorting, rendering layer).
Expand Down Expand Up @@ -127,12 +164,48 @@ public static void ApplyColorProperties(JObject @params, List<string> changes,
/// <param name="params">JSON parameters containing materialPath</param>
/// <param name="undoName">Name for the undo operation</param>
/// <param name="findMaterial">Function to find material by path</param>
public static object SetRendererMaterial(Renderer renderer, JObject @params, string undoName, Func<string, Material> findMaterial)
/// <param name="autoAssignDefault">If true, auto-assigns default material when materialPath is not provided</param>
public static object SetRendererMaterial(Renderer renderer, JObject @params, string undoName, Func<string, Material> findMaterial, bool autoAssignDefault = true)
{
if (renderer == null) return new { success = false, message = "Renderer not found" };

string path = @params["materialPath"]?.ToString();
if (string.IsNullOrEmpty(path)) return new { success = false, message = "materialPath required" };

if (string.IsNullOrEmpty(path))
{
if (!autoAssignDefault)
{
return new { success = false, message = "materialPath required" };
}

RenderPipelineUtility.VFXComponentType? componentType = null;
if (renderer is ParticleSystemRenderer)
{
componentType = RenderPipelineUtility.VFXComponentType.ParticleSystem;
}
else if (renderer is LineRenderer)
{
componentType = RenderPipelineUtility.VFXComponentType.LineRenderer;
}
else if (renderer is TrailRenderer)
{
componentType = RenderPipelineUtility.VFXComponentType.TrailRenderer;
}

if (componentType.HasValue)
{
Material defaultMat = RenderPipelineUtility.GetOrCreateDefaultVFXMaterial(componentType.Value);
if (defaultMat != null)
{
Undo.RecordObject(renderer, undoName);
renderer.sharedMaterial = defaultMat;
EditorUtility.SetDirty(renderer);
return new { success = true, message = $"Auto-assigned default material: {defaultMat.name}" };
}
}

return new { success = false, message = "materialPath required" };
}

Material mat = findMaterial(path);
if (mat == null) return new { success = false, message = $"Material not found: {path}" };
Expand Down
2 changes: 1 addition & 1 deletion MCPForUnity/Editor/Tools/ManageScript.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public static object HandleCommand(JObject @params)
}

// Extract parameters
string action = @params["action"]?.ToString()?.ToLower();
string action = @params["action"]?.ToString()?.ToLowerInvariant();
string name = @params["name"]?.ToString();
string path = @params["path"]?.ToString(); // Relative to Assets/
string contents = null;
Expand Down
90 changes: 90 additions & 0 deletions MCPForUnity/Editor/Tools/Vfx/LineCreate.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
using MCPForUnity.Editor.Helpers;

namespace MCPForUnity.Editor.Tools.Vfx
{
Expand All @@ -18,6 +19,29 @@ public static object CreateLine(JObject @params)
lr.positionCount = 2;
lr.SetPosition(0, start);
lr.SetPosition(1, end);

RendererHelpers.EnsureMaterial(lr);

// Apply optional width
if (@params["width"] != null)
{
float w = @params["width"].ToObject<float>();
lr.startWidth = w;
lr.endWidth = w;
}
if (@params["startWidth"] != null) lr.startWidth = @params["startWidth"].ToObject<float>();
if (@params["endWidth"] != null) lr.endWidth = @params["endWidth"].ToObject<float>();

// Apply optional color
if (@params["color"] != null)
{
Color c = ManageVfxCommon.ParseColor(@params["color"]);
lr.startColor = c;
lr.endColor = c;
}
if (@params["startColor"] != null) lr.startColor = ManageVfxCommon.ParseColor(@params["startColor"]);
if (@params["endColor"] != null) lr.endColor = ManageVfxCommon.ParseColor(@params["endColor"]);

EditorUtility.SetDirty(lr);

return new { success = true, message = "Created line" };
Expand Down Expand Up @@ -49,6 +73,28 @@ public static object CreateCircle(JObject @params)
lr.SetPosition(i, point);
}

RendererHelpers.EnsureMaterial(lr);

// Apply optional width
if (@params["width"] != null)
{
float w = @params["width"].ToObject<float>();
lr.startWidth = w;
lr.endWidth = w;
}
if (@params["startWidth"] != null) lr.startWidth = @params["startWidth"].ToObject<float>();
if (@params["endWidth"] != null) lr.endWidth = @params["endWidth"].ToObject<float>();

// Apply optional color
if (@params["color"] != null)
{
Color c = ManageVfxCommon.ParseColor(@params["color"]);
lr.startColor = c;
lr.endColor = c;
}
if (@params["startColor"] != null) lr.startColor = ManageVfxCommon.ParseColor(@params["startColor"]);
if (@params["endColor"] != null) lr.endColor = ManageVfxCommon.ParseColor(@params["endColor"]);

EditorUtility.SetDirty(lr);
return new { success = true, message = $"Created circle with {segments} segments" };
}
Expand Down Expand Up @@ -82,6 +128,28 @@ public static object CreateArc(JObject @params)
lr.SetPosition(i, point);
}

RendererHelpers.EnsureMaterial(lr);

// Apply optional width
if (@params["width"] != null)
{
float w = @params["width"].ToObject<float>();
lr.startWidth = w;
lr.endWidth = w;
}
if (@params["startWidth"] != null) lr.startWidth = @params["startWidth"].ToObject<float>();
if (@params["endWidth"] != null) lr.endWidth = @params["endWidth"].ToObject<float>();

// Apply optional color
if (@params["color"] != null)
{
Color c = ManageVfxCommon.ParseColor(@params["color"]);
lr.startColor = c;
lr.endColor = c;
}
if (@params["startColor"] != null) lr.startColor = ManageVfxCommon.ParseColor(@params["startColor"]);
if (@params["endColor"] != null) lr.endColor = ManageVfxCommon.ParseColor(@params["endColor"]);

EditorUtility.SetDirty(lr);
return new { success = true, message = $"Created arc with {segments} segments" };
}
Expand Down Expand Up @@ -123,6 +191,28 @@ public static object CreateBezier(JObject @params)
lr.SetPosition(i, point);
}

RendererHelpers.EnsureMaterial(lr);

// Apply optional width
if (@params["width"] != null)
{
float w = @params["width"].ToObject<float>();
lr.startWidth = w;
lr.endWidth = w;
}
if (@params["startWidth"] != null) lr.startWidth = @params["startWidth"].ToObject<float>();
if (@params["endWidth"] != null) lr.endWidth = @params["endWidth"].ToObject<float>();

// Apply optional color
if (@params["color"] != null)
{
Color c = ManageVfxCommon.ParseColor(@params["color"]);
lr.startColor = c;
lr.endColor = c;
}
if (@params["startColor"] != null) lr.startColor = ManageVfxCommon.ParseColor(@params["startColor"]);
if (@params["endColor"] != null) lr.endColor = ManageVfxCommon.ParseColor(@params["endColor"]);

EditorUtility.SetDirty(lr);
return new { success = true, message = $"Created {(isQuadratic ? "quadratic" : "cubic")} Bezier" };
}
Expand Down
Loading