Files
hexapod-robot/2026-current/Hexapod_Simulation_Unity/Assets/HexapodSim/Editor/OpenScadModelSnapshot.cs
T
mangavalkandClaude Opus 5.5 91b5b99fd5 2026: OpenSCAD robot and remote, physical joint limits, model in Unity
hardware/openscad/ rebuilds the broken Fusion models as OpenSCAD:
body (top/bottom plate, standoffs, battery bay, switch), legs (coxa
bracket, femur link, tibia, horn adapters, TPU socks, servo cables),
the whole robot, and the remote (shell, lid, Nextion 3.5", JH-D400X-R4
sticks, 18650 UPS board, IMU, Teensy 4.0, USB-C breakout). Every part
was checked by slicing against the original STEP solids; the original's
bugs are fixed and listed in PLANNING.md.

The robot now uses a hexagon leg layout (corner legs 60 deg round from
the mid legs; the corner coxa brackets need turning on their splines).
Physical limits from a collision sweep of the model are in Config.h:
per-leg coxa limits and LEG_CLEARANCE_MM 56 (a leg is 55.2 mm wide at
the joints); the sim API exports them (version 7). sim_smoke passes.

Unity loads the model (export_unity.py -> Resources/OpenScadModel,
OpenScadModel.cs builds a new rig in place of the old scene's robot).
The PCB model's mounting holes now use the exact EasyEDA grid. README
shows renders of both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 12:14:32 +01:00

76 lines
3.3 KiB
C#

using System.IO;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
namespace HexapodSim
{
// Batch-mode check of the OpenSCAD model in Main.unity, without entering
// Play mode: builds the model from the DLL's geometry, poses the legs in
// the firmware's standing pose and renders the main camera to PNGs.
// Unity.exe -batchmode -projectPath . -executeMethod HexapodSim.OpenScadModelSnapshot.Run
// -snapshotDir <folder> -quit
public static class OpenScadModelSnapshot
{
public static void Run()
{
string dir = Arg("-snapshotDir") ?? Path.GetFullPath("Temp/OpenScadSnapshot");
Directory.CreateDirectory(dir);
EditorSceneManager.OpenScene("Assets/Main.unity");
Transform chassis = null;
foreach (var t in Object.FindObjectsByType<Transform>())
if (t.name == "Leg" && t.Find("hip1") != null) { chassis = t.parent; break; }
if (chassis == null) { Debug.LogError("[Snapshot] no leg rig in Main.unity"); EditorApplication.Exit(1); return; }
string dll = Path.GetFullPath(Path.Combine(Application.dataPath, "..", "..", "sim", "build", "hexapod_sim.dll"));
HxGeometry g;
using (var native = HexapodSimNative.Load(dll)) g = native.Geometry;
OpenScadModel.Build(chassis, g, ForwardAxis.NegativeX, 0.1f);
// Firmware stance: coxa 0, femur -1.2, tibia -98.3 (HexapodSimulation.ApplyJoints).
foreach (Transform leg in chassis)
{
if (leg.name != "Leg" || !leg.gameObject.activeSelf) continue;
var hip2 = leg.Find("hip1/Hip2");
var knie = leg.Find("hip1/Hip2/Knie");
hip2.localRotation = Quaternion.Euler(1.2f, 0, 0);
knie.localRotation = Quaternion.Euler(-(-98.3f + 90f), 0, 0);
}
var cam = Camera.main;
if (cam == null) cam = new GameObject("SnapshotCamera").AddComponent<Camera>();
Vector3 c = chassis.position;
Shoot(cam, c + new Vector3(-30, 22, -38), c, Path.Combine(dir, "front_left.png"));
Shoot(cam, c + new Vector3(42, 30, 20), c, Path.Combine(dir, "rear_right.png"));
Shoot(cam, c + new Vector3(0.01f, 60, 0), c, Path.Combine(dir, "top.png"));
Debug.Log("[Snapshot] wrote " + dir);
EditorApplication.Exit(0);
}
static void Shoot(Camera cam, Vector3 pos, Vector3 target, string file)
{
cam.transform.position = pos;
cam.transform.LookAt(target);
cam.fieldOfView = 45;
var rt = new RenderTexture(1280, 900, 24);
cam.targetTexture = rt;
cam.Render();
RenderTexture.active = rt;
var tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false);
tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
tex.Apply();
File.WriteAllBytes(file, tex.EncodeToPNG());
cam.targetTexture = null;
RenderTexture.active = null;
}
static string Arg(string name)
{
var a = System.Environment.GetCommandLineArgs();
for (int i = 0; i < a.Length - 1; i++) if (a[i] == name) return a[i + 1];
return null;
}
}
}