diff --git a/ai2thor/controller.py b/ai2thor/controller.py index 057dc44fd8..423c5e2282 100644 --- a/ai2thor/controller.py +++ b/ai2thor/controller.py @@ -399,8 +399,8 @@ def __init__( server_timeout: Optional[float] = 100.0, server_start_timeout: float = 300.0, # objaverse_asset_ids=[], TODO add and implement when objaverse.load_thor_objects is available - action_hook_runner=None, - metadata_hook: Optional[MetadataHook] = None, + before_action_callback=None, + metadata_callback: Optional[MetadataHook] = None, **unity_initialization_parameters, ): self.receptacle_nearest_pivot_points = {} @@ -443,18 +443,28 @@ def __init__( ) ) - self.action_hook_runner = action_hook_runner - self.action_hooks = ( + if "action_hook_runner" in unity_initialization_parameters: + raise ValueError( + f"Deprecated argument 'action_hook_runner'. Use 'before_action_callback' instead." + ) + + if "metadata_hook" in unity_initialization_parameters: + raise ValueError( + f"Deprecated argument 'metadata_hook'. Use 'metadata_callback' instead." + ) + + self.before_action_callback = before_action_callback + self.action_callbacks = ( { func - for func in dir(action_hook_runner) - if callable(getattr(action_hook_runner, func)) and not func.startswith("__") + for func in dir(before_action_callback) + if callable(getattr(before_action_callback, func)) and not func.startswith("__") } - if self.action_hook_runner is not None + if self.before_action_callback is not None else None ) - self.metadata_hook = metadata_hook + self.metadata_callback = metadata_callback if self.gpu_device is not None: # numbers.Integral works for numpy.int32/64 and Python int @@ -971,11 +981,11 @@ def multi_step_physics(self, action, timeStep=0.05, max_steps=20): return events - def run_action_hook(self, action): - if self.action_hooks is not None and action["action"] in self.action_hooks: + def run_before_action_callback(self, action): + if self.action_callbacks is not None and action["action"] in self.action_callbacks: try: # print(f"action hooks: {self.action_hooks}") - method = getattr(self.action_hook_runner, action["action"]) + method = getattr(self.before_action_callback, action["action"]) event = method(action, self) if isinstance(event, list): self.last_event = event[-1] @@ -984,18 +994,18 @@ def run_action_hook(self, action): except AttributeError: traceback.print_stack() raise NotImplementedError( - "Action Hook Runner `{}` does not implement method `{}`," + "Action Callback `{}` does not implement method `{}`," " actions hooks are meant to run before an action, make sure that `action_hook_runner`" " passed to the controller implements a method for the desired action.".format( - self.action_hook_runner.__class__.__name__, action["action"] + self.before_action_callback.__class__.__name__, action["action"] ) ) return True return False def run_metadata_hook(self, metadata: MetadataWrapper) -> bool: - if self.metadata_hook is not None: - out = self.metadata_hook(metadata=metadata, controller=self) + if self.metadata_callback is not None: + out = self.metadata_callback(metadata=metadata, controller=self) assert ( out is None ), "`metadata_hook` must return `None` and change the metadata in place." @@ -1043,7 +1053,7 @@ def step(self, action: Union[str, Dict[str, Any]] = None, **action_args): # not deleting to allow for older builds to continue to work # del action[old] - self.run_action_hook(action) + self.run_before_action_callback(action) self.server.send(action) try: diff --git a/ai2thor/hooks/procedural_asset_hook.py b/ai2thor/hooks/procedural_asset_hook.py index 171ce5173f..0d7b1cbfc6 100644 --- a/ai2thor/hooks/procedural_asset_hook.py +++ b/ai2thor/hooks/procedural_asset_hook.py @@ -22,6 +22,8 @@ load_existing_thor_asset_file, ) +from objathor.dataset import load_assets_path, DatasetSaveConfig + logger = logging.getLogger(__name__) EXTENSIONS_LOADABLE_IN_UNITY = { @@ -248,7 +250,7 @@ def create_assets_if_not_exist( # return evt -class ProceduralAssetHookRunner: +class ProceduralAssetActionCallback: def __init__( self, asset_directory, @@ -268,6 +270,7 @@ def __init__( self.target_dir = target_dir self.extension = extension self.verbose = verbose + self.last_asset_id_set = set() def Initialize(self, action, controller): if self.asset_limit > 0: @@ -278,6 +281,10 @@ def Initialize(self, action, controller): def CreateHouse(self, action, controller): house = action["house"] asset_ids = get_all_asset_ids_recursively(house["objects"], []) + asset_ids_set = set(asset_ids) + if not asset_ids_set.issubset(self.last_asset_id_set): + controller.step(action="DeleteLRUFromProceduralCache", assetLimit=0) + self.last_asset_id_set = set(asset_ids) return create_assets_if_not_exist( controller=controller, asset_ids=asset_ids, @@ -320,27 +327,49 @@ def GetHouseFromTemplate(self, action, controller): ) -class ObjaverseAssetHookRunner(object): - def __init__(self): - import objaverse - - self.objaverse_uid_set = set(objaverse.load_uids()) +class DownloadObjaverseActionCallback(object): + def __init__( + self, + asset_dataset_version, + asset_download_path, + target_dir="processed_models", + asset_symlink=True, + load_file_in_unity=False, + stop_if_fail=False, + asset_limit=-1, + extension=None, + verbose=True, + ): + self.asset_download_path = asset_download_path + self.asset_symlink = asset_symlink + self.stop_if_fail = stop_if_fail + self.asset_limit = asset_limit + self.load_file_in_unity = load_file_in_unity + self.target_dir = target_dir + self.extension = extension + self.verbose = verbose + self.last_asset_id_set = set() + dsc = DatasetSaveConfig( + VERSION=asset_dataset_version, + BASE_PATH=asset_download_path, + ) + self.asset_path = load_assets_path(dsc) def CreateHouse(self, action, controller): - raise NotImplemented("Not yet implemented.") - house = action["house"] - asset_ids = list(set(obj["assetId"] for obj in house["objects"])) - evt = controller.step(action="AssetsInDatabase", assetIds=asset_ids) - asset_in_db = evt.metadata["actionReturn"] - assets_not_created = [asset_id for (asset_id, in_db) in asset_in_db.items() if in_db] - not_created_set = set(assets_not_created) - not_objeverse_not_created = not_created_set.difference(self.objaverse_uid_set) - if len(not_created_set): - raise ValueError( - f"Invalid asset ids are not in THOR AssetDatabase or part of objeverse: {not_objeverse_not_created}" - ) - - # TODO when transformed assets are in objaverse download them and create them - # objaverse.load_thor_objects - # create_assets() + asset_ids = get_all_asset_ids_recursively(house["objects"], []) + asset_ids_set = set(asset_ids) + if not asset_ids_set.issubset(self.last_asset_id_set): + controller.step(action="DeleteLRUFromProceduralCache", assetLimit=0) + self.last_asset_id_set = set(asset_ids) + return create_assets_if_not_exist( + controller=controller, + asset_ids=asset_ids, + asset_directory=self.asset_path, + copy_to_dir=os.path.join(controller._build.base_dir, self.target_dir), + asset_symlink=self.asset_symlink, + stop_if_fail=self.stop_if_fail, + load_file_in_unity=self.load_file_in_unity, + extension=self.extension, + verbose=self.verbose, + ) diff --git a/tasks.py b/tasks.py index 145983154c..4321fbc4c2 100644 --- a/tasks.py +++ b/tasks.py @@ -4727,10 +4727,10 @@ def test_create_prefab(ctx, json_path): def procedural_asset_hook_test(ctx, asset_dir, house_path, asset_id=""): import json import ai2thor.controller - from ai2thor.hooks.procedural_asset_hook import ProceduralAssetHookRunner + from ai2thor.hooks.procedural_asset_hook import ProceduralAssetActionCallback from objathor.asset_conversion.util import view_asset_in_thor - hook_runner = ProceduralAssetHookRunner( + hook_runner = ProceduralAssetActionCallback( asset_directory=asset_dir, asset_symlink=True, verbose=True, @@ -4747,7 +4747,7 @@ def procedural_asset_hook_test(ctx, asset_dir, house_path, asset_id=""): height=300, server_class=ai2thor.fifo_server.FifoServer, visibilityScheme="Distance", - action_hook_runner=hook_runner, + before_action_callback=hook_runner, ) # TODO bug why skybox is not changing? from just procedural pipeline @@ -4817,9 +4817,9 @@ def procedural_asset_hook_test(ctx, asset_dir, house_path, asset_id=""): def procedural_asset_cache_test(ctx, asset_dir, house_path, asset_ids="", cache_limit=1): import json import ai2thor.controller - from ai2thor.hooks.procedural_asset_hook import ProceduralAssetHookRunner + from ai2thor.hooks.procedural_asset_hook import ProceduralAssetActionCallback - hook_runner = ProceduralAssetHookRunner( + hook_runner = ProceduralAssetActionCallback( asset_directory=asset_dir, asset_symlink=True, verbose=True, asset_limit=1 ) controller = ai2thor.controller.Controller( @@ -4834,7 +4834,7 @@ def procedural_asset_cache_test(ctx, asset_dir, house_path, asset_ids="", cache_ height=300, server_class=ai2thor.wsgi_server.WsgiServer, visibilityScheme="Distance", - action_hook_runner=hook_runner, + before_action_callback=hook_runner, ) asset_ids = asset_ids.split(",") with open(house_path, "r") as f: diff --git a/unity/Assets/Scripts/PhysicsRemoteFPSAgentController.cs b/unity/Assets/Scripts/PhysicsRemoteFPSAgentController.cs index a827d8200a..b37962eecf 100644 --- a/unity/Assets/Scripts/PhysicsRemoteFPSAgentController.cs +++ b/unity/Assets/Scripts/PhysicsRemoteFPSAgentController.cs @@ -6113,47 +6113,9 @@ public void RotateUniverseAroundAgent(ServerAction action) { } public void ChangeFOV(float fieldOfView, string camera = "") { - if (fieldOfView > 0 && fieldOfView < 180) { - if (string.IsNullOrEmpty(camera)) { - m_Camera.fieldOfView = fieldOfView; - actionFinished(true); - } else { - var cameraTuples = new List<(Camera camera, bool isThirdPartyCamera, int id)>() - { - (camera: m_Camera, isThirdPartyCamera: false, id: -1) - }.Concat( - this.agentManager.thirdPartyCameras.Select( - (c, i) => (camera: c, isThirdPartyCamera: true, id: i) - ) - ); - var matches = cameraTuples; - if (camera != "*") { - matches = cameraTuples.Where(t => t.camera.name == camera); - } - // Debug.Log($"Camera matches: {matches.Count()} {string.Join(", ", matches.Select(m => m.camera.name))}"); - if (matches.Count() == 0) { - errorMessage = - $"Camera '{camera}' is not present in the agent, make sure the agent was initialized correctly or camera was added via 'AddThirdPartyCamera'."; - actionFinished(false); - } else { - foreach (var tuple in matches) { - if (tuple.isThirdPartyCamera) { - agentManager.UpdateThirdPartyCamera( - tuple.id, - fieldOfView: fieldOfView - ); - } else { - tuple.camera.fieldOfView = fieldOfView; - } - } - actionFinished(true); - } - } - } else { - errorMessage = "fov must be in (0, 180) noninclusive."; - Debug.Log(errorMessage); - actionFinished(false); - } + throw new InvalidOperationException( + "This action is deprecated. Use `UpdateMainCamera` or `UpdateThirdPartyCamera` instead." + ); } // in case you want to change the timescale diff --git a/unity/Assets/Scripts/ProceduralTools.cs b/unity/Assets/Scripts/ProceduralTools.cs index 95eb72b1af..9d779f1f86 100644 --- a/unity/Assets/Scripts/ProceduralTools.cs +++ b/unity/Assets/Scripts/ProceduralTools.cs @@ -1386,7 +1386,8 @@ public static GameObject CreateHouse( if (!validateHouseObjects(getAssetMap(), house.objects, missingIds)) { throw new ArgumentException( $"Object ids '{string.Join(", ", missingIds)}' not present in asset database." - + $" If it is a procedural asset make sure you call 'CreateAsset' before 'CreateHouse'" + + $" If it is a procedural asset make sure you call 'CreateAsset' before 'CreateHouse'." + + $"See https://ai2thor.allenai.org/ithor/documentation/objects/procedural-assets" ); }