diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 778ff0a48..e9796f6f0 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -11,6 +11,9 @@ on: - main - develop +env: + INFINIGEN_INSTALL_RUNBUILD: False # disable building terrain, opengl and fluids, which wont be tested by pytest + jobs: checks: @@ -20,21 +23,23 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Lint with ruff + run: | + pip install ruff + # stop the build if there are Python syntax errors or undefined names + ruff --format=github --select=E9,F63,F7,F82 . + # default set of ruff rules with GitHub Annotations + #ruff --format=github . # to be enabled in a future PR + - name: Set up Python 3.10 uses: actions/setup-python@v4 with: python-version: "3.10" - - name: Install dependencies + - name: Install infinigen & dependencies run: | pip install .[dev] - - name: Lint with ruff - run: | - # stop the build if there are Python syntax errors or undefined names - ruff --format=github --select=E9,F63,F7,F82 . - # default set of ruff rules with GitHub Annotations - #ruff --format=github . # to be enabled in a future PR - name: Test with pytest run: | pytest tests --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html \ No newline at end of file diff --git a/.gitignore b/.gitignore index bc84a6572..5148b1ace 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,6 @@ Blender-FLIP-Fluids *.obj *.blend *.blend1 -*.in *.out profiles_*.npz *outputs @@ -65,3 +64,6 @@ dist build blends *.ipynb_checkpoints + +process_mesh +worldgen diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 000000000..0798a9d04 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,15 @@ +# Inspired by https://github.com/pytorch/pytorch/blob/main/MANIFEST.in + +# Explanation: files denoted here are excluded from `python setup.py sdist`, +# but some are still explicitly included in the .whl via + +recursive-include infinigen *.* +recursive-include tests *.* +recursive-include docs *.* +recursive-include infinigen_examples *.* + +prune */__pycache__ +prune infinigen/datagen/customgt/build/* +prune infinigen/datagen/customgt/dependencies/* + +global-exclude *.o *.so *.pyc .git *.png \ No newline at end of file diff --git a/Makefile b/Makefile index fbabf0f12..f60e573be 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,19 @@ -build_terrain: +cleanpip: + rm -rf *.egg-info + rm -rf build + +clean_terrain: rm -rf infinigen/terrain/*.egg-info rm -rf infinigen/terrain/__pycache__ rm -rf infinigen/terrain/build + +terrain: clean_terrain bash infinigen/tools/install/compile_terrain.sh -build_custom_groundtruth: +customgt: bash ./infinigen/tools/install/compile_opengl.sh -build_flip_fluids: +flip_fluids: bash ./infinigen/tools/install/compile_flip_fluids.sh DOCKER_BUILD_PROGRESS ?= auto diff --git a/README.md b/README.md index 3e5d6df35..8c848c5d3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ + + ## [Infinigen: Infinite Photorealistic Worlds using Procedural Generation](https://infinigen.org) Please visit our website, [https://infinigen.org](https://infinigen.org) diff --git a/docs/ConfiguringInfinigen.md b/docs/ConfiguringInfinigen.md index 5563b380c..bd9f59db7 100644 --- a/docs/ConfiguringInfinigen.md +++ b/docs/ConfiguringInfinigen.md @@ -7,14 +7,14 @@ This document explains how to configure various features of Infinigen. It assume ## Overview Generating scenes with Infinigen typically involves two main python scripts: -1. [examples/generate_nature.py](../examples/generate_nature.py) - our example scene composition script, which invokes and places assets to create a realistic nature scene. +1. [infinigen_examples/generate_nature.py](../infinigen_examples/generate_nature.py) - our example scene composition script, which invokes and places assets to create a realistic nature scene. 1. [manage_jobs.py](../infinigen/datagen/manage_jobs.py) - a script which invokes the above scene composition script many times to generate a useful dataset. -`manage_jobs.py` controls how many and what jobs will be run, and `examples/generate_nature.py` determines what will happen during those jobs. Ultimately both programs must be configured correctly in order to make useful data. +`manage_jobs.py` controls how many and what jobs will be run, and `infinigen_examples/generate_nature.py` determines what will happen during those jobs. Ultimately both programs must be configured correctly in order to make useful data. ### Scene tasks -To complete one Infinigen scene, `manage_jobs.py` will run several "tasks", each composed of a single execution of `examples/generate_nature.py`(You ran similar processes yourself in the step by step part of "Hello World"). +To complete one Infinigen scene, `manage_jobs.py` will run several "tasks", each composed of a single execution of `infinigen_examples/generate_nature.py`(You ran similar processes yourself in the step by step part of "Hello World"). Typically, these tasks are as follows: 1. `coarse`, which generates coarse terrain shape, puts placeholders for creatures and large trees / obstacles, and generates all small assets (twigs, particles, etc) that will be "instanced", IE scattered across the terrain with repetition. @@ -31,15 +31,15 @@ Infinigen is designed to run many independent scenes in paralell. This means tha #### Overrides and Config Files -Both `manage_jobs.py` and `examples/generate_nature.py` can be configured via the commandline or config files, using [Google's "Gin Config"](https://github.com/google/gin-config). Gin allows you to insert new default keyword arguments ("kwargs") for any function decorated with `@gin.configurable`; many such functions exist in our codebase, and via gin overrides you can create datsets suiting many diverse applications, as is explained in the coming sections. +Both `manage_jobs.py` and `infinigen_examples/generate_nature.py` can be configured via the commandline or config files, using [Google's "Gin Config"](https://github.com/google/gin-config). Gin allows you to insert new default keyword arguments ("kwargs") for any function decorated with `@gin.configurable`; many such functions exist in our codebase, and via gin overrides you can create datsets suiting many diverse applications, as is explained in the coming sections. -To use gin, simply add commandline arguments such as `-p compose_scene.rain_particles_chance = 1.0` to override the chance of rain, or `--pipeline_overrides iterate_scene_tasks.frame_range=[1,25]` to set a video's length to 24 frames. You can chain many statements together, separated by spaces, to configure many parts of the system at once. These statements depend on knowing the python names of the function and keyword argument you wish to override. To find parameters you wish to override, you should browse `examples/configs/base.gin` and other configs, or `examples/generate_nature.py` and the definitions of any functions it calls. Better documentation and organization of the available parameters will come in future versions. +To use gin, simply add commandline arguments such as `-p compose_scene.rain_particles_chance = 1.0` to override the chance of rain, or `--pipeline_overrides iterate_scene_tasks.frame_range=[1,25]` to set a video's length to 24 frames. You can chain many statements together, separated by spaces, to configure many parts of the system at once. These statements depend on knowing the python names of the function and keyword argument you wish to override. To find parameters you wish to override, you should browse `infinigen_examples/configs/base.gin` and other configs, or `infinigen_examples/generate_nature.py` and the definitions of any functions it calls. Better documentation and organization of the available parameters will come in future versions. -If you find a useful and related combination of these commandline overrides, you can write them into a `.gin` file in `examples/configs`. Then, to load that config, just include the name of the file into the `--configs`. If your overrides target `manage_jobs` rather than `examples/generate_nature.py` you should place the config file in `datagen/configs` and use `--pipeline_configs` rather than `--configs`. +If you find a useful and related combination of these commandline overrides, you can write them into a `.gin` file in `infinigen_examples/configs`. Then, to load that config, just include the name of the file into the `--configs`. If your overrides target `manage_jobs` rather than `infinigen_examples/generate_nature.py` you should place the config file in `datagen/configs` and use `--pipeline_configs` rather than `--configs`. -Our `examples/generate_nature.py` driver always loads [`examples/configs/base.gin`][../examples/configs/base.gin], and you can inspect / modify this file to see many common and useful gin override options. +Our `infinigen_examples/generate_nature.py` driver always loads [`infinigen_examples/configs/base.gin`][../infinigen_examples/configs/base.gin], and you can inspect / modify this file to see many common and useful gin override options. -`examples/generate_nature.py` also expects that one file from (configs/scene_types/)[examples/configs/scene_types] will be loaded. These scene_type configs contain gin overrides designed to encode the semantic constraints of real natural habitats (e.g. `examples/configs/scene_types/desert.gin` causes sand to appear and cacti to be more likely). +`infinigen_examples/generate_nature.py` also expects that one file from (configs/scene_types/)[infinigen_examples/configs/scene_types] will be loaded. These scene_type configs contain gin overrides designed to encode the semantic constraints of real natural habitats (e.g. `infinigen_examples/configs/scene_types/desert.gin` causes sand to appear and cacti to be more likely). ### Moving beyond "Hello World" @@ -57,7 +57,7 @@ Here is a breakdown of what every commandline argument does, and ideas for how y - `--specific_seed 0` forces the system to use a random seed of your choice, rather than choosing one at random. Change this seed to get a different random variation, or remove it to have the program choose a seed at random - `--num_scenes` decides how many unique scenes the program will attempt to generate before terminating. Once you have removed `--specific_seed`, you can increase this to generate many scenes in sequence or in paralell. - `--configs desert.gin simple.gin` forces the command to generate a desert scene, and to do so with relatively low mesh detail, low render resolution, low render samples, and some asset types disabled. - - Do `--configs snowy_mountain.gin simple.gin` to try out a different scene type (`snowy_mountain.gin` can instead be any scene_type option from `examples/configs/scene_types/`) + - Do `--configs snowy_mountain.gin simple.gin` to try out a different scene type (`snowy_mountain.gin` can instead be any scene_type option from `infinigen_examples/configs/scene_types/`) - Remove the `desert.gin` and just specify `--configs simple.gin` to use random scene types according to the weighted list in `datagen/configs/base.gin`. - You have the option of removing `simple.gin` and specify neither of the original configs. This turns off the many detail-reduction options included in `simple.gin`, and will create scenes closer to those in our intro video, albeit at significant compute costs. Removing `simple.gin` will likely cause crashes unless using a workstation/server with large amounts of RAM and VRAM. You can find more details on optimizing scene content for performance [here](#config-overrides-for-mesh-detail-and-performance). - `--pipeline_configs local_16GB.gin monocular.gin blender_gt.gin` @@ -107,22 +107,22 @@ Generating a video, stereo or other dataset typically requires more render jobs, To create longer videos, modify `iterate_scene_tasks.frame_range` in `monocular_video.gin` (note: we use 24fps video by default). `iterate_scene_tasks.view_block_size` controls how many frames will be grouped into each `fine_terrain` and render / ground-truth task. -If you need more than two cameras, or want to customize their placement, see `examples/configs/base.gin`'s `camera.spawn_camera_rigs.camera_rig_config` for advice on existing options, or write your own code to instantiate a custom camera setup. +If you need more than two cameras, or want to customize their placement, see `infinigen_examples/configs/base.gin`'s `camera.spawn_camera_rigs.camera_rig_config` for advice on existing options, or write your own code to instantiate a custom camera setup. ### Config Overrides to Customize Scene Content :bulb: If you only care about few specific assets, or want to export Infinigen assets to another project, instead see [Generating individual assets](GeneratingIndividualAssets.md). -You can achieve a great deal of customization by browsing and editing `examples/configs/base.gin` - e.g. modifying cameras, lighting, asset placement, etc. +You can achieve a great deal of customization by browsing and editing `infinigen_examples/configs/base.gin` - e.g. modifying cameras, lighting, asset placement, etc. - `base.gin` only provides the default values of these configs, and may be overridden by scene_type configs. To apply a setting globally across all scene types, you should put them in a new config placed at the end of your `--configs` argument (so that it's overrides are applied last), or use commandline overrides. -However, many options exist which are not present in base.gin. At present, you must browse `examples/generate_nature.py` to find the part of the code you wish to customize, and look through the relevant code for what more advanced @gin.configurable functions are available. You can also add @gin.configurable to most functions to allow additional configuration. More documentation on available parameters is coming soon. +However, many options exist which are not present in base.gin. At present, you must browse `infinigen_examples/generate_nature.py` to find the part of the code you wish to customize, and look through the relevant code for what more advanced @gin.configurable functions are available. You can also add @gin.configurable to most functions to allow additional configuration. More documentation on available parameters is coming soon. -For most steps of `examples/generate_nature.py`'s `compose_scene` function, we use our `RandomStageExecutor` wrapper to decide whether the stage is run, and handle other bookkeeping. This means that if you want to decide the probability with which some asset is included in a scene, you can use the gin override `compose_scene.trees_chance=1.0` or something similar depending on the name string provided as the first argument of the relevant run_stage calls in this way, e.g. `compose_scene.rain_particles_chance=0.9`to make most scenes rainy, or `compose_scene.flowers_chance=0.1` to make flowers rarer. +For most steps of `infinigen_examples/generate_nature.py`'s `compose_scene` function, we use our `RandomStageExecutor` wrapper to decide whether the stage is run, and handle other bookkeeping. This means that if you want to decide the probability with which some asset is included in a scene, you can use the gin override `compose_scene.trees_chance=1.0` or something similar depending on the name string provided as the first argument of the relevant run_stage calls in this way, e.g. `compose_scene.rain_particles_chance=0.9`to make most scenes rainy, or `compose_scene.flowers_chance=0.1` to make flowers rarer. -A common request is to just turn off things you don't want to see, which can be achieved by adding `compose_scene.trees_chance=0.0` or similar to your `-p` argument or a loaded config file. To conveniently turn off lots of things at the same time, we provide configs in `examples/configs/disable_assets` to disable things like all creatures, or all particles. +A common request is to just turn off things you don't want to see, which can be achieved by adding `compose_scene.trees_chance=0.0` or similar to your `-p` argument or a loaded config file. To conveniently turn off lots of things at the same time, we provide configs in `infinigen_examples/configs/disable_assets` to disable things like all creatures, or all particles. -You will also encounter configs using what we term a "registry pattern", e.g. `examples/configs/base_surface_registry.gin`'s `ground_collection`. "Registries", in this project, are a list of discrete generators, with weights indicating how relatively likely they are to be chosen each time the registry is sampled. +You will also encounter configs using what we term a "registry pattern", e.g. `infinigen_examples/configs/base_surface_registry.gin`'s `ground_collection`. "Registries", in this project, are a list of discrete generators, with weights indicating how relatively likely they are to be chosen each time the registry is sampled. - For example, in `base_surface_registry.gin`, `surface.registry.beach` specifies `("sand", 10)` to indicate that sand has high weight to be chosen to be assigned for the beach category. - Weights are normalized by their overall sum to obtain a probability distribution. - Name strings undergo lookup in the relevant source code folders, e.g. the name "sand" in a surface registry maps to `infinigen/assets/materials/sand.py`. @@ -143,7 +143,7 @@ Infinigen curbs memory costs by only populating assets up to a certain distance - Similarly to the above, `compose_scene.near_distance` controls the maximum distance to scatter tiny particles like pine needles. - Infinigen does not populate assets which are far outside the camera frustrum. You may attempt to reduce camera FOV to minimize how many assets are in view, but be warned there will be minimal or significantly diminishing returns on performance, due to the need to keep out-of-view assets loaded to retain accurate lighting/shadows. -We also provide `examples/configs/performance/dev.gin`, a config which sets many of the above performance parameters to achieve lower scenes. We often use this config to obtain previews for development purposes, but it may also be suitable for generating lower resolution images/scenes for some tasks. +We also provide `infinigen_examples/configs/performance/dev.gin`, a config which sets many of the above performance parameters to achieve lower scenes. We often use this config to obtain previews for development purposes, but it may also be suitable for generating lower resolution images/scenes for some tasks. Our current system determines asset mesh resolution based on the _closest distance_ it comes to the camera during an entire trajectory. Therefore, longer videos are more expensive, as more assets will be closer to the camera at some point in the trajectory. Options exist to re-generate assets at new resolutions over the course of a video to curb these costs - please make a Github Issue for advice. @@ -152,7 +152,7 @@ If you find yourself bottlenecked by GPU time, you should consider the following - Reduce `base.gin`'s `full/render_image.num_samples = 8192` or `compose_scene.generate_resolution = (1920, 1080)`. This proportionally reduces rendering FLOPS, with some diminishing returns due to BVH setup time. - If your GPU(s) are _underutilized_, try the reverse of these tips. -Some scene type configs are also generally more expensive than others. `forest.gin` and `coral.gin` are very expensive due to dense detailed fauna, wheras `artic` and `snowy_mountain` are very cheap. Low-resource compute settings (<64GB) of RAM may only be able to handle a subset of our `examples/configs/scene_type/` options, and you may wish to tune the ratios of scene_types by editing `datagen/configs/base.gin` or otherwise overriding `sample_scene_spec.config_distribution`. +Some scene type configs are also generally more expensive than others. `forest.gin` and `coral.gin` are very expensive due to dense detailed fauna, wheras `artic` and `snowy_mountain` are very cheap. Low-resource compute settings (<64GB) of RAM may only be able to handle a subset of our `infinigen_examples/configs/scene_type/` options, and you may wish to tune the ratios of scene_types by editing `datagen/configs/base.gin` or otherwise overriding `sample_scene_spec.config_distribution`. ### Other `manage_jobs.py` commandline options @@ -204,7 +204,7 @@ python -m infinigen.datagen.manage_jobs --output_folder outputs/my_videos --num_ --overrides compose_scene.rain_particles_chance=1.0 ``` -:bulb: You can substitute the `rain_particles` in `rain_particles_chance` for any `run_stage` name argument string in `examples/generate_nature.py`, such as `trees` or `ground_creatures`. +: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: ``` @@ -212,7 +212,7 @@ python -m infinigen.datagen.manage_jobs --output_folder outputs/my_videos --num_ --pipeline_config slurm monocular cuda_terrain opengl_gt \ --cleanup big_files --warmup_sec 30000 --config no_assets ``` -:bulb: You can substitute "no_assets" for `no_creatures` or `no_particles`, or the name of any file under `examples/configs`. The command shown uses `examples/configs/disable_assets/no_assets.gin`. +:bulb: You can substitute "no_assets" for `no_creatures` or `no_particles`, or the name of any file under `infinigen_examples/configs`. The command shown uses `infinigen_examples/configs/disable_assets/no_assets.gin`. Create videos at birds-eye-view camera altitudes: @@ -223,7 +223,7 @@ python -m infinigen.datagen.manage_jobs --output_folder outputs/my_videos --num_ --overrides camera.camera_pose_proposal.altitude=["uniform", 20, 30] ``` -:bulb: The command shown is overriding `examples/configs/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. +:bulb: The command shown is overriding `infinigen_examples/configs/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: ``` @@ -233,4 +233,4 @@ python -m infinigen.datagen.manage_jobs --output_folder outputs/my_videos --num_ --pipeline_overrides iterate_scene_tasks.frame_range=[1,25] ``` -:bulb: This command uses `--pipeline_overrides` rather than `--overrides` since it is providing a gin override to the `manage_jobs.py` process, not some part of the main `examples/generate_nature.py` driver. +:bulb: This command uses `--pipeline_overrides` rather than `--overrides` since it is providing a gin override to the `manage_jobs.py` process, not some part of the main `infinigen_examples/generate_nature.py` driver. diff --git a/docs/GroundTruthAnnotations.md b/docs/GroundTruthAnnotations.md index 4131d45be..9ee454926 100644 --- a/docs/GroundTruthAnnotations.md +++ b/docs/GroundTruthAnnotations.md @@ -70,7 +70,7 @@ Continuing the [Hello-World](/README.md#generate-a-scene-step-by-step) example, 4. Export the geometry from blender to disk ``` -python examples/generate_nature.py -- --seed 0 --task mesh_save -g desert simple --input_folder outputs/helloworld/fine --output_folder outputs/helloworld/saved_mesh +python infinigen_examples/generate_nature.py -- --seed 0 --task mesh_save -g desert simple --input_folder outputs/helloworld/fine --output_folder outputs/helloworld/saved_mesh ``` 5. Generate dense annotations ``` diff --git a/docs/HelloWorld.md b/docs/HelloWorld.md index 978f0410f..0cd0f426b 100644 --- a/docs/HelloWorld.md +++ b/docs/HelloWorld.md @@ -20,16 +20,16 @@ Infinigen generates scenes by running multiple tasks (usually executed automatic mkdir outputs # Generate a scene layout -python examples/generate_nature.py -- --seed 0 --task coarse -g desert.gin simple.gin --output_folder outputs/helloworld/coarse +python infinigen_examples/generate_nature.py -- --seed 0 --task coarse -g desert.gin simple.gin --output_folder outputs/helloworld/coarse # Populate unique assets -python examples/generate_nature.py -- --seed 0 --task populate fine_terrain -g desert.gin simple.gin --input_folder outputs/helloworld/coarse --output_folder outputs/helloworld/fine +python infinigen_examples/generate_nature.py -- --seed 0 --task populate fine_terrain -g desert.gin simple.gin --input_folder outputs/helloworld/coarse --output_folder outputs/helloworld/fine # Render RGB images -python examples/generate_nature.py -- --seed 0 --task render -g desert.gin simple.gin --input_folder outputs/helloworld/fine --output_folder outputs/helloworld/frames +python infinigen_examples/generate_nature.py -- --seed 0 --task render -g desert.gin simple.gin --input_folder outputs/helloworld/fine --output_folder outputs/helloworld/frames # Render again for accurate ground-truth -python examples/generate_nature.py -- --seed 0 --task render -g desert.gin simple.gin --input_folder outputs/helloworld/fine --output_folder outputs/helloworld/frames -p render.render_image_func=@flat/render_image +python infinigen_examples/generate_nature.py -- --seed 0 --task render -g desert.gin simple.gin --input_folder outputs/helloworld/fine --output_folder outputs/helloworld/frames -p render.render_image_func=@flat/render_image ``` Output logs should indicate what the code is working on. Use `--debug` for even more detail. After each command completes you can inspect it's `--output_folder` for results, including running `$BLENDER outputs/helloworld/coarse/scene.blend` or similar to view blender files. We hide many meshes by default for viewport stability; to view them, click "Render" or use the UI to unhide them. diff --git a/docs/ImplementingAssets.md b/docs/ImplementingAssets.md index 348e11bb8..15e952558 100644 --- a/docs/ImplementingAssets.md +++ b/docs/ImplementingAssets.md @@ -158,7 +158,7 @@ class MyAssetFactory(AssetFactory): You can implement the `create_asset` function however you wish so long as it produces a Blender Object as a result. Many existing assets use various different strategies, which you can use as examples: - `assets/flower.py` uses mostly auto-generated code from transpiling a hand-designed geometry node-graph. - `assets/grassland/grass_tuft.py` uses pure NumPy code to create and define a mesh. -- `assets/trees/examples/generate_nature.py` combines transpiled materials & leaves with a python-only space colonization algorithm. +- `assets/trees/infinigen_examples/generate_nature.py` combines transpiled materials & leaves with a python-only space colonization algorithm. The simplest implementation for a new asset is to create a geometry nodes equivelant, transpile it similarly to as shown above, copy the code into the same file as the template shown above, and implement the `create_asset` function as shown: diff --git a/examples/configs/performance/simple.gin b/examples/configs/performance/simple.gin deleted file mode 100644 index 724381975..000000000 --- a/examples/configs/performance/simple.gin +++ /dev/null @@ -1,5 +0,0 @@ -include 'examples/configs/performance/dev.gin' -include 'examples/configs/disable_assets/no_creatures.gin' -include 'examples/configs/performance/fast_terrain_assets.gin' -run_erosion.n_iters = [1,1] -full/render_image.num_samples = 64 \ No newline at end of file diff --git a/examples/scripts/hello_world_stepbystep.sh b/examples/scripts/hello_world_stepbystep.sh deleted file mode 100644 index 415f239a1..000000000 --- a/examples/scripts/hello_world_stepbystep.sh +++ /dev/null @@ -1,13 +0,0 @@ -# DO NOT MODIFY STRUCTURE OR COMMENTS: commands are read by test_hello_world.py - -# Generate a scene layout -python examples/generate_nature.py -- --seed 0 --task coarse -g desert.gin simple.gin --output_folder outputs/helloworld/coarse - -# Populate unique assets -python examples/generate_nature.py -- --seed 0 --task populate fine_terrain -g desert.gin simple.gin --input_folder outputs/helloworld/coarse --output_folder outputs/helloworld/fine - -# Render RGB images -python examples/generate_nature.py -- --seed 0 --task render -g desert.gin simple.gin --input_folder outputs/helloworld/fine --output_folder outputs/helloworld/frames - -# Render again for accurate ground-truth -python examples/generate_nature.py -- --seed 0 --task render -g desert.gin simple.gin --input_folder outputs/helloworld/fine --output_folder outputs/helloworld/frames -p render.render_image_func=@flat/render_image \ No newline at end of file diff --git a/examples/scripts/render_river_video.sh b/examples/scripts/render_river_video.sh deleted file mode 100644 index ad627d111..000000000 --- a/examples/scripts/render_river_video.sh +++ /dev/null @@ -1,11 +0,0 @@ -# TEMP. REMOVE BEFORE MERGE. - -HOSTFIRST=$(hostname | tr "." "\n" | head -n 1) -JOBNAME=$(date '+%m_%d_%H_%M').$HOSTFIRST.$1 -if [ "$2" = "dev" ]; then - python -m infinigen.datagen.manage_jobs --output_folder $3/$JOBNAME --num_scenes 20 \ - --pipeline_config $1 monocular_video_river enable_gpu opengl_gt --wandb_mode online --cleanup none --warmup_sec 12000 --config trailer_river dev reuse_terrain_assets simulated_river -else - python -m infinigen.datagen.manage_jobs --output_folder $3/$JOBNAME --num_scenes 50 \ - --pipeline_config $1 monocular_video_river enable_gpu opengl_gt --wandb_mode online --cleanup big_files --warmup_sec 12000 --config trailer_river reuse_terrain_assets simulated_river high_quality_terrain -fi diff --git a/examples/scripts/render_video_fire.sh b/examples/scripts/render_video_fire.sh deleted file mode 100644 index 874d665e6..000000000 --- a/examples/scripts/render_video_fire.sh +++ /dev/null @@ -1,6 +0,0 @@ -# TEMP. REMOVE BEFORE MERGE. - -HOSTFIRST=$(hostname | tr "." "\n" | head -n 1) -JOBNAME=$(date '+%m_%d_%H_%M').$HOSTFIRST.$1 -python -m infinigen.datagen.manage_jobs --output_folder $3/$JOBNAME --num_scenes $2 \ - --pipeline_config $1 monocular_video enable_gpu opengl_gt --wandb_mode online --cleanup none --warmup_sec 10000 --config trailer high_quality_terrain reuse_terrain_assets use_on_the_fly_fire diff --git a/infinigen/__init__.py b/infinigen/__init__.py index 8cac5793a..48db5a42e 100644 --- a/infinigen/__init__.py +++ b/infinigen/__init__.py @@ -1,3 +1,9 @@ -import pkg_resources -from pathlib import Path -__version__ = pkg_resources.get_distribution(Path(__file__).parent.name).version \ No newline at end of file +import logging + +__version__ = "1.1.0" + +logging.basicConfig( + format='[%(asctime)s.%(msecs)03d] [%(name)s] [%(levelname)s] | %(message)s', + level=logging.INFO, + datefmt='%H:%M:%S' +) \ No newline at end of file diff --git a/infinigen/assets/creatures/bird.py b/infinigen/assets/creatures/bird.py index 94d3e7c0d..52377fd55 100644 --- a/infinigen/assets/creatures/bird.py +++ b/infinigen/assets/creatures/bird.py @@ -88,7 +88,7 @@ def duck_genome(mode): body_lrr = np.array((0.85, 0.25, 0.38)) * N(1, 0.2) * N(1, 0.2, 3) body_fac = parts.generic_nurbs.NurbsBody(prefix='body_bird', tags=['body', 'rigid'], var=U(0.3, 1)) body = genome.part(body_fac) - l = body_fac.params['length'].reshape(1) + l = body_fac.params['length'][0] tail = genome.part(parts.wings.BirdTail()) genome.attach(tail, body, coord=(0.2, 1, 0.5), joint=Joint(rest=(0, 170 * N(1, 0.1), 0))) @@ -163,7 +163,7 @@ def flying_bird_genome(mode): body_lrr = np.array((0.95, 0.13, 0.18)) * N(1.0, 0.05, size=(3,)) body = genome.part(parts.body.BirdBody({'length_rad1_rad2': body_lrr})) - l = body_lrr[:1] + l = body_lrr[0] tail = genome.part(parts.wings.FlyingBirdTail()) genome.attach(tail, body, coord=(U(0.08, 0.15), 1, 0.5), joint=Joint(rest=(0, 180 * N(1, 0.1), 0))) diff --git a/infinigen/assets/creatures/carnivore.py b/infinigen/assets/creatures/carnivore.py index f9cd91018..5ed21e341 100644 --- a/infinigen/assets/creatures/carnivore.py +++ b/infinigen/assets/creatures/carnivore.py @@ -111,7 +111,7 @@ def tiger_genome(): head_fac = parts.generic_nurbs.NurbsHead(prefix='head_carnivore', tags=['head'], var=0.5) head = genome.part(head_fac) - headl = head_fac.params['length'] + headl = head_fac.params['length'][0] head_length_rad1_rad2 = np.array((headl, 0.20, 0.18)) * N(1, 0.1, 3) jaw_pct = np.array((0.7, 0.55, 0.5)) diff --git a/infinigen/assets/creatures/parts/generic_nurbs.py b/infinigen/assets/creatures/parts/generic_nurbs.py index b3a08abab..c5f3f8a92 100644 --- a/infinigen/assets/creatures/parts/generic_nurbs.py +++ b/infinigen/assets/creatures/parts/generic_nurbs.py @@ -22,9 +22,10 @@ from infinigen.core.util.logging import Suppress from infinigen.assets.utils.tag import tag_object, tag_nodegroup -NURBS_BASE_PATH = Path('infinigen/assets/creatures/parts/nurbs_data/') -load_nurbs = lambda p: np.load(p)[..., :3] # strip W coordinate if present -ALL_NURBS = {p.stem: load_nurbs(p) for p in NURBS_BASE_PATH.iterdir()} +NURBS_BASE_PATH = Path(__file__).parent/'nurbs_data' +NURBS_KEYS = [p.stem for p in NURBS_BASE_PATH.iterdir()] +def load_nurbs(name:str): + return np.load(NURBS_BASE_PATH/(name + '.npy'))[..., :3] def decompose_nurbs_handles(handles): @@ -92,13 +93,13 @@ def sample_params(self, select=None): N = lambda u, v, d=1: np.random.normal(u, np.array(v) * self.var, d) - target_keys = [k for k in ALL_NURBS if self.prefix is None or k.startswith(self.prefix)] + target_keys = [k for k in NURBS_KEYS if self.prefix is None or k.startswith(self.prefix)] weights = part_util.random_convex_coord(target_keys, select=select, temp=self.temperature) if self.exps is not None: for k, exp in self.exps.items(): weights[k] = weights[k] ** exp - handles = sum(w * ALL_NURBS[k] for k, w in weights.items()) + handles = sum(w * load_nurbs(k) for k, w in weights.items()) decomp = decompose_nurbs_handles(handles) sz = N(1, 0.1) diff --git a/infinigen/assets/creatures/util/genome.py b/infinigen/assets/creatures/util/genome.py index 61116795c..c60ed5334 100644 --- a/infinigen/assets/creatures/util/genome.py +++ b/infinigen/assets/creatures/util/genome.py @@ -39,15 +39,15 @@ class Joint: stretch: float = 0 def __post_init__(self): - self.rest = np.array(self.rest, dtype=np.float) + self.rest = np.array(self.rest, dtype=float) assert self.rest.shape == (3,), self.rest if self.pose is not None: - self.pose = np.array(self.pose, dtype=np.float) + self.pose = np.array(self.pose, dtype=float) assert self.pose.shape == (3,), self.pose if self.bounds is not None: - self.bounds = np.array(self.bounds, dtype=np.float) + self.bounds = np.array(self.bounds, dtype=float) @dataclass @@ -61,7 +61,7 @@ class Attachment: smooth_rad: float = 0.0 def __post_init__(self): - self.coord = np.array(self.coord, dtype=np.float) + self.coord = np.array(self.coord, dtype=float) @dataclass diff --git a/infinigen/assets/fluid/fluid.py b/infinigen/assets/fluid/fluid.py index 96b08399e..bf42c2180 100644 --- a/infinigen/assets/fluid/fluid.py +++ b/infinigen/assets/fluid/fluid.py @@ -33,6 +33,11 @@ from infinigen.core.util import blender as butil +try: + bpy.ops.flip_fluid_operators.complete_installation() +except Exception as e: + logging.warning(f'Could not complete flip fluids installation {e}') + # find next available number for fluid cache folder def find_available_cache(cache_folder): Path(cache_folder).mkdir(parents=True, exist_ok=True) diff --git a/infinigen/assets/materials/grass_blade_texture.py b/infinigen/assets/materials/grass_blade_texture.py index 27ce9653e..b9aa2d47b 100644 --- a/infinigen/assets/materials/grass_blade_texture.py +++ b/infinigen/assets/materials/grass_blade_texture.py @@ -161,6 +161,8 @@ ((0.1329, 0.1812, 0.0395, 1.0), 0.9756), ((0.1195, 0.1651, 0.0319, 1.0), 1.0)] +pallettes = np.array([pallete1, pallete2, pallete3, pallete4, pallete5], dtype=object) + def shader_grass_texture_original(nw: NodeWrangler): # Code generated using version 2.4.3 of the node_transpiler @@ -195,7 +197,7 @@ def shader_grass_texture_original(nw: NodeWrangler): map_range_1 = nw.new_node(Nodes.MapRange, input_kwargs={0: uniform(), 3: object_info.outputs["Random"], 4: mix_1}) colorramp = nw.new_node(Nodes.ColorRamp, input_kwargs={'Fac': map_range_1.outputs["Result"]}) - pallete = np.random.choice([pallete1, pallete2, pallete3, pallete4, pallete5]) + pallete = np.random.choice(pallettes) np.random.shuffle(pallete) pallete = pallete[:np.random.randint(4, len(pallete))] for _ in range(len(pallete)-2): diff --git a/infinigen/assets/materials/simple_greenery.py b/infinigen/assets/materials/simple_greenery.py index 596eeaa55..6ea4eb814 100644 --- a/infinigen/assets/materials/simple_greenery.py +++ b/infinigen/assets/materials/simple_greenery.py @@ -3,10 +3,10 @@ # Authors: Alexander Raistrick - import bpy import mathutils from numpy.random import uniform as U, normal as N, randint + from infinigen.core.nodes.node_wrangler import Nodes, NodeWrangler from infinigen.core.nodes import node_utils from infinigen.core.nodes.color import color_category diff --git a/infinigen/core/execute_tasks.py b/infinigen/core/execute_tasks.py index ccbe0ae47..d77aa61a9 100644 --- a/infinigen/core/execute_tasks.py +++ b/infinigen/core/execute_tasks.py @@ -65,7 +65,7 @@ from infinigen.core.rendering.render import render_image from infinigen.core.rendering.resample import resample_scene from infinigen.assets.monocot import kelp -from infinigen.core import surface +from infinigen.core import surface, init from infinigen.core.util.organization import Task, Attributes, TerrainNames @@ -82,24 +82,10 @@ ) from infinigen.core.util.math import FixedSeed, int_hash -from infinigen.core.util.logging import Timer, save_polycounts, create_text_file, Suppress +from infinigen.core.util.logging import Timer, save_polycounts, create_text_file from infinigen.core.util.pipeline import RandomStageExecutor from infinigen.core.util.random import sample_registry from infinigen.assets.utils.tag import tag_system - -def sanitize_gin_override(overrides: list): - if len(overrides) > 0: - print("Overriden parameters:", overrides) - output = list(overrides) - for i, o in enumerate(overrides): - if ('=' in o) and not any((c in o) for c in "\"'[]"): - k, v = o.split('=') - try: - ast.literal_eval(v) - except: - if "@" not in v: - output[i] = f'{k}="{v}"' - return output @gin.configurable def populate_scene( @@ -276,7 +262,7 @@ def save_meshes(scene_seed, output_folder, frame_range, resample_idx=False): def validate_version(scene_version): if scene_version is None or scene_version.split('.')[:-1] != infinigen.__version__.split('.')[:-1]: raise ValueError( - f'examples/generate_nature.py {infinigen.__version__=} attempted to load a scene created by version {scene_version=}') + f'infinigen_examples/generate_nature.py {infinigen.__version__=} attempted to load a scene created by version {scene_version=}') if scene_version != infinigen.__version__: logging.warning(f'{infinigen.__version__=} has minor version mismatch with {scene_version=}') @@ -330,28 +316,13 @@ def execute_tasks( bpy.context.scene.frame_start = int(frame_range[0]) bpy.context.scene.frame_end = int(frame_range[1]) bpy.context.scene.frame_set(int(frame_range[0])) - bpy.context.view_layer.update() - - surface.registry.initialize_from_gin() - - for name in ['ant_landscape', 'real_snow', 'flip_fluids_addon']: - try: - with Suppress(): - bpy.ops.preferences.addon_enable(module=name) - except Exception: - logging.warning(f'Could not load addon "{name}"') - - bpy.context.preferences.system.scrollback = 0 - bpy.context.preferences.edit.undo_steps = 0 bpy.context.scene.render.resolution_x = generate_resolution[0] bpy.context.scene.render.resolution_y = generate_resolution[1] - bpy.context.scene.render.engine = 'CYCLES' - bpy.context.scene.cycles.device = 'GPU' - - bpy.context.scene.cycles.volume_step_rate = 0.1 - bpy.context.scene.cycles.volume_preview_step_rate = 0.1 - bpy.context.scene.cycles.volume_max_steps = 32 + bpy.context.view_layer.update() + surface.registry.initialize_from_gin() + init.configure_blender() + if Task.Coarse in task: butil.clear_scene(targets=[bpy.data.objects]) butil.spawn_empty(f'{infinigen.__version__=}') @@ -421,67 +392,6 @@ def execute_tasks( frame_range=frame_range, ) - -def determine_scene_seed(args): - - if args.seed is None: - if Task.Coarse not in args.task: - raise ValueError( - 'Running tasks on an already generated scene, you need to specify --seed or results will' - ' not be view-consistent') - return randint(1e7), 'chosen at random' - - # WARNING: Do not add support for decimal numbers here, it will cause ambiguity, as some hex numbers are valid decimals - - try: - return int(args.seed, 16), 'parsed as hexadecimal' - except ValueError: - pass - - return int_hash(args.seed), 'hashed string to integer' - -def apply_scene_seed(args): - scene_seed, reason = determine_scene_seed(args) - logging.info(f'Converted {args.seed=} to {scene_seed=}, {reason}') - gin.constant('OVERALL_SEED', scene_seed) - del args.seed - - random.seed(scene_seed) - np.random.seed(scene_seed) - return scene_seed - -@gin.configurable -def apply_gin_configs( - args, - configs_folder: Path, - skip_unknown=False, - mandatory_config_dir: Path =None, -): - - if mandatory_config_dir is not None: - assert mandatory_config_dir.exists() - scene_types = [p.stem for p in mandatory_config_dir.iterdir()] - scenetype_specified = any(s in scene_types or s.split('.')[0] in scene_types for s in args.configs) - - if not scenetype_specified: - print(scene_types) - raise ValueError( - f"Please load one or more config from {mandatory_config_dir} using --configs to avoid unexpected behavior. " - "If you are sure you want to proceed without, override `apply_gin_configs.mandatory_config_dir=None`" - ) - - def find_config(g): - for p in configs_folder.glob('**/*.gin'): - if p.parts[-1] == g: - return p - if p.parts[-1] == f'{g}.gin': - return p - raise ValueError(f'Couldn not locate {g} or {g}.gin in anywhere config/**') - - bindings = sanitize_gin_override(args.overrides) - confs = [find_config(g) for g in ['base.gin'] + args.configs] - gin.parse_config_files_and_bindings(confs, bindings=bindings, skip_unknown=skip_unknown) - def main( input_folder, output_folder, diff --git a/infinigen/core/init.py b/infinigen/core/init.py new file mode 100644 index 000000000..a769e05e8 --- /dev/null +++ b/infinigen/core/init.py @@ -0,0 +1,163 @@ +# Copyright (c) Princeton University. +# This source code is licensed under the BSD 3-Clause license found in the LICENSE file in the root directory of this source tree. +import bpy + +import argparse +import ast +import os +import random +import sys +import cProfile +import shutil +from pathlib import Path +import logging +from functools import partial +import pprint +from collections import defaultdict + +# ruff: noqa: F402 +os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "1" # This must be done BEFORE import cv2. +# See https://github.com/opencv/opencv/issues/21326#issuecomment-1008517425 + +import gin +import numpy as np +from numpy.random import randint + +from infinigen.core.util.math import int_hash +from infinigen.core.util.organization import Task +from infinigen.core.util.logging import Suppress + +def parse_seed(seed, task=None): + + if seed is None: + if task is not None and Task.Coarse not in task: + raise ValueError( + 'Running tasks on an already generated scene, you need to specify --seed or results will' + ' not be view-consistent') + return randint(1e7), 'chosen at random' + + # WARNING: Do not add support for decimal numbers here, it will cause ambiguity, as some hex numbers are valid decimals + + try: + return int(seed, 16), 'parsed as hexadecimal' + except ValueError: + pass + + return int_hash(seed), 'hashed string to integer' + +def apply_scene_seed(seed, task=None): + scene_seed, reason = parse_seed(seed, task) + logging.info(f'Converted {seed=} to {scene_seed=}, {reason}') + gin.constant('OVERALL_SEED', scene_seed) + random.seed(scene_seed) + np.random.seed(scene_seed) + return scene_seed + +def sanitize_override(override: list): + + if ( + ('=' in override) and + not any((c in override) for c in "\"'[]") + ): + k, v = override.split('=') + try: + ast.literal_eval(v) + except ValueError: + if "@" not in v: + override = f'{k}="{v}"' + + return override + +def repo_root(): + return Path(__file__).parent.parent.parent + +def contains_any(filenames, folder): + if not folder.exists(): + return False + names = [p.stem for p in folder.iterdir()] + return any(s.stem in names or s.name in names for s in map(Path, filenames)) + +def mandatory_config_dir_satisfied(mandatory_folder, root, configs): + mandatory_folder = Path(mandatory_folder) + mandatory_folder_rel = root/mandatory_folder + + if not (mandatory_folder.exists() or mandatory_folder_rel.exists()): + raise FileNotFoundError(f'Could not find {mandatory_folder} or {mandatory_folder_rel}') + + return ( + contains_any(configs, mandatory_folder) or + contains_any(configs, mandatory_folder_rel) + ) + +@gin.configurable +def apply_gin_configs( + configs_folder: Path, + configs: list[str] = None, + overrides: list[str] = None, + skip_unknown=False, + mandatory_folders: Path = None, +): + + if configs is None: + configs = [] + if overrides is None: + overrides = [] + if mandatory_folders is None: + mandatory_folders = [] + configs_folder = Path(configs_folder) + + root = repo_root() + print(root) + gin.add_config_file_search_path(root) + + configs_folder_rel = root/configs_folder + if configs_folder_rel.exists(): + configs_folder = configs_folder_rel + gin.add_config_file_search_path(configs_folder) + elif configs_folder.exists(): + gin.add_config_file_search_path(configs_folder) + else: + raise FileNotFoundError(f'Couldnt find {configs_folder} or {configs_folder_rel}') + + for p in mandatory_folders: + if not mandatory_config_dir_satisfied(p, root, configs): + raise ValueError( + f"Please load one or more config from {p} to avoid unexpected behavior." + ) + + search_paths = [configs_folder, root, Path('.')] + + def find_config(p): + p = Path(p) + try: + return next( + file + for folder in search_paths + for file in folder.glob('**/*.gin') + if file.stem == p.stem + ) + except StopIteration: + raise FileNotFoundError(f'Could not find {p} or {p.stem} in any of {search_paths}') + + gin.parse_config_files_and_bindings( + [find_config(g) for g in ['base.gin'] + configs], + bindings=[sanitize_override(o) for o in overrides], + skip_unknown=skip_unknown + ) + +def configure_blender(): + bpy.context.preferences.system.scrollback = 0 + bpy.context.preferences.edit.undo_steps = 0 + bpy.context.scene.render.engine = 'CYCLES' + bpy.context.scene.cycles.device = 'GPU' + + bpy.context.scene.cycles.volume_step_rate = 0.1 + bpy.context.scene.cycles.volume_preview_step_rate = 0.1 + bpy.context.scene.cycles.volume_max_steps = 32 + + for name in ['ant_landscape', 'real_snow', 'flip_fluids_addon']: + try: + with Suppress(): + bpy.ops.preferences.addon_enable(module=name) + except Exception: + logging.warning(f'Could not load addon "{name}"') \ No newline at end of file diff --git a/infinigen/core/nodes/node_transpiler/transpiler.py b/infinigen/core/nodes/node_transpiler/transpiler.py index e51fde7b5..3150d7091 100644 --- a/infinigen/core/nodes/node_transpiler/transpiler.py +++ b/infinigen/core/nodes/node_transpiler/transpiler.py @@ -107,7 +107,7 @@ def represent_default_value(val, simple=True): elif isinstance(val, (tuple, bpy.types.bpy_prop_array, mathutils.Vector, mathutils.Euler)): code = represent_tuple(tuple(val)) elif isinstance(val, bpy.types.Collection): - logging.warn(f'Encountered collection {repr(val.name)} as a default_value - please edit the code to remove this dependency on a collection already existing') + logging.warning(f'Encountered collection {repr(val.name)} as a default_value - please edit the code to remove this dependency on a collection already existing') code = f'bpy.data.collections[{repr(val.name)}]' elif isinstance(val, bpy.types.Material): if val.use_nodes: @@ -115,10 +115,10 @@ def represent_default_value(val, simple=True): new_transpiler_targets[funcname] = val code = f'surface.shaderfunc_to_material({funcname})' else: - logging.warn(f'Encountered material {val} but it has use_nodes=False') + logging.warning(f'Encountered material {val} but it has use_nodes=False') code = repr(val) elif val is None: - logging.warn('Transpiler introduced a None into result script, this may not have been intended by the user') + logging.warning('Transpiler introduced a None into result script, this may not have been intended by the user') code = 'None' else: raise ValueError(f'represent_default_value was unable to handle {val=} with type {type(val)}, please contact the developer') @@ -396,10 +396,10 @@ def update_inputs(i, k, v): for link in links: if not link.from_socket.enabled: - logging.warn(f'Transpiler encountered link from disabled socket {link.from_socket}, ignoring it') + logging.warning(f'Transpiler encountered link from disabled socket {link.from_socket}, ignoring it') continue if not link.to_socket.enabled: - logging.warn(f'Transpiler encountered link to disabled socket {link.to_socket}, ignoring it') + logging.warning(f'Transpiler encountered link to disabled socket {link.to_socket}, ignoring it') continue input_varname, input_code, targets = create_node( @@ -502,7 +502,7 @@ def get_nodetype_expression(node): return repr(node.node_tree.name) else: node_name = node.name.split('.')[0].replace(' ', '') - logging.warn( + logging.warning( f'Please add an alias for \"{id}\" in nodes.node_info.Nodes.' f'\n\t Suggestion: {node_name} = {repr(id)}' ) diff --git a/infinigen/core/placement/detail.py b/infinigen/core/placement/detail.py index a4df66fdc..2cb7ce544 100644 --- a/infinigen/core/placement/detail.py +++ b/infinigen/core/placement/detail.py @@ -21,7 +21,7 @@ logger = logging.getLogger('detail') -IS_COARSE = False # Global VARIABLE, set by examples/generate_nature.py and used only for whether to emit warnings +IS_COARSE = False # Global VARIABLE, set by infinigen_examples/generate_nature.py and used only for whether to emit warnings @gin.configurable def scatter_res_distance(dist=4): diff --git a/infinigen/core/surface.py b/infinigen/core/surface.py index 512590c1b..97c9dc44d 100644 --- a/infinigen/core/surface.py +++ b/infinigen/core/surface.py @@ -372,8 +372,8 @@ def get_surface(name): ] for prefix in prefixes: try: - return importlib.import_module(prefix + '.' + name) - except ModuleNotFoundError: + return importlib.import_module('.' + name, prefix) + except ModuleNotFoundError as e: continue raise ValueError(f'Could not find {name=} in any of {prefixes}') diff --git a/infinigen/core/util/blender.py b/infinigen/core/util/blender.py index b3ef65745..c8f4a60a2 100644 --- a/infinigen/core/util/blender.py +++ b/infinigen/core/util/blender.py @@ -577,7 +577,7 @@ def apply_modifiers(obj, mod=None, quiet=True): bpy.ops.object.modifier_apply(modifier=m.name) except RuntimeError as e: if m.type == 'NODES': - logging.warn(f'apply_modifers on {obj.name=} {m.name=} raised {e}, ignoring and returning empty mesh for pre-3.5 compatibility reasons') + logging.warning(f'apply_modifers on {obj.name=} {m.name=} raised {e}, ignoring and returning empty mesh for pre-3.5 compatibility reasons') bpy.ops.object.modifier_remove(modifier=m.name) clear_mesh(obj) else: @@ -696,7 +696,7 @@ def object_to_vertex_attributes(obj): def object_to_trimesh(obj): verts_bpy = obj.data.vertices faces_bpy = obj.data.polygons - verts = np.zeros((len(verts_bpy) * 3), dtype=np.float) + verts = np.zeros((len(verts_bpy) * 3), dtype=float) verts_bpy.foreach_get("co", verts) faces = np.zeros((len(faces_bpy) * 3), dtype=np.int32) faces_bpy.foreach_get("vertices", faces) diff --git a/infinigen/core/util/math.py b/infinigen/core/util/math.py index 4cfafa960..af21ad72b 100644 --- a/infinigen/core/util/math.py +++ b/infinigen/core/util/math.py @@ -18,7 +18,8 @@ class FixedSeed: def __init__(self, seed): - self.seed = seed + + self.seed = int(seed) self.py_state = None self.np_state = None diff --git a/infinigen/core/util/random.py b/infinigen/core/util/random.py index c27bdd9f9..02b828b9b 100644 --- a/infinigen/core/util/random.py +++ b/infinigen/core/util/random.py @@ -7,7 +7,6 @@ from infinigen.core.nodes.color import color_category import gin import numpy as np -from infinigen.core.util.math import md5_hash, clip_gaussian import random import json import json5 @@ -16,10 +15,44 @@ from matplotlib import colors from numpy.random import normal, uniform +from infinigen.core.util.math import md5_hash, clip_gaussian +from infinigen.core.init import repo_root + def log_uniform(low, high, size=1): return np.exp(uniform(np.log(low), np.log(high), size)) +def sample_json_palette(pallette_name, n_sample=1): + + rel = f"infinigen_examples/configs/palette/{pallette_name}.json" + + with (repo_root()/rel).open('r') as f: + color_template = json5.load(f) + + colors = color_template["color"] + means = np.array(color_template["hsv"]) + stds = np.array(color_template["std"]) + probs = np.array(color_template["prob"]) + selected = np.zeros(len(means), dtype=bool) + for c in colors: + selected[int(c)] = 1 + means = means[selected] + stds = stds[selected] + probs = probs[selected] + i = np.random.choice(range(len(colors)), 1, p=probs / np.sum(probs))[0] + color_samples = [] + for j in range(n_sample): + color = np.array(means[i]) + np.matmul(np.array(stds[i]).reshape((3, 3)), np.clip(np.random.randn(3), a_min=-1, a_max=1)) + color[2] = max(min(color[2], 0.9), 0.1) + color = colorsys.hsv_to_rgb(*color) + color = np.clip(color, a_min=0, a_max=1) + color = np.where(color >= 0.04045,((color+0.055)/1.055) ** 2.4, color / 12.92) + color = np.concatenate((color, np.ones(1))) + color_samples.append(color) + if n_sample == 1: + return color + return color_samples + def random_general(var): if not (isinstance(var, tuple) or isinstance(var, list)): return var @@ -56,34 +89,7 @@ def random_general(var): elif func == "choice": return np.random.choice(args[0], 1, p=args[1])[0] elif func == "palette": - if len(args) == 1: - num_sample = 1 - else: - num_sample = args[1] - color_template = json5.load(open(f"examples/configs/palette/{args[0]}.json", "r")) - colors = color_template["color"] - means = np.array(color_template["hsv"]) - stds = np.array(color_template["std"]) - probs = np.array(color_template["prob"]) - selected = np.zeros(len(means), dtype=bool) - for c in colors: - selected[int(c)] = 1 - means = means[selected] - stds = stds[selected] - probs = probs[selected] - i = np.random.choice(range(len(colors)), 1, p=probs / np.sum(probs))[0] - color_samples = [] - for j in range(num_sample): - color = np.array(means[i]) + np.matmul(np.array(stds[i]).reshape((3, 3)), np.clip(np.random.randn(3), a_min=-1, a_max=1)) - color[2] = max(min(color[2], 0.9), 0.1) - color = colorsys.hsv_to_rgb(*color) - color = np.clip(color, a_min=0, a_max=1) - color = np.where(color >= 0.04045,((color+0.055)/1.055) ** 2.4, color / 12.92) - color = np.concatenate((color, np.ones(1))) - color_samples.append(color) - if len(args) == 1: - return color - return color_samples + return sample_json_palette(*args) elif func == "color_category": return color_category(*args) else: diff --git a/infinigen/datagen/configs/compute_platform/local_128GB.gin b/infinigen/datagen/configs/compute_platform/local_128GB.gin index 374dfab6e..bff783e2b 100644 --- a/infinigen/datagen/configs/compute_platform/local_128GB.gin +++ b/infinigen/datagen/configs/compute_platform/local_128GB.gin @@ -1,4 +1,4 @@ -include 'infinigen/datagen/configs/compute_platform/local_256GB.gin' +include 'compute_platform/local_256GB.gin' manage_datagen_jobs.num_concurrent=8 diff --git a/infinigen/datagen/configs/compute_platform/local_16GB.gin b/infinigen/datagen/configs/compute_platform/local_16GB.gin index 5150853b8..83041fdad 100644 --- a/infinigen/datagen/configs/compute_platform/local_16GB.gin +++ b/infinigen/datagen/configs/compute_platform/local_16GB.gin @@ -1,4 +1,4 @@ -include 'infinigen/datagen/configs/compute_platform/local_256GB.gin' +include 'compute_platform/local_256GB.gin' manage_datagen_jobs.num_concurrent=1 diff --git a/infinigen/datagen/configs/compute_platform/local_64GB.gin b/infinigen/datagen/configs/compute_platform/local_64GB.gin index ca98c6f5d..51c6baa03 100644 --- a/infinigen/datagen/configs/compute_platform/local_64GB.gin +++ b/infinigen/datagen/configs/compute_platform/local_64GB.gin @@ -1,4 +1,4 @@ -include 'infinigen/datagen/configs/compute_platform/local_256GB.gin' +include 'compute_platform/local_256GB.gin' manage_datagen_jobs.num_concurrent=3 diff --git a/infinigen/datagen/configs/compute_platform/slurm_1h.gin b/infinigen/datagen/configs/compute_platform/slurm_1h.gin index 6319f9a41..5a2189bf2 100644 --- a/infinigen/datagen/configs/compute_platform/slurm_1h.gin +++ b/infinigen/datagen/configs/compute_platform/slurm_1h.gin @@ -1,4 +1,4 @@ -include 'infinigen/datagen/configs/compute_platform/slurm.gin' +include 'compute_platform/slurm.gin' iterate_scene_tasks.view_block_size = 3 diff --git a/infinigen/datagen/configs/compute_platform/slurm_high_memory.gin b/infinigen/datagen/configs/compute_platform/slurm_high_memory.gin index 0f22426e3..b121518db 100644 --- a/infinigen/datagen/configs/compute_platform/slurm_high_memory.gin +++ b/infinigen/datagen/configs/compute_platform/slurm_high_memory.gin @@ -1,4 +1,4 @@ -include 'infinigen/datagen/configs/compute_platform/slurm.gin' +include 'compute_platform/slurm.gin' # Combined (only used when `stereo_combined.gin` or similar is included) queue_combined.mem_gb = 48 diff --git a/infinigen/datagen/configs/data_schema/monocular_flow.gin b/infinigen/datagen/configs/data_schema/monocular_flow.gin index f92f9e9d4..d55133bd9 100644 --- a/infinigen/datagen/configs/data_schema/monocular_flow.gin +++ b/infinigen/datagen/configs/data_schema/monocular_flow.gin @@ -1,4 +1,4 @@ -include 'infinigen/datagen/configs/data_schema/monocular.gin' +include 'data_schema/monocular.gin' iterate_scene_tasks.frame_range=(1, 2) diff --git a/infinigen/datagen/configs/data_schema/stereo_video.gin b/infinigen/datagen/configs/data_schema/stereo_video.gin index 48caec7f5..d0d755393 100644 --- a/infinigen/datagen/configs/data_schema/stereo_video.gin +++ b/infinigen/datagen/configs/data_schema/stereo_video.gin @@ -1,2 +1,2 @@ -include 'infinigen/datagen/configs/data_schema/monocular_video.gin' +include 'data_schema/monocular_video.gin' iterate_scene_tasks.cam_id_ranges = [1, 2] diff --git a/infinigen/datagen/configs/gt_options/opengl_gt_noshortrender.gin b/infinigen/datagen/configs/gt_options/opengl_gt_noshortrender.gin index f6023c87c..56416831c 100644 --- a/infinigen/datagen/configs/gt_options/opengl_gt_noshortrender.gin +++ b/infinigen/datagen/configs/gt_options/opengl_gt_noshortrender.gin @@ -1,4 +1,4 @@ -include 'infinigen/datagen/configs/opengl_gt.gin' # incase someone adds other settings to it +include 'opengl_gt.gin' # incase someone adds other settings to it iterate_scene_tasks.camera_dependent_tasks = [ {'name': 'renderbackup', 'func': @renderbackup/queue_render}, # still call it "backup" since it is reusing the compute_platform's backup config. we are just skipping straight to the backup diff --git a/infinigen/datagen/configs/opengl_gt_noshortrender.gin b/infinigen/datagen/configs/opengl_gt_noshortrender.gin new file mode 100644 index 000000000..56416831c --- /dev/null +++ b/infinigen/datagen/configs/opengl_gt_noshortrender.gin @@ -0,0 +1,7 @@ +include 'opengl_gt.gin' # incase someone adds other settings to it + +iterate_scene_tasks.camera_dependent_tasks = [ + {'name': 'renderbackup', 'func': @renderbackup/queue_render}, # still call it "backup" since it is reusing the compute_platform's backup config. we are just skipping straight to the backup + {'name': 'savemesh', 'func': @queue_mesh_save}, + {'name': 'opengl', 'func': @queue_opengl} +] \ No newline at end of file diff --git a/infinigen/datagen/tools/export/README.md b/infinigen/datagen/export/README.md similarity index 100% rename from infinigen/datagen/tools/export/README.md rename to infinigen/datagen/export/README.md diff --git a/__init__.py b/infinigen/datagen/export/__init__.py similarity index 100% rename from __init__.py rename to infinigen/datagen/export/__init__.py diff --git a/infinigen/datagen/tools/export/export.py b/infinigen/datagen/export/export.py similarity index 100% rename from infinigen/datagen/tools/export/export.py rename to infinigen/datagen/export/export.py diff --git a/infinigen/datagen/tools/export/install.sh b/infinigen/datagen/export/install.sh similarity index 100% rename from infinigen/datagen/tools/export/install.sh rename to infinigen/datagen/export/install.sh diff --git a/infinigen/datagen/job_funcs.py b/infinigen/datagen/job_funcs.py index 1ce515406..21d1951f3 100644 --- a/infinigen/datagen/job_funcs.py +++ b/infinigen/datagen/job_funcs.py @@ -28,8 +28,7 @@ def get_cmd( configs, taskname, output_folder, - blender_thread_limit=None, - driver_script='examples/generate_nature.py', + driver_script='infinigen_examples.generate_nature', # replace with a regular path to a .py, or another installed module input_folder=None, process_niceness=None, ): @@ -40,7 +39,12 @@ def get_cmd( cmd = '' if process_niceness is not None: cmd += f'nice -n {process_niceness} ' - cmd += f'python {driver_script} ' + cmd += 'python ' + + if driver_script.endswith('.py'): + cmd += driver_script + ' ' + else: + cmd += '-m ' + driver_script + ' ' # No longer supported using pip bpy #if blender_thread_limit is not None: diff --git a/infinigen/datagen/manage_jobs.py b/infinigen/datagen/manage_jobs.py index 3cbf5afc3..bdecd7ff7 100644 --- a/infinigen/datagen/manage_jobs.py +++ b/infinigen/datagen/manage_jobs.py @@ -63,10 +63,9 @@ queue_render, queue_upload ) -from .util.submitit_emulator import ScheduledLocalExecutor, ImmediateLocalExecutor, LocalScheduleHandler, LocalJob -from .util import upload_util -from .util.upload_util import upload_job_folder # for pickle not to freak out +import infinigen.core.init +from .util.cleanup import cleanup # used only if enabled in gin configs PARTITION_ENVVAR = 'INFINIGEN_SLURMPARTITION' @@ -284,8 +283,11 @@ def get_disk_usage(folder): return int(re.compile("[\s\S]* ([0-9]+)% [\s\S]*").fullmatch(out).group(1)) / 100 def make_html_page(output_path, scenes, frame, camera_pair_id, **kwargs): + + template_path = infinigen.core.init.repo_root()/"infinigen/datagen/util" + assert template_path.exists(), template_path env = Environment( - loader=FileSystemLoader("infinigen/datagen/tools"), + loader=FileSystemLoader(template_path), autoescape=select_autoescape(), ) @@ -323,12 +325,10 @@ def run_task( scene_dict[f'{taskname}_submitted'] = 1 return - seed = scene_dict['seed'] - job_obj, output_folder = queue_func( + seed=scene_dict['seed'], folder=scene_folder, name=stage_scene_name, - seed=seed, taskname=taskname ) scene_dict[f'{taskname}_job_obj'] = job_obj @@ -772,19 +772,14 @@ def main(args, shuffle=True, wandb_project='render', upload_commandfile_method=N random.seed(args.meta_seed) np.random.seed(args.meta_seed) - def find_config(g): - for p in Path('infinigen/datagen/configs').glob('**/*.gin'): - if p.parts[-1] == g: - return p - if p.parts[-1] == f'{g}.gin': - return p - raise ValueError( - f'Couldn not locate {g} or {g}.gin in anywhere pipeline_configs/**' - ) - configs = [find_config(n) for n in ['base.gin'] + args.pipeline_configs] - for c in configs: - assert os.path.exists(c), c - bindings = args.pipeline_overrides - gin.parse_config_files_and_bindings(configs, bindings=bindings) + infinigen.core.init.apply_gin_configs( + configs_folder=Path('infinigen/datagen/configs'), + configs=args.pipeline_configs, + overrides=args.pipeline_overrides, + mandatory_folders=[ + 'infinigen/datagen/configs/compute_platform', + 'infinigen/datagen/configs/data_schema' + ] + ) main(args) diff --git a/infinigen/datagen/tools/blendscript_import_infinigen.py b/infinigen/datagen/tools/blendscript_import_infinigen.py deleted file mode 100644 index dedb1c4df..000000000 --- a/infinigen/datagen/tools/blendscript_import_infinigen.py +++ /dev/null @@ -1,27 +0,0 @@ - - -''' -Copy this file into blender's scripting window and run it whenever you open a new blender instance. - -It will configure the sys.path and load gin. This is necessary before any other procgen files can be imported/used within blender. - -Once this is done, you can do things like `from assets.creatures.genomes.carnivore import CarnivoreFactory` then `CarnivoreFactory(0).spawn_asset(0)` directly in the blender commandline -''' - -import bpy -from pathlib import Path -import sys -import os - -# ruff: noqa -pwd = os.getcwd() -sys.path.append(pwd) - -import generate # so gin can find all its targets -import gin -gin.clear_config() -gin.enter_interactive_mode() - -gin.parse_config_files_and_bindings(['config/base.gin'], []) -from surfaces.surface import registry -registry.initialize_from_gin() \ No newline at end of file diff --git a/infinigen/datagen/tools/dev/landtile_viewer.py b/infinigen/datagen/tools/dev/landtile_viewer.py deleted file mode 100644 index d08235314..000000000 --- a/infinigen/datagen/tools/dev/landtile_viewer.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) Princeton University. -# This source code is licensed under the BSD 3-Clause license found in the LICENSE file in the root directory of this source tree. - -# Authors: Zeyu Ma - - -import os -import sys -import argparse - -import bpy -import numpy as np - -from infinigen.core.nodes import Nodes, NodeWrangler -from terrain.utils import Mesh, read -from infinigen.core.util.blender import clear_scene -from infinigen.core.util.organization import AssetFile - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('-i', '--input', type=str) - parser.add_argument('-o', '--overlay', type=int, default=False) - args = parser.parse_args(sys.argv[sys.argv.index("--") + 1:]) - folder = os.path.dirname(args.input) - tile_size = float(np.loadtxt(f"{folder}/{AssetFile.TileSize}.txt")) - image = read(args.input) - mesh = Mesh(heightmap=image, L=tile_size) - if args.overlay: - image = read(args.input.replace(AssetFile.Heightmap, AssetFile.Mask)) - mesh.vertex_attributes["attribute"] = image.reshape(-1).astype(np.float32) - clear_scene() - obj = mesh.export_blender("preview") - if args.overlay: - material = bpy.data.materials.new(name="preview_material") - material.use_nodes = True - nw = NodeWrangler(material.node_tree) - new_attribute_node = nw.new_node(Nodes.Attribute, [], {"attribute_name": "attribute"}) - material.node_tree.links.new(new_attribute_node.outputs['Color'], material.node_tree.nodes['Principled BSDF'].inputs['Base Color']) - obj.active_material = material diff --git a/infinigen/datagen/tools/dev/palette/readme.md b/infinigen/datagen/tools/dev/palette/readme.md deleted file mode 100644 index 0b510dc45..000000000 --- a/infinigen/datagen/tools/dev/palette/readme.md +++ /dev/null @@ -1,47 +0,0 @@ -# How to use - -This palette generating tool is enabled using `GoogleImagesSearch` (currently using Zeyu's API key). Also you need `json5`. - -## Step 1 - -``` -cd infinigen/tools/terrain/palette/ -# you can change "mountain rock" to other key words -python palette.py -k "mountain rock" -``` - -This downloads 10 images from Google by default: -![](demo1.png) - -and runs Mixed Gaussian Model (MGM) on HSV space to get an initial palette: -![](demo2.png) - -You can see the above palette in folder `images`. By default it has 10 centers, and the first row is the color of all centers, and the second row is the range of color when you vary it according to MGM with range (-std, std) (V dimension is ommited for 2D display) - -Some colors are background colors and do not belong to the keyword you search. - -## Step 2 - -Under folder `json`, there is a `mountain rock.json`. By installing Add-on `Flutter Color` and `JSON5 syntax` in VSCode, you can see the color icon. - -![](demo3.png) - -After manually comemnt out them, you have: - -![](demo4.png) - -Then you move the ready palatte to location: `infinigen/examples/configs/palette` - -## Step 3 - -When you want some color to sample from it, use the `palette` keyword in parameter parsing, e.g.: - -``` -surfaces.templates.water.shader.color = ("palette", "water") -surfaces.templates.ocean.shader.color = ("palette", "water") -surfaces.templates.mountain.shader.color = ("palette", "mountain rock") -surfaces.templates.sand.shader.color = ("palette", "desert") -surfaces.templates.sandstone.shader.color = ("palette", "sandstone") -``` - -Note that Blender uses lienar RGB while web content uses sRGB, so a conversion is done in the parsing function `random_general` \ No newline at end of file diff --git a/infinigen/datagen/tools/results/__init__.py b/infinigen/datagen/tools/results/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/infinigen/datagen/util/submitit_emulator.py b/infinigen/datagen/util/submitit_emulator.py index 9f68363f7..593190791 100644 --- a/infinigen/datagen/util/submitit_emulator.py +++ b/infinigen/datagen/util/submitit_emulator.py @@ -250,7 +250,7 @@ def get_all_processes(): if test == 'render': - random_command = "python examples/generate_nature.py -- --seed 56823 --task coarse -p main.output_folder=outputs/test2 move_camera.stereo_baseline=0.15 LOG_DIR=logs" + random_command = "python infinigen_examples/generate_nature.py -- --seed 56823 --task coarse -p main.output_folder=outputs/test2 move_camera.stereo_baseline=0.15 LOG_DIR=logs" executor = ScheduledLocalExecutor(folder="test_log") executor.update_parameters(gpus=1) diff --git a/infinigen/datagen/util/upload_util.py b/infinigen/datagen/util/upload_util.py index 9ba18cdc8..d13073b49 100644 --- a/infinigen/datagen/util/upload_util.py +++ b/infinigen/datagen/util/upload_util.py @@ -110,8 +110,11 @@ def get_commit_hash(): git = shutil.which('git') if git is None: return None - cmd = f"{git} rev-parse HEAD" - return subprocess.check_output(cmd.split()).decode().strip() + try: + cmd = f"{git} rev-parse HEAD" + return subprocess.check_output(cmd.split()).decode().strip() + except subprocess.CalledProcessError: + return None def write_metadata(parent_folder, seed, all_images): diff --git a/infinigen/terrain/assets/caves/pcfg.py b/infinigen/terrain/assets/caves/pcfg.py index b8e6a322c..e1d04800e 100644 --- a/infinigen/terrain/assets/caves/pcfg.py +++ b/infinigen/terrain/assets/caves/pcfg.py @@ -13,7 +13,7 @@ import time from itertools import chain -CONFIG_FILE = Path(f"{os.path.split(os.path.abspath(__file__))[0]}/cfg.txt") +CONFIG_FILE = Path(__file__).parent/'cfg.txt' assert CONFIG_FILE.exists(), CONFIG_FILE.resolve() STARTING_SYMBOL = 'Q' diff --git a/infinigen/terrain/assets/landtiles/ant_landscape.py b/infinigen/terrain/assets/landtiles/ant_landscape.py index 058f3b7a8..236d514df 100644 --- a/infinigen/terrain/assets/landtiles/ant_landscape.py +++ b/infinigen/terrain/assets/landtiles/ant_landscape.py @@ -56,7 +56,7 @@ def ant_landscape_asset( create(preset_name, N, N) obj = bpy.context.active_object N = int(len(obj.data.vertices) ** 0.5) - mverts_co = np.zeros((len(obj.data.vertices)*3), dtype=np.float) + mverts_co = np.zeros((len(obj.data.vertices)*3), dtype=float) obj.data.vertices.foreach_get("co", mverts_co) mverts_co = mverts_co.reshape((N, N, 3)) heightmap = cv2.resize(np.float32(mverts_co[..., -1]), (resolution, resolution)) * tile_size / 2 diff --git a/infinigen/terrain/utils/image_processing.py b/infinigen/terrain/utils/image_processing.py index 5c11e4e5c..31684c604 100644 --- a/infinigen/terrain/utils/image_processing.py +++ b/infinigen/terrain/utils/image_processing.py @@ -40,7 +40,7 @@ def read(input_heightmap_path): def grid_distance(source, downsample): M = source.shape[0] - source = cv2.resize(source.astype(np.float), (downsample, downsample)) > 0.5 + source = cv2.resize(source.astype(float), (downsample, downsample)) > 0.5 dist = np.zeros_like(source, dtype=np.float32) + 1e9 N = source.shape[0] I, J = np.meshgrid(np.arange(N), np.arange(N), indexing="ij") diff --git a/infinigen/terrain/utils/mesh.py b/infinigen/terrain/utils/mesh.py index adab067b2..da9706966 100644 --- a/infinigen/terrain/utils/mesh.py +++ b/infinigen/terrain/utils/mesh.py @@ -103,7 +103,7 @@ def __init__(self, normal_mode=NormalMode.Mean, elif obj is not None: verts_bpy = obj.data.vertices faces_bpy = obj.data.polygons - verts = np.zeros((len(verts_bpy)*3), dtype=np.float) + verts = np.zeros((len(verts_bpy)*3), dtype=float) verts_bpy.foreach_get("co", verts) verts = verts.reshape((-1, 3)) faces = np.zeros((len(faces_bpy)*3), dtype=np.int32) diff --git a/infinigen/tools/submit_asset_cache.py b/infinigen/tools/submit_asset_cache.py deleted file mode 100644 index a08a753cb..000000000 --- a/infinigen/tools/submit_asset_cache.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) Princeton University. -# This source code is licensed under the BSD 3-clause license found in the LICENSE file in the root directory of this source tree. - -# Authors: Karhan Kayan - -import submitit -import argparse -from pathlib import Path -import sys -import os -import time - -sys.path.append(str(Path(os.path.split(os.path.abspath(__file__))[0]) / "..")) - - -def get_slurm_banned_nodes(config_path=None): - if config_path is None: - return [] - with Path(config_path).open('r') as f: - return list(f.read().split()) - - -parser = argparse.ArgumentParser() -parser.add_argument('-b', '--blender_path', type=str) -parser.add_argument('-f', '--asset_folder', type=str) -parser.add_argument('-a', '--assets', nargs='+', default=[ - 'CachedBushFactory', - 'CachedTreeFactory', - 'CachedCactusFactory', - 'CachedCreatureFactory', - 'CachedBoulderFactory' -]) -parser.add_argument('-n', '--number', type=int, default=1) -parser.add_argument('-s', '--start_frame', type=int, default=-20) -parser.add_argument('-d', '--simulation_duration', type=int, default=24*20+20) -# parser.add_argument('-r', '--resolution', type=int) -# parser.add_argument('--dissolve_speed', type=int, default=25) -# parser.add_argument('--dom_scale', type=int, default=1) -args = parser.parse_args() - -Path(args.asset_folder).mkdir(parents=True, exist_ok=True) - - -for asset in args.assets: - for i in range(args.number): - cmd = f"{args.blender_path} --background -noaudio --python fluid/run_asset_cache.py -- -f {args.asset_folder}/ -a {asset} -s {args.start_frame} -d {args.simulation_duration}".split(" ") - print(cmd) - executor = submitit.AutoExecutor(folder=str(Path(args.asset_folder) / "logs")) - executor.update_parameters( - mem_gb=16, - name=f"{asset}_{i}", - cpus_per_task=4, - timeout_min=60*24, - slurm_account="pvl", - slurm_exclude= "node408,node409", - ) - render_fn = submitit.helpers.CommandFunction(cmd) - executor.submit(render_fn) diff --git a/infinigen/tools/terrain/generate_terrain_assets.py b/infinigen/tools/terrain/generate_terrain_assets.py deleted file mode 100644 index f2cc8dc31..000000000 --- a/infinigen/tools/terrain/generate_terrain_assets.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) Princeton University. -# This source code is licensed under the BSD 3-Clause license found in the LICENSE file in the root directory of this source tree. - -# Authors: Zeyu Ma - - -''' -fileheader placeholder -''' - -import os -import sys -import argparse -from pathlib import Path - -import bpy -from infinigen.terrain.assets.caves import caves_asset -from infinigen.terrain.assets.landtiles import landtile_asset -from infinigen.terrain.assets.upsidedown_mountains import upsidedown_mountains_asset -from infinigen.core.util import blender as butil -from infinigen.core.util.math import int_hash, FixedSeed -from infinigen.core.util.organization import Assets, LandTile, AssetFile - - -def asset_generation( - output_folder, - assets, - instance_ids, - seed, - device, - check_only=False, -): - for i in instance_ids: - for asset in assets: - if asset in [LandTile.Mesa, LandTile.Canyon, LandTile.Canyons, LandTile.Cliff, LandTile.Mountain, LandTile.River, LandTile.Volcano, LandTile.MultiMountains, LandTile.Coast]: - if not (output_folder/asset/f"{i}"/AssetFile.Finish).exists(): - print(asset, i) - if not check_only: - with FixedSeed(int_hash([asset, seed, i])): - landtile_asset(output_folder/asset/f"{i}", asset, device=device) - if asset == Assets.UpsidedownMountains: - if not (output_folder/asset/f"{i}"/AssetFile.Finish).exists(): - print(asset, i) - if not check_only: - with FixedSeed(int_hash([asset, seed, i])): - upsidedown_mountains_asset(output_folder/Assets.UpsidedownMountains/f"{i}", device=device) - if asset == Assets.Caves: - if not (output_folder/asset/f"{i}"/AssetFile.Finish).exists(): - print(asset, i) - if not check_only: - with FixedSeed(int_hash([asset, seed, i])): - caves_asset(output_folder/Assets.Caves/f"{i}") - - -if __name__ == "__main__": - # by default infinigen does on-the-fly terrain asset generation, but if you want to pre-generate a pool of assets, run this code - parser = argparse.ArgumentParser() - parser.add_argument('-a', '--assets', nargs='+', default=[ - LandTile.MultiMountains, - LandTile.Coast, - LandTile.Mesa, - LandTile.Canyon, - LandTile.Canyons, - LandTile.Cliff, - LandTile.Mountain, - LandTile.River, - LandTile.Volcano, - Assets.UpsidedownMountains, - Assets.Caves, - ]) - parser.add_argument('-s', '--start', type=int, default=0) - parser.add_argument('-e', '--end', type=int, default=1) - parser.add_argument('-f', '--folder') - parser.add_argument('--seed', type=int, default=0) - parser.add_argument('--check_only', type=int, default=0) - parser.add_argument('--device', type=str, default="cpu") - args = parser.parse_args(sys.argv[sys.argv.index("--") + 1:]) - bpy.ops.preferences.addon_enable(module='add_mesh_extra_objects') - bpy.ops.preferences.addon_enable(module='ant_landscape') - butil.clear_scene(targets=[bpy.data.objects]) - asset_generation(Path(args.folder), args.assets, list(range(args.start, args.end)), args.seed, args.device, check_only=args.check_only) diff --git a/infinigen/tools/terrain/palette/.gitignore b/infinigen/tools/terrain/palette/.gitignore deleted file mode 100644 index c1eaafafe..000000000 --- a/infinigen/tools/terrain/palette/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -images -css -json \ No newline at end of file diff --git a/infinigen/tools/terrain/palette/demo1.png b/infinigen/tools/terrain/palette/demo1.png deleted file mode 100644 index 640f96627..000000000 Binary files a/infinigen/tools/terrain/palette/demo1.png and /dev/null differ diff --git a/infinigen/tools/terrain/palette/demo2.png b/infinigen/tools/terrain/palette/demo2.png deleted file mode 100644 index f819fcf43..000000000 Binary files a/infinigen/tools/terrain/palette/demo2.png and /dev/null differ diff --git a/infinigen/tools/terrain/palette/demo3.png b/infinigen/tools/terrain/palette/demo3.png deleted file mode 100644 index ce3f888d8..000000000 Binary files a/infinigen/tools/terrain/palette/demo3.png and /dev/null differ diff --git a/infinigen/tools/terrain/palette/demo4.png b/infinigen/tools/terrain/palette/demo4.png deleted file mode 100644 index 96938a095..000000000 Binary files a/infinigen/tools/terrain/palette/demo4.png and /dev/null differ diff --git a/infinigen/tools/terrain/palette/palette.py b/infinigen/tools/terrain/palette/palette.py deleted file mode 100644 index a87532f6a..000000000 --- a/infinigen/tools/terrain/palette/palette.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) Princeton University. -# This source code is licensed under the BSD 3-Clause license found in the LICENSE file in the root directory of this source tree. - -# Authors: Zeyu Ma, Lingjie Mei - - -from google_images_search import GoogleImagesSearch -from sklearn.mixture import GaussianMixture -import argparse -import numpy as np -import os -import cv2 -import matplotlib.pyplot as plt -import colorsys -from pathlib import Path - - -def make_palette(keyword, num_images, num_colors, overwrite=False): - # define search params - # option for commonly used search param are shown below for easy reference. - # For param marked with '##': - # - Multiselect is currently not feasible. Choose ONE option only - # - This param can also be omitted from _search_params if you do not wish to define any value - _search_params = { - 'q': keyword, - 'num': num_images, - 'fileType': 'jpg|png', - } - - # this will search and download: - folder = f'{os.path.split(os.path.abspath(__file__))[0]}/images/{keyword}' - if os.path.exists(folder) and not overwrite: - print("folder existing, skip") - else: - # set your environment variables: GCS_DEVELOPER_KEY, GCS_CX - gis = GoogleImagesSearch(os.environ["GCS_DEVELOPER_KEY"], os.environ["GCS_CX"]) - gis.search(search_params=_search_params, path_to_dir=folder) - - colors = np.zeros((0, 3)) - for image_name in os.listdir(folder): - if image_name.endswith("svg"): continue - image = cv2.imread(f"{folder}/{image_name}") - image = cv2.resize(image, (128, 128)) - image = image[:, :, :3] - colors = np.concatenate((colors, image.reshape((-1, 3)))) - colors = colors[:, ::-1] / 255 - - for i in range(len(colors)): - colors[i] = colorsys.rgb_to_hsv(*colors[i]) - - model = GaussianMixture(num_colors, random_state=0).fit(colors) - - weights = model.weights_.copy() - index = np.argsort(weights)[::-1] - weights = weights[index] - colors_hsv = model.means_.copy()[index] - cov = model.covariances_.copy()[index] - colors_rgb = colors_hsv.copy() - for i in range(num_colors): - colors_rgb[i] = colorsys.hsv_to_rgb(*colors_rgb[i]) - cov[i] = np.linalg.cholesky(cov[i] + 1e-5 * np.eye(3)) - - S = 20 - diagrams = np.zeros((2, S, num_colors, S, 3)) - x, y = np.meshgrid(np.linspace(-1, 1, S), np.linspace(-1, 1, S), indexing="ij") - for i in range(num_colors): - diagrams[0, :, i, :, 0] = colors_rgb[i, 0] - diagrams[0, :, i, :, 1] = colors_rgb[i, 1] - diagrams[0, :, i, :, 2] = colors_rgb[i, 2] - diagrams[1, :, i, :, 0] = colors_hsv[i, 0] + cov[i, 0, 0] * x + cov[i, 0, 1] * y - diagrams[1, :, i, :, 1] = colors_hsv[i, 1] + cov[i, 1, 0] * x + cov[i, 1, 1] * y - diagrams[1, :, i, :, 2] = colors_hsv[i, 2] + cov[i, 2, 0] * x + cov[i, 2, 1] * y - for j in range(S): - for k in range(S): - diagrams[1, j, i, k] = colorsys.hsv_to_rgb(*diagrams[1, j, i, k]) - - diagrams = np.clip(diagrams * 256, a_min=0, a_max=255).astype(np.int32) - diagrams = diagrams.reshape((2 * S, num_colors * S, 3)) - - Path(f'{os.path.split(os.path.abspath(__file__))[0]}/images').mkdir(parents=True, exist_ok=True) - Path(f'{os.path.split(os.path.abspath(__file__))[0]}/json').mkdir(parents=True, exist_ok=True) - - plt.figure(figsize=(20, 5)) - plt.imshow(diagrams) - plt.savefig(f'{os.path.split(os.path.abspath(__file__))[0]}/images/{keyword}.png') - - colors_rgb = np.clip(colors_rgb * 256, a_min=0, a_max=255).astype(np.int32) - with open(f"{os.path.split(os.path.abspath(__file__))[0]}/json/{keyword}.json", "w") as f: - f.write("{\n") - f.write(' "color": {\n') - for i, color in enumerate(colors_rgb): - f.write(f' "{i}": "#{color[0]:02X}{color[1]:02X}{color[2]:02X}",\n') - f.write(" },\n") - f.write(' "hsv": [\n') - for color_hsv in colors_hsv: - f.write(f' [{color_hsv[0]}, {color_hsv[1]}, {color_hsv[2]}],\n') - f.write(" ],\n") - f.write(' "std": [\n') - for std in cov: - covs = ','.join([str(x) for x in std.reshape(-1)]) - f.write(f' [{covs}],\n') - f.write(" ],\n") - f.write(' "prob": [\n') - for i in range(num_colors): - f.write(f' {weights[i]},\n') - f.write(" ]\n") - f.write("}\n") - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('-k', '--keyword', type=str) - parser.add_argument('-i', '--num_images', default=10) - parser.add_argument('-c', '--num_colors', default=10) - parser.add_argument('-o', '--overwrite', action='store_true') - args = parser.parse_args() - make_palette(args.keyword, args.num_images, args.num_colors, args.overwrite) diff --git a/infinigen/tools/terrain/params_parser.py b/infinigen/tools/terrain/params_parser.py deleted file mode 100644 index 682986181..000000000 --- a/infinigen/tools/terrain/params_parser.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) Princeton University. -# This source code is licensed under the BSD 3-Clause license found in the LICENSE file in the root directory of this source tree. - -# Authors: Zeyu Ma - - -import argparse - -parser = argparse.ArgumentParser() -parser.add_argument('-f', '--file_path', type=str) -args = parser.parse_args() - -output = "" -current_type = "" -current_vars = [] - -code = "" - -def get_code(current_type, variables): - code = "" - for i, v in enumerate(variables): - code += f" {current_type} {v} = {current_type[0]}_params[{i}];\n" - return code - -with open(args.file_path, "r") as f: - lines = f.readlines() - i = 0 - while i < len(lines): - line = lines[i] - if line.lstrip().startswith("/* params"): - while True: - i += 1 - if lines[i].rstrip().endswith(":"): - if current_type != "": - code += get_code(current_type, current_vars) - if lines[i].lstrip().startswith("int"): - current_type = "int" - current_vars = [] - elif lines[i].lstrip().startswith("float"): - current_type = "float" - current_vars = [] - elif lines[i].rstrip().endswith("*/"): - code += get_code(current_type, current_vars) - break - else: - current_vars.extend([x.lstrip().rstrip() for x in lines[i].lstrip().rstrip().rstrip(',').split(",") if x.lstrip().rstrip() != ""]) - i += 1 - -print(code) diff --git a/examples/__init__.py b/infinigen_examples/__init__.py similarity index 100% rename from examples/__init__.py rename to infinigen_examples/__init__.py diff --git a/examples/configs/.gitignore b/infinigen_examples/configs/.gitignore similarity index 100% rename from examples/configs/.gitignore rename to infinigen_examples/configs/.gitignore diff --git a/examples/configs/asset_demo.gin b/infinigen_examples/configs/asset_demo.gin similarity index 100% rename from examples/configs/asset_demo.gin rename to infinigen_examples/configs/asset_demo.gin diff --git a/examples/configs/base.gin b/infinigen_examples/configs/base.gin similarity index 98% rename from examples/configs/base.gin rename to infinigen_examples/configs/base.gin index f6afc5926..49e931012 100644 --- a/examples/configs/base.gin +++ b/infinigen_examples/configs/base.gin @@ -225,6 +225,6 @@ group_collections.config = [ {'name': 'animhelper', 'hide_viewport': False, 'hide_render': True}, # curves and iks ] -include 'examples/configs/base_surface_registry.gin' -include 'examples/configs/natural.gin' +include 'base_surface_registry.gin' +include 'natural.gin' diff --git a/examples/configs/base_surface_registry.gin b/infinigen_examples/configs/base_surface_registry.gin similarity index 100% rename from examples/configs/base_surface_registry.gin rename to infinigen_examples/configs/base_surface_registry.gin diff --git a/examples/configs/disable_assets/no_assets.gin b/infinigen_examples/configs/disable_assets/no_assets.gin similarity index 100% rename from examples/configs/disable_assets/no_assets.gin rename to infinigen_examples/configs/disable_assets/no_assets.gin diff --git a/examples/configs/disable_assets/no_creatures.gin b/infinigen_examples/configs/disable_assets/no_creatures.gin similarity index 100% rename from examples/configs/disable_assets/no_creatures.gin rename to infinigen_examples/configs/disable_assets/no_creatures.gin diff --git a/examples/configs/disable_assets/no_particles.gin b/infinigen_examples/configs/disable_assets/no_particles.gin similarity index 100% rename from examples/configs/disable_assets/no_particles.gin rename to infinigen_examples/configs/disable_assets/no_particles.gin diff --git a/examples/configs/disable_assets/no_rocks.gin b/infinigen_examples/configs/disable_assets/no_rocks.gin similarity index 100% rename from examples/configs/disable_assets/no_rocks.gin rename to infinigen_examples/configs/disable_assets/no_rocks.gin diff --git a/examples/configs/experimental.gin b/infinigen_examples/configs/experimental.gin similarity index 100% rename from examples/configs/experimental.gin rename to infinigen_examples/configs/experimental.gin diff --git a/examples/configs/monocular.gin b/infinigen_examples/configs/monocular.gin similarity index 100% rename from examples/configs/monocular.gin rename to infinigen_examples/configs/monocular.gin diff --git a/examples/configs/natural.gin b/infinigen_examples/configs/natural.gin similarity index 100% rename from examples/configs/natural.gin rename to infinigen_examples/configs/natural.gin diff --git a/examples/configs/palette/desert.json b/infinigen_examples/configs/palette/desert.json similarity index 100% rename from examples/configs/palette/desert.json rename to infinigen_examples/configs/palette/desert.json diff --git a/examples/configs/palette/mountain soil.json b/infinigen_examples/configs/palette/mountain soil.json similarity index 100% rename from examples/configs/palette/mountain soil.json rename to infinigen_examples/configs/palette/mountain soil.json diff --git a/examples/configs/palette/sandstone.json b/infinigen_examples/configs/palette/sandstone.json similarity index 100% rename from examples/configs/palette/sandstone.json rename to infinigen_examples/configs/palette/sandstone.json diff --git a/examples/configs/palette/water.json b/infinigen_examples/configs/palette/water.json similarity index 100% rename from examples/configs/palette/water.json rename to infinigen_examples/configs/palette/water.json diff --git a/examples/configs/performance/dev.gin b/infinigen_examples/configs/performance/dev.gin similarity index 100% rename from examples/configs/performance/dev.gin rename to infinigen_examples/configs/performance/dev.gin diff --git a/examples/configs/performance/fast_terrain_assets.gin b/infinigen_examples/configs/performance/fast_terrain_assets.gin similarity index 100% rename from examples/configs/performance/fast_terrain_assets.gin rename to infinigen_examples/configs/performance/fast_terrain_assets.gin diff --git a/examples/configs/performance/high_quality_terrain.gin b/infinigen_examples/configs/performance/high_quality_terrain.gin similarity index 100% rename from examples/configs/performance/high_quality_terrain.gin rename to infinigen_examples/configs/performance/high_quality_terrain.gin diff --git a/examples/configs/performance/reuse_terrain_assets.gin b/infinigen_examples/configs/performance/reuse_terrain_assets.gin similarity index 100% rename from examples/configs/performance/reuse_terrain_assets.gin rename to infinigen_examples/configs/performance/reuse_terrain_assets.gin diff --git a/infinigen_examples/configs/performance/simple.gin b/infinigen_examples/configs/performance/simple.gin new file mode 100644 index 000000000..c7871e156 --- /dev/null +++ b/infinigen_examples/configs/performance/simple.gin @@ -0,0 +1,5 @@ +include 'performance/dev.gin' +include 'disable_assets/no_creatures.gin' +include 'performance/fast_terrain_assets.gin' +run_erosion.n_iters = [1,1] +full/render_image.num_samples = 64 \ No newline at end of file diff --git a/examples/configs/scene_types/arctic.gin b/infinigen_examples/configs/scene_types/arctic.gin similarity index 94% rename from examples/configs/scene_types/arctic.gin rename to infinigen_examples/configs/scene_types/arctic.gin index f042e6a90..57bcb33d2 100644 --- a/examples/configs/scene_types/arctic.gin +++ b/infinigen_examples/configs/scene_types/arctic.gin @@ -29,7 +29,7 @@ scene.warped_rocks_chance = 0 scene.ground_ice_chance = 1 scene.waterbody_chance = 1 -include 'examples/configs/disable_assets/no_assets.gin' +include 'disable_assets/no_assets.gin' compose_scene.wind_chance = 0.5 compose_scene.turbulence_chance = 0.5 diff --git a/examples/configs/scene_types/canyon.gin b/infinigen_examples/configs/scene_types/canyon.gin similarity index 100% rename from examples/configs/scene_types/canyon.gin rename to infinigen_examples/configs/scene_types/canyon.gin diff --git a/examples/configs/scene_types/cave.gin b/infinigen_examples/configs/scene_types/cave.gin similarity index 100% rename from examples/configs/scene_types/cave.gin rename to infinigen_examples/configs/scene_types/cave.gin diff --git a/examples/configs/scene_types/cliff.gin b/infinigen_examples/configs/scene_types/cliff.gin similarity index 100% rename from examples/configs/scene_types/cliff.gin rename to infinigen_examples/configs/scene_types/cliff.gin diff --git a/examples/configs/scene_types/coast.gin b/infinigen_examples/configs/scene_types/coast.gin similarity index 100% rename from examples/configs/scene_types/coast.gin rename to infinigen_examples/configs/scene_types/coast.gin diff --git a/examples/configs/scene_types/coral_reef.gin b/infinigen_examples/configs/scene_types/coral_reef.gin similarity index 81% rename from examples/configs/scene_types/coral_reef.gin rename to infinigen_examples/configs/scene_types/coral_reef.gin index 6fdb25b74..86f02b746 100644 --- a/examples/configs/scene_types/coral_reef.gin +++ b/infinigen_examples/configs/scene_types/coral_reef.gin @@ -1,4 +1,4 @@ -include 'examples/configs/scene_types/under_water.gin' +include 'scene_types/under_water.gin' compose_scene.kelp_chance = 0.1 compose_scene.urchin_chance = 0.1 diff --git a/examples/configs/scene_types/desert.gin b/infinigen_examples/configs/scene_types/desert.gin similarity index 100% rename from examples/configs/scene_types/desert.gin rename to infinigen_examples/configs/scene_types/desert.gin diff --git a/examples/configs/scene_types/forest.gin b/infinigen_examples/configs/scene_types/forest.gin similarity index 100% rename from examples/configs/scene_types/forest.gin rename to infinigen_examples/configs/scene_types/forest.gin diff --git a/examples/configs/scene_types/kelp_forest.gin b/infinigen_examples/configs/scene_types/kelp_forest.gin similarity index 93% rename from examples/configs/scene_types/kelp_forest.gin rename to infinigen_examples/configs/scene_types/kelp_forest.gin index b20df1df5..e054dad15 100644 --- a/examples/configs/scene_types/kelp_forest.gin +++ b/infinigen_examples/configs/scene_types/kelp_forest.gin @@ -1,4 +1,4 @@ -include 'examples/configs/scene_types/under_water.gin' +include 'scene_types/under_water.gin' multi_mountains_params.height = ("uniform", 1, 4) multi_mountains_params.min_freq = ("uniform", 0.01, 0.015) diff --git a/examples/configs/scene_types/mountain.gin b/infinigen_examples/configs/scene_types/mountain.gin similarity index 100% rename from examples/configs/scene_types/mountain.gin rename to infinigen_examples/configs/scene_types/mountain.gin diff --git a/examples/configs/scene_types/plain.gin b/infinigen_examples/configs/scene_types/plain.gin similarity index 100% rename from examples/configs/scene_types/plain.gin rename to infinigen_examples/configs/scene_types/plain.gin diff --git a/examples/configs/scene_types/river.gin b/infinigen_examples/configs/scene_types/river.gin similarity index 100% rename from examples/configs/scene_types/river.gin rename to infinigen_examples/configs/scene_types/river.gin diff --git a/examples/configs/scene_types/snowy_mountain.gin b/infinigen_examples/configs/scene_types/snowy_mountain.gin similarity index 94% rename from examples/configs/scene_types/snowy_mountain.gin rename to infinigen_examples/configs/scene_types/snowy_mountain.gin index 5b40e8066..09737c895 100644 --- a/examples/configs/scene_types/snowy_mountain.gin +++ b/infinigen_examples/configs/scene_types/snowy_mountain.gin @@ -1,4 +1,4 @@ -include 'examples/configs/disable_assets/no_assets.gin' +include 'disable_assets/no_assets.gin' surface.registry.rock_collection = [ ('mountain', 1), diff --git a/examples/configs/scene_types/under_water.gin b/infinigen_examples/configs/scene_types/under_water.gin similarity index 100% rename from examples/configs/scene_types/under_water.gin rename to infinigen_examples/configs/scene_types/under_water.gin diff --git a/examples/configs/scene_types_fluidsim/simulated_river.gin b/infinigen_examples/configs/scene_types_fluidsim/simulated_river.gin similarity index 95% rename from examples/configs/scene_types_fluidsim/simulated_river.gin rename to infinigen_examples/configs/scene_types_fluidsim/simulated_river.gin index 13bdf5145..8692be95d 100644 --- a/examples/configs/scene_types_fluidsim/simulated_river.gin +++ b/infinigen_examples/configs/scene_types_fluidsim/simulated_river.gin @@ -1,4 +1,4 @@ -include 'examples/configs/scene_types/river.gin' +include 'scene_types/river.gin' UniformMesher.enclosed=1 animate_cameras.policy_registry = @cam/AnimPolicyRandomForwardWalk diff --git a/examples/configs/scene_types_fluidsim/tilted_river.gin b/infinigen_examples/configs/scene_types_fluidsim/tilted_river.gin similarity index 95% rename from examples/configs/scene_types_fluidsim/tilted_river.gin rename to infinigen_examples/configs/scene_types_fluidsim/tilted_river.gin index 560ae7569..1505f5f64 100644 --- a/examples/configs/scene_types_fluidsim/tilted_river.gin +++ b/infinigen_examples/configs/scene_types_fluidsim/tilted_river.gin @@ -1,4 +1,4 @@ -include 'examples/configs/scene_types/river.gin' +include 'scene_types/river.gin' UniformMesher.enclosed=1 animate_cameras.policy_registry = @cam/AnimPolicyRandomForwardWalk diff --git a/examples/configs/stereo_training.gin b/infinigen_examples/configs/stereo_training.gin similarity index 100% rename from examples/configs/stereo_training.gin rename to infinigen_examples/configs/stereo_training.gin diff --git a/examples/configs/use_cached_fire.gin b/infinigen_examples/configs/use_cached_fire.gin similarity index 100% rename from examples/configs/use_cached_fire.gin rename to infinigen_examples/configs/use_cached_fire.gin diff --git a/examples/configs/use_on_the_fly_fire.gin b/infinigen_examples/configs/use_on_the_fly_fire.gin similarity index 100% rename from examples/configs/use_on_the_fly_fire.gin rename to infinigen_examples/configs/use_on_the_fly_fire.gin diff --git a/examples/generate_asset_demo.py b/infinigen_examples/generate_asset_demo.py similarity index 88% rename from examples/generate_asset_demo.py rename to infinigen_examples/generate_asset_demo.py index 9b61a7deb..72484928d 100644 --- a/examples/generate_asset_demo.py +++ b/infinigen_examples/generate_asset_demo.py @@ -31,7 +31,7 @@ from infinigen.core.placement.split_in_view import split_inview from infinigen.core.util import blender as butil -from infinigen.core import execute_tasks +from infinigen.core import execute_tasks, init, surface def find_flat_location( mesh, @@ -83,8 +83,7 @@ def circular_camera_path(camera_rig, target_obj, rad, alt, duration): @gin.configurable def compose_scene( output_folder: Path, - terrain: Terrain, - scene_seed: int, + scene_seed: int, asset_factory=None, # provided via gin grid_rad=1.2, @@ -109,7 +108,8 @@ def compose_scene( camera_rigs = cam_util.spawn_camera_rigs() cam = cam_util.get_camera(0, 0) - # find a flat spot on the terrain to do the demo + # find a flat spot on the terrain to do the demo\ + terrain = Terrain(scene_seed, surface.registry, task='coarse', on_the_fly_asset_folder=output_folder/"assets") terrain_mesh = terrain.coarse_terrain() terrain_bvh = bvhtree.BVHTree.FromObject(terrain_mesh, bpy.context.evaluated_depsgraph_get()) if asset_factory is not None: @@ -172,22 +172,22 @@ def compose_scene( def main(): parser = argparse.ArgumentParser() - parser.add_argument('--output_folder', type=Path) + parser.add_argument('--output_folder', type=Path, required=True) parser.add_argument('--input_folder', type=Path, default=None) parser.add_argument('-s', '--seed', default=None, help="The seed used to generate the scene") parser.add_argument('-t', '--task', nargs='+', default=['coarse'], choices=['coarse', 'populate', 'fine_terrain', 'ground_truth', 'render', 'mesh_save']) - parser.add_argument('-g', '--gin_config', nargs='+', default=['base'], + parser.add_argument('-g', '--configs', nargs='+', default=['base'], help='Set of config files for gin (separated by spaces) ' - 'e.g. --gin_config file1 file2 (exclude .gin from path)') - parser.add_argument('-p', '--gin_param', nargs='+', default=[], + 'e.g. --configs file1 file2 (exclude .gin from path)') + parser.add_argument('-p', '--overrides', nargs='+', default=[], help='Parameter settings that override config defaults ' - 'e.g. --gin_param module_1.a=2 module_2.b=3') + 'e.g. --overrides module_1.a=2 module_2.b=3') parser.add_argument('--task_uniqname', type=str, default=None) parser.add_argument('-d', '--debug', action="store_const", dest="loglevel", const=logging.DEBUG, default=logging.INFO) parser.add_argument( '-v', '--verbose', action="store_const", dest="loglevel", const=logging.INFO) - args = parser.parse_args(sys.argv[sys.argv.index("--") + 1:]) + args = parser.parse_args() extras = '[%(filename)s:%(lineno)d] ' if args.loglevel == logging.DEBUG else '' logging.basicConfig( @@ -196,11 +196,12 @@ def main(): datefmt='%H:%M:%S' ) - scene_seed = execute_tasks.apply_scene_seed(args) - execute_tasks.apply_gin_configs( - args, - Path(__file__).parent/'configs', - mandatory_config_dir=Path(__file__).parent/'configs/scene_types', + scene_seed = init.apply_scene_seed(args.seed, task=args.task) + init.apply_gin_configs( + configs=args.configs, + overrides=args.overrides, + configs_folder='infinigen_examples/configs', + mandatory_folders=['infinigen_examples/configs/scene_types'], skip_unknown=True ) diff --git a/examples/generate_individual_assets.py b/infinigen_examples/generate_individual_assets.py similarity index 91% rename from examples/generate_individual_assets.py rename to infinigen_examples/generate_individual_assets.py index 808cf27e2..0f3a51576 100644 --- a/examples/generate_individual_assets.py +++ b/infinigen_examples/generate_individual_assets.py @@ -28,7 +28,7 @@ from infinigen.assets.utils.tag import tag_object, tag_nodegroup, tag_system from infinigen.assets.lighting import sky_lighting -from infinigen.core import surface +from infinigen.core import surface, init from infinigen.core.placement import density, factory from infinigen.core.rendering.render import enable_gpu from infinigen.core.util.math import FixedSeed @@ -36,7 +36,7 @@ from infinigen.core.util.logging import Suppress from infinigen.core.util import blender as butil -from infinigen.datagen.tools.results import strip_alpha_background as strip_alpha_background +from .tools.results import strip_alpha_background as strip_alpha_background import generate_nature # to load most/all factory.AssetFactory subclasses @@ -239,39 +239,15 @@ def setup_camera(args): cam_info_ng.nodes['Object Info'].inputs['Object'].default_value = camera return camera, camera.parent - -def import_surface_registry(): - def find_config(g): - as_scene_type = f'config/scene_types/{g}.gin' - if os.path.exists(as_scene_type): - return as_scene_type - as_base = f'config/{g}.gin' - if os.path.exists(as_base): - return as_base - raise ValueError(f'Couldn not locate {g} in either config/ or config/scene_types') - - def sanitize_gin_override(overrides: list): - if len(overrides) > 0: - print("Overriden parameters:", overrides) - output = list(overrides) - for i, o in enumerate(overrides): - if ('=' in o) and not any((c in o) for c in "\"'[]"): - k, v = o.split('=') - try: - ast.literal_eval(v) - except: - output[i] = f'{k}="{v}"' - return output - - gin.parse_config_files_and_bindings( - ['config/base.gin'] + [find_config(g) for g in ['base_surface_registry']], - bindings=sanitize_gin_override([]), skip_unknown=True) - surface.registry.initialize_from_gin() + def main(args): bpy.context.window.workspace = bpy.data.workspaces['Geometry Nodes'] - import_surface_registry() + + init.apply_gin_configs('infinigen_examples/config') + surface.registry.initialize_from_gin() + name = '_'.join(args.factories) path = Path(os.getcwd()) / 'outputs' / name path.mkdir(exist_ok=True) diff --git a/examples/generate_nature.py b/infinigen_examples/generate_nature.py similarity index 96% rename from examples/generate_nature.py rename to infinigen_examples/generate_nature.py index 058fac953..1ddd9d0f3 100644 --- a/examples/generate_nature.py +++ b/infinigen_examples/generate_nature.py @@ -15,6 +15,8 @@ import numpy as np from numpy.random import uniform, normal, randint +logging.basicConfig(level=logging.INFO) + from infinigen.core.placement import ( particles, placement, density, camera as cam_util, @@ -56,7 +58,7 @@ from infinigen.core.util.organization import Tags from infinigen.core.util.random import sample_registry, random_general from infinigen.core.util.math import FixedSeed, int_hash -from infinigen.core import execute_tasks, surface +from infinigen.core import execute_tasks, surface, init @gin.configurable def compose_scene(output_folder, scene_seed, **params): @@ -388,8 +390,27 @@ def add_snow_particles(): p.save_results(output_folder/'pipeline_coarse.csv') return terrain, terrain_mesh -def main(): +def main(args): + + scene_seed = init.apply_scene_seed(args.seed) + init.apply_gin_configs( + configs=args.configs, + overrides=args.overrides, + configs_folder='infinigen_examples/configs', + mandatory_folders=['infinigen_examples/configs/scene_types'], + ) + execute_tasks.main( + compose_scene_func=compose_scene, + input_folder=args.input_folder, + output_folder=args.output_folder, + task=args.task, + task_uniqname=args.task_uniqname, + scene_seed=scene_seed + ) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() parser.add_argument('--output_folder', type=Path) parser.add_argument('--input_folder', type=Path, default=None) @@ -404,34 +425,9 @@ def main(): 'e.g. --gin_param module_1.a=2 module_2.b=3') parser.add_argument('--task_uniqname', type=str, default=None) parser.add_argument('-d', '--debug', action="store_const", dest="loglevel", const=logging.DEBUG, default=logging.INFO) - parser.add_argument( '-v', '--verbose', action="store_const", dest="loglevel", const=logging.INFO) # handle case where running with blender --python -- argvs = sys.argv[sys.argv.index('--')+1:] if '--' in sys.argv else sys.argv[1:] args = parser.parse_args(argvs) - extras = '[%(filename)s:%(lineno)d] ' if args.loglevel == logging.DEBUG else '' - logging.basicConfig( - format=f'[%(asctime)s.%(msecs)03d] [%(name)s] [%(levelname)s] {extras}| %(message)s', - level=args.loglevel, - datefmt='%H:%M:%S' - ) - - scene_seed = execute_tasks.apply_scene_seed(args) - execute_tasks.apply_gin_configs( - args, - Path(__file__).parent/'configs', - mandatory_config_dir=Path(__file__).parent/'configs/scene_types', - ) - - execute_tasks.main( - compose_scene_func=compose_scene, - input_folder=args.input_folder, - output_folder=args.output_folder, - task=args.task, - task_uniqname=args.task_uniqname, - scene_seed=scene_seed - ) - -if __name__ == "__main__": - main() + main(args) diff --git a/examples/scripts/hello_world.sh b/infinigen_examples/scripts/hello_world.sh similarity index 100% rename from examples/scripts/hello_world.sh rename to infinigen_examples/scripts/hello_world.sh diff --git a/infinigen_examples/scripts/hello_world_stepbystep.sh b/infinigen_examples/scripts/hello_world_stepbystep.sh new file mode 100644 index 000000000..b2902a334 --- /dev/null +++ b/infinigen_examples/scripts/hello_world_stepbystep.sh @@ -0,0 +1,11 @@ +# Generate a scene layout +python infinigen_examples/generate_nature.py -- --seed 0 --task coarse -g desert.gin simple.gin --output_folder outputs/helloworld/coarse + +# Populate unique assets +python infinigen_examples/generate_nature.py -- --seed 0 --task populate fine_terrain -g desert.gin simple.gin --input_folder outputs/helloworld/coarse --output_folder outputs/helloworld/fine + +# Render RGB images +python infinigen_examples/generate_nature.py -- --seed 0 --task render -g desert.gin simple.gin --input_folder outputs/helloworld/fine --output_folder outputs/helloworld/frames + +# Render again for accurate ground-truth +python infinigen_examples/generate_nature.py -- --seed 0 --task render -g desert.gin simple.gin --input_folder outputs/helloworld/fine --output_folder outputs/helloworld/frames -p render.render_image_func=@flat/render_image \ No newline at end of file diff --git a/examples/scripts/render_video_1080p.sh b/infinigen_examples/scripts/render_video_1080p.sh similarity index 100% rename from examples/scripts/render_video_1080p.sh rename to infinigen_examples/scripts/render_video_1080p.sh diff --git a/examples/scripts/render_video_720p.sh b/infinigen_examples/scripts/render_video_720p.sh similarity index 100% rename from examples/scripts/render_video_720p.sh rename to infinigen_examples/scripts/render_video_720p.sh diff --git a/examples/scripts/render_video_stereo.sh b/infinigen_examples/scripts/render_video_stereo.sh similarity index 100% rename from examples/scripts/render_video_stereo.sh rename to infinigen_examples/scripts/render_video_stereo.sh diff --git a/infinigen/datagen/tools/__init__.py b/infinigen_examples/tools/__init__.py similarity index 100% rename from infinigen/datagen/tools/__init__.py rename to infinigen_examples/tools/__init__.py diff --git a/infinigen/tools/blendscript_import_infinigen.py b/infinigen_examples/tools/blendscript_import_infinigen.py similarity index 100% rename from infinigen/tools/blendscript_import_infinigen.py rename to infinigen_examples/tools/blendscript_import_infinigen.py diff --git a/infinigen/datagen/tools/datarelease_toolkit.py b/infinigen_examples/tools/datarelease_toolkit.py similarity index 100% rename from infinigen/datagen/tools/datarelease_toolkit.py rename to infinigen_examples/tools/datarelease_toolkit.py diff --git a/infinigen/datagen/tools/ground_truth/bounding_boxes_3d.py b/infinigen_examples/tools/ground_truth/bounding_boxes_3d.py similarity index 100% rename from infinigen/datagen/tools/ground_truth/bounding_boxes_3d.py rename to infinigen_examples/tools/ground_truth/bounding_boxes_3d.py diff --git a/infinigen/datagen/tools/ground_truth/depth_to_normals.py b/infinigen_examples/tools/ground_truth/depth_to_normals.py similarity index 100% rename from infinigen/datagen/tools/ground_truth/depth_to_normals.py rename to infinigen_examples/tools/ground_truth/depth_to_normals.py diff --git a/infinigen/datagen/tools/ground_truth/optical_flow_warp.py b/infinigen_examples/tools/ground_truth/optical_flow_warp.py similarity index 100% rename from infinigen/datagen/tools/ground_truth/optical_flow_warp.py rename to infinigen_examples/tools/ground_truth/optical_flow_warp.py diff --git a/infinigen/datagen/tools/ground_truth/rigid_warp.py b/infinigen_examples/tools/ground_truth/rigid_warp.py similarity index 100% rename from infinigen/datagen/tools/ground_truth/rigid_warp.py rename to infinigen_examples/tools/ground_truth/rigid_warp.py diff --git a/infinigen/datagen/tools/ground_truth/segmentation_lookup.py b/infinigen_examples/tools/ground_truth/segmentation_lookup.py similarity index 100% rename from infinigen/datagen/tools/ground_truth/segmentation_lookup.py rename to infinigen_examples/tools/ground_truth/segmentation_lookup.py diff --git a/infinigen/datagen/tools/export/__init__.py b/infinigen_examples/tools/results/__init__.py similarity index 100% rename from infinigen/datagen/tools/export/__init__.py rename to infinigen_examples/tools/results/__init__.py diff --git a/infinigen/datagen/tools/results/aggregate_job_stats.py b/infinigen_examples/tools/results/aggregate_job_stats.py similarity index 100% rename from infinigen/datagen/tools/results/aggregate_job_stats.py rename to infinigen_examples/tools/results/aggregate_job_stats.py diff --git a/infinigen/datagen/tools/results/job_stats.py b/infinigen_examples/tools/results/job_stats.py similarity index 100% rename from infinigen/datagen/tools/results/job_stats.py rename to infinigen_examples/tools/results/job_stats.py diff --git a/infinigen/datagen/tools/results/make_grid_figure.py b/infinigen_examples/tools/results/make_grid_figure.py similarity index 100% rename from infinigen/datagen/tools/results/make_grid_figure.py rename to infinigen_examples/tools/results/make_grid_figure.py diff --git a/infinigen/datagen/tools/results/parse_times.py b/infinigen_examples/tools/results/parse_times.py similarity index 100% rename from infinigen/datagen/tools/results/parse_times.py rename to infinigen_examples/tools/results/parse_times.py diff --git a/infinigen/datagen/tools/results/parse_videos.py b/infinigen_examples/tools/results/parse_videos.py similarity index 100% rename from infinigen/datagen/tools/results/parse_videos.py rename to infinigen_examples/tools/results/parse_videos.py diff --git a/infinigen/datagen/tools/results/resource_stats.py b/infinigen_examples/tools/results/resource_stats.py similarity index 100% rename from infinigen/datagen/tools/results/resource_stats.py rename to infinigen_examples/tools/results/resource_stats.py diff --git a/infinigen/datagen/tools/results/scatter_figure.py b/infinigen_examples/tools/results/scatter_figure.py similarity index 100% rename from infinigen/datagen/tools/results/scatter_figure.py rename to infinigen_examples/tools/results/scatter_figure.py diff --git a/infinigen/datagen/tools/results/strip_alpha_background.py b/infinigen_examples/tools/results/strip_alpha_background.py similarity index 100% rename from infinigen/datagen/tools/results/strip_alpha_background.py rename to infinigen_examples/tools/results/strip_alpha_background.py diff --git a/infinigen/datagen/tools/results/summarize.py b/infinigen_examples/tools/results/summarize.py similarity index 100% rename from infinigen/datagen/tools/results/summarize.py rename to infinigen_examples/tools/results/summarize.py diff --git a/infinigen/datagen/tools/submit_asset_cache.py b/infinigen_examples/tools/submit_asset_cache.py similarity index 87% rename from infinigen/datagen/tools/submit_asset_cache.py rename to infinigen_examples/tools/submit_asset_cache.py index a08a753cb..fd4da46d1 100644 --- a/infinigen/datagen/tools/submit_asset_cache.py +++ b/infinigen_examples/tools/submit_asset_cache.py @@ -21,7 +21,6 @@ def get_slurm_banned_nodes(config_path=None): parser = argparse.ArgumentParser() -parser.add_argument('-b', '--blender_path', type=str) parser.add_argument('-f', '--asset_folder', type=str) parser.add_argument('-a', '--assets', nargs='+', default=[ 'CachedBushFactory', @@ -43,7 +42,7 @@ def get_slurm_banned_nodes(config_path=None): for asset in args.assets: for i in range(args.number): - cmd = f"{args.blender_path} --background -noaudio --python fluid/run_asset_cache.py -- -f {args.asset_folder}/ -a {asset} -s {args.start_frame} -d {args.simulation_duration}".split(" ") + cmd = f"python fluid/run_asset_cache.py -- -f {args.asset_folder}/ -a {asset} -s {args.start_frame} -d {args.simulation_duration}".split(" ") print(cmd) executor = submitit.AutoExecutor(folder=str(Path(args.asset_folder) / "logs")) executor.update_parameters( diff --git a/infinigen/datagen/tools/template.html b/infinigen_examples/tools/template.html similarity index 100% rename from infinigen/datagen/tools/template.html rename to infinigen_examples/tools/template.html diff --git a/infinigen/datagen/tools/dev/generate_terrain_assets.py b/infinigen_examples/tools/terrain/generate_terrain_assets.py similarity index 100% rename from infinigen/datagen/tools/dev/generate_terrain_assets.py rename to infinigen_examples/tools/terrain/generate_terrain_assets.py diff --git a/infinigen/tools/terrain/kernelize_surfaces.py b/infinigen_examples/tools/terrain/kernelize_surfaces.py similarity index 100% rename from infinigen/tools/terrain/kernelize_surfaces.py rename to infinigen_examples/tools/terrain/kernelize_surfaces.py diff --git a/infinigen/tools/terrain/landtile_viewer.py b/infinigen_examples/tools/terrain/landtile_viewer.py similarity index 100% rename from infinigen/tools/terrain/landtile_viewer.py rename to infinigen_examples/tools/terrain/landtile_viewer.py diff --git a/infinigen/datagen/tools/dev/palette/.gitignore b/infinigen_examples/tools/terrain/palette/.gitignore similarity index 100% rename from infinigen/datagen/tools/dev/palette/.gitignore rename to infinigen_examples/tools/terrain/palette/.gitignore diff --git a/infinigen/datagen/tools/dev/palette/demo1.png b/infinigen_examples/tools/terrain/palette/demo1.png similarity index 100% rename from infinigen/datagen/tools/dev/palette/demo1.png rename to infinigen_examples/tools/terrain/palette/demo1.png diff --git a/infinigen/datagen/tools/dev/palette/demo2.png b/infinigen_examples/tools/terrain/palette/demo2.png similarity index 100% rename from infinigen/datagen/tools/dev/palette/demo2.png rename to infinigen_examples/tools/terrain/palette/demo2.png diff --git a/infinigen/datagen/tools/dev/palette/demo3.png b/infinigen_examples/tools/terrain/palette/demo3.png similarity index 100% rename from infinigen/datagen/tools/dev/palette/demo3.png rename to infinigen_examples/tools/terrain/palette/demo3.png diff --git a/infinigen/datagen/tools/dev/palette/demo4.png b/infinigen_examples/tools/terrain/palette/demo4.png similarity index 100% rename from infinigen/datagen/tools/dev/palette/demo4.png rename to infinigen_examples/tools/terrain/palette/demo4.png diff --git a/infinigen/datagen/tools/dev/palette/palette.py b/infinigen_examples/tools/terrain/palette/palette.py similarity index 100% rename from infinigen/datagen/tools/dev/palette/palette.py rename to infinigen_examples/tools/terrain/palette/palette.py diff --git a/infinigen/tools/terrain/palette/readme.md b/infinigen_examples/tools/terrain/palette/readme.md similarity index 94% rename from infinigen/tools/terrain/palette/readme.md rename to infinigen_examples/tools/terrain/palette/readme.md index a4eda076a..1f964d395 100644 --- a/infinigen/tools/terrain/palette/readme.md +++ b/infinigen_examples/tools/terrain/palette/readme.md @@ -32,7 +32,7 @@ After manually comemnt out them, you have: ![](demo4.png) -Then you move the ready palatte to location: `infinigen/examples/configs/palette` +Then you move the ready palatte to location: `infinigen/infinigen_examples/configs/palette` ## Step 3 diff --git a/infinigen/datagen/tools/dev/params_parser.py b/infinigen_examples/tools/terrain/params_parser.py similarity index 100% rename from infinigen/datagen/tools/dev/params_parser.py rename to infinigen_examples/tools/terrain/params_parser.py diff --git a/infinigen/datagen/tools/torch_dataset.py b/infinigen_examples/tools/torch_dataset.py similarity index 100% rename from infinigen/datagen/tools/torch_dataset.py rename to infinigen_examples/tools/torch_dataset.py diff --git a/pyproject.toml b/pyproject.toml index 098aebd58..5b571213a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,8 +4,10 @@ build-backend = "setuptools.build_meta" [project] name = "infinigen" -version = "1.1.0" -requires-python = "==3.10.*" +readme = "README.md" +license = {file = "LICENSE"} +dynamic = ["version"] + description = "Infinite Photorealistic Worlds using Procedural Generation" keywords = [ "computer vision", @@ -14,32 +16,36 @@ keywords = [ ] classifiers = [ "Framework :: Blender", - "Programming Language :: Python :: 3" + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10" ] + +requires-python = "==3.10.*" dependencies = [ "bpy", "einops", "flow_vis", "frozendict", "geomdl", - "gin_config", + "gin_config>=0.5.0", "imageio", "matplotlib", - "numpy", - "opencv-python", + "numpy==1.21.5", + "opencv-python<=4.8.0.74", "psutil", - "scikit-image", + "scikit-image==0.19.3", "scikit-learn", "scipy", "submitit", "tqdm", - "trimesh", + "trimesh==3.22.1", "wandb", "zarr", "pandas", - "landlab == 2.4.1", - "pycparser", - "pyrender" + "landlab==2.4.1", + "pycparser==2.21", + "pyrender", + "json5" ] [project.optional-dependencies] @@ -53,20 +59,38 @@ dev = [ ] [tool.setuptools] -packages = ["infinigen"] +# include-package-data is terribly named. package-data is still included if false, +# just not the package-data setuptools would otherwise autogenerate from MANIFEST.in or version control +include-package-data = false + +[tool.setuptools.packages.find] +include = ["infinigen*"] + +# must be specified as module paths with ., using classic filepaths with / will silently not do anything +exclude = [ + "infinigen.datagen.customgt.dependencies*", + "infinigen.datagen.customgt.build*", +] + +[tool.setuptools.package-data] + +"*" = ["*.gin", "*.txt"] + +# Must be specified as paths relative to infinigen/ +"infinigen" = [ + "terrain/**/*.soil", # extra files for SoilMachine + "terrain/lib/**/*.so", # created by terrain compilation + "terrain/lib/**/*.o", # created by terrain compilation + "datagen/customgt/build/customgt", # created during opengl compilation + "assets/creatures/parts/nurbs_data/*.npy", # stores creature nurbs centroids data, ideally will be deprecated +] + +[tool.setuptools.dynamic] +version = {attr = "infinigen.__version__"} [tool.pytest.ini_options] -addopts = "--cov-report xml:coverage.xml --cov infinigen --cov-fail-under 0" testpaths = "tests" junit_family = "xunit2" -markers = [ - "integration: marks as integration test", - "notebooks: marks as notebook test", - "gpu: marks as gpu test", - "spark: marks tests which need Spark", - "slow: marks tests as slow", - "unit: fast offline tests", -] [tool.ruff] select = [ diff --git a/setup.py b/setup.py index 2468bf357..3509cb510 100644 --- a/setup.py +++ b/setup.py @@ -1,96 +1,65 @@ from pathlib import Path import subprocess import sys +import os -from setuptools import setup, Extension +from setuptools import setup, find_packages, Extension import numpy from Cython.Build import cythonize cwd = Path(__file__).parent -TERRAIN = True -CUSTOMGT = True -FLUIDS = True -RUN_BUILD_DEPS = True - -filtered_args = [] -for i, arg in enumerate(sys.argv): - if arg in ["clean", "egg_info", "sdist"]: - RUN_BUILD_DEPS = False - elif arg == '--noterrain': - TERRAIN = False - elif arg == '--nogt': - CUSTOMGT = False - elif arg == '--nofluids': - FLUIDS = False - filtered_args.append(arg) -sys.argv = filtered_args - -def get_submodule_folders(): +def ensure_submodules(): # Inspired by https://github.com/pytorch/pytorch/blob/main/setup.py + with (cwd/'.gitmodules').open() as f: - return [ + submodule_folders = [ cwd/line.split("=", 1)[1].strip() for line in f.readlines() if line.strip().startswith("path") ] -def ensure_submodules(): - # Inspired by https://github.com/pytorch/pytorch/blob/main/setup.py - - folders = get_submodule_folders() - - if any(not p.exists() or not any(p.iterdir()) for p in folders): + if any(not p.exists() or not any(p.iterdir()) for p in submodule_folders): subprocess.run( ["git", "submodule", "update", "--init", "--recursive"], cwd=cwd - ) + ) ensure_submodules() -def build_deps(deps): - for dep, enabled in deps: - if not enabled: - continue - print(f'Building external executable {dep}') - try: - # Defer to Makefile - subprocess.run(['make', f'build_{dep}'], cwd=cwd) - except subprocess.CalledProcessError as e: - print(f'[WARNING] build_{dep} failed! {dep} features will not function. {e}') - -if RUN_BUILD_DEPS: - deps = [ - ('terrain', TERRAIN), - ('custom_groundtruth', CUSTOMGT), - ('flip_fluids', FLUIDS) - ] - build_deps(deps) - -cython_extensions = [ - Extension( - name="bnurbs", - sources=["infinigen/assets/creatures/util/geometry/cpp_utils/bnurbs.pyx"], - include_dirs=[numpy.get_include()] - ), +# inspired by https://github.com/pytorch/pytorch/blob/161ea463e690dcb91a30faacbf7d100b98524b6b/setup.py#L290 +# theirs seems to not exclude dist_info but this causes duplicate compiling in my tests +dont_build_steps = ["clean", "egg_info", "dist_info", "sdist", "--help"] +RUN_BUILD = not any(x in sys.argv[1] for x in dont_build_steps) +str_true = "True" # use strings as os.environ will turn any bool into a string anyway +if RUN_BUILD and os.environ.get('INFINIGEN_INSTALL_RUNBUILD', str_true) == str_true: + if os.environ.get('INFINIGEN_INSTALL_TERRAIN', str_true) == str_true: + subprocess.run(['make', 'terrain'], cwd=cwd) + if os.environ.get('INFINIGEN_INSTALL_CUSTOMGT', str_true) == str_true: + subprocess.run(['make', 'customgt'], cwd=cwd) + if os.environ.get('INFINIGEN_INSTALL_FLUIDS', str_true) == str_true: + subprocess.run(['make', 'flip_fluids'], cwd=cwd) + +cython_extensions = [] + +cython_extensions.append(Extension( + name="bnurbs", + sources=["infinigen/assets/creatures/util/geometry/cpp_utils/bnurbs.pyx"], + include_dirs=[numpy.get_include()] +)) +cython_extensions.append( Extension( name="infinigen.terrain.marching_cubes", sources=["infinigen/terrain/marching_cubes/_marching_cubes_lewiner_cy.pyx"], include_dirs=[numpy.get_include()] - ), -] + ) +) setup( ext_modules=[ *cythonize(cython_extensions) - ], - package_data={ - "infinigen": [ - "infinigen/terrain/lib", - "infinigen/datagen/customgt/build" - ] - } - # other opts come from pyproject.toml and setup.cfg + ] + # other opts come from pyproject.toml ) diff --git a/tests/test_execute_tasks.py b/tests/test_execute_tasks.py new file mode 100644 index 000000000..af4dbbf4e --- /dev/null +++ b/tests/test_execute_tasks.py @@ -0,0 +1,53 @@ +from pathlib import Path +from types import SimpleNamespace +import logging +import importlib + +import pytest +import bpy +import gin + +from infinigen_examples import generate_nature +from infinigen.core import execute_tasks +from infinigen.core.placement import camera +from infinigen.core import init + +from utils import setup_gin + +''' +@pytest.mark.order('last') +def test_noassets_noterrain(): + args = SimpleNamespace( + input_folder=None, + output_folder='/tmp/test_noassets_noterrain', + seed="0", + task='coarse', + configs=['desert.gin', 'simple.gin', 'no_assets.gin'], + overrides=['compose_scene.generate_resolution = (480, 270)'], + task_uniqname='coarse', + loglevel=logging.DEBUG + ) + generate_nature.main(args) +''' + +def test_compose_cube(): + + setup_gin() + + def compose_cube(output_folder, scene_seed, **params): + camera_rigs = camera.spawn_camera_rigs() + bpy.ops.mesh.primitive_cube_add() + + output = Path('/tmp/test_compose_cube') + output.mkdir(exist_ok=True) + + execute_tasks.execute_tasks( + compose_cube, + input_folder=None, + output_folder=output, + task='coarse populate', + scene_seed=0, + frame_range=[0, 100], + camera_id=(0, 0) + ) + diff --git a/tests/test_nature_materials_basic.py b/tests/test_materials_basic.py similarity index 58% rename from tests/test_nature_materials_basic.py rename to tests/test_materials_basic.py index 33029a9f9..cfc76ca28 100644 --- a/tests/test_nature_materials_basic.py +++ b/tests/test_materials_basic.py @@ -10,15 +10,17 @@ from utils import ( setup_gin, load_txt_list, + import_item ) setup_gin() -@pytest.mark.parametrize('factory_name', load_txt_list('test_nature_materials_basic.txt')) -def test_material_runs(factory_name, **kwargs): +@pytest.mark.parametrize('pathspec', load_txt_list('test_materials_basic.txt')) +def test_material_runs(pathspec, **kwargs): + butil.clear_scene() - with gin.unlock_config(): - mat = importlib.import_module(f'infinigen.assets.materials.{factory_name}') bpy.ops.mesh.primitive_ico_sphere_add(radius=.8, subdivisions=5) asset = bpy.context.active_object + + mat = import_item(pathspec) mat.apply(asset) \ No newline at end of file diff --git a/tests/test_materials_basic.txt b/tests/test_materials_basic.txt new file mode 100644 index 000000000..e16b33986 --- /dev/null +++ b/tests/test_materials_basic.txt @@ -0,0 +1,57 @@ +infinigen.assets.materials.aluminumdisp2tut +infinigen.assets.materials.atmosphere_light_haze +infinigen.assets.materials.bark_birch +infinigen.assets.materials.bark +infinigen.assets.materials.bark_random +infinigen.assets.materials.basic_bsdf +infinigen.assets.materials.beak +infinigen.assets.materials.bird +infinigen.assets.materials.blackbody_shader +infinigen.assets.materials.bone +infinigen.assets.materials.chitin +infinigen.assets.materials.chunkyrock +infinigen.assets.materials.cobble_stone +infinigen.assets.materials.cracked_ground +infinigen.assets.materials.dirt +infinigen.assets.materials.eyeball +infinigen.assets.materials.face_size_visualizer +infinigen.assets.materials.fishbody +infinigen.assets.materials.fish_eye_shader +infinigen.assets.materials.fishfin +infinigen.assets.materials.giraffe_attr +infinigen.assets.materials.grass_blade_texture +infinigen.assets.materials.horn +infinigen.assets.materials.ice +infinigen.assets.materials.lava +infinigen.assets.materials.mountain +infinigen.assets.materials.mud +infinigen.assets.materials.new_whitewater +infinigen.assets.materials.nose +infinigen.assets.materials.reptile_brown_circle_attr +infinigen.assets.materials.reptile_gray_attr +infinigen.assets.materials.reptile_two_color_attr +infinigen.assets.materials.river_water +infinigen.assets.materials.sand +infinigen.assets.materials.sandstone +infinigen.assets.materials.scale +infinigen.assets.materials.simple_brownish +infinigen.assets.materials.simple_whitish +infinigen.assets.materials.slimy +infinigen.assets.materials.smoke_material +infinigen.assets.materials.snake_plant +infinigen.assets.materials.snake_scale +infinigen.assets.materials.snake_shaders +infinigen.assets.materials.snow +infinigen.assets.materials.soil +infinigen.assets.materials.spider_plant +infinigen.assets.materials.spot_sparse_attr +infinigen.assets.materials.stone +infinigen.assets.materials.succulent +infinigen.assets.materials.three_color_spots +infinigen.assets.materials.tiger_attr +infinigen.assets.materials.tongue +infinigen.assets.materials.two_color_spots +infinigen.assets.materials.twocolorz +infinigen.assets.materials.waterfall_material +infinigen.assets.materials.water +infinigen.assets.materials.wood diff --git a/tests/test_nature_meshes_basic.py b/tests/test_meshes_basic.py similarity index 52% rename from tests/test_nature_meshes_basic.py rename to tests/test_meshes_basic.py index 1753fe448..8b79785dd 100644 --- a/tests/test_nature_meshes_basic.py +++ b/tests/test_meshes_basic.py @@ -8,14 +8,14 @@ from utils import ( setup_gin, - get_def_from_folder, + import_item, load_txt_list, check_factory_runs ) setup_gin() -@pytest.mark.parametrize('factory_name', load_txt_list('test_nature_meshes_basic.txt')) -def test_factory_runs(factory_name, **kwargs): - fac_class = get_def_from_folder(factory_name, 'infinigen/assets') +@pytest.mark.parametrize('pathspec', load_txt_list('test_meshes_basic.txt')) +def test_factory_runs(pathspec, **kwargs): + fac_class = import_item(pathspec) check_factory_runs(fac_class, **kwargs) \ No newline at end of file diff --git a/tests/test_meshes_basic.txt b/tests/test_meshes_basic.txt new file mode 100644 index 000000000..8bb4e16ba --- /dev/null +++ b/tests/test_meshes_basic.txt @@ -0,0 +1,109 @@ +#AntSwarmFactory +#BoidSwarmFactory +#ChameleonFactory +#FanCoralFactory +#FrogFactory +#FruitFactoryGeneralFruit +#GenericTreeFactory +#HerbivoreFactory +#infinigen.assets.creatures.CrabFactory # slow +#infinigen.assets.creatures.LobsterFactory # slow +#infinigen.assets.creatures.SpinyLobsterFactory # slow +#infinigen.assets.trees.TreeFactory # slow, TODO test with no leaves +#LeafFactoryIvy +#LizardFactory +#OctopusFactory +#ReedMonocotFactory +infinigen.assets.cactus.CactusFactory +infinigen.assets.cactus.ColumnarCactusFactory +infinigen.assets.cactus.GlobularCactusFactory +infinigen.assets.cactus.KalidiumCactusFactory +infinigen.assets.cactus.PrickyPearCactusFactory +infinigen.assets.corals.BrainCoralFactory +infinigen.assets.corals.BushCoralFactory +infinigen.assets.corals.CauliflowerCoralFactory +infinigen.assets.corals.CoralFactory +infinigen.assets.corals.ElkhornCoralFactory +infinigen.assets.corals.HoneycombCoralFactory +infinigen.assets.corals.LeatherCoralFactory +infinigen.assets.corals.StarCoralFactory +infinigen.assets.corals.TableCoralFactory +infinigen.assets.corals.TubeCoralFactory +infinigen.assets.corals.TwigCoralFactory +infinigen.assets.creatures.BeetleFactory +infinigen.assets.creatures.BirdFactory +infinigen.assets.creatures.CarnivoreFactory +infinigen.assets.creatures.CrustaceanFactory +infinigen.assets.creatures.DragonflyFactory +infinigen.assets.creatures.FishFactory +infinigen.assets.creatures.FlyingBirdFactory +infinigen.assets.creatures.JellyfishFactory +infinigen.assets.creatures.SnakeFactory +infinigen.assets.debris.LichenFactory +infinigen.assets.debris.MossFactory +infinigen.assets.debris.PineNeedleFactory +infinigen.assets.fruits.FruitFactoryApple +infinigen.assets.fruits.FruitFactoryBlackberry +infinigen.assets.fruits.FruitFactoryCoconutgreen +infinigen.assets.fruits.FruitFactoryCoconuthairy +infinigen.assets.fruits.FruitFactoryCompositional +infinigen.assets.fruits.FruitFactoryDurian +infinigen.assets.fruits.FruitFactoryPineapple +infinigen.assets.fruits.FruitFactoryStarfruit +infinigen.assets.fruits.FruitFactoryStrawberry +infinigen.assets.grassland.FlowerFactory +infinigen.assets.grassland.FlowerPlantFactory +infinigen.assets.grassland.GrassTuftFactory +infinigen.assets.leaves.LeafFactory +infinigen.assets.leaves.LeafFactoryBroadleaf +infinigen.assets.leaves.LeafFactoryGinko +infinigen.assets.leaves.LeafFactoryMaple +infinigen.assets.leaves.LeafFactoryPine +infinigen.assets.leaves.LeafFactoryV2 +infinigen.assets.lighting.CausticsLampFactory +infinigen.assets.lighting.GlowingRocksFactory +infinigen.assets.mollusk.AugerFactory +infinigen.assets.mollusk.ClamFactory +infinigen.assets.mollusk.ConchFactory +infinigen.assets.mollusk.MolluskFactory +infinigen.assets.mollusk.MusselFactory +infinigen.assets.mollusk.NautilusFactory +infinigen.assets.mollusk.ScallopFactory +infinigen.assets.mollusk.VoluteFactory +infinigen.assets.monocot.AgaveMonocotFactory +infinigen.assets.monocot.BananaMonocotFactory +infinigen.assets.monocot.GrassesMonocotFactory +infinigen.assets.monocot.KelpMonocotFactory +infinigen.assets.monocot.MaizeMonocotFactory +infinigen.assets.monocot.MonocotFactory +infinigen.assets.monocot.PineconeFactory +infinigen.assets.monocot.TaroMonocotFactory +infinigen.assets.monocot.TussockMonocotFactory +infinigen.assets.monocot.VeratrumMonocotFactory +infinigen.assets.monocot.WheatEarMonocotFactory +infinigen.assets.monocot.WheatMonocotFactory +infinigen.assets.mushroom.MushroomFactory +infinigen.assets.rocks.BlenderRockFactory +infinigen.assets.rocks.BoulderFactory +infinigen.assets.small_plants.FernFactory +infinigen.assets.small_plants.SnakePlantFactory +infinigen.assets.small_plants.SpiderPlantFactory +infinigen.assets.small_plants.SucculentFactory +infinigen.assets.trees.BushFactory +infinigen.assets.trees.TreeFlowerFactory +infinigen.assets.tropic_plants.CoconutTreeFactory +infinigen.assets.tropic_plants.LeafBananaTreeFactory +infinigen.assets.tropic_plants.LeafPalmPlantFactory +infinigen.assets.tropic_plants.LeafPalmTreeFactory +infinigen.assets.tropic_plants.PalmTreeFactory +infinigen.assets.tropic_plants.PlantBananaTreeFactory +infinigen.assets.underwater.SeaweedFactory +infinigen.assets.underwater.UrchinFactory +infinigen.assets.weather.AltocumulusFactory +infinigen.assets.weather.CloudFactory +infinigen.assets.weather.CumulonimbusFactory +infinigen.assets.weather.CumulusFactory +infinigen.assets.weather.DustMoteFactory +infinigen.assets.weather.RaindropFactory +infinigen.assets.weather.SnowflakeFactory +infinigen.assets.weather.StratocumulusFactory \ No newline at end of file diff --git a/tests/test_nature_materials_basic.txt b/tests/test_nature_materials_basic.txt deleted file mode 100644 index 476309fad..000000000 --- a/tests/test_nature_materials_basic.txt +++ /dev/null @@ -1,58 +0,0 @@ -aluminumdisp2tut -atmosphere_light_haze -bark_birch -bark -bark_random -basic_bsdf -beak -bird -blackbody_shader -bone -chitin -chunkyrock -cobble_stone -cracked_ground -dirt -eyeball -face_size_visualizer -fishbody -fish_eye_shader -fishfin -giraffe_attr -grass_blade_texture -horn -ice -lava -mountain -mud -new_whitewater -nose -reptile_brown_circle_attr -reptile_gray_attr -reptile_two_color_attr -river_water -sand -sandstone -scale -simple_brownish -simple_greenery -simple_whitish -slimy -smoke_material -snake_plant -snake_scale -snake_shaders -snow -soil -spider_plant -spot_sparse_attr -stone -succulent -three_color_spots -tiger_attr -tongue -two_color_spots -twocolorz -waterfall_material -water -wood diff --git a/tests/test_nature_meshes_basic.txt b/tests/test_nature_meshes_basic.txt deleted file mode 100644 index 2cc788b4f..000000000 --- a/tests/test_nature_meshes_basic.txt +++ /dev/null @@ -1,109 +0,0 @@ -#AntSwarmFactory -#BoidSwarmFactory -#ChameleonFactory -#FanCoralFactory -#FrogFactory -#FruitFactoryGeneralFruit -#GenericTreeFactory -#HerbivoreFactory -#LeafFactoryIvy -#LizardFactory -#OctopusFactory -#ReedMonocotFactory -AgaveMonocotFactory -AltocumulusFactory -AugerFactory -BananaMonocotFactory -BeetleFactory -BirdFactory -BlenderRockFactory -BoulderFactory -BrainCoralFactory -BushCoralFactory -BushFactory -CactusFactory -CarnivoreFactory -CauliflowerCoralFactory -CausticsLampFactory -ClamFactory -CloudFactory -CoconutTreeFactory -ColumnarCactusFactory -ConchFactory -CoralFactory -CrabFactory -CrustaceanFactory -CumulonimbusFactory -CumulusFactory -DragonflyFactory -DustMoteFactory -ElkhornCoralFactory -FernFactory -FishFactory -FlowerFactory -FlowerPlantFactory -FlyingBirdFactory -FruitFactoryApple -FruitFactoryBlackberry -FruitFactoryCoconutgreen -FruitFactoryCoconuthairy -FruitFactoryCompositional -FruitFactoryDurian -FruitFactoryPineapple -FruitFactoryStarfruit -FruitFactoryStrawberry -GlobularCactusFactory -GlowingRocksFactory -GrassesMonocotFactory -GrassTuftFactory -HoneycombCoralFactory -JellyfishFactory -KalidiumCactusFactory -KelpMonocotFactory -LeafBananaTreeFactory -LeafFactory -LeafFactoryBroadleaf -LeafFactoryGinko -LeafFactoryMaple -LeafFactoryPine -LeafFactoryV2 -LeafPalmPlantFactory -LeafPalmTreeFactory -LeatherCoralFactory -LichenFactory -LobsterFactory -MaizeMonocotFactory -MolluskFactory -MonocotFactory -MossFactory -MushroomFactory -MusselFactory -NautilusFactory -PalmTreeFactory -PineconeFactory -PineNeedleFactory -PlantBananaTreeFactory -PrickyPearCactusFactory -RaindropFactory -ScallopFactory -SeaweedFactory -SnakeFactory -SnakePlantFactory -SnowflakeFactory -SpiderPlantFactory -SpinyLobsterFactory -StarCoralFactory -StratocumulusFactory -SucculentFactory -TableCoralFactory -TaroMonocotFactory -TreeFactory -TreeFlowerFactory -TubeCoralFactory -TussockMonocotFactory -TwigCoralFactory -UrchinFactory -VeratrumMonocotFactory -VoluteFactory -WheatEarMonocotFactory -WheatMonocotFactory \ No newline at end of file diff --git a/tests/utils.py b/tests/utils.py index ff298fb1c..3b0817f6f 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -7,30 +7,27 @@ from infinigen.core import surface from infinigen.core.util import blender as butil +from infinigen.core import init def setup_gin(): gin.clear_config() - - gin.parse_config_files_and_bindings( - config_files=['examples/configs/base.gin'], - bindings=None, + init.apply_gin_configs( + configs_folder='infinigen_examples/configs', skip_unknown=True ) - surface.registry.initialize_from_gin() -def get_def_from_folder(name, folder): - root = Path(__file__).parent.parent - for file in (root/folder).iterdir(): - with gin.unlock_config(): - module_parent = str(file.parent.relative_to(root)).replace('/', '.') - module = importlib.import_module(f'{module_parent}.{file.stem}') - if hasattr(module, name): - return getattr(module, name) - - raise ModuleNotFoundError(f'Could not find any factory with {name=}, make sure it is imported by a direct descendent of infinigen.assets') +def import_item(name): + *path_parts, name = name.split('.') + with gin.unlock_config(): + + try: + return importlib.import_module('.' + name, '.'.join(path_parts)) + except ModuleNotFoundError: + mod = importlib.import_module('.'.join(path_parts)) + return getattr(mod, name) def load_txt_list(path): res = (Path(__file__).parent/path).read_text().splitlines() diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 02e2c2426..000000000 --- a/tox.ini +++ /dev/null @@ -1,40 +0,0 @@ -[tox] -min_version = 4.0 -env_list = - py310 - py39 - type - -[testenv] -description = run unit tests -deps = - pytest - coverage[toml] -commands = - coverage run -m pytest {posargs} - coverage xml - coverage report - -[testenv:ruff] -description = Lightening-fast linting for Python -skip_install = true -deps = ruff -commands = ruff {posargs:.} - -[testenv:package] -description = Build package and check metadata (or upload package) -skip_install = true -deps = - build -# twine -commands = - python -m build -# twine {posargs:check --strict} dist/* -#passenv = -# TWINE_USERNAME -# TWINE_PASSWORD -# TWINE_REPOSITORY_URL - -[testenv:type] -deps = mypy -commands = mypy src \ No newline at end of file