Spaces:
Running
on
Zero
Running
on
Zero
# 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: Beining Han | |
import bpy | |
import numpy as np | |
from numpy.random import uniform | |
import random | |
import time | |
from infinigen.assets.materials.plastics.plastic_rough import shader_rough_plastic | |
from infinigen.core import surface, tagging | |
from infinigen.core.nodes import node_utils | |
from infinigen.core.nodes.node_wrangler import Nodes, NodeWrangler | |
from infinigen.core.placement.factory import AssetFactory | |
def nodegroup_holes(nw: NodeWrangler): | |
# Code generated using version 2.6.4 of the node_transpiler | |
group_input = nw.new_node( | |
Nodes.GroupInput, | |
expose_input=[ | |
("NodeSocketFloat", "height", 0.5000), | |
("NodeSocketFloat", "gap_size", 0.5000), | |
("NodeSocketFloat", "hole_edge_gap", 0.5000), | |
("NodeSocketFloat", "hole_size", 0.5000), | |
("NodeSocketFloat", "depth", 0.5000), | |
("NodeSocketFloat", "width", 0.5000), | |
], | |
) | |
add = nw.new_node( | |
Nodes.Math, | |
input_kwargs={0: group_input.outputs["hole_edge_gap"], 1: 0.0000}, | |
attrs={"operation": "ADD"} | |
) | |
subtract = nw.new_node( | |
Nodes.Math, | |
input_kwargs={0: group_input.outputs["height"], 1: add}, | |
attrs={"operation": "SUBTRACT"}, | |
) | |
add_1 = nw.new_node( | |
Nodes.Math, | |
input_kwargs={0: group_input.outputs["width"], 1: 0.0000}, | |
attrs={"operation": "ADD"} | |
) | |
subtract_1 = nw.new_node( | |
Nodes.Math, input_kwargs={0: add_1, 1: add}, attrs={"operation": "SUBTRACT"} | |
) | |
add_2 = nw.new_node( | |
Nodes.Math, input_kwargs={0: group_input.outputs["hole_size"], 1: 0.0000}, attrs={"operation": "ADD"} | |
) | |
add_3 = nw.new_node( | |
Nodes.Math, input_kwargs={0: add_2, 1: group_input.outputs["gap_size"]}, attrs={"operation": "ADD"} | |
) | |
divide = nw.new_node( | |
Nodes.Math, input_kwargs={0: subtract, 1: add_3}, attrs={"operation": "DIVIDE"} | |
) | |
divide_1 = nw.new_node( | |
Nodes.Math, | |
input_kwargs={0: subtract_1, 1: add_3}, | |
attrs={"operation": "DIVIDE"}, | |
) | |
grid = nw.new_node( | |
Nodes.MeshGrid, | |
input_kwargs={ | |
"Size X": subtract, | |
"Size Y": subtract_1, | |
"Vertices X": divide, | |
"Vertices Y": divide_1, | |
}, | |
) | |
store_named_attribute = nw.new_node( | |
Nodes.StoreNamedAttribute, | |
input_kwargs={ | |
"Geometry": grid.outputs["Mesh"], | |
"Name": "uv_map", | |
3: grid.outputs["UV Map"], | |
}, | |
attrs={"domain": "CORNER", "data_type": "FLOAT_VECTOR"}, | |
) | |
transform_1 = nw.new_node( | |
Nodes.Transform, | |
input_kwargs={ | |
"Geometry": store_named_attribute, | |
"Rotation": (0.0000, 1.5708, 0.0000), | |
}, | |
) | |
add_4 = nw.new_node( | |
Nodes.Math, input_kwargs={0: group_input.outputs["depth"], 1: 0.0000}, attrs={"operation": "ADD"} | |
) | |
add_5 = nw.new_node(Nodes.Math, input_kwargs={0: add_4, 1: 0.1}, attrs={"operation": "ADD"}) | |
combine_xyz_3 = nw.new_node( | |
Nodes.CombineXYZ, input_kwargs={"X": add_5, "Y": add_2, "Z": add_2} | |
) | |
cube_2 = nw.new_node(Nodes.MeshCube, input_kwargs={"Size": combine_xyz_3}) | |
store_named_attribute_1 = nw.new_node( | |
Nodes.StoreNamedAttribute, | |
input_kwargs={ | |
"Geometry": cube_2.outputs["Mesh"], | |
"Name": "uv_map", | |
3: cube_2.outputs["UV Map"], | |
}, | |
attrs={"domain": "CORNER", "data_type": "FLOAT_VECTOR"}, | |
) | |
instance_on_points = nw.new_node( | |
Nodes.InstanceOnPoints, | |
input_kwargs={"Points": transform_1, "Instance": store_named_attribute_1}, | |
) | |
subtract_2 = nw.new_node( | |
Nodes.Math, input_kwargs={0: add_4, 1: add}, attrs={"operation": "SUBTRACT"} | |
) | |
divide_2 = nw.new_node( | |
Nodes.Math, | |
input_kwargs={0: subtract_2, 1: add_3}, | |
attrs={"operation": "DIVIDE"}, | |
) | |
grid_1 = nw.new_node( | |
Nodes.MeshGrid, | |
input_kwargs={ | |
"Size X": subtract_2, | |
"Size Y": subtract, | |
"Vertices X": divide_2, | |
"Vertices Y": divide, | |
}, | |
) | |
store_named_attribute_2 = nw.new_node( | |
Nodes.StoreNamedAttribute, | |
input_kwargs={ | |
"Geometry": grid_1.outputs["Mesh"], | |
"Name": "uv_map", | |
3: grid_1.outputs["UV Map"], | |
}, | |
attrs={"domain": "CORNER", "data_type": "FLOAT_VECTOR"}, | |
) | |
transform_2 = nw.new_node( | |
Nodes.Transform, | |
input_kwargs={ | |
"Geometry": store_named_attribute_2, | |
"Rotation": (1.5708, 0.0000, 0.0000), | |
}, | |
) | |
add_6 = nw.new_node(Nodes.Math, input_kwargs={0: add_1, 1: 0.1}, attrs={"operation": "ADD"}) | |
combine_xyz_4 = nw.new_node( | |
Nodes.CombineXYZ, input_kwargs={"X": add_2, "Y": add_6, "Z": add_2} | |
) | |
cube_3 = nw.new_node(Nodes.MeshCube, input_kwargs={"Size": combine_xyz_4}) | |
store_named_attribute_3 = nw.new_node( | |
Nodes.StoreNamedAttribute, | |
input_kwargs={ | |
"Geometry": cube_3.outputs["Mesh"], | |
"Name": "uv_map", | |
3: cube_3.outputs["UV Map"], | |
}, | |
attrs={"domain": "CORNER", "data_type": "FLOAT_VECTOR"}, | |
) | |
instance_on_points_1 = nw.new_node( | |
Nodes.InstanceOnPoints, | |
input_kwargs={"Points": transform_2, "Instance": store_named_attribute_3}, | |
) | |
group_output = nw.new_node( | |
Nodes.GroupOutput, | |
input_kwargs={ | |
"Instances1": instance_on_points, | |
"Instances2": instance_on_points_1, | |
}, | |
attrs={"is_active_output": True}, | |
) | |
def nodegroup_handle_hole(nw: NodeWrangler): | |
# Code generated using version 2.6.4 of the node_transpiler | |
group_input = nw.new_node( | |
Nodes.GroupInput, | |
expose_input=[ | |
("NodeSocketFloat", "X", 0.0000), | |
("NodeSocketFloat", "Z", 0.0000), | |
("NodeSocketFloat", "height", 0.5000), | |
("NodeSocketFloat", "hole_dist", 0.5000), | |
("NodeSocketInt", "Level", 0), | |
], | |
) | |
combine_xyz_3 = nw.new_node( | |
Nodes.CombineXYZ, | |
input_kwargs={ | |
"X": group_input.outputs["X"], | |
"Y": 1.0000, | |
"Z": group_input.outputs["Z"], | |
}, | |
) | |
cube_2 = nw.new_node(Nodes.MeshCube, input_kwargs={"Size": combine_xyz_3}) | |
store_named_attribute = nw.new_node( | |
Nodes.StoreNamedAttribute, | |
input_kwargs={ | |
"Geometry": cube_2.outputs["Mesh"], | |
"Name": "uv_map", | |
3: cube_2.outputs["UV Map"], | |
}, | |
attrs={"domain": "CORNER", "data_type": "FLOAT_VECTOR"}, | |
) | |
subdivide_mesh_2 = nw.new_node( | |
Nodes.SubdivideMesh, input_kwargs={"Mesh": store_named_attribute} | |
) | |
subdivision_surface_2 = nw.new_node( | |
Nodes.SubdivisionSurface, | |
input_kwargs={"Mesh": subdivide_mesh_2, "Level": group_input.outputs["Level"]}, | |
) | |
multiply = nw.new_node( | |
Nodes.Math, | |
input_kwargs={0: group_input.outputs["height"]}, | |
attrs={"operation": "MULTIPLY"}, | |
) | |
subtract = nw.new_node( | |
Nodes.Math, | |
input_kwargs={0: multiply, 1: group_input.outputs["hole_dist"]}, | |
attrs={"operation": "SUBTRACT"}, | |
) | |
combine_xyz_4 = nw.new_node(Nodes.CombineXYZ, input_kwargs={"Z": subtract}) | |
transform_1 = nw.new_node( | |
Nodes.Transform, | |
input_kwargs={"Geometry": subdivision_surface_2, "Translation": combine_xyz_4}, | |
) | |
group_output = nw.new_node( | |
Nodes.GroupOutput, | |
input_kwargs={"Geometry": transform_1}, | |
attrs={"is_active_output": True}, | |
) | |
def geometry_nodes(nw: NodeWrangler, **kwargs): | |
# Code generated using version 2.6.4 of the node_transpiler | |
depth = nw.new_node(Nodes.Value, label="depth") | |
depth.outputs[0].default_value = kwargs["depth"] | |
width = nw.new_node(Nodes.Value, label="width") | |
width.outputs[0].default_value = kwargs["width"] | |
height = nw.new_node(Nodes.Value, label="height") | |
height.outputs[0].default_value = kwargs["height"] | |
combine_xyz = nw.new_node( | |
Nodes.CombineXYZ, input_kwargs={"X": depth, "Y": width, "Z": height} | |
) | |
cube = nw.new_node(Nodes.MeshCube, input_kwargs={"Size": combine_xyz}) | |
store_named_attribute = nw.new_node( | |
Nodes.StoreNamedAttribute, | |
input_kwargs={ | |
"Geometry": cube.outputs["Mesh"], | |
"Name": "uv_map", | |
3: cube.outputs["UV Map"], | |
}, | |
attrs={"domain": "CORNER", "data_type": "FLOAT_VECTOR"}, | |
) | |
subdivide_mesh = nw.new_node( | |
Nodes.SubdivideMesh, input_kwargs={"Mesh": store_named_attribute, "Level": 2} | |
) | |
sub_level = nw.new_node(Nodes.Integer, label="sub_level") | |
sub_level.integer = kwargs["frame_sub_level"] | |
subdivision_surface = nw.new_node( | |
Nodes.SubdivisionSurface, | |
input_kwargs={"Mesh": subdivide_mesh, "Level": sub_level}, | |
) | |
differences = [] | |
if kwargs["has_handle"]: | |
hole_depth = nw.new_node(Nodes.Value, label="hole_depth") | |
hole_depth.outputs[0].default_value = kwargs["handle_depth"] | |
hole_height = nw.new_node(Nodes.Value, label="hole_height") | |
hole_height.outputs[0].default_value = kwargs["handle_height"] | |
hole_dist = nw.new_node(Nodes.Value, label="hole_dist") | |
hole_dist.outputs[0].default_value = kwargs["handle_dist_to_top"] | |
handle_level = nw.new_node(Nodes.Integer, label="handle_level") | |
handle_level.integer = kwargs["handle_sub_level"] | |
handle_hole = nw.new_node( | |
nodegroup_handle_hole().name, | |
input_kwargs={ | |
"X": hole_depth, | |
"Z": hole_height, | |
"height": height, | |
"hole_dist": hole_dist, | |
"Level": handle_level, | |
}, | |
) | |
differences.append(handle_hole) | |
thickness = nw.new_node(Nodes.Value, label="thickness") | |
thickness.outputs[0].default_value = kwargs["thickness"] | |
subtract = nw.new_node( | |
Nodes.Math, | |
input_kwargs={0: depth, 1: thickness}, | |
attrs={"operation": "SUBTRACT"}, | |
) | |
subtract_1 = nw.new_node( | |
Nodes.Math, | |
input_kwargs={0: width, 1: thickness}, | |
attrs={"operation": "SUBTRACT"}, | |
) | |
combine_xyz_1 = nw.new_node( | |
Nodes.CombineXYZ, input_kwargs={"X": subtract, "Y": subtract_1, "Z": height} | |
) | |
cube_1 = nw.new_node(Nodes.MeshCube, input_kwargs={"Size": combine_xyz_1}) | |
store_named_attribute_1 = nw.new_node( | |
Nodes.StoreNamedAttribute, | |
input_kwargs={ | |
"Geometry": cube_1.outputs["Mesh"], | |
"Name": "uv_map", | |
3: cube_1.outputs["UV Map"], | |
}, | |
attrs={"domain": "CORNER", "data_type": "FLOAT_VECTOR"}, | |
) | |
subdivide_mesh_1 = nw.new_node( | |
Nodes.SubdivideMesh, input_kwargs={"Mesh": store_named_attribute_1, "Level": 2} | |
) | |
subdivision_surface_1 = nw.new_node( | |
Nodes.SubdivisionSurface, | |
input_kwargs={"Mesh": subdivide_mesh_1, "Level": sub_level}, | |
) | |
multiply = nw.new_node( | |
Nodes.Math, | |
input_kwargs={0: thickness, 2: 0.2500}, | |
attrs={"operation": "MULTIPLY"}, | |
) | |
combine_xyz_2 = nw.new_node(Nodes.CombineXYZ, input_kwargs={"Z": multiply}) | |
transform = nw.new_node( | |
Nodes.Transform, | |
input_kwargs={"Geometry": subdivision_surface_1, "Translation": combine_xyz_2}, | |
) | |
if kwargs["has_holes"]: | |
gap_size = nw.new_node(Nodes.Value, label="gap_size") | |
gap_size.outputs[0].default_value = kwargs["hole_gap_size"] | |
hole_edge_gap = nw.new_node(Nodes.Value, label="hole_edge_gap") | |
hole_edge_gap.outputs[0].default_value = kwargs["hole_edge_gap"] | |
hole_size = nw.new_node(Nodes.Value, label="hole_size") | |
hole_size.outputs[0].default_value = kwargs["hole_size"] | |
holes = nw.new_node( | |
nodegroup_holes().name, | |
input_kwargs={ | |
"height": height, | |
"gap_size": gap_size, | |
"hole_edge_gap": hole_edge_gap, | |
"hole_size": hole_size, | |
"depth": depth, | |
"width": width, | |
}, | |
) | |
differences.extend([holes.outputs["Instances1"], holes.outputs["Instances2"]]) | |
difference = nw.new_node( | |
Nodes.MeshBoolean, | |
input_kwargs={ | |
"Mesh 1": subdivision_surface, | |
"Mesh 2": [transform] + differences, | |
}, | |
) | |
realize_instances = nw.new_node( | |
Nodes.RealizeInstances, input_kwargs={"Geometry": difference.outputs["Mesh"]} | |
) | |
multiply_1 = nw.new_node( | |
Nodes.Math, input_kwargs={0: height}, attrs={"operation": "MULTIPLY"} | |
) | |
combine_xyz_3 = nw.new_node(Nodes.CombineXYZ, input_kwargs={"Z": multiply_1}) | |
transform_geometry = nw.new_node( | |
Nodes.Transform, | |
input_kwargs={"Geometry": realize_instances, "Translation": combine_xyz_3}, | |
) | |
set_material = nw.new_node( | |
Nodes.SetMaterial, | |
input_kwargs={ | |
"Geometry": transform_geometry, | |
"Material": surface.shaderfunc_to_material(shader_rough_plastic), | |
}, | |
) | |
group_output = nw.new_node( | |
Nodes.GroupOutput, | |
input_kwargs={"Geometry": set_material}, | |
attrs={"is_active_output": True}, | |
) | |
class BasketBaseFactory(AssetFactory): | |
def __init__(self, factory_seed, coarse=False): | |
super(BasketBaseFactory, self).__init__(factory_seed, coarse=coarse) | |
self.params = self.get_asset_params() | |
self.seed = factory_seed | |
self.get_params_dict() | |
def get_params_dict(self): | |
self.params_dict = { | |
"depth": ['continuous', (0.1, 0.6)], | |
"width": ['continuous', (0.1, 0.7)], | |
"height": ['continuous', (0.05, 0.4)], | |
"frame_sub_level": ['discrete', [0, 3]], | |
"thickness": ['continuous', (0.001, 0.03)], | |
"has_handle": ['discrete', [0, 1]], | |
"handle_sub_level": ['discrete', [0, 1, 2]], | |
"handle_depth": ['continuous', (0.2, 0.6)], | |
"handle_height": ['continuous', (0.1, 0.3)], | |
"handle_dist_to_top": ['continuous', (0.08, 0.4)], | |
"has_holes": ['discrete', [0, 1]], | |
"hole_gap_size": ['continuous', (0.5, 2.0)], | |
"hole_edge_gap": ['continuous', (0.04, 0.1)], | |
"hole_size": ['continuous', (0.007, 0.02)] | |
} | |
def fix_unused_params(self, params): | |
if params['height'] < 0.12: | |
params['has_holes'] = 0 | |
if params['has_handle'] == 0: | |
params["handle_sub_level"] = 1 | |
params["handle_depth"] = 0.3 | |
params["handle_height"] = 0.2 | |
params["handle_dist_to_top"] = 0.115 | |
if params['has_holes'] == 0: | |
params["hole_gap_size"] = 0.95 | |
params["hole_edge_gap"] = 0.05 | |
params["hole_size"] = 0.0075 | |
return params | |
def update_params(self, params): | |
# TODO: to allow random material | |
self.seed = int(1000 * time.time()) % 2**32 | |
handle_depth = params['depth'] * params['handle_depth'] | |
handle_height = params['height'] * params['handle_height'] | |
handle_dist_to_top = handle_height * 0.5 + params['height'] * params["handle_dist_to_top"] | |
if params['height'] < 0.12: | |
params["has_holes"] = 0 | |
hole_gap_size = params['hole_size'] * params["hole_gap_size"] | |
parameters = { | |
"depth": params["depth"], | |
"width": params["width"], | |
"height": params["height"], | |
"frame_sub_level": params["frame_sub_level"], | |
"thickness": params["thickness"], | |
"has_handle": params["has_handle"] > 0, | |
"handle_sub_level": params["handle_sub_level"], | |
"handle_depth": handle_depth, | |
"handle_height": handle_height, | |
"handle_dist_to_top": handle_dist_to_top, | |
"has_holes": params["has_holes"] > 0, | |
"hole_gap_size": hole_gap_size, | |
"hole_edge_gap": params["hole_edge_gap"], | |
"hole_size": params["hole_size"], | |
} | |
self.params.update(parameters) | |
def get_asset_params(self, i=0): | |
params = {} | |
if params.get("depth", None) is None: | |
params["depth"] = uniform(0.15, 0.4) | |
if params.get("width", None) is None: | |
params["width"] = uniform(0.2, 0.6) | |
if params.get("height", None) is None: | |
params["height"] = uniform(0.06, 0.24) | |
if params.get("frame_sub_level", None) is None: | |
params["frame_sub_level"] = np.random.choice([0, 3], p=[0.5, 0.5]) | |
if params.get("thickness", None) is None: | |
params["thickness"] = uniform(0.001, 0.005) | |
if params.get("has_handle", None) is None: | |
params["has_handle"] = np.random.choice([True, False], p=[0.8, 0.2]) | |
if params.get("handle_sub_level", None) is None: | |
params["handle_sub_level"] = np.random.choice([0, 1, 2], p=[0.2, 0.4, 0.4]) | |
if params.get("handle_depth", None) is None: | |
params["handle_depth"] = params["depth"] * uniform(0.2, 0.4) | |
if params.get("handle_height", None) is None: | |
params["handle_height"] = params["height"] * uniform(0.1, 0.25) | |
if params.get("handle_dist_to_top", None) is None: | |
params["handle_dist_to_top"] = params["handle_height"] * 0.5 + params[ | |
"height" | |
] * uniform(0.08, 0.15) | |
if params.get("has_holes", None) is None: | |
if params["height"] < 0.12: | |
params["has_holes"] = False | |
else: | |
params["has_holes"] = np.random.choice([True, False], p=[0.5, 0.5]) | |
if params.get("hole_size", None) is None: | |
params["hole_size"] = uniform(0.005, 0.01) | |
if params.get("hole_gap_size", None) is None: | |
params["hole_gap_size"] = params["hole_size"] * uniform(0.8, 1.1) | |
if params.get("hole_edge_gap", None) is None: | |
params["hole_edge_gap"] = uniform(0.04, 0.06) | |
return params | |
def create_asset(self, i=0, **params): | |
bpy.ops.mesh.primitive_plane_add( | |
size=1, | |
enter_editmode=False, | |
align="WORLD", | |
location=(0, 0, 0), | |
scale=(1, 1, 1), | |
) | |
obj = bpy.context.active_object | |
np.random.seed(self.seed) | |
random.seed(self.seed) | |
surface.add_geomod( | |
obj, geometry_nodes, attributes=[], apply=True, input_kwargs=self.params | |
) | |
tagging.tag_system.relabel_obj(obj) | |
return obj | |