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
68 changes: 58 additions & 10 deletions Editor/Commands/MaterialCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using Newtonsoft.Json.Linq;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering;
using UnityEditor;
using System.IO;

namespace MCPServer.Editor.Commands
{
Expand All @@ -22,26 +24,72 @@ public static object SetMaterial(JObject @params)
// Check if URP is being used
bool isURP = GraphicsSettings.currentRenderPipeline is UniversalRenderPipelineAsset;

// Create material with appropriate shader based on render pipeline
Material material;
if (isURP)
Material material = null;
string materialName = (string)@params["material_name"];
bool createIfMissing = (bool)(@params["create_if_missing"] ?? true);
string materialPath = null;

// If material name is specified, try to find or create it
if (!string.IsNullOrEmpty(materialName))
{
material = new Material(Shader.Find("Universal Render Pipeline/Lit"));
// Ensure Materials folder exists
const string materialsFolder = "Assets/Materials";
if (!Directory.Exists(materialsFolder))
{
Directory.CreateDirectory(materialsFolder);
}

materialPath = $"{materialsFolder}/{materialName}.mat";
material = AssetDatabase.LoadAssetAtPath<Material>(materialPath);

if (material == null && createIfMissing)
{
// Create new material with appropriate shader
material = new Material(isURP ? Shader.Find("Universal Render Pipeline/Lit") : Shader.Find("Standard"));
material.name = materialName;

// Save the material asset
AssetDatabase.CreateAsset(material, materialPath);
AssetDatabase.SaveAssets();
}
else if (material == null)
{
throw new System.Exception($"Material '{materialName}' not found and create_if_missing is false.");
}
}
else
{
material = new Material(Shader.Find("Standard"));
// Create a temporary material if no name specified
material = new Material(isURP ? Shader.Find("Universal Render Pipeline/Lit") : Shader.Find("Standard"));
}

if (@params.ContainsKey("material_name")) material.name = (string)@params["material_name"];
// Apply color if specified
if (@params.ContainsKey("color"))
{
var colorArray = (JArray)@params["color"] ?? throw new System.Exception("Invalid color parameter.");
if (colorArray.Count != 3) throw new System.Exception("Color must be an array of 3 floats [r, g, b].");
material.color = new Color((float)colorArray[0], (float)colorArray[1], (float)colorArray[2]);
var colorArray = (JArray)@params["color"];
if (colorArray.Count < 3 || colorArray.Count > 4)
throw new System.Exception("Color must be an array of 3 (RGB) or 4 (RGBA) floats.");

Color color = new Color(
(float)colorArray[0],
(float)colorArray[1],
(float)colorArray[2],
colorArray.Count > 3 ? (float)colorArray[3] : 1.0f
);
material.color = color;

// If this is a saved material, make sure to save the color change
if (!string.IsNullOrEmpty(materialPath))
{
EditorUtility.SetDirty(material);
AssetDatabase.SaveAssets();
}
}

// Apply the material to the renderer
renderer.material = material;
return new { material_name = material.name };

return new { material_name = material.name, path = materialPath };
}
}
}
31 changes: 22 additions & 9 deletions Python/tools/material_tools.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from mcp.server.fastmcp import FastMCP, Context
from typing import List
from typing import List, Optional
from unity_connection import get_unity_connection

def register_material_tools(mcp: FastMCP):
Expand All @@ -9,18 +9,22 @@ def register_material_tools(mcp: FastMCP):
def set_material(
ctx: Context,
object_name: str,
material_name: str = None,
color: List[float] = None,
material_name: Optional[str] = None,
color: Optional[List[float]] = None,
create_if_missing: bool = True
) -> str:
"""
Apply or create a material for a game object.
Apply or create a material for a game object. If material_name is provided,
the material will be saved as a shared asset in the Materials folder.

Args:
object_name: Target game object.
material_name: Optional material name.
color: Optional [R, G, B] values (0.0-1.0).
material_name: Optional material name. If provided, creates/uses a shared material asset.
color: Optional [R, G, B] or [R, G, B, A] values (0.0-1.0).
create_if_missing: Whether to create the material if it doesn't exist (default: True).

Returns:
str: Status message indicating success or failure.
"""
try:
unity = get_unity_connection()
Expand Down Expand Up @@ -63,14 +67,23 @@ def set_material(
return f"Error: Color {channel} value must be in the range 0.0-1.0, but got {value}."

# Set up parameters for the command
params = {"object_name": object_name}
params = {
"object_name": object_name,
"create_if_missing": create_if_missing
}
if material_name:
params["material_name"] = material_name
params["create_if_missing"] = create_if_missing
if color:
params["color"] = color

result = unity.send_command("SET_MATERIAL", params)
return f"Applied material to {object_name}: {result.get('material_name', 'unknown')}"
material_name = result.get("material_name", "unknown")
material_path = result.get("path")

if material_path:
return f"Applied shared material '{material_name}' to {object_name} (saved at {material_path})"
else:
return f"Applied instance material '{material_name}' to {object_name}"

except Exception as e:
return f"Error setting material: {str(e)}"