Skip to content

Latest commit

 

History

History
353 lines (261 loc) · 15.1 KB

proc_gen_kitchen.md

File metadata and controls

353 lines (261 loc) · 15.1 KB
Scene Setup (High-Level APIs)

Procedural Generation (the ProcGenKitchen add-on)

The ProcGenKitchen add-on is currently TDW's only high-level proc-gen scene setup implementation. In the near future, we intend to add proc-gen add-ons for other types of rooms e.g. a ProcGenBedroom.

Create procedurally-generated kitchens by creating a ProcGenKitchen, adding it to c.add_ons, and then calling proc_gen_kitchen.create():

from tdw.controller import Controller
from tdw.add_ons.proc_gen_kitchen import ProcGenKitchen
from tdw.add_ons.third_person_camera import ThirdPersonCamera
from tdw.add_ons.image_capture import ImageCapture
from tdw.backend.paths import EXAMPLE_CONTROLLER_OUTPUT_PATH

path = EXAMPLE_CONTROLLER_OUTPUT_PATH.joinpath("proc_gen_kitchen_minimal")
print(f"Images will be saved to: {path}")
proc_gen_kitchen = ProcGenKitchen()
proc_gen_kitchen.create()
camera = ThirdPersonCamera(position={"x": 2, "y": 1.8, "z": -0.5},
                           look_at={"x": 0, "y": 0.6, "z": 0},
                           avatar_id="a")
capture = ImageCapture(avatar_ids=["a"], path=path, pass_masks=["_img"])
c = Controller()
c.add_ons.extend([proc_gen_kitchen, camera, capture])
c.communicate([])
c.communicate({"$type": "terminate"})

Result:

How ProcGenKitchen works

Broadly speaking, ProcGenKitchen adds arrangements of objects to the scene. These arrangements are quasi-atomic groups of objects, meaning that ProcGenKitchen will position and rotate all objects belonging to an arrangement as if they were a single object. Object arrangements are thus positioned in the room to create the kitchen. For example, ProcGenKitchen will create lateral arrangements of Arrangement groups, e.g. kitchen counters, sinks, stoves, etc.

A detailed explanation of how ProcGenKitchen works can be found in the API documentation. When reading the API documentation, be sure to click through to the documentation for each Arrangement because most of the parameters controlling scene generation will be found in Arrangement and its subclasses, not ProcGenKitchen. For an overview of how Arrangements work, read this.

That said, you can set some optional parameters in create() and by adjusting ProcGenKitchen class variables, as described below:

create() optional parameters

rng

If you try to run the previous example, you'll get a different image because every kitchen scene is randomly generated. proc_gen_kitchen includes an optional rng parameter that can be either an integer or a numpy RandomState object (for example: rng=numpy.random.RandomState(0)).

If you set the rng parameter, you'll recreate the same scene every time.

from tdw.controller import Controller
from tdw.add_ons.proc_gen_kitchen import ProcGenKitchen
from tdw.add_ons.third_person_camera import ThirdPersonCamera
from tdw.add_ons.image_capture import ImageCapture
from tdw.backend.paths import EXAMPLE_CONTROLLER_OUTPUT_PATH

path = EXAMPLE_CONTROLLER_OUTPUT_PATH.joinpath("proc_gen_kitchen_random_seed")
print(f"Images will be saved to: {path}")
proc_gen_kitchen = ProcGenKitchen()
proc_gen_kitchen.create(rng=0)
camera = ThirdPersonCamera(position={"x": -2, "y": 1.8, "z": -0.5},
                           look_at={"x": 0, "y": 0.6, "z": 0},
                           avatar_id="a")
capture = ImageCapture(avatar_ids=["a"], path=path, pass_masks=["_img"])
c = Controller()
c.add_ons.extend([proc_gen_kitchen, camera, capture])
c.communicate([])
c.communicate({"$type": "terminate"})

Result:

scene

Set the scene parameter to specify the scene. If None, a random valid scene will be selected.

Only certain scenes are valid for ProcGenKitchen. They must be single-room interior scenes with pre-set room data. Although it is not a requirement per se, ProcGenKitchen does work best with smaller rooms.

If scene isn't set or is set to None, the scene is randomly selected. To review the list of possible scenes:

from tdw.add_ons.proc_gen_kitchen import ProcGenKitchen

for scene_name in ProcGenKitchen.SCENE_NAMES:
    print(scene_name)

Output:

mm_craftroom_2a
mm_craftroom_2b
mm_craftroom_3a
mm_craftroom_3b
mm_kitchen_2a
mm_kitchen_2b
mm_kitchen_3a
mm_kitchen_3b

scene can alternatively be:

  • A string, for example "mm_craftroom_2a"
  • A SceneRecord, for example SceneLibrarian().get_record("mm_craftroom_2a")
  • A Room, in which case the add-on assumes that the actual scene has already been loaded.
  • A list of strings and/or SceneRecord, in which case a random string or SceneRecord will be selected.

