DI-PCG / core /assets /table.py
thuzhaowang's picture
init
b6a9b6d
raw
history blame
18 kB
# Copyright (C) 2023, 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: Yiming Zuo
import bpy
from numpy.random import choice, normal, uniform
from infinigen.assets.material_assignments import AssetList
from infinigen.assets.objects.tables.legs.single_stand import (
nodegroup_generate_single_stand,
)
from infinigen.assets.objects.tables.legs.square import nodegroup_generate_leg_square
from infinigen.assets.objects.tables.legs.straight import (
nodegroup_generate_leg_straight,
)
from infinigen.assets.objects.tables.strechers import nodegroup_strecher
from infinigen.assets.objects.tables.table_top import nodegroup_generate_table_top
from infinigen.assets.objects.tables.table_utils import (
nodegroup_create_anchors,
nodegroup_create_legs_and_strechers,
)
from infinigen.core import surface, tagging
from infinigen.core import tags as t
from infinigen.core.nodes import node_utils
# from infinigen.assets.materials import metal, metal_shader_list
# from infinigen.assets.materials.fabrics import fabric
from infinigen.core.nodes.node_wrangler import Nodes, NodeWrangler
from infinigen.core.placement.factory import AssetFactory
from infinigen.core.surface import NoApply
from infinigen.core.util.math import FixedSeed
@node_utils.to_nodegroup(
"geometry_create_legs", singleton=False, type="GeometryNodeTree"
)
def geometry_create_legs(nw: NodeWrangler, **kwargs):
createanchors = nw.new_node(
nodegroup_create_anchors().name,
input_kwargs={
"Profile N-gon": kwargs["Leg Number"],
"Profile Width": kwargs["Leg Placement Top Relative Scale"]
* kwargs["Top Profile Width"],
"Profile Aspect Ratio": kwargs["Top Profile Aspect Ratio"],
},
)
if kwargs["Leg Style"] == "single_stand":
leg = nw.new_node(
nodegroup_generate_single_stand(**kwargs).name,
input_kwargs={
"Leg Height": kwargs["Leg Height"],
"Leg Diameter": kwargs["Leg Diameter"],
"Resolution": 64,
},
)
leg = nw.new_node(
nodegroup_create_legs_and_strechers().name,
input_kwargs={
"Anchors": createanchors,
"Keep Legs": True,
"Leg Instance": leg,
"Table Height": kwargs["Top Height"],
"Leg Bottom Relative Scale": kwargs[
"Leg Placement Bottom Relative Scale"
],
"Align Leg X rot": True,
},
)
elif kwargs["Leg Style"] == "straight":
leg = nw.new_node(
nodegroup_generate_leg_straight(**kwargs).name,
input_kwargs={
"Leg Height": kwargs["Leg Height"],
"Leg Diameter": kwargs["Leg Diameter"],
"Resolution": 32,
"N-gon": kwargs["Leg NGon"],
"Fillet Ratio": 0.1,
},
)
strecher = nw.new_node(
nodegroup_strecher().name,
input_kwargs={"Profile Width": kwargs["Leg Diameter"] * 0.5},
)
leg = nw.new_node(
nodegroup_create_legs_and_strechers().name,
input_kwargs={
"Anchors": createanchors,
"Keep Legs": True,
"Leg Instance": leg,
"Table Height": kwargs["Top Height"],
"Strecher Instance": strecher,
"Strecher Index Increment": kwargs["Strecher Increament"],
"Strecher Relative Position": kwargs["Strecher Relative Pos"],
"Leg Bottom Relative Scale": kwargs[
"Leg Placement Bottom Relative Scale"
],
"Align Leg X rot": True,
},
)
elif kwargs["Leg Style"] == "square":
leg = nw.new_node(
nodegroup_generate_leg_square(**kwargs).name,
input_kwargs={
"Height": kwargs["Leg Height"],
"Width": 0.707
* kwargs["Leg Placement Top Relative Scale"]
* kwargs["Top Profile Width"]
* kwargs["Top Profile Aspect Ratio"],
"Has Bottom Connector": (kwargs["Strecher Increament"] > 0),
"Profile Width": kwargs["Leg Diameter"],
},
)
leg = nw.new_node(
nodegroup_create_legs_and_strechers().name,
input_kwargs={
"Anchors": createanchors,
"Keep Legs": True,
"Leg Instance": leg,
"Table Height": kwargs["Top Height"],
"Leg Bottom Relative Scale": kwargs[
"Leg Placement Bottom Relative Scale"
],
"Align Leg X rot": True,
},
)
else:
raise NotImplementedError
leg = nw.new_node(
Nodes.SetMaterial,
input_kwargs={"Geometry": leg, "Material": kwargs["LegMaterial"]},
)
group_output = nw.new_node(
Nodes.GroupOutput,
input_kwargs={"Geometry": leg},
attrs={"is_active_output": True},
)
def geometry_assemble_table(nw: NodeWrangler, **kwargs):
# Code generated using version 2.6.4 of the node_transpiler
generatetabletop = nw.new_node(
nodegroup_generate_table_top().name,
input_kwargs={
"Thickness": kwargs["Top Thickness"],
"N-gon": kwargs["Top Profile N-gon"],
"Profile Width": kwargs["Top Profile Width"],
"Aspect Ratio": kwargs["Top Profile Aspect Ratio"],
"Fillet Ratio": kwargs["Top Profile Fillet Ratio"],
"Fillet Radius Vertical": kwargs["Top Vertical Fillet Ratio"],
},
)
tabletop_instance = nw.new_node(
Nodes.Transform,
input_kwargs={
"Geometry": generatetabletop,
"Translation": (0.0000, 0.0000, kwargs["Top Height"]),
},
)
tabletop_instance = nw.new_node(
Nodes.SetMaterial,
input_kwargs={"Geometry": tabletop_instance, "Material": kwargs["TopMaterial"]},
)
legs = nw.new_node(geometry_create_legs(**kwargs).name)
join_geometry = nw.new_node(
Nodes.JoinGeometry, input_kwargs={"Geometry": [tabletop_instance, legs]}
)
group_output = nw.new_node(
Nodes.GroupOutput,
input_kwargs={"Geometry": join_geometry},
attrs={"is_active_output": True},
)
class TableDiningFactory(AssetFactory):
def __init__(self, factory_seed, coarse=False, dimensions=None):
super(TableDiningFactory, self).__init__(factory_seed, coarse=coarse)
self.dimensions = dimensions
self.get_params_dict()
self.leg_styles = ["single_stand", "square", "straight"]
with FixedSeed(factory_seed):
self.params = self.sample_parameters(dimensions)
# self.clothes_scatter = ClothesCover(factory_fn=blanket.BlanketFactory, width=log_uniform(.8, 1.2),
# size=uniform(.8, 1.2)) if uniform() < .3 else NoApply()
self.clothes_scatter = NoApply()
self.material_params, self.scratch, self.edge_wear = (
self.get_material_params()
)
self.params.update(self.material_params)
def get_params_dict(self):
# list all the parameters (key:name, value: [type, range]) used in this generator
self.params_dict = {
"ngon": ["discrete", (4, 36)],
"dimension_x": ["continuous", (0.9, 2.2)],
"dimension_y": ["continuous", (0.9, 2.2)],
"dimension_z": ["continuous", (0.5, 0.9)],
"leg_style": ["discrete", (0, 1, 2)],
"leg_number": ["discrete", (1, 2, 4)],
"leg_ngon": ["discrete", (4, 12)],
"leg_diameter": ["continuous", (0, 1)],
"leg_height": ["continuous", (0.6, 2.0)],
"leg_curve_ctrl_pts0": ["continuous", (0, 1)],
"leg_curve_ctrl_pts1": ["continuous", (0, 1)],
"leg_curve_ctrl_pts2": ["continuous", (0, 1)],
"top_scale": ["continuous", (0.6, 0.8)], # leg start point relative position
"bottom_scale": ["continuous", (0.9, 1.3)], # leg end point relative position
"top_thickness": ["continuous", (0.02, 0.1)],
"top_profile_fillet_ratio": ["continuous", (-0.6, 0.6)], # table corner round / square
"top_vertical_fillet_ratio": ["continuous", (0.0, 0.2)], # table corner round / square
"strecher_relative_pos": ["continuous", (0.15, 0.8)],
"strecher_increament": ["discrete", (0, 1, 2)],
}
def get_material_params(self):
material_assignments = AssetList["TableDiningFactory"]()
params = {
"TopMaterial": material_assignments["top"].assign_material(),
"LegMaterial": material_assignments["leg"].assign_material(),
}
wrapped_params = {
k: surface.shaderfunc_to_material(v) for k, v in params.items()
}
scratch_prob, edge_wear_prob = material_assignments["wear_tear_prob"]
scratch, edge_wear = material_assignments["wear_tear"]
is_scratch = uniform() < scratch_prob
is_edge_wear = uniform() < edge_wear_prob
if not is_scratch:
scratch = None
if not is_edge_wear:
edge_wear = None
return wrapped_params, scratch, edge_wear
@staticmethod
def sample_parameters(dimensions):
# not used in DI-PCG
if dimensions is None:
width = uniform(0.91, 1.16)
if uniform() < 0.7:
# oblong
length = uniform(1.4, 2.8)
else:
# approx square
length = width * normal(1, 0.1)
dimensions = (length, width, uniform(0.65, 0.85))
# all in meters
x, y, z = dimensions
NGon = 4
leg_style = choice(["straight", "single_stand", "square"], p=[0.5, 0.1, 0.4])
# leg_style = choice(['straight'])
if leg_style == "single_stand":
leg_number = 2
leg_diameter = uniform(0.22 * x, 0.28 * x)
leg_curve_ctrl_pts = [
(0.0, uniform(0.1, 0.2)),
(0.5, uniform(0.1, 0.2)),
(0.9, uniform(0.2, 0.3)),
(1.0, 1.0),
]
top_scale = uniform(0.6, 0.7)
bottom_scale = 1.0
elif leg_style == "square":
leg_number = 2
leg_diameter = uniform(0.07, 0.10)
leg_curve_ctrl_pts = None
top_scale = 0.8
bottom_scale = 1.0
elif leg_style == "straight":
leg_diameter = uniform(0.05, 0.07)
leg_number = 4
leg_curve_ctrl_pts = [
(0.0, 1.0),
(0.4, uniform(0.85, 0.95)),
(1.0, uniform(0.4, 0.6)),
]
top_scale = 0.8
bottom_scale = uniform(1.0, 1.2)
else:
raise NotImplementedError
top_thickness = uniform(0.03, 0.06)
parameters = {
"Top Profile N-gon": NGon,
"Top Profile Width": 1.414 * x,
"Top Profile Aspect Ratio": y / x,
"Top Profile Fillet Ratio": uniform(0.0, 0.02),
"Top Thickness": top_thickness,
"Top Vertical Fillet Ratio": uniform(0.1, 0.3),
# 'Top Material': choice(['marble', 'tiled_wood', 'metal', 'fabric'], p=[.3, .3, .2, .2]),
"Height": z,
"Top Height": z - top_thickness,
"Leg Number": leg_number,
"Leg Style": leg_style,
"Leg NGon": 4,
"Leg Placement Top Relative Scale": top_scale,
"Leg Placement Bottom Relative Scale": bottom_scale,
"Leg Height": 1.0,
"Leg Diameter": leg_diameter,
"Leg Curve Control Points": leg_curve_ctrl_pts,
# 'Leg Material': choice(['metal', 'wood', 'glass', 'plastic']),
"Strecher Relative Pos": uniform(0.2, 0.6),
"Strecher Increament": choice([0, 1, 2]),
}
return parameters
def fix_unused_params(self, params):
if params['leg_style'] == 0:
# single stand only allow 1 or 2 legs
if params['leg_number'] == 4:
params['leg_number'] = 2
params['bottom_scale'] = 1.1
params['strecher_increament'] = 1
elif params['leg_style'] == 1:
params['leg_number'] = 2
params['leg_curve_ctrl_pts0'] = 0.5
params['leg_curve_ctrl_pts1'] = 0.5
params['leg_curve_ctrl_pts2'] = 0.5
params['bottom_scale'] = 1.1
params['top_scale'] = 0.8
params['strecher_increament'] = 1
elif params['leg_style'] == 2:
params['leg_number'] = 4
params['leg_curve_ctrl_pts0'] = 0.5
params['top_scale'] = 0.8
if params['ngon'] == 36:
params['top_profile_fillet_ratio'] = 0.0
params['top_vertical_fillet_ratio'] = 0.0
return params
def update_params(self, params):
x, y, z = params["dimension_x"], params["dimension_y"], params["dimension_z"]
NGon = params['ngon']
leg_style = self.leg_styles[int(params['leg_style'])]
if leg_style == "single_stand":
leg_number = params['leg_number']
if leg_number == 4:
leg_number = 2
leg_diameter = (0.2 + 0.2 * params['leg_diameter']) * x
leg_curve_ctrl_pts = [
(0.0, 0.1 + 0.8 * params['leg_curve_ctrl_pts0']),
(0.5, 0.1 + 0.8 * params['leg_curve_ctrl_pts1']),
(0.9, 0.2 + 0.8 * params['leg_curve_ctrl_pts2']),
(1.0, 1.0),
]
top_scale = params['top_scale']
bottom_scale = 1.0
strecher_increament = 1
elif leg_style == "square":
leg_number = 2
leg_diameter = 0.05 + 0.2 * params['leg_diameter']
leg_curve_ctrl_pts = None
top_scale = 0.8
bottom_scale = 1.0
strecher_increament = 1
elif leg_style == "straight":
leg_diameter = 0.05 + 0.2 * params['leg_diameter']
leg_number = 4
leg_curve_ctrl_pts = [
(0.0, 1.0),
(0.4, 0.5 + 0.5 * params['leg_curve_ctrl_pts1']),
(1.0, 0.3 + 0.5 * params['leg_curve_ctrl_pts2'])
]
top_scale = 0.8
bottom_scale = params['bottom_scale']
strecher_increament = params["strecher_increament"]
else:
raise NotImplementedError
if params['ngon'] == 36:
top_profile_fillet_ratio = 0.0
top_vertical_fillet_ratio = 0.0
else:
top_profile_fillet_ratio = params['top_profile_fillet_ratio']
top_vertical_fillet_ratio = params['top_vertical_fillet_ratio']
top_thickness = params['top_thickness']
parameters = {
"Top Profile N-gon": NGon,
"Top Profile Width": 1.414 * x,
"Top Profile Aspect Ratio": y / x,
"Top Profile Fillet Ratio": top_profile_fillet_ratio,
"Top Thickness": top_thickness,
"Top Vertical Fillet Ratio": top_vertical_fillet_ratio,
"Height": z,
"Top Height": z - top_thickness,
"Leg Number": leg_number,
"Leg Style": leg_style,
"Leg NGon": params['leg_ngon'],
"Leg Placement Top Relative Scale": top_scale,
"Leg Placement Bottom Relative Scale": bottom_scale,
"Leg Height": params['leg_height'],
"Leg Diameter": leg_diameter,
"Leg Curve Control Points": leg_curve_ctrl_pts,
"Strecher Relative Pos": params["strecher_relative_pos"],
"Strecher Increament": strecher_increament,
}
self.params.update(parameters)
self.clothes_scatter = NoApply()
self.material_params, self.scratch, self.edge_wear = (
self.get_material_params()
)
self.params.update(self.material_params)
def create_asset(self, **params):
bpy.ops.mesh.primitive_plane_add(
size=2,
enter_editmode=False,
align="WORLD",
location=(0, 0, 0),
scale=(1, 1, 1),
)
obj = bpy.context.active_object
# surface.add_geomod(obj, geometry_assemble_table, apply=False, input_kwargs=self.params)
surface.add_geomod(
obj, geometry_assemble_table, apply=True, input_kwargs=self.params
)
tagging.tag_system.relabel_obj(obj)
assert tagging.tagged_face_mask(obj, {t.Subpart.SupportSurface}).sum() != 0
return obj
def finalize_assets(self, assets):
if self.scratch:
self.scratch.apply(assets)
if self.edge_wear:
self.edge_wear.apply(assets)
# def finalize_assets(self, assets):
# self.clothes_scatter.apply(assets)
class SideTableFactory(TableDiningFactory):
def __init__(self, factory_seed, coarse=False, dimensions=None):
if dimensions is None:
w = 0.55 * normal(1, 0.05)
h = 0.95 * w * normal(1, 0.05)
dimensions = (w, w, h)
super().__init__(factory_seed, coarse=coarse, dimensions=dimensions)
class CoffeeTableFactory(TableDiningFactory):
def __init__(self, factory_seed, coarse=False, dimensions=None):
if dimensions is None:
dimensions = (uniform(1, 1.5), uniform(0.6, 0.9), uniform(0.4, 0.5))
super().__init__(factory_seed, coarse=coarse, dimensions=dimensions)