diff --git a/ai2thor/tests/test_unity.py b/ai2thor/tests/test_unity.py index c9cacfd7ec..e96d452ca9 100644 --- a/ai2thor/tests/test_unity.py +++ b/ai2thor/tests/test_unity.py @@ -2108,6 +2108,25 @@ def test_rotate_hand(controller): assert_near(h1["localRotation"], dict(x=0, y=0, z=0)) assert_near(h2["localRotation"], dict(x=90, y=180, z=0)) +def test_settle_physics(fifo_controller): + from dictdiffer import diff + fifo_controller.reset(agentMode="arm") + + for i in range(30): + fifo_controller.step("AdvancePhysicsStep", raise_for_failure=True) + + first_objs = {o['objectId']: o for o in fifo_controller.last_event.metadata["objects"]} + + for i in range(30): + fifo_controller.step("AdvancePhysicsStep", raise_for_failure=True) + + diffs = [] + last_objs = {o['objectId']: o for o in fifo_controller.last_event.metadata["objects"]} + for object_id, object_metadata in first_objs.items(): + for d in (diff(object_metadata, last_objs.get(object_id, {}), tolerance=0.00001, ignore=set(["receptacleObjectIds"]))): + diffs.append((object_id, d)) + + assert diffs == [] @pytest.mark.parametrize("controller", fifo_wsgi) def test_fill_liquid(controller): diff --git a/setup.py b/setup.py index 463be38313..1a800c0f9b 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ "werkzeug>=0.15.0", # needed for unix socket support ], setup_requires=["pytest-runner"], - tests_require=["pytest", "pytest-timeout", "pytest-cov", "jsonschema", "shapely", "pytest-mock"], + tests_require=["pytest", "pytest-timeout", "pytest-cov", "jsonschema", "shapely", "pytest-mock", "dictdiffer"], scripts=["scripts/ai2thor-xorg"], include_package_data=False, ) diff --git a/unity/Assets/Scripts/CanOpen_Object.cs b/unity/Assets/Scripts/CanOpen_Object.cs index e567935cd1..772149515a 100644 --- a/unity/Assets/Scripts/CanOpen_Object.cs +++ b/unity/Assets/Scripts/CanOpen_Object.cs @@ -60,11 +60,12 @@ public List WhatReceptaclesMustBeOffToOpen() { return MustBeOffToOpen; } - // Use this for initialization - void Start() { - // init Itween in all doors to prep for animation + void Awake() { if (MovingParts != null) { + // init Itween in all doors to prep for animation foreach (GameObject go in MovingParts) { + // Init is getting called in Awake() vs Start() so that cloned gameobjects can add MovingParts + // before iTween.Awake() gets called which would throw an error if this was done in Start() iTween.Init(go); // check to make sure all doors have a Fridge_Door.cs script on them, if not throw a warning @@ -72,6 +73,10 @@ void Start() { // Debug.Log("Fridge Door is missing Fridge_Door.cs component! OH NO!"); } } + } + + // Use this for initialization + void Start() { #if UNITY_EDITOR if (!this.GetComponent().DoesThisObjectHaveThisSecondaryProperty(SimObjSecondaryProperty.CanOpen)) { diff --git a/unity/Assets/Scripts/CanToggleOnOff.cs b/unity/Assets/Scripts/CanToggleOnOff.cs index c49b4dc5f0..97a5da9707 100644 --- a/unity/Assets/Scripts/CanToggleOnOff.cs +++ b/unity/Assets/Scripts/CanToggleOnOff.cs @@ -111,13 +111,18 @@ public void SetMovementToRotate() { #endif - // Use this for initialization - void Start() { + void Awake() { if (MovingParts != null) { foreach (GameObject go in MovingParts) { + // Init is getting called in Awake() vs Start() so that cloned gameobjects can add MovingParts + // before iTween.Awake() gets called which would throw an error if this was done in Start() iTween.Init(go); } } + } + + // Use this for initialization + void Start() { #if UNITY_EDITOR if (!this.GetComponent().DoesThisObjectHaveThisSecondaryProperty(SimObjSecondaryProperty.CanToggleOnOff)) { diff --git a/unity/Assets/Scripts/SimObjPhysics.cs b/unity/Assets/Scripts/SimObjPhysics.cs index 6f0858d8d6..09b4deab55 100644 --- a/unity/Assets/Scripts/SimObjPhysics.cs +++ b/unity/Assets/Scripts/SimObjPhysics.cs @@ -302,20 +302,16 @@ private ObjectOrientedBoundingBox objectOrientedBoundingBox() { } - // Align SimObject to origin and axes - Vector3 cachedPosition = this.transform.position; - Quaternion cachedRotation = this.transform.rotation; - - this.transform.position = Vector3.zero; - this.transform.rotation = Quaternion.identity; - if (!Physics.autoSyncTransforms) { - Physics.SyncTransforms(); - } + // The GameObject is cloned instead of teleporting/rotating to avoid + // waking up the rigidbody, which can cause the SimObj to shift by tiny amounts + // since it will never sleep. + GameObject clone = Instantiate(this.gameObject, Vector3.zero, Quaternion.identity); + // Get all colliders on the sop, excluding colliders if they are not enabled List<(Collider, LayerMask)> cols = new List<(Collider, LayerMask)>(); var nonInteractiveLayer = LayerMask.NameToLayer("NonInteractive"); - foreach (Collider c in this.transform.GetComponentsInChildren()) { + foreach (Collider c in clone.transform.GetComponentsInChildren()) { if (c.enabled) { // save the state of all the layers prior to modifying cols.Add((c, c.transform.gameObject.layer)); @@ -331,12 +327,12 @@ private ObjectOrientedBoundingBox objectOrientedBoundingBox() { // Encapsulate all active colliders in SimObject's array foreach ((Collider, LayerMask) colAndLayerMask in cols) { Collider c = colAndLayerMask.Item1; - if (!c.isTrigger && c.gameObject != this.BoundingBox) { + if (!c.isTrigger && c.gameObject != clone.GetComponent().BoundingBox) { newBB.Encapsulate(c.bounds); } } // Encapsulate all visilibity points of the SimObject array - foreach (Transform visPoint in this.VisibilityPoints) { + foreach (Transform visPoint in clone.GetComponent().VisibilityPoints) { newBB.Encapsulate(visPoint.position); } @@ -346,17 +342,9 @@ private ObjectOrientedBoundingBox objectOrientedBoundingBox() { this.BoundingBox.GetComponent().center = newBB.center; this.BoundingBox.GetComponent().size = newBB.extents * 2.0f; - // Revert SimObject back to its initial transform - this.transform.position = cachedPosition; - this.transform.rotation = cachedRotation; - if (!Physics.autoSyncTransforms) { - Physics.SyncTransforms(); - } - // Re-enable colliders, moving them back to their original layer foreach ((Collider, LayerMask) colAndLayerMask in cols) { colAndLayerMask.Item1.transform.gameObject.layer = colAndLayerMask.Item2; - } // reparent child simobjects @@ -364,6 +352,16 @@ private ObjectOrientedBoundingBox objectOrientedBoundingBox() { childSimObject.SetParent(this.transform); } + DestroyImmediate(clone); + + // iTween adds references to the iTween.tweens List + for (int i = 0; i < iTween.tweens.Count; i++) { + if (((GameObject)iTween.tweens[i]["target"]) == null) { + iTween.tweens.RemoveAt(i); + } + } + + // Get corner points of SimObject's new BoundingBox, in its correct transformation List points = new List(); points.Add(this.transform.TransformPoint(newBB.center + new Vector3(newBB.size.x, -newBB.size.y, newBB.size.z) * 0.5f));