From b5cd0f29f57cdaba788c862ca89fa5f7d9072fc7 Mon Sep 17 00:00:00 2001 From: Leo Qu Date: Thu, 12 Mar 2026 23:12:41 -0400 Subject: [PATCH] Initial commit after symposium --- ...dd0ec-396d-4455-60d8-0a95e21a992d.run.lock | 0 Assets/Icons/homeIcon.png | Bin 6259 -> 129 bytes .../SavedTimelines/CarDrive.json | 23 + .../SavedTimelines/CarDrive.json.meta | 7 + .../SavedTimelines/crawler.json | 80 + .../SavedTimelines/crawler.json.meta | 7 + .../timeline_2026-03-11_09-58-54.json | 80 + .../timeline_2026-03-11_09-58-54.json.meta | 7 + .../timeline_2026-03-11_10-22-38.json | 115 + .../timeline_2026-03-11_10-22-38.json.meta | 7 + Assets/_Recovery/0 (1).unity | 19272 ++++++++++++++ Assets/_Recovery/0 (1).unity.meta | 7 + Assets/_Recovery/0 (10).unity | 19558 ++++++++++++++ Assets/_Recovery/0 (10).unity.meta | 7 + Assets/_Recovery/0 (11).unity | 19558 ++++++++++++++ Assets/_Recovery/0 (11).unity.meta | 7 + Assets/_Recovery/0 (12).unity | 19558 ++++++++++++++ Assets/_Recovery/0 (12).unity.meta | 7 + Assets/_Recovery/0 (13).unity | 17002 ++++++++++++ Assets/_Recovery/0 (13).unity.meta | 7 + Assets/_Recovery/0 (14).unity | 17002 ++++++++++++ Assets/_Recovery/0 (14).unity.meta | 7 + Assets/_Recovery/0 (15).unity | 16866 ++++++++++++ Assets/_Recovery/0 (15).unity.meta | 7 + Assets/_Recovery/0 (16).unity | 16866 ++++++++++++ Assets/_Recovery/0 (16).unity.meta | 7 + Assets/_Recovery/0 (17).unity | 19917 ++++++++++++++ Assets/_Recovery/0 (17).unity.meta | 7 + Assets/_Recovery/0 (18).unity | 21712 ++++++++++++++++ Assets/_Recovery/0 (18).unity.meta | 7 + Assets/_Recovery/0 (19).unity | 21712 ++++++++++++++++ Assets/_Recovery/0 (19).unity.meta | 7 + Assets/_Recovery/0 (2).unity | 19272 ++++++++++++++ Assets/_Recovery/0 (2).unity.meta | 7 + Assets/_Recovery/0 (3).unity | 19272 ++++++++++++++ Assets/_Recovery/0 (3).unity.meta | 7 + Assets/_Recovery/0 (4).unity | 19272 ++++++++++++++ Assets/_Recovery/0 (4).unity.meta | 7 + Assets/_Recovery/0 (5).unity | 19272 ++++++++++++++ Assets/_Recovery/0 (5).unity.meta | 7 + Assets/_Recovery/0 (6).unity | 19272 ++++++++++++++ Assets/_Recovery/0 (6).unity.meta | 7 + Assets/_Recovery/0 (7).unity | 19272 ++++++++++++++ Assets/_Recovery/0 (7).unity.meta | 7 + Assets/_Recovery/0 (8).unity | 19558 ++++++++++++++ Assets/_Recovery/0 (8).unity.meta | 7 + Assets/_Recovery/0 (9).unity | 19558 ++++++++++++++ Assets/_Recovery/0 (9).unity.meta | 7 + Assets/_Recovery/0.unity | 19302 ++++++++++++++ Assets/_Recovery/0.unity.meta | 7 + ProjectSettings/URPProjectSettings.asset | 16 + README.md | 326 + convert_tmp_to_text.py | 127 + 53 files changed, 384008 insertions(+) create mode 100644 .sentry-native/e4add0ec-396d-4455-60d8-0a95e21a992d.run.lock create mode 100644 Assets/ProgrammedMovements/SavedTimelines/CarDrive.json create mode 100644 Assets/ProgrammedMovements/SavedTimelines/CarDrive.json.meta create mode 100644 Assets/ProgrammedMovements/SavedTimelines/crawler.json create mode 100644 Assets/ProgrammedMovements/SavedTimelines/crawler.json.meta create mode 100644 Assets/ProgrammedMovements/SavedTimelines/timeline_2026-03-11_09-58-54.json create mode 100644 Assets/ProgrammedMovements/SavedTimelines/timeline_2026-03-11_09-58-54.json.meta create mode 100644 Assets/ProgrammedMovements/SavedTimelines/timeline_2026-03-11_10-22-38.json create mode 100644 Assets/ProgrammedMovements/SavedTimelines/timeline_2026-03-11_10-22-38.json.meta create mode 100644 Assets/_Recovery/0 (1).unity create mode 100644 Assets/_Recovery/0 (1).unity.meta create mode 100644 Assets/_Recovery/0 (10).unity create mode 100644 Assets/_Recovery/0 (10).unity.meta create mode 100644 Assets/_Recovery/0 (11).unity create mode 100644 Assets/_Recovery/0 (11).unity.meta create mode 100644 Assets/_Recovery/0 (12).unity create mode 100644 Assets/_Recovery/0 (12).unity.meta create mode 100644 Assets/_Recovery/0 (13).unity create mode 100644 Assets/_Recovery/0 (13).unity.meta create mode 100644 Assets/_Recovery/0 (14).unity create mode 100644 Assets/_Recovery/0 (14).unity.meta create mode 100644 Assets/_Recovery/0 (15).unity create mode 100644 Assets/_Recovery/0 (15).unity.meta create mode 100644 Assets/_Recovery/0 (16).unity create mode 100644 Assets/_Recovery/0 (16).unity.meta create mode 100644 Assets/_Recovery/0 (17).unity create mode 100644 Assets/_Recovery/0 (17).unity.meta create mode 100644 Assets/_Recovery/0 (18).unity create mode 100644 Assets/_Recovery/0 (18).unity.meta create mode 100644 Assets/_Recovery/0 (19).unity create mode 100644 Assets/_Recovery/0 (19).unity.meta create mode 100644 Assets/_Recovery/0 (2).unity create mode 100644 Assets/_Recovery/0 (2).unity.meta create mode 100644 Assets/_Recovery/0 (3).unity create mode 100644 Assets/_Recovery/0 (3).unity.meta create mode 100644 Assets/_Recovery/0 (4).unity create mode 100644 Assets/_Recovery/0 (4).unity.meta create mode 100644 Assets/_Recovery/0 (5).unity create mode 100644 Assets/_Recovery/0 (5).unity.meta create mode 100644 Assets/_Recovery/0 (6).unity create mode 100644 Assets/_Recovery/0 (6).unity.meta create mode 100644 Assets/_Recovery/0 (7).unity create mode 100644 Assets/_Recovery/0 (7).unity.meta create mode 100644 Assets/_Recovery/0 (8).unity create mode 100644 Assets/_Recovery/0 (8).unity.meta create mode 100644 Assets/_Recovery/0 (9).unity create mode 100644 Assets/_Recovery/0 (9).unity.meta create mode 100644 Assets/_Recovery/0.unity create mode 100644 Assets/_Recovery/0.unity.meta create mode 100644 ProjectSettings/URPProjectSettings.asset create mode 100644 README.md create mode 100644 convert_tmp_to_text.py diff --git a/.sentry-native/e4add0ec-396d-4455-60d8-0a95e21a992d.run.lock b/.sentry-native/e4add0ec-396d-4455-60d8-0a95e21a992d.run.lock new file mode 100644 index 0000000..e69de29 diff --git a/Assets/Icons/homeIcon.png b/Assets/Icons/homeIcon.png index c40c6b2296a5a1ebeaaf9ba657b2357fafb84de0..b6f1c395d6c1d71ddab416420d729065d7406164 100644 GIT binary patch literal 129 zcmWN?K@!3s3;@78uiyg~5+IcRrVSv>sB{eW;OliSdzH8J@zQP1Q#NDnecm2bmjCT@ zEIgh@&cfybHG0<4b_}!;M|3_EbQIRTqC;m9EqnH4UFPHgLd_AvL94g8CUTrA$7%_H MKuP}=O&F5;0dhGdegFUf literal 6259 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 vttncb7V~lG5Xza-|97Im|KbE~TZP8fES55(ta +cd ui +``` + +Open the project in Unity Hub using Unity 6000.3.6f1. + +### 2. Run Without Hardware (Mock Mode) + +To run without the native control library: + +1. Open `ProjectSettings` or locate the `TopologyBuilder` component in your scene. +2. Enable **Use JSON File** and **Mock Control Library**. +3. The app will load topology from JSON files in `Assets/Resources/` (e.g. `mockData.json`, `mockDataSimple.json`, `mockDataWithDC.json`). + +### 3. Run With Hardware + +1. Ensure `libc_control` is built and available in the appropriate platform directory (e.g. `Plugins/`). +2. Disable mock mode in `TopologyBuilder`. +3. Use the **Discovery** overlay to select a connected robot leader. + +### 4. Main Scene + +- **SampleScene** (`Assets/Scenes/SampleScene.unity`) – primary scene for robot control and visualization. + +--- + +## Project Structure + +``` +ui/ +├── Assets/ +│ ├── Scenes/ # Main scenes (SampleScene, Playground) +│ ├── Resources/ # JSON configs for mock mode +│ │ ├── mockData.json +│ │ ├── mockDataSimple.json +│ │ ├── mockDataWithDC.json +│ │ └── ... +│ ├── Module/ # Topology, Hub, Discovery +│ ├── IK/ # Inverse kinematics +│ ├── ProgrammedMovements/ # Timeline-based movement +│ │ └── SavedTimelines/ # Saved timeline JSON files +│ ├── ControlLibrary/ # P/Invoke bridge to libc_control +│ ├── FlatBuffers/ # FlatBuffers runtime +│ ├── Flatbuffers_generated/ # Generated types (RobotConfiguration, etc.) +│ ├── StarterAssets/ # Third-person controller, input +│ ├── StandaloneFileBrowser/ # Cross-platform file dialogs +│ ├── SyntyStudios/ # Polygon assets (PolygonCity, etc.) +│ └── Editor/ # Build scripts +├── ProjectSettings/ +├── Packages/ +└── README.md +``` + +--- + +## Technical Architecture + +### Unity Version & Packages + + +| Package | Version | +| ------------------------------- | ------- | +| Cinemachine | 2.10.3 | +| Input System | 1.14.0 | +| Universal Render Pipeline (URP) | 14.0.12 | +| TextMeshPro | 3.0.7 | +| Timeline | 1.7.7 | +| UGUI | 1.0.0 | +| Visual Scripting | 1.9.4 | +| Collab Proxy | 2.8.2 | +| Development | 1.0.1 | + + +### Native Integration + +The `ControlLibrary` communicates with the native C library `libc_control` via P/Invoke: + + +| Function | Purpose | +| ---------------------------------------- | ------------------------------------- | +| `init()` | Initialize the control library | +| `cleanup()` | Cleanup resources | +| `send_angle_control(module_id, angle)` | Send angle command to a servo module | +| `send_string_control(module_id, s)` | Send string command to a module | +| `get_distance_control(module_id)` | Get distance sensor reading | +| `get_configuration(out size, leader_id)` | Get robot configuration (FlatBuffers) | +| `get_leaders(out length)` | Get list of connected robot leaders | +| `control_sentry_*` | Sentry error reporting integration | + + +If `libc_control` is missing, native calls are disabled and the app falls back to mock/JSON mode. + +### Data Flow + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ ViewBannerUI (3 modes) │ +├─────────────────┬─────────────────────┬──────────────────────────┤ +│ Live View │ Inverse Kinematics │ Programmed Movements │ +│ LiveViewPanel │ IKController + │ ProgrammedMovementsCtrl │ +│ ModuleSelector │ PositionControlGizmo│ Timeline (4 tracks) │ +└────────┬────────┴──────────┬──────────┴──────────────┬───────────┘ + │ │ │ + ▼ ▼ ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ ModuleBase (Servo, DC, Hub, Sensor, etc.) │ +│ SendToControlLibrary() │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ ControlLibrary (P/Invoke) ←→ libc_control (native C library) │ +│ getRobotConfiguration, send_angle_control, get_leaders, etc. │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Major Features + +### 1. View Modes + +The top banner (`ViewBannerUI`) provides three modes: + + +| Mode | Description | +| ------------------------ | ---------------------------------------------------------------- | +| **Live** | Real-time control of individual modules (sliders, buttons) | +| **Inverse Kinematics** | Drag a 3D gizmo to move the end-effector; IK solves joint angles | +| **Programmed Movements** | Timeline-based sequences (up to 10 seconds, 4 tracks) | + + +### 2. Module System + + +| Module Type | Description | +| ----------------- | ------------------------------------------------------------------------------------ | +| **ModuleBase** | Base class for all modules; sends commands via `ControlLibrary` | +| **Hub modules** | `HubModule_MMMM`, `TriangleHubModule_MMF`, `TriangleHubModule_MMM`, `HubModule_MMMF` | +| **Servo modules** | `ServoBendModule`, `ServoStraightModule`, `GripperModule` | +| **Actuators** | `DCMotorModule` | +| **Sensors** | `DistanceSensorModule`, `IMUSensorModule` | +| **Other** | `DisplayModule`, `SpeakerModule`, `PowerModule`, `Battery` | + + +### 3. Topology Builder + +- Builds the robot graph from JSON or from `ControlLibrary.getRobotConfiguration()`. +- Uses `ModuleSpawner` to instantiate prefabs and connect them. +- Options: `useJsonFile`, `skipControlLibraryCalls`, `mockControlLibrary`. + +### 4. Discovery + +- Lists robot leaders from `ControlLibrary.getRobotLeaders()`. +- Mock mode uses JSON files in `Resources/`. +- Selecting a leader loads its topology. + +### 5. Live View + +- Side panel for the selected module. +- **Servo**: angle slider (0–180°). +- **DC motor**: direction and rotation controls. +- **Sensors**: readouts (e.g. distance). +- Uses `ModuleSelector` for selection. + +### 6. Inverse Kinematics + +- CCD-based IK solver for kinematic chains. +- Builds chain from selected module to root (`GeneratedTopology`). +- `PositionControlGizmo`: 3D gizmo for dragging end-effector position. +- Supports `ServoStraightModule` joints with configurable deadzone and smoothing. + +### 7. Programmed Movements + +- Timeline: 4 tracks, 0–10 seconds. +- Movement blocks per second per track. +- **Run**: plays timeline and sends commands to modules. +- **Save/Load**: JSON files in `Assets/ProgrammedMovements/SavedTimelines/`. +- Uses `StandaloneFileBrowser` for cross-platform file dialogs. + +### 8. File Browser + +- **StandaloneFileBrowser** – cross-platform open/save dialogs (Windows, Mac, Linux). + +--- + +## Data Formats + +### Topology JSON + +```json +{ + "Modules": [ + { "Id": "splitter1", "Type": "Splitter4", "Degree": null }, + { "Id": "servo1", "Type": "Servo1", "Degree": 45 } + ], + "Connections": [ + { + "FromModuleId": "splitter1", + "ToModuleId": "servo1", + "FromSocket": "MaleSocket1", + "ToSocket": "FemaleSocket", + "Orientation": 0 + } + ] +} +``` + +### Programmed Movement Timeline JSON + +```json +{ + "Timeline": [ + { + "Second": 0, + "Movements": [ + { + "ModuleId": "11", + "ModuleType": "Servo", + "Degree": 180.0, + "Direction": 1, + "Track": 0 + } + ] + } + ] +} +``` + +### Robot Configuration + +- **FlatBuffers** format: `RobotConfiguration`, `RobotModule`, `MotorState`, `ModuleConnection`, etc. +- Generated types in `Assets/Flatbuffers_generated/`. + +--- + +## Building + +Build targets are configured in `Assets/Editor/BuildScript.cs`: + + +| Platform | Output | +| -------- | ------------------------------ | +| Windows | `Builds/Windows/botchain.exe` | +| Linux | `Builds/Linux/botchain.x86_64` | +| macOS | `Builds/macOS/botchain.app` | + + +### Default Resolution + +- 1920×1080 (configurable in Project Settings) + +--- + +## Key Scripts Reference + + +| Script | Purpose | +| ---------------------------------- | ------------------------------------------------------ | +| `ModuleBase.cs` | Base for all modules; sends commands to ControlLibrary | +| `ModuleSpawner.cs` | Spawns module prefabs by type | +| `TopologyBuilder.cs` | Builds topology from JSON or native config | +| `TopologyGraphModel.cs` | Graph data structures | +| `ModuleSelector.cs` | Tracks selected module | +| `ControlLibrary.cs` | P/Invoke bridge to `libc_control` | +| `ViewBannerUI.cs` | Top banner and view switching | +| `LiveViewModulePanel.cs` | Live view side panel | +| `InverseKinematicsController.cs` | CCD IK solver | +| `PositionControlGizmo.cs` | 3D drag gizmo for IK | +| `ProgrammedMovementsController.cs` | Timeline UI and playback | +| `DiscoverOverlayController.cs` | Discovery overlay UI | + + diff --git a/convert_tmp_to_text.py b/convert_tmp_to_text.py new file mode 100644 index 0000000..c4ad6e2 --- /dev/null +++ b/convert_tmp_to_text.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +"""Bulk-convert TextMeshProUGUI components to Unity UI Text in a .unity scene file.""" +import re, sys + +TMP_GUID = "f4688fdb7df04437aeb418b961361dc5" +TEXT_GUID = "5f7201a12d95ffc409449d95f23cf332" +ARIAL_FONT = "{fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}" + +# Already converted IDs (skip these) +ALREADY_DONE = {"960000007", "1501844973", "1385057283"} + +def extract_prop(block, key): + m = re.search(rf'^\s+{key}:\s*(.+)$', block, re.MULTILINE) + return m.group(1).strip() if m else None + +def alignment_from_tmp(h_align, v_align): + h = int(h_align) if h_align else 1 + v = int(v_align) if v_align else 256 + col = {1: 0, 2: 1, 4: 2, 8: 0}.get(h, 0) # Left=0, Center=1, Right=2 + row = {256: 0, 512: 1, 1024: 2}.get(v, 0) # Upper=0, Middle=1, Lower=2 + return row * 3 + col + +def convert_block(block, file_id, game_obj_id): + text = extract_prop(block, "m_text") or "" + font_size = extract_prop(block, "m_fontSize") or "22" + font_style = extract_prop(block, "m_fontStyle") or "0" + color_r = color_g = color_b = color_a = None + color_match = re.search(r'm_Color:\s*\{r:\s*([\d.]+),\s*g:\s*([\d.]+),\s*b:\s*([\d.]+),\s*a:\s*([\d.]+)\}', block) + if color_match: + color_r, color_g, color_b, color_a = color_match.groups() + raycast = extract_prop(block, "m_RaycastTarget") or "1" + h_align = extract_prop(block, "m_HorizontalAlignment") + v_align = extract_prop(block, "m_VerticalAlignment") + alignment = alignment_from_tmp(h_align, v_align) + + color_str = f"{{r: {color_r}, g: {color_g}, b: {color_b}, a: {color_a}}}" if color_r else "{r: 1, g: 1, b: 1, a: 1}" + + font_size_int = int(float(font_size)) + + return f"""--- !u!114 &{file_id} +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {{fileID: 0}} + m_PrefabInstance: {{fileID: 0}} + m_PrefabAsset: {{fileID: 0}} + m_GameObject: {{fileID: {game_obj_id}}} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {{fileID: 11500000, guid: {TEXT_GUID}, type: 3}} + m_Name: + m_EditorClassIdentifier: + m_Material: {{fileID: 0}} + m_Color: {color_str} + m_RaycastTarget: {raycast} + m_RaycastPadding: {{x: 0, y: 0, z: 0, w: 0}} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {ARIAL_FONT} + m_FontSize: {font_size_int} + m_FontStyle: {font_style} + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: {alignment} + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: {text}""" + +def main(): + path = sys.argv[1] if len(sys.argv) > 1 else "Assets/Scenes/SampleScene.unity" + with open(path, 'r') as f: + content = f.read() + + # Split into YAML documents (each starting with --- !u!) + parts = re.split(r'(--- !u!\d+ &\d+)', content) + + converted = 0 + result = [] + i = 0 + while i < len(parts): + if i + 1 < len(parts) and parts[i].startswith('--- !u!'): + header = parts[i] + body = parts[i + 1] + + file_id_match = re.search(r'&(\d+)', header) + file_id = file_id_match.group(1) if file_id_match else None + + if file_id and file_id not in ALREADY_DONE and TMP_GUID in body: + game_obj_match = re.search(r'm_GameObject:\s*\{fileID:\s*(\d+)', body) + game_obj_id = game_obj_match.group(1) if game_obj_match else "0" + + full_block = header + body + new_block = convert_block(full_block, file_id, game_obj_id) + + # Find where the next --- !u! starts in body, keep the rest + next_header_pos = body.find('\n--- !u!') + if next_header_pos >= 0: + remainder = body[next_header_pos:] + result.append(new_block + remainder) + else: + result.append(new_block + '\n') + + converted += 1 + i += 2 + continue + + result.append(header) + result.append(body) + i += 2 + else: + result.append(parts[i]) + i += 1 + + output = ''.join(result) + with open(path, 'w') as f: + f.write(output) + + print(f"Converted {converted} TMP components to Unity Text") + +if __name__ == '__main__': + main()