This example explicitly sets the scene parameter to a scene name. Notice that we're also setting the rng parameter; this is not necessary but it will be used for setting everything in the scene except for selecting the scene itself.

from tdw.controller import Controller
from tdw.add_ons.proc_gen_kitchen import ProcGenKitchen
from tdw.add_ons.third_person_camera import ThirdPersonCamera
from tdw.add_ons.image_capture import ImageCapture
from tdw.backend.paths import EXAMPLE_CONTROLLER_OUTPUT_PATH

path = EXAMPLE_CONTROLLER_OUTPUT_PATH.joinpath("proc_gen_kitchen_scene")
print(f"Images will be saved to: {path}")
proc_gen_kitchen = ProcGenKitchen()
proc_gen_kitchen.create(scene="mm_kitchen_2b", rng=0)
camera = ThirdPersonCamera(position={"x": -2, "y": 1.8, "z": -1},
                           look_at={"x": 0, "y": 0.6, "z": 0},
                           avatar_id="a")
capture = ImageCapture(avatar_ids=["a"], path=path, pass_masks=["_img"])
c = Controller()
c.add_ons.extend([proc_gen_kitchen, camera, capture])
c.communicate([])
c.communicate({"$type": "terminate"})

Result:

room_index

The room_index parameter is mostly for future-proofing ProcGenKitchen for a time when it can be used in a multi-room scene; this parameter should almost always be set to 0 (the default value).

cabinetry_type

cabinetry_type is a CabinetryType enum value that will determine which set of kitchen counters, sinks, and wall cabinets to add to the scene. By default, this value is None, meaning that the cabinetry set is chosen randomly.

ProcGenKitchen class variables

TALL_ARRANGEMENTS

ProcGenKitchen.TALL_ARRANGEMENTS A list of lowercase names of "tall" Arrangements:

from tdw.add_ons.proc_gen_kitchen import ProcGenKitchen

for arrangement in ProcGenKitchen.TALL_ARRANGEMENTS:
    print(arrangement)

Output:

refrigerator
shelf

When adding arrangements along a wall with windows, ProcGenKitchen will swap tall arrangements for shorter arrangements (which shorter arrangement varies and is hardcoded; read the API documentation).

This example controller generates a kitchen that allows refrigerators and shelves to be placed along walls with windows:

from tdw.controller import Controller
from tdw.add_ons.proc_gen_kitchen import ProcGenKitchen
from tdw.add_ons.third_person_camera import ThirdPersonCamera
from tdw.add_ons.image_capture import ImageCapture
from tdw.backend.paths import EXAMPLE_CONTROLLER_OUTPUT_PATH

ProcGenKitchen.TALL_ARRANGEMENTS = []
path = EXAMPLE_CONTROLLER_OUTPUT_PATH.joinpath("proc_gen_kitchen_tall")
print(f"Images will be saved to: {path}")
proc_gen_kitchen = ProcGenKitchen()
proc_gen_kitchen.create()
camera = ThirdPersonCamera(position={"x": 2, "y": 1.8, "z": -0.5},
                           look_at={"x": 0, "y": 0.6, "z": 0},
                           avatar_id="a")
capture = ImageCapture(avatar_ids=["a"], path=path, pass_masks=["_img"])
c = Controller()
c.add_ons.extend([proc_gen_kitchen, camera, capture])
c.communicate([])
c.communicate({"$type": "terminate"})

SECONDARY_ARRANGEMENTS

"Secondary arrangements" are arrangements that can be "appended" to a "main lateral arrangement" (for example, at the end of a span of kitchen counters), or added elsewhere in the room (such as an alcove).

Secondary arrangements are stored in ProcGenKitchen.SECONDARY_ARRANGEMENTS as a dictionary. The dictionary has three keys that should never be adjusted. The values of the dictionary are dictionaries, where the key is a string representation of an arrangement and the value is an integer describing the probability of adding that arrangement.

This will print the likelihood of adding different arrangements to the end of a "main lateral arrangement":

from tdw.add_ons.proc_gen_kitchen import ProcGenKitchen

print(ProcGenKitchen.SECONDARY_ARRANGEMENTS["main"])

Output:

{'side_table': 1, 'basket': 2, 'shelf': 2, 'painting': 4, 'void': 3, 'radiator': 1, 'stool': 2, 'suitcase': 1}

SCENE_NAMES

ProcGenKitchen.SCENE_NAMES is a list of scene names. If the scene parameter in create() is None (the default value), the scene will be randomly selected from ProcGenKitchen.SCENE_NAMES.

This list contains only a subset of valid scenes. ProcGenKitchen tends to create more plausible kitchens in scenes with only one room and one region; scenes with rooms that have alcoves have been excluded.

This example adds all scenes with room data to the list of possible scenes:

