diff --git a/docs/ConfiguringInfinigen.md b/docs/ConfiguringInfinigen.md index 3561f1462..b2a35e5e4 100644 --- a/docs/ConfiguringInfinigen.md +++ b/docs/ConfiguringInfinigen.md @@ -45,7 +45,7 @@ Our `infinigen_examples/generate_nature.py` driver always loads [`infinigen_exam Now that you understand the two major python programs and how to configure them, you may notice and wonder about the many configs/overrides provided in our original one-command "Hello World" example: -``` +```bash # Original hello world command python -m infinigen.datagen.manage_jobs --output_folder outputs/hello_world --num_scenes 1 --specific_seed 0 \ --configs desert.gin simple.gin --pipeline_configs local_16GB.gin monocular.gin blender_gt.gin \ @@ -169,23 +169,23 @@ All commands below are shown with using `local_256GB` config, but you can attemp We recommend this command as a starting point for generating high quality videos. Generating multi-view consistent terrain is not computationally tractible without CUDA accelleration, so make sure to follow the CUDA Terrain instructions in Installation.md, and we recommend not to remove the `cuda_terrain` flag below. -```` +```bash python -m infinigen.datagen.manage_jobs --output_folder outputs/my_videos --num_scenes 500 \ --pipeline_config slurm monocular_video cuda_terrain opengl_gt \ --cleanup big_files --warmup_sec 60000 --config trailer_video high_quality_terrain -```` +``` #### Creating large-scale stereo datasets -```` +```bash python -m infinigen.datagen.manage_jobs --output_folder outputs/stereo_data --num_scenes 10000 \ --pipeline_config slurm stereo cuda_terrain opengl_gt \ --cleanup big_files --warmup_sec 60000 --config high_quality_terrain -```` +``` #### Creating a few low-resolution images to your test changes -``` +```bash screen python -m infinigen.datagen.manage_jobs --output_folder outputs/dev --num_scenes 50 \ --pipeline_config slurm monocular cuda_terrain \ --cleanup big_files --warmup_sec 1200 --configs dev @@ -196,7 +196,7 @@ screen python -m infinigen.datagen.manage_jobs --output_folder outputs/dev --num These commands are intended as inspiration - please read docs above for more advice on customizing all aspects of Infinigen. Create images that always have rain: -``` +```bash python -m infinigen.datagen.manage_jobs --output_folder outputs/my_videos --num_scenes 500 \ --pipeline_config slurm monocular cuda_terrain opengl_gt \ --cleanup big_files --warmup_sec 30000 \ @@ -206,7 +206,7 @@ python -m infinigen.datagen.manage_jobs --output_folder outputs/my_videos --num_ :bulb: You can substitute the `rain_particles` in `rain_particles_chance` for any `run_stage` name argument string in `infinigen_examples/generate_nature.py`, such as `trees` or `ground_creatures`. Create images that only have terrain: -``` +```bash python -m infinigen.datagen.manage_jobs --output_folder outputs/my_videos --num_scenes 500 \ --pipeline_config slurm monocular cuda_terrain opengl_gt \ --cleanup big_files --warmup_sec 30000 --config no_assets @@ -215,7 +215,7 @@ python -m infinigen.datagen.manage_jobs --output_folder outputs/my_videos --num_ Create videos at birds-eye-view camera altitudes: -``` +```bash python -m infinigen.datagen.manage_jobs --output_folder outputs/my_videos --num_scenes 500 \ --pipeline_config slurm monocular_video cuda_terrain opengl_gt \ --cleanup big_files --warmup_sec 30000 --config trailer_video high_quality_terrain \ @@ -225,7 +225,7 @@ python -m infinigen.datagen.manage_jobs --output_folder outputs/my_videos --num_ :bulb: The command shown is overriding `infinigen_examples/configs_nature/base.gin`'s default setting of `camera.camera_pose_proposal.altitude`. You can use a similar syntax to override any number of .gin config entries. Separate multiple entries with spaces. Create 1 second video clips: -``` +```bash python -m infinigen.datagen.manage_jobs --output_folder outputs/my_videos --num_scenes 500 \ --pipeline_config slurm monocular_video cuda_terrain opengl_gt \ --cleanup big_files --warmup_sec 30000 --config trailer_video high_quality_terrain \ diff --git a/docs/GeneratingFluidSimulations.md b/docs/GeneratingFluidSimulations.md index 387eab50e..52756b96a 100644 --- a/docs/GeneratingFluidSimulations.md +++ b/docs/GeneratingFluidSimulations.md @@ -9,13 +9,13 @@ Before you can generate fluids, you must run an additional installation step: `b ## Example Commands #### Generate a video of a single scene with simulated fire generated on the fly -``` +```bash python -m infinigen.datagen.manage_jobs --specific_seed 3930249d --output_folder outputs/fire --num_scenes 1 --pipeline_config local_256GB.gin monocular_video.gin --cleanup none --config plain.gin fast_terrain_assets.gin use_on_the_fly_fire.gin ``` Because fluid simulation takes a long time, the fire resolution can be reduced in use_on_the_fly_fire.gin, by setting `set_obj_on_fire.resolution = {resolution}`. This will reduce the fire quality but speed up the simulation. #### Generate a video of a single valley scene with simulated river -``` +```bash python -m infinigen.datagen.manage_jobs --specific_seed 61fc881a --output_folder outputs/river --num_scenes 1 --pipeline_config local_256GB.gin monocular_video.gin opengl_gt.gin cuda_terrain.gin --pipeline_overrides iterate_scene_tasks.frame_range=[100,244] --config river.gin simulated_river.gin no_assets.gin no_creatures.gin fast_terrain_assets.gin --cleanup none ``` Similar to fire, the simulation can be sped up by reducing the resolution. In simulated_river.gin, the resolution can be modified by setting `make_river.resolution = {resolution}`. The simulation can also be sped up by reducing the simulation duration in simulated_river.gin by setting `make_river.simulation_duration = {duration}`. For instance, before running the command above, the duration can be reduced to a number greater than 200 since that is the last frame of the video. @@ -23,7 +23,7 @@ Similar to fire, the simulation can be sped up by reducing the resolution. In si Also, note that this command will produce a scene without assets to speed up the process. However, the liquids generally interact with the objects by splashing on them, and a scene like this can be produced by removing the `no_assets.gin` option in the above command. #### Generate videos of random scene types, with simulated fire generated on the fly when needed -``` +```bash python -m infinigen.datagen.manage_jobs --output_folder outputs/onthefly --num_scenes 10 \ --pipeline_config slurm_high_memory.gin monocular_video.gin \ --config fast_terrain_assets.gin use_on_the_fly_fire.gin \ @@ -31,7 +31,7 @@ python -m infinigen.datagen.manage_jobs --output_folder outputs/onthefly --num_ ``` #### Generate videos of valley scenes with simulated rivers -``` +```bash python -m infinigen.datagen.manage_jobs --output_folder /n/fs/pvl-renders/kkayan/river --num_scenes 10 \ --pipeline_config slurm_high_memory.gin monocular_video.gin opengl_gt.gin cuda_terrain.gin \ --pipeline_overrides iterate_scene_tasks.frame_range=[100,244] \ @@ -44,20 +44,21 @@ python -m infinigen.datagen.manage_jobs --output_folder /n/fs/pvl-renders/kkayan ## Using Pre-Generated Fire High-resolution fluid simulations take a long time, so assets on fire can pre-generated and imported in the scene instead of being baked on-the-fly. This allows for fire to be simulated once, instead of every time a scene is generated. To pre-generate fire assets of all types (bush, tree, creature, cactus, boulder), run: -``` +```bash python -m infinigen.tools.submit_asset_cache -f {fire_asset_folder} -n 1 -s -40 -d 184 ``` where `fire_asset_folder` is where you want to save the fire. The number of assets per type, start frame and duration can be adjusted. If you only want to pre-generate one type of asset once, run: -``` +```bash python -m infinigen.assets.fluid.run_asset_cache -f {fire_asset_folder} -a {asset} -s {start_frame} -d {simulation_duration} ``` where `fire_asset_folder` is where you want to save the fire. `asset` can be one of `CachedBushFactory`, `CachedTreeFactory`, `CachedCactusFactory`, `CachedCreatureFactory`, `CachedBoulderFactory`. ### Import pre-generated fire when generating a scene After fire is pre-generated with one of the previous commands, edit config/use_cached_fire.gin and set the `FireCachingSystem.asset_folder` variable to `fire_asset_folder` you used when pre-generating fire. After this `use_cached_fire.gin` can be used instead of `use_on_the_fly_fire.gin` when generating a scene. This will import the fire from the folder it is saved instead of simulating it on-the-fly. + #### Example Command -``` +```bash python -m infinigen.datagen.manage_jobs --specific_seed 3930249d --output_folder outputs/fire --num_scenes 1 --pipeline_config local_256GB.gin monocular_video.gin --cleanup none --config plain.gin fast_terrain_assets.gin use_cached_fire.gin ``` diff --git a/docs/HelloWorld.md b/docs/HelloWorld.md index 76f9b3113..74d988455 100644 --- a/docs/HelloWorld.md +++ b/docs/HelloWorld.md @@ -16,7 +16,7 @@ Infinigen generates scenes by running multiple tasks (usually executed automatic :exclamation: If you encounter any missing .so files, missing dependencies (such as `gin`), or similar crashes, please check again that all steps of installation ran successfully. If you cannot resolve any issues with installation, please see our README and 'Bug Report' Git Issue template for advice on posting Git Issues to get help quickly - you must include the full installation logs in your issue so that we can help debug. -``` +```bash mkdir outputs # Generate a scene layout @@ -40,7 +40,7 @@ Output logs should indicate what the code is working on. Use `--debug` for even We provide `infinigen/datagen/manage_jobs.py`, a utility which runs similar steps automatically. -``` +```bash python -m infinigen.datagen.manage_jobs --output_folder outputs/hello_world --num_scenes 1 --specific_seed 0 \ --configs desert.gin simple.gin --pipeline_configs local_16GB.gin monocular.gin blender_gt.gin --pipeline_overrides LocalScheduleHandler.use_gpu=False ``` diff --git a/infinigen/assets/scatters/monocot.py b/infinigen/assets/scatters/monocots.py similarity index 100% rename from infinigen/assets/scatters/monocot.py rename to infinigen/assets/scatters/monocots.py diff --git a/infinigen/core/execute_tasks.py b/infinigen/core/execute_tasks.py index 4a39d8a05..e6a6214b8 100644 --- a/infinigen/core/execute_tasks.py +++ b/infinigen/core/execute_tasks.py @@ -6,6 +6,7 @@ import pickle import shutil import time +import typing from collections import defaultdict from pathlib import Path @@ -16,257 +17,24 @@ import bpy import gin from frozendict import frozendict -from numpy.random import uniform import infinigen.assets.scatters -from infinigen.assets import fluid -from infinigen.assets.objects import cactus, cloud, creatures, monocot, rocks, trees -from infinigen.assets.scatters import ( - ground_mushroom, - ivy, - lichen, - moss, - slime_mold, - snow_layer, -) -from infinigen.assets.scatters.utils.selection import scatter_lower, scatter_upward from infinigen.core import init, surface from infinigen.core.placement import camera as cam_util -from infinigen.core.placement import placement from infinigen.core.rendering.render import render_image from infinigen.core.rendering.resample import resample_scene from infinigen.core.tagging import tag_system from infinigen.core.util import blender as butil from infinigen.core.util import exporting from infinigen.core.util.logging import Timer, create_text_file, save_polycounts -from infinigen.core.util.math import FixedSeed, int_hash +from infinigen.core.util.math import int_hash from infinigen.core.util.organization import Task -from infinigen.core.util.pipeline import RandomStageExecutor from infinigen.terrain import Terrain from infinigen.tools.export import export_scene, triangulate_meshes logger = logging.getLogger(__name__) -@gin.configurable -def populate_scene(output_folder, scene_seed, **params): - p = RandomStageExecutor(scene_seed, output_folder, params) - camera = [cam_util.get_camera(i, j) for i, j in cam_util.get_cameras_ids()] - - season = p.run_stage( - "choose_season", trees.random_season, use_chance=False, default=[] - ) - - fire_cache_system = fluid.FireCachingSystem() if params.get("cached_fire") else None - - populated = {} - populated["trees"] = p.run_stage( - "populate_trees", - use_chance=False, - default=[], - fn=lambda: placement.populate_all( - trees.TreeFactory, camera, season=season, vis_cull=4 - ), - ) # , - # meshing_camera=camera, adapt_mesh_method='subdivide', cam_meshing_max_dist=8)) - populated["boulders"] = p.run_stage( - "populate_boulders", - use_chance=False, - default=[], - fn=lambda: placement.populate_all(rocks.BoulderFactory, camera, vis_cull=3), - ) # , - # meshing_camera=camera, adapt_mesh_method='subdivide', cam_meshing_max_dist=8)) - populated["bushes"] = p.run_stage( - "populate_bushes", - use_chance=False, - fn=lambda: placement.populate_all( - trees.BushFactory, camera, vis_cull=1, adapt_mesh_method="subdivide" - ), - ) - p.run_stage( - "populate_kelp", - use_chance=False, - fn=lambda: placement.populate_all( - monocot.KelpMonocotFactory, camera, vis_cull=5 - ), - ) - populated["cactus"] = p.run_stage( - "populate_cactus", - use_chance=False, - fn=lambda: placement.populate_all(cactus.CactusFactory, camera, vis_cull=6), - ) - p.run_stage( - "populate_clouds", - use_chance=False, - fn=lambda: placement.populate_all( - cloud.CloudFactory, camera, dist_cull=None, vis_cull=None - ), - ) - p.run_stage( - "populate_glowing_rocks", - use_chance=False, - fn=lambda: placement.populate_all( - rocks.GlowingRocksFactory, camera, dist_cull=None, vis_cull=None - ), - ) - - populated["cached_fire_trees"] = p.run_stage( - "populate_cached_fire_trees", - use_chance=False, - default=[], - fn=lambda: placement.populate_all( - fluid.CachedTreeFactory, - camera, - season=season, - vis_cull=4, - dist_cull=70, - cache_system=fire_cache_system, - ), - ) - populated["cached_fire_boulders"] = p.run_stage( - "populate_cached_fire_boulders", - use_chance=False, - default=[], - fn=lambda: placement.populate_all( - fluid.CachedBoulderFactory, - camera, - vis_cull=3, - dist_cull=70, - cache_system=fire_cache_system, - ), - ) - populated["cached_fire_bushes"] = p.run_stage( - "populate_cached_fire_bushes", - use_chance=False, - fn=lambda: placement.populate_all( - fluid.CachedBushFactory, - camera, - vis_cull=1, - adapt_mesh_method="subdivide", - cache_system=fire_cache_system, - ), - ) - populated["cached_fire_cactus"] = p.run_stage( - "populate_cached_fire_cactus", - use_chance=False, - fn=lambda: placement.populate_all( - fluid.CachedCactusFactory, - camera, - vis_cull=6, - cache_system=fire_cache_system, - ), - ) - - grime_selection_funcs = { - "trees": scatter_lower, - "boulders": scatter_upward, - } - grime_types = { - "slime_mold": slime_mold.SlimeMold, - "lichen": lichen.Lichen, - "ivy": ivy.Ivy, - "mushroom": ground_mushroom.Mushrooms, - "moss": moss.MossCover, - } - - def apply_grime(grime_type, surface_cls): - surface_fac = surface_cls() - for ( - target_type, - results, - ) in populated.items(): - selection_func = grime_selection_funcs.get(target_type, None) - for fac_seed, fac_pholders, fac_assets in results: - if len(fac_pholders) == 0: - continue - for inst_seed, obj in fac_assets: - with FixedSeed(int_hash((grime_type, fac_seed, inst_seed))): - p_k = f"{grime_type}_on_{target_type}_per_instance_chance" - if uniform() > params.get(p_k, 0.4): - continue - logger.debug(f"Applying {surface_fac} on {obj}") - surface_fac.apply(obj, selection=selection_func) - - for grime_type, surface_cls in grime_types.items(): - p.run_stage(grime_type, lambda: apply_grime(grime_type, surface_cls)) - - def apply_snow_layer(surface_cls): - surface_fac = surface_cls() - for ( - target_type, - results, - ) in populated.items(): - selection_func = grime_selection_funcs.get(target_type, None) - for fac_seed, fac_pholders, fac_assets in results: - if len(fac_pholders) == 0: - continue - for inst_seed, obj in fac_assets: - tmp = obj.users_collection[0].hide_viewport - obj.users_collection[0].hide_viewport = False - surface_fac.apply(obj, selection=selection_func) - obj.users_collection[0].hide_viewport = tmp - - p.run_stage("snow_layer", lambda: apply_snow_layer(snow_layer.Snowlayer)) - - creature_facs = { - "beetles": creatures.BeetleFactory, - "bird": creatures.BirdFactory, - "carnivore": creatures.CarnivoreFactory, - "crab": creatures.CrabFactory, - "crustacean": creatures.CrustaceanFactory, - "dragonfly": creatures.DragonflyFactory, - "fish": creatures.FishFactory, - "flyingbird": creatures.FlyingBirdFactory, - "herbivore": creatures.HerbivoreFactory, - "snake": creatures.SnakeFactory, - } - for k, fac in creature_facs.items(): - p.run_stage( - f"populate_{k}", - use_chance=False, - fn=lambda: placement.populate_all(fac, camera=None), - ) - - fire_warmup = params.get("fire_warmup", 50) - simulation_duration = ( - bpy.context.scene.frame_end - bpy.context.scene.frame_start + fire_warmup - ) - - def set_fire(assets): - objs = [o for *_, a in assets for _, o in a] - with butil.EnableParentCollections(objs): - fluid.set_fire_to_assets( - assets, - bpy.context.scene.frame_start - fire_warmup, - simulation_duration, - output_folder, - ) - - p.run_stage( - "trees_fire_on_the_fly", set_fire, populated["trees"], prereq="populate_trees" - ) - p.run_stage( - "bushes_fire_on_the_fly", - set_fire, - populated["bushes"], - prereq="populate_bushes", - ) - p.run_stage( - "boulders_fire_on_the_fly", - set_fire, - populated["boulders"], - prereq="populate_boulders", - ) - p.run_stage( - "cactus_fire_on_the_fly", - set_fire, - populated["cactus"], - prereq="populate_cactus", - ) - - p.save_results(output_folder / "pipeline_fine.csv") - - def get_scene_tag(name): try: o = next(o for o in bpy.data.objects if o.name.startswith(f"{name}=")) @@ -360,17 +128,18 @@ def group_collections(config): @gin.configurable def execute_tasks( - compose_scene_func, - input_folder, - output_folder, - task, - scene_seed, - frame_range, - camera_id, - resample_idx=None, - output_blend_name="scene.blend", + compose_scene_func: typing.Callable, + populate_scene_func: typing.Callable, + input_folder: Path, + output_folder: Path, + task: str, + scene_seed: int, + frame_range: tuple[int], + camera_id: tuple[int], + resample_idx: int = None, + output_blend_name: str = "scene.blend", generate_resolution=(1280, 720), - fps=24, + fps: int = 24, reset_assets=True, dryrun=False, optimize_terrain_diskusage=False, @@ -432,8 +201,8 @@ def execute_tasks( group_collections() - if Task.Populate in task: - populate_scene(output_folder, scene_seed) + if Task.Populate in task and populate_scene_func is not None: + populate_scene_func(output_folder, scene_seed) need_terrain_processing = "OpaqueTerrain" in bpy.data.objects diff --git a/infinigen/core/rendering/render.py b/infinigen/core/rendering/render.py index 3d224db8e..9e5fdec6f 100644 --- a/infinigen/core/rendering/render.py +++ b/infinigen/core/rendering/render.py @@ -269,6 +269,7 @@ def postprocess_blendergt_outputs(frames_folder, output_stem): if flow_color is not None: imwrite( flow_dst_path.with_name(f"Flow{output_stem}.png"), + flow_color, ) flow_dst_path.unlink() diff --git a/infinigen/datagen/manage_jobs.py b/infinigen/datagen/manage_jobs.py index da067c540..0890fd925 100644 --- a/infinigen/datagen/manage_jobs.py +++ b/infinigen/datagen/manage_jobs.py @@ -745,13 +745,10 @@ def main(args, shuffle=True, wandb_project="render", upload_commandfile_method=N ] if __name__ == "__main__": - os.umask(0o007) - slurm_available = which("sbatch") is not None - parser = ( - argparse.ArgumentParser() - ) # to guarantee that the render scenes finish, try render_image.time_limit=2000 - parser.add_argument("-o", "--output_folder", type=Path, required=True) + parser = argparse.ArgumentParser() + + parser.add_argument("-o", "--output_folder", type=Path, default=None) # parser.add_argument( "--num_scenes", type=int, @@ -859,12 +856,22 @@ def main(args, shuffle=True, wandb_project="render", upload_commandfile_method=N assert args.specific_seed is None or args.num_scenes == 1 + if args.output_folder is None: + date_str = datetime.now().strftime("%y-%m-%d_%H-%M") + hostname = os.uname().nodename + + output_base = Path("outputs") + assert output_base.exists(), output_base + + args.output_folder = Path(f"outputs/{date_str}_{hostname}") + overwrite_ok = args.use_existing or args.overwrite if args.output_folder.exists() and not overwrite_ok: raise FileExistsError( f"--output_folder {args.output_folder} already exists! Please delete it," " specify a different --output_folder, or use --overwrite" ) + args.output_folder.mkdir(parents=True, exist_ok=overwrite_ok) if args.meta_seed is not None: diff --git a/infinigen_examples/generate_indoors.py b/infinigen_examples/generate_indoors.py index d00ce6499..a557f4232 100644 --- a/infinigen_examples/generate_indoors.py +++ b/infinigen_examples/generate_indoors.py @@ -477,6 +477,7 @@ def main(args): execute_tasks.main( compose_scene_func=compose_indoors, + populate_scene_func=None, input_folder=args.input_folder, output_folder=args.output_folder, task=args.task, diff --git a/infinigen_examples/generate_nature.py b/infinigen_examples/generate_nature.py index a7254cdea..e1b164060 100644 --- a/infinigen_examples/generate_nature.py +++ b/infinigen_examples/generate_nature.py @@ -21,10 +21,9 @@ level=logging.INFO, ) -from infinigen.assets import fluid, lighting, weather - # unused imports required for gin to find modules currently, # TODO remove # ruff: noqa: F401 +from infinigen.assets import fluid, lighting, weather from infinigen.assets.materials import ( atmosphere_light_haze, chunkyrock, @@ -62,35 +61,34 @@ ground_leaves, ground_mushroom, ground_twigs, + ivy, jellyfish, + lichen, + monocots, + moss, pebbles, pine_needle, pinecone, seashells, seaweed, + slime_mold, + snow_layer, urchin, ) -from infinigen.assets.scatters import monocot as monocots +from infinigen.assets.scatters.utils.selection import scatter_lower, scatter_upward from infinigen.core import execute_tasks, init, surface from infinigen.core.placement import camera as cam_util -from infinigen.core.placement import ( - density, - placement, - split_in_view, -) +from infinigen.core.placement import density, placement, split_in_view from infinigen.core.util import blender as butil from infinigen.core.util import logging as logging_util from infinigen.core.util import pipeline -from infinigen.core.util.math import int_hash -from infinigen.core.util.organization import Tags +from infinigen.core.util.math import FixedSeed, int_hash +from infinigen.core.util.organization import Tags, Task +from infinigen.core.util.pipeline import RandomStageExecutor from infinigen.core.util.random import random_general, sample_registry from infinigen.terrain import Terrain -logging.basicConfig( - format="[%(asctime)s.%(msecs)03d] [%(name)s] [%(levelname)s] | %(message)s", - datefmt="%H:%M:%S", - level=logging.WARNING, -) +logger = logging.getLogger(__name__) @gin.configurable @@ -729,6 +727,225 @@ def add_tilted_river(): } +@gin.configurable +def populate_scene(output_folder, scene_seed, **params): + p = RandomStageExecutor(scene_seed, output_folder, params) + camera = [cam_util.get_camera(i, j) for i, j in cam_util.get_cameras_ids()] + + season = p.run_stage( + "choose_season", trees.random_season, use_chance=False, default=[] + ) + + fire_cache_system = fluid.FireCachingSystem() if params.get("cached_fire") else None + + populated = {} + populated["trees"] = p.run_stage( + "populate_trees", + use_chance=False, + default=[], + fn=lambda: placement.populate_all( + trees.TreeFactory, camera, season=season, vis_cull=4 + ), + ) # , + # meshing_camera=camera, adapt_mesh_method='subdivide', cam_meshing_max_dist=8)) + populated["boulders"] = p.run_stage( + "populate_boulders", + use_chance=False, + default=[], + fn=lambda: placement.populate_all(rocks.BoulderFactory, camera, vis_cull=3), + ) # , + # meshing_camera=camera, adapt_mesh_method='subdivide', cam_meshing_max_dist=8)) + populated["bushes"] = p.run_stage( + "populate_bushes", + use_chance=False, + fn=lambda: placement.populate_all( + trees.BushFactory, camera, vis_cull=1, adapt_mesh_method="subdivide" + ), + ) + p.run_stage( + "populate_kelp", + use_chance=False, + fn=lambda: placement.populate_all( + monocot.KelpMonocotFactory, camera, vis_cull=5 + ), + ) + populated["cactus"] = p.run_stage( + "populate_cactus", + use_chance=False, + fn=lambda: placement.populate_all(cactus.CactusFactory, camera, vis_cull=6), + ) + p.run_stage( + "populate_clouds", + use_chance=False, + fn=lambda: placement.populate_all( + cloud.CloudFactory, camera, dist_cull=None, vis_cull=None + ), + ) + p.run_stage( + "populate_glowing_rocks", + use_chance=False, + fn=lambda: placement.populate_all( + rocks.GlowingRocksFactory, camera, dist_cull=None, vis_cull=None + ), + ) + + populated["cached_fire_trees"] = p.run_stage( + "populate_cached_fire_trees", + use_chance=False, + default=[], + fn=lambda: placement.populate_all( + fluid.CachedTreeFactory, + camera, + season=season, + vis_cull=4, + dist_cull=70, + cache_system=fire_cache_system, + ), + ) + populated["cached_fire_boulders"] = p.run_stage( + "populate_cached_fire_boulders", + use_chance=False, + default=[], + fn=lambda: placement.populate_all( + fluid.CachedBoulderFactory, + camera, + vis_cull=3, + dist_cull=70, + cache_system=fire_cache_system, + ), + ) + populated["cached_fire_bushes"] = p.run_stage( + "populate_cached_fire_bushes", + use_chance=False, + fn=lambda: placement.populate_all( + fluid.CachedBushFactory, + camera, + vis_cull=1, + adapt_mesh_method="subdivide", + cache_system=fire_cache_system, + ), + ) + populated["cached_fire_cactus"] = p.run_stage( + "populate_cached_fire_cactus", + use_chance=False, + fn=lambda: placement.populate_all( + fluid.CachedCactusFactory, + camera, + vis_cull=6, + cache_system=fire_cache_system, + ), + ) + + grime_selection_funcs = { + "trees": scatter_lower, + "boulders": scatter_upward, + } + grime_types = { + "slime_mold": slime_mold.SlimeMold, + "lichen": lichen.Lichen, + "ivy": ivy.Ivy, + "mushroom": ground_mushroom.Mushrooms, + "moss": moss.MossCover, + } + + def apply_grime(grime_type, surface_cls): + surface_fac = surface_cls() + for ( + target_type, + results, + ) in populated.items(): + selection_func = grime_selection_funcs.get(target_type, None) + for fac_seed, fac_pholders, fac_assets in results: + if len(fac_pholders) == 0: + continue + for inst_seed, obj in fac_assets: + with FixedSeed(int_hash((grime_type, fac_seed, inst_seed))): + p_k = f"{grime_type}_on_{target_type}_per_instance_chance" + if uniform() > params.get(p_k, 0.4): + continue + logger.debug("Applying {surface_fac} on {obj}") + surface_fac.apply(obj, selection=selection_func) + + for grime_type, surface_cls in grime_types.items(): + p.run_stage(grime_type, lambda: apply_grime(grime_type, surface_cls)) + + def apply_snow_layer(surface_cls): + surface_fac = surface_cls() + for ( + target_type, + results, + ) in populated.items(): + selection_func = grime_selection_funcs.get(target_type, None) + for fac_seed, fac_pholders, fac_assets in results: + if len(fac_pholders) == 0: + continue + for inst_seed, obj in fac_assets: + tmp = obj.users_collection[0].hide_viewport + obj.users_collection[0].hide_viewport = False + surface_fac.apply(obj, selection=selection_func) + obj.users_collection[0].hide_viewport = tmp + + p.run_stage("snow_layer", lambda: apply_snow_layer(snow_layer.Snowlayer)) + + creature_facs = { + "beetles": creatures.BeetleFactory, + "bird": creatures.BirdFactory, + "carnivore": creatures.CarnivoreFactory, + "crab": creatures.CrabFactory, + "crustacean": creatures.CrustaceanFactory, + "dragonfly": creatures.DragonflyFactory, + "fish": creatures.FishFactory, + "flyingbird": creatures.FlyingBirdFactory, + "herbivore": creatures.HerbivoreFactory, + "snake": creatures.SnakeFactory, + } + for k, fac in creature_facs.items(): + p.run_stage( + f"populate_{k}", + use_chance=False, + fn=lambda: placement.populate_all(fac, camera=None), + ) + + fire_warmup = params.get("fire_warmup", 50) + simulation_duration = ( + bpy.context.scene.frame_end - bpy.context.scene.frame_start + fire_warmup + ) + + def set_fire(assets): + objs = [o for *_, a in assets for _, o in a] + with butil.EnableParentCollections(objs): + fluid.set_fire_to_assets( + assets, + bpy.context.scene.frame_start - fire_warmup, + simulation_duration, + output_folder, + ) + + p.run_stage( + "trees_fire_on_the_fly", set_fire, populated["trees"], prereq="populate_trees" + ) + p.run_stage( + "bushes_fire_on_the_fly", + set_fire, + populated["bushes"], + prereq="populate_bushes", + ) + p.run_stage( + "boulders_fire_on_the_fly", + set_fire, + populated["boulders"], + prereq="populate_boulders", + ) + p.run_stage( + "cactus_fire_on_the_fly", + set_fire, + populated["cactus"], + prereq="populate_cactus", + ) + + p.save_results(output_folder / "pipeline_fine.csv") + + def main(args): scene_seed = init.apply_scene_seed(args.seed) mandatory_exclusive = [Path("infinigen_examples/configs_nature/scene_types")] @@ -742,6 +959,7 @@ def main(args): execute_tasks.main( compose_scene_func=compose_nature, + populate_scene_func=populate_scene, input_folder=args.input_folder, output_folder=args.output_folder, task=args.task, diff --git a/tests/core/test_execute_tasks.py b/tests/core/test_execute_tasks.py index 9be464fac..0d36d2789 100644 --- a/tests/core/test_execute_tasks.py +++ b/tests/core/test_execute_tasks.py @@ -25,6 +25,7 @@ def compose_cube(output_folder, scene_seed, **params): execute_tasks.execute_tasks( compose_cube, + populate_scene_func=None, input_folder=None, output_folder=output, task="coarse populate",