diff --git a/algorithms/sb3/ppo/ippo.py b/algorithms/sb3/ppo/ippo.py index 4f8872e1..93799766 100755 --- a/algorithms/sb3/ppo/ippo.py +++ b/algorithms/sb3/ppo/ippo.py @@ -54,6 +54,7 @@ def __init__( self.exp_config = exp_config self.mlp_class = mlp_class self.mlp_config = mlp_config + self.resample_counter = 0 super().__init__(*args, **kwargs) def collect_rollouts( @@ -68,6 +69,33 @@ def collect_rollouts( assert ( self._last_obs is not None ), "No previous observation was provided" + + # Check resampling criterion and resample batch of scenarios if needed + if self.env.exp_config.resample_scenarios: + if self.env.exp_config.resample_criterion == "global_step": + if self.resample_counter >= self.env.exp_config.resample_freq: + print( + f"Resampling {self.env.num_worlds} scenarios at global_step {self.num_timesteps:,}..." + ) + # Re-initialize the scenes and controlled agents mask + self.env.resample_scenario_batch() + self.resample_counter = 0 + # Get new initial observation + self._last_obs = self.env.reset() + # Update storage shapes + self.n_envs = env.num_valid_controlled_agents_across_worlds + rollout_buffer.n_envs = self.n_envs + self._last_episode_starts = ( + self.env._env.get_dones().clone()[ + ~self.env.dead_agent_mask + ] + ) + + else: + raise NotImplementedError( + f"Resampling criterion {self.env.exp_config.resample_criterion} not implemented" + ) + # Switch to eval mode (this affects batch norm / dropout) self.policy.set_training_mode(False) @@ -166,16 +194,11 @@ def collect_rollouts( new_obs, rewards, dones, infos = env.step(clipped_actions) - # # (dc) DEBUG - # mask = ~torch.isnan(rewards) - # if ( - # self._last_obs[mask].max() > 1 - # or self._last_obs[mask].min() < -1 - # ): - # logging.error("New observation is out of bounds") - # EDIT_2: Increment the global step by the number of valid samples in rollout step self.num_timesteps += int((~rewards.isnan()).float().sum().item()) + self.resample_counter += int( + (~rewards.isnan()).float().sum().item() + ) # Give access to local variables callback.update_locals(locals()) if callback.on_step() is False: @@ -198,7 +221,6 @@ def collect_rollouts( self._last_episode_starts = dones # # # # # END LOOP # # # # # - total_steps = self.n_envs * n_rollout_steps elapsed_time = time.perf_counter() - time_rollout fps = total_steps / elapsed_time diff --git a/baselines/ippo/README.md b/baselines/ippo/README.md index f1491392..a4c22b4a 100644 --- a/baselines/ippo/README.md +++ b/baselines/ippo/README.md @@ -6,6 +6,26 @@ To run the multi-agent IPPO baseline using stable-baselines 3 (SB3): python baselines/ippo/run_sb3_ppo.py ``` +### Resampling the data + +The configuration for resampling traffic scenarios includes: + +- **`resample_scenarios`**: A boolean that enables or disables traffic scenario resampling when set to `True`. + +- **`resample_criterion`**: Set to `"global_step"`, indicating resampling occurs based on the global step count. + +- **`resample_freq`**: Specifies resampling frequency at `50,000` steps, recommended to align with `num_worlds * n_steps`. + +- **`resample_mode`**: Set to `"random"` for random selection of new scenarios. + +``` +# RESAMPLE TRAFFIC SCENARIOS +resample_scenarios: bool = True +resample_criterion: str = "global_step" # Options: "global_step" +resample_freq: int = 100_000 # Resample every k steps (recommended to be a multiple of num_worlds * n_steps) +resample_mode: str = "random" # Options: "random" +``` + ## Implemented networks ### Classic Observations diff --git a/baselines/ippo/config.py b/baselines/ippo/config.py index b2465a82..bdb33f3d 100755 --- a/baselines/ippo/config.py +++ b/baselines/ippo/config.py @@ -26,6 +26,12 @@ class ExperimentConfig: goal_achieved_weight: float = 1.0 off_road_weight: float = 0.0 + # RESAMPLE TRAFFIC SCENARIOS + resample_scenarios: bool = True + resample_criterion: str = "global_step" # Options: "global_step" + resample_freq: int = 1e6 # Resample every k steps (recommended to be a multiple of num_worlds * n_steps) + resample_mode: str = "random" # Options: "random" + # RENDERING render: bool = True render_mode: str = "rgb_array" @@ -59,7 +65,7 @@ class ExperimentConfig: gae_lambda: float = 0.95 clip_range: float = 0.2 vf_coef: float = 0.5 - n_steps: int = 91 + n_steps: int = 91 # Number of steps per rollout num_minibatches: int = 5 # Used to determine the minibatch size verbose: int = 0 total_timesteps: int = 2e7 diff --git a/baselines/ippo/run_sb3_ppo.py b/baselines/ippo/run_sb3_ppo.py index 82f7dd26..6ca16166 100755 --- a/baselines/ippo/run_sb3_ppo.py +++ b/baselines/ippo/run_sb3_ppo.py @@ -40,6 +40,7 @@ def train(env_config: EnvConfig, exp_config: ExperimentConfig, scene_config: Sce env = SB3MultiAgentEnv( config=env_config, scene_config=scene_config, + exp_config=exp_config, # Control up to all agents in the scene max_cont_agents=env_config.max_num_agents_in_scene, device=exp_config.device, diff --git a/pygpudrive/env/README.md b/pygpudrive/env/README.md index c8bb8f61..6990bcf5 100755 --- a/pygpudrive/env/README.md +++ b/pygpudrive/env/README.md @@ -35,7 +35,12 @@ env = GPUDriveTorchEnv( Step the environment using: ```Python -obs, reward, done, info = env.step_dynamics(action) +env.step_dynamics(actions) + +# Extract info +obs = env.get_obs() +reward = env.get_rewards() +done = env.get_dones() ``` Further configuration details are available in `config.py`. @@ -116,7 +121,7 @@ A reward of +1 is assigned when an agent is within the `dist_to_goal_threshold` Upon initialization, every vehicle starts at the beginning of the expert trajectory. -## Dataset +## Dataset The `SceneConfig` dataclass is used to configure how scenes are selected from a dataset. It has four attributes: @@ -125,6 +130,21 @@ The `SceneConfig` dataclass is used to configure how scenes are selected from a - `discipline`: The method for selecting scenes, defaulting to `SelectionDiscipline.PAD_N`. (See options in Table below) - `k_unique_scenes`: Specifies the number of unique scenes to select, if applicable. +### Resampling traffic scenarios + +The `reinit_scenarios()` method in `pygpudrive/env/base_env.py` reinitializes the simulator with new traffic scenarios as follows: + +1. **Scene re-initialization:** + This function updates the simulation maps by calling `self.sim.set_maps(dataset)`, replacing the current scenes with those provided `dataset`, which should be a list with paths to traffic scenarios. + +2. **Controlled agent mask re-nitialization:** + It reinitializes the controlled agents' mask using `self.get_controlled_agents_mask()`, determining which agents are user-controlled (this will change based on the specific traffic scenes used). + +3. **Agent count update:** + The function updates `self.max_agent_count` to reflect the number of controlled agents and recomputes `self.num_valid_controlled_agents_across_worlds`, indicating the total active controlled agents across all scenarios. + +See the `resample_scenario_batch()` method in `pygpudrive/env/wrappers/sb3_wrapper.py` for an example of how you can use this method with IPPO. + ## Render Render settings can be changed using the `RenderConfig`. diff --git a/pygpudrive/env/base_env.py b/pygpudrive/env/base_env.py index aa44fb29..43a0ad2e 100755 --- a/pygpudrive/env/base_env.py +++ b/pygpudrive/env/base_env.py @@ -1,4 +1,5 @@ import os +from typing import List, Optional import gymnasium as gym from pygpudrive.env.config import RenderConfig, RenderMode from pygpudrive.env.viz import PyGameVisualizer @@ -57,7 +58,10 @@ def _set_reward_params(self): """ reward_params = gpudrive.RewardParams() - if self.config.reward_type == "sparse_on_goal_achieved" or self.config.reward_type == "weighted_combination": + if ( + self.config.reward_type == "sparse_on_goal_achieved" + or self.config.reward_type == "weighted_combination" + ): reward_params.rewardType = gpudrive.RewardType.OnGoalAchieved else: raise ValueError(f"Invalid reward type: {self.config.reward_type}") @@ -119,8 +123,10 @@ def _setup_environment_parameters(self): if self.config.lidar_obs: if not self.config.lidar_obs and self.config.disable_classic_obs: - raise ValueError("Lidar observations must be enabled if classic observations are disabled.") - + raise ValueError( + "Lidar observations must be enabled if classic observations are disabled." + ) + else: params.enableLidar = self.config.lidar_obs params.disableClassicalObs = self.config.disable_classic_obs @@ -243,6 +249,25 @@ def render(self, world_render_idx=0, color_objects_by_actor=None): }: return self.visualizer.getRender() + def reinit_scenarios(self, dataset: List[str]): + """Resample the scenes. + Args: + dataset (List[str]): List of scene names to resample. + + Returns: + None + """ + + # Resample the scenes + self.sim.set_maps(dataset) + + # Re-initialize the controlled agents mask + self.cont_agent_mask = self.get_controlled_agents_mask() + self.max_agent_count = self.cont_agent_mask.shape[1] + self.num_valid_controlled_agents_across_worlds = ( + self.cont_agent_mask.sum().item() + ) + def close(self): """Destroy the simulator and visualizer.""" del self.sim diff --git a/pygpudrive/env/config.py b/pygpudrive/env/config.py index 98316a8e..5a717d11 100755 --- a/pygpudrive/env/config.py +++ b/pygpudrive/env/config.py @@ -103,7 +103,7 @@ class EnvConfig: # Reward settings reward_type: str = ( - "sparse_on_goal_achieved" # Alternatively, "weighted_combination" + "sparse_on_goal_achieved" # Alternatively, "weighted_combination" ) dist_to_goal_threshold: float = ( diff --git a/pygpudrive/env/env_torch.py b/pygpudrive/env/env_torch.py index 84b66e90..f3ff51e3 100755 --- a/pygpudrive/env/env_torch.py +++ b/pygpudrive/env/env_torch.py @@ -26,6 +26,7 @@ def __init__( ): # Initialization of environment configurations self.config = config + self.scene_config = scene_config self.num_worlds = scene_config.num_scenes self.max_cont_agents = max_cont_agents self.device = device diff --git a/pygpudrive/env/wrappers/sb3_wrapper.py b/pygpudrive/env/wrappers/sb3_wrapper.py index 46afaad3..5c9e0a1d 100755 --- a/pygpudrive/env/wrappers/sb3_wrapper.py +++ b/pygpudrive/env/wrappers/sb3_wrapper.py @@ -2,7 +2,9 @@ import logging from typing import Optional, Sequence import torch +import os import gymnasium as gym +import random import numpy as np from stable_baselines3.common.vec_env.base_vec_env import ( VecEnv, @@ -28,6 +30,7 @@ class SB3MultiAgentEnv(VecEnv): def __init__( self, config, + exp_config, scene_config, max_cont_agents, device, @@ -43,6 +46,13 @@ def __init__( self._env = make(dynamics_id=DynamicsModel.DELTA_LOCAL, action_id=ActionSpace.DISCRETE, kwargs=kwargs) self.config = config + self.exp_config = exp_config + self.all_scene_paths = [ + os.path.join(self.exp_config.data_dir, scene) + for scene in sorted(os.listdir(self.exp_config.data_dir)) + if scene.startswith("tfrecord") + ] + self.unique_scene_paths = list(set(self.all_scene_paths)) self.num_worlds = self._env.num_worlds self.max_agent_count = self._env.max_agent_count self.num_envs = self._env.cont_agent_mask.sum().item() @@ -229,6 +239,44 @@ def seed(self, seed=None): self._seeds = [seed + idx for idx in range(self.num_envs)] return self._seeds + def resample_scenario_batch(self): + """Swap out the dataset.""" + if self.exp_config.resample_mode == "random": + total_unique = len(self.unique_scene_paths) + + # Check if N is greater than the number of unique scenes + if self.num_worlds <= total_unique: + dataset = random.sample( + self.unique_scene_paths, self.num_worlds + ) + + # If N is greater, repeat the unique scenes until we get N scenes + dataset = [] + while len(dataset) < self.num_worlds: + dataset.extend( + random.sample(self.unique_scene_paths, total_unique) + ) + if len(dataset) > self.num_worlds: + dataset = dataset[ + : self.num_worlds + ] # Trim the result to N scenes + else: + raise NotImplementedError( + f"Resample mode {self.exp_config.resample_mode} is currently not supported." + ) + + # Re-initialize the simulator with the new dataset + print( + f"Re-initializing sim with {len(set(dataset))} {self.exp_config.resample_mode} unique scenes.\n" + ) + self._env.reinit_scenarios(dataset) + + # Update controlled agent mask + self.controlled_agent_mask = self._env.cont_agent_mask.clone() + self.max_agent_count = self._env.max_agent_count + self.num_valid_controlled_agents_across_worlds = self._env.num_valid_controlled_agents_across_worlds + self.num_envs = self.controlled_agent_mask.sum().item() + def _update_info_dict(self, info, indices) -> None: """Update the info logger.""" diff --git a/src/bindings.cpp b/src/bindings.cpp index d2574045..7c2050e4 100755 --- a/src/bindings.cpp +++ b/src/bindings.cpp @@ -124,7 +124,8 @@ namespace gpudrive .def("rgb_tensor", &Manager::rgbTensor) .def("depth_tensor", &Manager::depthTensor) .def("response_type_tensor", &Manager::responseTypeTensor) - .def("expert_trajectory_tensor", &Manager::expertTrajectoryTensor); + .def("expert_trajectory_tensor", &Manager::expertTrajectoryTensor) + .def("set_maps", &Manager::setMaps); } } diff --git a/src/headless.cpp b/src/headless.cpp index 3fc8fb27..ab0ee901 100755 --- a/src/headless.cpp +++ b/src/headless.cpp @@ -34,9 +34,9 @@ int main(int argc, char *argv[]) } uint64_t num_steps = std::stoul(argv[2]); - std::vector scenes = {"../data/examples/tfrecord-00001-of-01000_307.json", - "../data/examples/tfrecord-00003-of-01000_109.json", - "../data/examples/tfrecord-00012-of-01000_389.json"}; + std::vector scenes = {"../data/processed/examples/tfrecord-00001-of-01000_307.json", + "../data/processed/examples/tfrecord-00003-of-01000_109.json", + "../data/processed/examples/tfrecord-00012-of-01000_389.json"}; uint64_t num_worlds = scenes.size(); bool rand_actions = false; diff --git a/src/level_gen.cpp b/src/level_gen.cpp index 14282f4f..b3c667e9 100755 --- a/src/level_gen.cpp +++ b/src/level_gen.cpp @@ -309,6 +309,8 @@ void createCameraEntity(Engine &ctx) camera, 150.f, 0.001f, 1.5f * math::up); + + ctx.data().camera_agent = camera; } static inline bool shouldAgentBeCreated(Engine &ctx, const MapObject &agentInit) @@ -326,22 +328,25 @@ static inline bool shouldAgentBeCreated(Engine &ctx, const MapObject &agentInit) return true; } -void createPersistentEntities(Engine &ctx, Map *map) { +void createPersistentEntities(Engine &ctx) { // createFloorPlane(ctx); + const auto& map = ctx.singleton(); + if (ctx.data().enableRender) { createCameraEntity(ctx); } ctx.data().mean = {0, 0}; - ctx.data().mean.x = map->mean.x; - ctx.data().mean.y = map->mean.y; + ctx.data().mean.x = map.mean.x; + ctx.data().mean.y = map.mean.y; ctx.data().numControlledAgents = 0; + ctx.singleton().reset = 0; CountT agentIdx = 0; - for (CountT agentCtr = 0; agentCtr < map->numObjects && agentIdx < consts::kMaxAgentCount; ++agentCtr) { - const auto &agentInit = map->objects[agentCtr]; + for (CountT agentCtr = 0; agentCtr < map.numObjects && agentIdx < consts::kMaxAgentCount; ++agentCtr) { + const auto &agentInit = map.objects[agentCtr]; if (not shouldAgentBeCreated(ctx, agentInit)) { @@ -356,9 +361,9 @@ void createPersistentEntities(Engine &ctx, Map *map) { ctx.data().numAgents = agentIdx; CountT roadIdx = 0; - for(CountT roadCtr = 0; roadCtr < map->numRoads && roadIdx < consts::kMaxRoadEntityCount; roadCtr++) + for(CountT roadCtr = 0; roadCtr < map.numRoads && roadIdx < consts::kMaxRoadEntityCount; roadCtr++) { - const auto &roadInit = map->roads[roadCtr]; + const auto &roadInit = map.roads[roadCtr]; createRoadEntities(ctx, roadInit, roadIdx); } ctx.data().numRoads = roadIdx; @@ -404,7 +409,40 @@ static void resetPersistentEntities(Engine &ctx) } } -void generateWorld(Engine &ctx) +void destroyWorld(Engine &ctx) +{ + for (CountT idx = 0; idx < ctx.data().numAgents; ++idx) + { + Entity agent = ctx.data().agents[idx]; + ctx.destroyRenderableEntity(agent); + } + for (CountT idx = 0; idx < ctx.data().numRoads; idx++) + { + Entity road = ctx.data().roads[idx]; + ctx.destroyRenderableEntity(road); + } + if (ctx.data().enableRender) + { + ctx.destroyRenderableEntity(ctx.data().camera_agent); + } + for (CountT idx = 0; idx < consts::kMaxAgentCount; ++idx) + { + Entity agent_iface = ctx.data().agent_ifaces[idx]; + ctx.destroyEntity(agent_iface); + } + for (CountT idx = 0; idx < consts::kMaxRoadEntityCount; ++idx) + { + Entity road_iface = ctx.data().road_ifaces[idx]; + ctx.destroyEntity(road_iface); + } + ctx.data().numAgents = 0; + ctx.data().numRoads = 0; + ctx.data().numControlledAgents = 0; + ctx.data().mean = {0, 0}; +} + + +void resetWorld(Engine &ctx) { resetPersistentEntities(ctx); } diff --git a/src/level_gen.hpp b/src/level_gen.hpp index 31c28cea..16eb4d02 100644 --- a/src/level_gen.hpp +++ b/src/level_gen.hpp @@ -6,11 +6,12 @@ namespace gpudrive { - void createPersistentEntities(Engine &ctx, Map *map); + void createPersistentEntities(Engine &ctx); - // First, destroys any non-persistent state for the current world and then - // generates a new play area. - void generateWorld(Engine &ctx); +void resetWorld(Engine &ctx); + +// Destroys all entities in the world +void destroyWorld(Engine &ctx); static inline Action getZeroAction(DynamicsModel model) { diff --git a/src/mgr.cpp b/src/mgr.cpp index 0d3ce561..d7621c05 100755 --- a/src/mgr.cpp +++ b/src/mgr.cpp @@ -595,6 +595,62 @@ void Manager::reset(std::vector worldsToReset) { } } +void Manager::setMaps(const std::vector &maps) +{ + assert(impl_->cfg.scenes.size() == maps.size()); + impl_->cfg.scenes = maps; + + ResetMap resetmap{ + 1, + }; + + if (impl_->cfg.execMode == madrona::ExecMode::CUDA) + { +#ifdef MADRONA_CUDA_SUPPORT + auto &gpu_exec = static_cast(impl_.get())->gpuExec; + for (size_t world_idx = 0; world_idx < maps.size(); world_idx++) + { + Map *map = static_cast(MapReader::parseAndWriteOut(maps[world_idx], + ExecMode::CUDA, impl_->cfg.params.polylineReductionThreshold)); + Map *mapDevicePtr = (Map *)gpu_exec.getExported((uint32_t)ExportID::Map) + world_idx; + REQ_CUDA(cudaMemcpy(mapDevicePtr, map, sizeof(Map), cudaMemcpyHostToDevice)); + madrona::cu::deallocGPU(map); + + auto resetMapPtr = (ResetMap *)gpu_exec.getExported((uint32_t)ExportID::ResetMap) + world_idx; + REQ_CUDA(cudaMemcpy(resetMapPtr, &resetmap, sizeof(ResetMap), cudaMemcpyHostToDevice)); + } + +#else + // Handle the case where CUDA support is not available + FATAL("Madrona was not compiled with CUDA support"); +#endif + } + else + { + + auto &cpu_exec = static_cast(impl_.get())->cpuExec; + + for (size_t world_idx = 0; world_idx < maps.size(); world_idx++) + { + // Parse the map string into your MapData structure + Map *map = static_cast(MapReader::parseAndWriteOut(maps[world_idx], + ExecMode::CPU, impl_->cfg.params.polylineReductionThreshold)); + + Map *mapDevicePtr = (Map *)cpu_exec.getExported((uint32_t)ExportID::Map) + world_idx; + memcpy(mapDevicePtr, map, sizeof(Map)); + delete map; + + auto resetMapPtr = (ResetMap *)cpu_exec.getExported((uint32_t)ExportID::ResetMap) + world_idx; + memcpy(resetMapPtr, &resetmap, sizeof(ResetMap)); + } + } + + // Vector of range on integers from 0 to the number of worlds + std::vector worldIndices(maps.size()); + std::iota(worldIndices.begin(), worldIndices.end(), 0); + reset(worldIndices); +} + Tensor Manager::actionTensor() const { return impl_->exportTensor(ExportID::Action, TensorElementType::Float32, diff --git a/src/mgr.hpp b/src/mgr.hpp index 58584381..a5b682c0 100755 --- a/src/mgr.hpp +++ b/src/mgr.hpp @@ -75,6 +75,7 @@ class Manager { MGR_EXPORT void setAction(int32_t world_idx, int32_t agent_idx, float acceleration, float steering, float headAngle); + MGR_EXPORT void setMaps(const std::vector &maps); // TODO: remove parameters MGR_EXPORT std::vector getShapeTensorFromDeviceMemory(); diff --git a/src/sim.cpp b/src/sim.cpp index 6918312a..45d75b15 100755 --- a/src/sim.cpp +++ b/src/sim.cpp @@ -55,6 +55,8 @@ void Sim::registerTypes(ECSRegistry ®istry, const Config &cfg) registry.registerComponent(); registry.registerSingleton(); registry.registerSingleton(); + registry.registerSingleton(); + registry.registerSingleton(); registry.registerArchetype(); registry.registerArchetype(); @@ -64,6 +66,8 @@ void Sim::registerTypes(ECSRegistry ®istry, const Config &cfg) registry.exportSingleton((uint32_t)ExportID::Reset); registry.exportSingleton((uint32_t)ExportID::Shape); + registry.exportSingleton((uint32_t)ExportID::Map); + registry.exportSingleton((uint32_t)ExportID::ResetMap); registry.exportColumn( (uint32_t)ExportID::Action); registry.exportColumn( @@ -96,7 +100,7 @@ void Sim::registerTypes(ECSRegistry ®istry, const Config &cfg) } static inline void cleanupWorld(Engine &ctx) { - // TODO: Implement cleanup for changing worlds during runtime + destroyWorld(ctx); } static inline void initWorld(Engine &ctx) @@ -109,8 +113,15 @@ static inline void initWorld(Engine &ctx) ctx.data().rng = RNG::make(episode_idx); ctx.data().curEpisodeIdx = episode_idx; + if(ctx.singleton().reset == 1) + { + createPersistentEntities(ctx); + ctx.singleton().reset = 0; + phys::PhysicsSystem::reset(ctx); + } + // Defined in src/level_gen.hpp / src/level_gen.cpp - generateWorld(ctx); + resetWorld(ctx); } // This system runs in TaskGraphID::Reset and checks if the code external to the @@ -118,13 +129,19 @@ static inline void initWorld(Engine &ctx) // reset is needed, cleanup the existing world and generate a new one. inline void resetSystem(Engine &ctx, WorldReset &reset) { - if (reset.reset == 0) { - return; + if (reset.reset == 0) + { + return; } reset.reset = 0; - cleanupWorld(ctx); + auto resetMap = ctx.singleton(); + + if (resetMap.reset == 1) + { + cleanupWorld(ctx); + } initWorld(ctx); } @@ -838,7 +855,8 @@ Sim::Sim(Engine &ctx, // Currently the physics system needs an upper bound on the number of // entities that will be stored in the BVH. We plan to fix this in // a future release. - auto max_total_entities = init.map->numObjects + init.map->numRoadSegments; + // auto max_total_entities = init.map->numObjects + init.map->numRoadSegments; + auto max_total_entities = consts::kMaxAgentCount + consts::kMaxRoadEntityCount; phys::PhysicsSystem::init(ctx, init.rigidBodyObjMgr, consts::deltaT, consts::numPhysicsSubsteps, -9.8f * math::up, @@ -850,8 +868,10 @@ Sim::Sim(Engine &ctx, RenderingSystem::init(ctx, cfg.renderBridge); } + auto& map = ctx.singleton(); + map = *(init.map); // Creates agents, walls, etc. - createPersistentEntities(ctx, init.map); + createPersistentEntities(ctx); // Generate initial world state initWorld(ctx); diff --git a/src/sim.hpp b/src/sim.hpp index cec78620..f844b42a 100755 --- a/src/sim.hpp +++ b/src/sim.hpp @@ -33,6 +33,8 @@ enum class ExportID : uint32_t { Info, ResponseType, Trajectory, + Map, + ResetMap, NumExports }; @@ -124,6 +126,8 @@ struct Sim : public madrona::WorldBase { Entity agent_ifaces[consts::kMaxAgentCount]; Entity road_ifaces[consts::kMaxRoadEntityCount]; + Entity camera_agent; + madrona::CountT numControlledAgents; madrona::math::Vector2 mean; diff --git a/src/types.hpp b/src/types.hpp index 54fa89d2..3f3a9181 100755 --- a/src/types.hpp +++ b/src/types.hpp @@ -61,7 +61,10 @@ struct AgentID { int32_t reset; }; - struct ClassicAction +struct ResetMap { + int32_t reset; +}; + struct ClassicAction { float acceleration; float steering;