Files
server-deploy/windows-dev-stack/godot-mcp-pro-v1.14.1/server/build/tools/scene-3d-tools.js
Joywayer dd3eb24d0f refactor: 拆分 claude-dev-stack 为 windows-dev-stack 和 wsl-dev-stack
将原 claude-dev-stack 目录拆分为独立的 Windows 和 WSL 部署栈,便于分别维护和使用。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-29 01:11:20 +08:00

405 lines
17 KiB
JavaScript

import { z } from "zod";
import { formatErrorForMcp } from "../utils/errors.js";
export function registerScene3DTools(server, godot) {
// ─── 1. add_mesh_instance ──────────────────────────────────────────────
server.tool("add_mesh_instance", "Add a MeshInstance3D node with a primitive mesh (Box, Sphere, Cylinder, Capsule, Plane, Prism, Torus, Quad) or load a 3D model file (.glb/.gltf/.obj). Set position, rotation, scale, and mesh-specific properties.", {
mesh_type: z
.string()
.optional()
.describe("Primitive mesh type: BoxMesh, SphereMesh, CylinderMesh, CapsuleMesh, PlaneMesh, PrismMesh, TorusMesh, QuadMesh"),
mesh_file: z
.string()
.optional()
.describe("Path to a 3D model file (res://path/to/model.glb, .gltf, .obj). Use instead of mesh_type for imported models"),
parent_path: z
.string()
.optional()
.describe("Parent node path (default: root '.')"),
name: z.string().optional().describe("Node name (default: MeshInstance3D)"),
position: z
.any()
.optional()
.describe("Position as Vector3 string 'Vector3(x,y,z)', object {x,y,z}, or array [x,y,z]"),
rotation: z
.any()
.optional()
.describe("Rotation in degrees as Vector3 string, object {x,y,z}, or array [x,y,z]"),
scale: z
.any()
.optional()
.describe("Scale as Vector3 string, object {x,y,z}, or array [x,y,z]"),
mesh_properties: z
.record(z.string(), z.any())
.optional()
.describe("Properties to set on the mesh resource (e.g. {\"size\": \"Vector3(2,1,2)\"} for BoxMesh)"),
}, async (params) => {
try {
const result = await godot.sendCommand("add_mesh_instance", params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
}
catch (e) {
return {
content: [{ type: "text", text: formatErrorForMcp(e) }],
isError: true,
};
}
});
// ─── 2. setup_lighting ─────────────────────────────────────────────────
server.tool("setup_lighting", "Add a light node (DirectionalLight3D, OmniLight3D, SpotLight3D) to the scene. Supports preset configurations: 'sun' (directional with shadows), 'indoor' (warm omni), 'dramatic' (focused spot with shadows).", {
light_type: z
.string()
.optional()
.describe("Light type: DirectionalLight3D, OmniLight3D, SpotLight3D. Not needed if preset is specified"),
preset: z
.string()
.optional()
.describe("Preset configuration: 'sun' (directional, shadows, -45deg), 'indoor' (warm omni, range 8), 'dramatic' (spot, high energy, shadows)"),
parent_path: z
.string()
.optional()
.describe("Parent node path (default: root '.')"),
name: z.string().optional().describe("Node name"),
color: z
.any()
.optional()
.describe("Light color as Color string or hex (default: white)"),
energy: z
.number()
.optional()
.describe("Light energy/intensity (default: 1.0)"),
shadows: z
.boolean()
.optional()
.describe("Enable shadow casting (default: false, true for sun/dramatic presets)"),
range: z
.number()
.optional()
.describe("Range for OmniLight3D/SpotLight3D (default: 5.0)"),
attenuation: z
.number()
.optional()
.describe("Attenuation for OmniLight3D/SpotLight3D (default: 1.0)"),
spot_angle: z
.number()
.optional()
.describe("Spot angle in degrees for SpotLight3D (default: 45.0)"),
spot_angle_attenuation: z
.number()
.optional()
.describe("Spot angle attenuation for SpotLight3D (default: 1.0)"),
position: z
.any()
.optional()
.describe("Position as Vector3 string, object {x,y,z}, or array [x,y,z]"),
rotation: z
.any()
.optional()
.describe("Rotation in degrees as Vector3 string, object {x,y,z}, or array [x,y,z]"),
}, async (params) => {
try {
const result = await godot.sendCommand("setup_lighting", params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
}
catch (e) {
return {
content: [{ type: "text", text: formatErrorForMcp(e) }],
isError: true,
};
}
});
// ─── 3. set_material_3d ────────────────────────────────────────────────
server.tool("set_material_3d", "Create and apply a StandardMaterial3D to a MeshInstance3D. Configure PBR properties: albedo color/texture, metallic, roughness, emission, transparency, normal maps.", {
node_path: z
.string()
.describe("Path to the MeshInstance3D node"),
surface_index: z
.number()
.optional()
.describe("Surface index to apply material to (default: 0)"),
albedo_color: z
.any()
.optional()
.describe("Albedo color as Color string 'Color(r,g,b,a)', hex '#ff0000', or object {r,g,b,a}"),
albedo_texture: z
.string()
.optional()
.describe("Path to albedo texture (res://path/to/texture.png)"),
metallic: z
.number()
.optional()
.describe("Metallic value 0.0-1.0 (default: 0.0)"),
roughness: z
.number()
.optional()
.describe("Roughness value 0.0-1.0 (default: 1.0)"),
metallic_texture: z
.string()
.optional()
.describe("Path to metallic texture"),
roughness_texture: z
.string()
.optional()
.describe("Path to roughness texture"),
normal_texture: z
.string()
.optional()
.describe("Path to normal map texture (auto-enables normal mapping)"),
emission: z
.any()
.optional()
.describe("Emission color (auto-enables emission). Color string or hex"),
emission_color: z
.any()
.optional()
.describe("Alias for emission"),
emission_energy: z
.number()
.optional()
.describe("Emission energy multiplier (default: 1.0)"),
emission_texture: z
.string()
.optional()
.describe("Path to emission texture"),
transparency: z
.string()
.optional()
.describe("Transparency mode: DISABLED, ALPHA, ALPHA_SCISSOR, ALPHA_HASH, ALPHA_DEPTH_PRE_PASS"),
cull_mode: z
.string()
.optional()
.describe("Cull mode: BACK, FRONT, DISABLED"),
}, async (params) => {
try {
const result = await godot.sendCommand("set_material_3d", params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
}
catch (e) {
return {
content: [{ type: "text", text: formatErrorForMcp(e) }],
isError: true,
};
}
});
// ─── 4. setup_environment ──────────────────────────────────────────────
server.tool("setup_environment", "Add or configure a WorldEnvironment node with sky, ambient light, tonemap, fog, glow, SSAO, SSR, and SDFGI settings.", {
parent_path: z
.string()
.optional()
.describe("Parent node path (default: root '.')"),
node_path: z
.string()
.optional()
.describe("Path to an existing WorldEnvironment to modify instead of creating a new one"),
name: z
.string()
.optional()
.describe("Node name (default: WorldEnvironment)"),
background_mode: z
.string()
.optional()
.describe("Background mode: 'sky', 'color', 'canvas', 'clear_color' (default: sky)"),
background_color: z
.any()
.optional()
.describe("Background color when mode is 'color'"),
sky: z
.object({
sky_top_color: z.any().optional().describe("Sky top color"),
sky_horizon_color: z.any().optional().describe("Sky horizon color"),
ground_bottom_color: z.any().optional().describe("Ground bottom color"),
ground_horizon_color: z
.any()
.optional()
.describe("Ground horizon color"),
sun_angle_max: z
.number()
.optional()
.describe("Maximum sun angle in degrees"),
sky_curve: z
.number()
.optional()
.describe("Sky color curve (0.0-1.0)"),
})
.optional()
.describe("ProceduralSkyMaterial settings"),
ambient_light_color: z.any().optional().describe("Ambient light color"),
ambient_light_energy: z
.number()
.optional()
.describe("Ambient light energy"),
ambient_light_source: z
.string()
.optional()
.describe("Ambient light source: BACKGROUND, DISABLED, COLOR, SKY"),
tonemap_mode: z
.string()
.optional()
.describe("Tonemap mode: LINEAR, REINHARDT, FILMIC, ACES"),
tonemap_exposure: z.number().optional().describe("Tonemap exposure"),
tonemap_white: z.number().optional().describe("Tonemap white point"),
fog_enabled: z.boolean().optional().describe("Enable volumetric fog"),
fog_light_color: z.any().optional().describe("Fog light color"),
fog_density: z.number().optional().describe("Fog density"),
fog_light_energy: z.number().optional().describe("Fog light energy"),
glow_enabled: z.boolean().optional().describe("Enable glow/bloom"),
glow_intensity: z.number().optional().describe("Glow intensity"),
glow_strength: z.number().optional().describe("Glow strength"),
glow_bloom: z.number().optional().describe("Glow bloom amount"),
ssao_enabled: z
.boolean()
.optional()
.describe("Enable Screen-Space Ambient Occlusion"),
ssao_radius: z.number().optional().describe("SSAO radius"),
ssao_intensity: z.number().optional().describe("SSAO intensity"),
ssr_enabled: z
.boolean()
.optional()
.describe("Enable Screen-Space Reflections"),
ssr_max_steps: z.number().optional().describe("SSR max steps"),
ssr_fade_in: z.number().optional().describe("SSR fade in"),
ssr_fade_out: z.number().optional().describe("SSR fade out"),
sdfgi_enabled: z
.boolean()
.optional()
.describe("Enable Signed Distance Field Global Illumination"),
}, async (params) => {
try {
const result = await godot.sendCommand("setup_environment", params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
}
catch (e) {
return {
content: [{ type: "text", text: formatErrorForMcp(e) }],
isError: true,
};
}
});
// ─── 5. setup_camera_3d ────────────────────────────────────────────────
server.tool("setup_camera_3d", "Add or configure a Camera3D node. Set projection mode, FOV, near/far planes, position, rotation, look-at target, and cull mask.", {
parent_path: z
.string()
.optional()
.describe("Parent node path (default: root '.')"),
node_path: z
.string()
.optional()
.describe("Path to an existing Camera3D to configure instead of creating a new one"),
name: z.string().optional().describe("Node name (default: Camera3D)"),
projection: z
.string()
.optional()
.describe("Projection mode: 'perspective', 'orthogonal'/'orthographic', 'frustum'"),
fov: z
.number()
.optional()
.describe("Field of view in degrees for perspective (default: 75)"),
size: z
.number()
.optional()
.describe("View size for orthogonal projection"),
near: z
.number()
.optional()
.describe("Near clipping plane (default: 0.05)"),
far: z
.number()
.optional()
.describe("Far clipping plane (default: 4000)"),
cull_mask: z
.number()
.optional()
.describe("Cull mask as integer bitmask"),
current: z
.boolean()
.optional()
.describe("Make this the current/active camera (default: false)"),
position: z
.any()
.optional()
.describe("Position as Vector3 (default: (0, 1, 3) for new cameras)"),
rotation: z
.any()
.optional()
.describe("Rotation in degrees as Vector3"),
look_at: z
.any()
.optional()
.describe("Target position to look at as Vector3 (overrides rotation)"),
environment_path: z
.string()
.optional()
.describe("Path to an Environment resource for camera-specific environment override"),
}, async (params) => {
try {
const result = await godot.sendCommand("setup_camera_3d", params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
}
catch (e) {
return {
content: [{ type: "text", text: formatErrorForMcp(e) }],
isError: true,
};
}
});
// ─── 6. add_gridmap ────────────────────────────────────────────────────
server.tool("add_gridmap", "Add or configure a GridMap node with a MeshLibrary. Optionally set cells at specific grid positions with item IDs and orientations.", {
parent_path: z
.string()
.optional()
.describe("Parent node path (default: root '.')"),
node_path: z
.string()
.optional()
.describe("Path to an existing GridMap to configure instead of creating a new one"),
name: z.string().optional().describe("Node name (default: GridMap)"),
mesh_library_path: z
.string()
.optional()
.describe("Path to a MeshLibrary resource (res://path/to/library.meshlib or .tres)"),
cell_size: z
.any()
.optional()
.describe("Cell size as Vector3 (default: (2, 2, 2))"),
position: z.any().optional().describe("GridMap position as Vector3"),
cells: z
.array(z.object({
x: z.number().describe("Cell X coordinate"),
y: z.number().describe("Cell Y coordinate"),
z: z.number().describe("Cell Z coordinate"),
item: z
.number()
.optional()
.describe("MeshLibrary item index (default: 0)"),
orientation: z
.number()
.optional()
.describe("Cell orientation index (default: 0)"),
}))
.optional()
.describe("Array of cells to set with grid positions and item IDs"),
}, async (params) => {
try {
const result = await godot.sendCommand("add_gridmap", params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
}
catch (e) {
return {
content: [{ type: "text", text: formatErrorForMcp(e) }],
isError: true,
};
}
});
}
//# sourceMappingURL=scene-3d-tools.js.map