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
162 changes: 162 additions & 0 deletions MCPForUnity/Editor/Helpers/TextureOps.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json.Linq;
using UnityEngine;

namespace MCPForUnity.Editor.Helpers
{
public static class TextureOps
{
public static byte[] EncodeTexture(Texture2D texture, string assetPath)
{
if (texture == null)
return null;

string extension = Path.GetExtension(assetPath);
if (string.IsNullOrEmpty(extension))
{
McpLog.Warn($"[TextureOps] No file extension for '{assetPath}', defaulting to PNG.");
return texture.EncodeToPNG();
}

switch (extension.ToLowerInvariant())
{
case ".png":
return texture.EncodeToPNG();
case ".jpg":
case ".jpeg":
return texture.EncodeToJPG();
default:
McpLog.Warn($"[TextureOps] Unsupported extension '{extension}' for '{assetPath}', defaulting to PNG.");
return texture.EncodeToPNG();
}
}

public static void FillTexture(Texture2D texture, Color32 color)
{
if (texture == null)
return;

Color32[] pixels = new Color32[texture.width * texture.height];
for (int i = 0; i < pixels.Length; i++)
{
pixels[i] = color;
}
texture.SetPixels32(pixels);
}

public static Color32 ParseColor32(JArray colorArray)
{
if (colorArray == null || colorArray.Count < 3)
return new Color32(255, 255, 255, 255);

byte r = (byte)Mathf.Clamp(colorArray[0].ToObject<int>(), 0, 255);
byte g = (byte)Mathf.Clamp(colorArray[1].ToObject<int>(), 0, 255);
byte b = (byte)Mathf.Clamp(colorArray[2].ToObject<int>(), 0, 255);
byte a = colorArray.Count > 3 ? (byte)Mathf.Clamp(colorArray[3].ToObject<int>(), 0, 255) : (byte)255;

return new Color32(r, g, b, a);
}

public static List<Color32> ParsePalette(JArray paletteArray)
{
if (paletteArray == null)
return null;

List<Color32> palette = new List<Color32>();
foreach (var item in paletteArray)
{
if (item is JArray colorArray)
{
palette.Add(ParseColor32(colorArray));
}
}
return palette.Count > 0 ? palette : null;
}

public static void ApplyPixelData(Texture2D texture, JToken pixelsToken, int width, int height)
{
ApplyPixelDataToRegion(texture, pixelsToken, 0, 0, width, height);
}

public static void ApplyPixelDataToRegion(Texture2D texture, JToken pixelsToken, int offsetX, int offsetY, int regionWidth, int regionHeight)
{
if (texture == null || pixelsToken == null)
return;

int textureWidth = texture.width;
int textureHeight = texture.height;

if (pixelsToken is JArray pixelArray)
{
int index = 0;
for (int y = 0; y < regionHeight && index < pixelArray.Count; y++)
{
for (int x = 0; x < regionWidth && index < pixelArray.Count; x++)
{
var pixelColor = pixelArray[index] as JArray;
if (pixelColor != null)
{
int px = offsetX + x;
int py = offsetY + y;
if (px >= 0 && px < textureWidth && py >= 0 && py < textureHeight)
{
texture.SetPixel(px, py, ParseColor32(pixelColor));
}
}
index++;
}
}

int expectedCount = regionWidth * regionHeight;
if (pixelArray.Count != expectedCount)
{
McpLog.Warn($"[TextureOps] Pixel array size mismatch: expected {expectedCount} entries, got {pixelArray.Count}");
}
}
else if (pixelsToken.Type == JTokenType.String)
{
string pixelString = pixelsToken.ToString();
string base64 = pixelString.StartsWith("base64:") ? pixelString.Substring(7) : pixelString;
if (!pixelString.StartsWith("base64:"))
{
McpLog.Warn("[TextureOps] Base64 pixel data missing 'base64:' prefix; attempting to decode.");
}

byte[] rawData = Convert.FromBase64String(base64);

// Assume RGBA32 format: 4 bytes per pixel
int expectedBytes = regionWidth * regionHeight * 4;
if (rawData.Length == expectedBytes)
{
int pixelIndex = 0;
for (int y = 0; y < regionHeight; y++)
{
for (int x = 0; x < regionWidth; x++)
{
int px = offsetX + x;
int py = offsetY + y;
if (px >= 0 && px < textureWidth && py >= 0 && py < textureHeight)
{
int byteIndex = pixelIndex * 4;
Color32 color = new Color32(
rawData[byteIndex],
rawData[byteIndex + 1],
rawData[byteIndex + 2],
rawData[byteIndex + 3]
);
texture.SetPixel(px, py, color);
}
pixelIndex++;
}
}
}
else
{
McpLog.Warn($"[TextureOps] Base64 data size mismatch: expected {expectedBytes} bytes, got {rawData.Length}");
}
}
}
}
}
11 changes: 11 additions & 0 deletions MCPForUnity/Editor/Helpers/TextureOps.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions MCPForUnity/Editor/Tools/GameObjects/GameObjectModify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using MCPForUnity.Editor.Helpers;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEditorInternal;
using UnityEngine;

Expand Down Expand Up @@ -239,6 +240,18 @@ internal static object Handle(JObject @params, JToken targetToken, string search
}

EditorUtility.SetDirty(targetGo);

// Mark the appropriate scene as dirty (handles both regular scenes and prefab stages)
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
if (prefabStage != null)
{
EditorSceneManager.MarkSceneDirty(prefabStage.scene);
}
else
{
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}

return new SuccessResponse(
$"GameObject '{targetGo.name}' modified successfully.",
Helpers.GameObjectSerializer.GetGameObjectData(targetGo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,16 @@ internal static List<GameObject> FindObjectsInternal(
}
else
{
#if UNITY_2023_1_OR_NEWER
var inactive = searchInactive ? FindObjectsInactive.Include : FindObjectsInactive.Exclude;
searchPoolComp = UnityEngine.Object.FindObjectsByType(componentType, inactive, FindObjectsSortMode.None)
.Cast<Component>()
.Select(c => c.gameObject);
#else
searchPoolComp = UnityEngine.Object.FindObjectsOfType(componentType, searchInactive)
.Cast<Component>()
.Select(c => c.gameObject);
#endif
}
results.AddRange(searchPoolComp.Where(go => go != null));
}
Expand Down
2 changes: 1 addition & 1 deletion MCPForUnity/Editor/Tools/ManageAsset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static class ManageAsset

public static object HandleCommand(JObject @params)
{
string action = @params["action"]?.ToString().ToLower();
string action = @params["action"]?.ToString()?.ToLowerInvariant();
if (string.IsNullOrEmpty(action))
{
return new ErrorResponse("Action parameter is required.");
Expand Down
2 changes: 1 addition & 1 deletion MCPForUnity/Editor/Tools/ManageEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static class ManageEditor
/// </summary>
public static object HandleCommand(JObject @params)
{
string action = @params["action"]?.ToString().ToLower();
string action = @params["action"]?.ToString()?.ToLowerInvariant();
// Parameters for specific actions
string tagName = @params["tagName"]?.ToString();
string layerName = @params["layerName"]?.ToString();
Expand Down
2 changes: 1 addition & 1 deletion MCPForUnity/Editor/Tools/ManageMaterial.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static class ManageMaterial
{
public static object HandleCommand(JObject @params)
{
string action = @params["action"]?.ToString();
string action = @params["action"]?.ToString()?.ToLowerInvariant();
if (string.IsNullOrEmpty(action))
{
return new ErrorResponse("Action is required");
Expand Down
Loading