diff --git a/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Bridge/AddForce.cs b/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Bridge/AddForce.cs new file mode 100644 index 00000000..2d666629 --- /dev/null +++ b/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Bridge/AddForce.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using UnityEngine; +using UnityEngine.Events; + +namespace Simulate { + public class AddForce : ICommand { + [JsonProperty(Required = Required.Always)] public string name; + [JsonConverter(typeof(Vector3Converter)), JsonProperty(Required = Required.Always)] public Vector3 force = Vector3.zero; + [JsonConverter(typeof(EnumConverter)), JsonProperty(PropertyName = "force_mode")] public ForceMode forceMode = ForceMode.Force; + + public void Execute(Dictionary kwargs, UnityAction callback) { + if (!Simulator.TryGetNode(name, out Node node) || node.rigidbody == null) { + Debug.LogWarning($"Couldn't find node with rigidbody named {name}"); + return; + } + node.rigidbody.AddForce(force, forceMode); + callback("{}"); + } + } +} diff --git a/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Bridge/AddForce.cs.meta b/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Bridge/AddForce.cs.meta new file mode 100644 index 00000000..d579dfe3 --- /dev/null +++ b/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Bridge/AddForce.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 127cf7aa07e2031458e414b53e1455b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Bridge/UpdateAsset.cs b/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Bridge/UpdateAsset.cs new file mode 100644 index 00000000..270c8e6a --- /dev/null +++ b/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Bridge/UpdateAsset.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Events; +using Newtonsoft.Json; + +namespace Simulate { + public class UpdateAsset : ICommand { + [JsonProperty(Required = Required.Always)] public string name; + [JsonConverter(typeof(Matrix4x4Converter))] public Matrix4x4 matrix = Matrix4x4.identity; + [JsonConverter(typeof(Vector3Converter))] public Vector3 position = Vector3.zero; + [JsonConverter(typeof(QuaternionConverter))] public Quaternion rotation = Quaternion.identity; + [JsonConverter(typeof(Vector3Converter))] public Vector3 scale = Vector3.one; + + public void Execute(Dictionary kwargs, UnityAction callback) { + if (!Simulator.TryGetNode(name, out Node node)) { + Debug.LogWarning($"Node {name} not found"); + return; + } + if (matrix != Matrix4x4.identity) + matrix.UnpackMatrix(ref position, ref rotation, ref scale); + node.transform.localPosition = position; + node.transform.localRotation = rotation; + node.transform.localScale = scale; + callback("{}"); + } + } +} \ No newline at end of file diff --git a/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Bridge/UpdateAsset.cs.meta b/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Bridge/UpdateAsset.cs.meta new file mode 100644 index 00000000..c086fd9b --- /dev/null +++ b/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Bridge/UpdateAsset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fcf132227f293a349a34f424fbdb12b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Node.cs b/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Node.cs index 2c10af4a..bd21dcf1 100644 --- a/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Node.cs +++ b/integrations/Unity/simulate-unity/Assets/Simulate/Runtime/Node.cs @@ -2,7 +2,7 @@ using Simulate.GLTF; using UnityEngine.Rendering.Universal; using Newtonsoft.Json; -using System.Collections.Generic; + namespace Simulate { public class Node : MonoBehaviour { public GLTFCamera cameraData; @@ -14,6 +14,7 @@ public class Node : MonoBehaviour { public HFStateSensors.HFStateSensor stateSensorData; public HFRaycastSensors.HFRaycastSensor raycastSensorData; public HFRewardFunctions.HFRewardFunction rewardFunctionData; + public new RenderCamera camera { get; private set; } public new Light light { get; private set; } public new Collider collider { get; private set; } @@ -38,6 +39,7 @@ public void Initialize() { InitializeActuator(); initialState = GetData(); } + public void ResetState(Vector3 offset = default(Vector3)) { if (articulationBody == null) { // you cannot teleport articulation bodies so simply (see below) @@ -79,13 +81,6 @@ void InitializeCamera() { camera = new RenderCamera(this, cameraData); } - /* void InitializeStateSensor() { - sensor = new StateSensor(this, stateSensorData); - } - void InitializeRaycastSensor() { - sensor = new RaycastSensor(this, raycastSensorData); - } */ - void InitializeLight() { light = gameObject.AddComponent(); light.gameObject.AddComponent(); diff --git a/src/simulate/assets/asset.py b/src/simulate/assets/asset.py index 4d8ca830..89732c4e 100644 --- a/src/simulate/assets/asset.py +++ b/src/simulate/assets/asset.py @@ -1095,6 +1095,34 @@ def transformation_matrix(self, value: Optional[Union[float, List[float], proper self._post_asset_modification() + def add_force(self, force: List[float] = None, force_mode: Optional[str] = None): + """ + Add a force to a node. + + Args: + force (`List[float]`): + The three-dimensional force vector, which is applied depending on the force mode. + force_mode (`str`, *optional*, defaults to `Force`): + The mode of applying the force, one of: + - `Force`: Interprets the force value as a force, changing velocity according to mass and time. + - `Acceleration`: Interprets the force value as a change in velocity over time, ignoring mass. + - `Impulse`: Interprets the force value as a change in velocity according to mass, ignoring time. + - `VelocityChange`: Interprets the force value as a change in velocity, ignoring mass and time. + """ + if self.physics_component is None: + raise TypeError("Forces can only be applied to objects with a physics_component.") + force = force if force is not None else [0, 0, 0] + force_mode = force_mode if force_mode is not None else "Force" + if ( + getattr(self.tree_root, "engine", None) is not None + ): + getattr(self.tree_root, "engine").run_command( + "AddForce", + name=self.name, + force=force, + force_mode=force_mode + ) + def _post_asset_modification(self): """Method called after an asset is modified.""" if ( diff --git a/src/simulate/engine/godot_engine.py b/src/simulate/engine/godot_engine.py index 6dd6fdc6..1b91fe27 100644 --- a/src/simulate/engine/godot_engine.py +++ b/src/simulate/engine/godot_engine.py @@ -181,7 +181,7 @@ def show(self, **kwargs: Any) -> Union[Dict, str]: kwargs.update({"b64bytes": b64bytes}) return self.run_command("initialize", **kwargs) - def update_asset(self, root_node: "Asset"): + def update_asset(self, node: "Asset"): # TODO update and make this API more consistent with all the # update_asset_in_scene, recreate_scene, show raise NotImplementedError() diff --git a/src/simulate/engine/unity_engine.py b/src/simulate/engine/unity_engine.py index 25e35729..d7bb5028 100644 --- a/src/simulate/engine/unity_engine.py +++ b/src/simulate/engine/unity_engine.py @@ -253,10 +253,14 @@ def _get_response(self) -> str: return response - def update_asset(self, root_node: "Asset"): - # TODO update and make this API more consistent with all the - # update_asset, update, show - raise NotImplementedError() + def update_asset(self, node: "Asset"): + self.run_command( + "UpdateAsset", + name=node.name, + position=list(node.position), + rotation=list(node.rotation), + scale=list(node.scaling), + ) def update_all_assets(self): raise NotImplementedError()