from tdw.controller import Controller
from tdw.add_ons.proc_gen_kitchen import ProcGenKitchen
from tdw.add_ons.third_person_camera import ThirdPersonCamera
from tdw.add_ons.image_capture import ImageCapture
from tdw.backend.paths import EXAMPLE_CONTROLLER_OUTPUT_PATH
from tdw.librarian import SceneLibrarian

scene_librarian = SceneLibrarian()
for record in scene_librarian.records:
    if len(record.rooms) > 0 and record.name not in ProcGenKitchen.SCENE_NAMES:
        print(record.name)
        ProcGenKitchen.SCENE_NAMES.append(record.name)
path = EXAMPLE_CONTROLLER_OUTPUT_PATH.joinpath("proc_gen_kitchen_tall")
print(f"Images will be saved to: {path}")
proc_gen_kitchen = ProcGenKitchen()
proc_gen_kitchen.create()
camera = ThirdPersonCamera(position={"x": 2, "y": 1.8, "z": -0.5},
                           look_at={"x": 0, "y": 0.6, "z": 0},
                           avatar_id="a")
capture = ImageCapture(avatar_ids=["a"], path=path, pass_masks=["_img"])
c = Controller()
c.add_ons.extend([proc_gen_kitchen, camera, capture])
c.communicate([])
c.communicate({"$type": "terminate"})

Output:

mm_craftroom_1a
mm_craftroom_1b
mm_craftroom_4a
mm_craftroom_4b
mm_kitchen_1a
mm_kitchen_1b
mm_kitchen_4a
mm_kitchen_4b

ProcGenKitchen and other add-ons

ProcGenKitchen is meant to be as modular as possible and it can be combined with other add-ons and commands. You've already seen that it's possible to use ProcGenKitchen along with ThirdPersonCamera and ImageCapture.

This example combines ProcGenKitchen with InteriorSceneLighting. Note that we can use the same random seed for both add-ons to ensure that the scene is recreated exactly the same every time:

import numpy as np
from tdw.controller import Controller
from tdw.add_ons.proc_gen_kitchen import ProcGenKitchen
from tdw.add_ons.third_person_camera import ThirdPersonCamera
from tdw.add_ons.image_capture import ImageCapture
from tdw.add_ons.interior_scene_lighting import InteriorSceneLighting
from tdw.backend.paths import EXAMPLE_CONTROLLER_OUTPUT_PATH

path = EXAMPLE_CONTROLLER_OUTPUT_PATH.joinpath("proc_gen_kitchen_lighting")
print(f"Images will be saved to: {path}")
proc_gen_kitchen = ProcGenKitchen()
random_seed = 14
proc_gen_kitchen.create(rng=np.random.RandomState(random_seed))
interior_scene_lighting = InteriorSceneLighting(rng=np.random.RandomState(random_seed))
camera = ThirdPersonCamera(position={"x": -1, "y": 1.8, "z": 2},
                           look_at={"x": 0, "y": 1, "z": 0},
                           avatar_id="a")
capture = ImageCapture(avatar_ids=["a"], path=path, pass_masks=["_img"])
c = Controller()
c.add_ons.extend([proc_gen_kitchen, interior_scene_lighting, camera, capture])
c.communicate([{"$type": "set_screen_size",
                "width": 1280,
                "height": 720}])
c.communicate({"$type": "terminate"})

Result:

How to reset the scene

Unlike most add-ons, it is not necessary to reset ProcGenKitchen when a scene resets. Do not do this: proc_gen_kitchen.initialized = False. Instead, simply call proc_gen_kitchen.create(scene) to create a new scene.

Saving scene setup commands

Whenever ProcGenKitchen is updated, it is possible that a random seed used in a previous version will generate a different scene in a newer version. In fact, it's likely that subsequent changes to ProcGenKitchen since this document was written have adjusted the procedural generator such that if you run the example controllers, you'll capture different images.

In order to guarantee that you can recreate a kitchen scene, you can save the list of commands in addition to the random seed.

When you call proc_gen_kitchen.create(), the add-on appends a long list of commands to proc_gen_kitchen.commands. As with all other add-ons, the commands list will be automatically sent to the build the next time c.communicate() is called.

You can save proc_gen_kitchen.commands to a JSON file like this:

from json import dumps
from pathlib import Path
from tdw.add_ons.proc_gen_kitchen import ProcGenKitchen

proc_gen_kitchen = ProcGenKitchen()
proc_gen_kitchen.create(rng=0)
Path("proc_gen_kitchen.json").write_text(dumps(proc_gen_kitchen.commands))

...and then reload them like this:

from json import loads
from pathlib import Path
from tdw.controller import Controller

c = Controller()
c.communicate(loads(Path("proc_gen_kitchen.json").read_text()))

Next: Regions, interior regions, and rooms

Return to the README


Example controllers:

Python API: