Created
September 28, 2021 05:00
-
-
Save CGArtPython/e59fa975cec458c3adb2669fe58e09e9 to your computer and use it in GitHub Desktop.
The code for this art: https://www.artstation.com/artwork/L3oVvv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Author: Viktor Stepanov | |
Licence: MIT | |
The code for this art: | |
https://www.artstation.com/artwork/L3oVvv | |
Tested with Blender 2.93 | |
""" | |
import math | |
import random | |
import time | |
import bpy | |
import mathutils | |
def delete_all_objects(): | |
""" | |
Removing all of the objects from the scene | |
""" | |
if bpy.context.active_object and bpy.context.active_object.mode == "EDIT": | |
bpy.ops.object.editmode_toggle() | |
for obj in bpy.data.objects: | |
obj.hide_set(False) | |
obj.hide_select = False | |
obj.hide_viewport = False | |
bpy.ops.object.select_all(action="SELECT") | |
bpy.ops.object.delete() | |
cl = [c.name for c in bpy.data.collections] | |
for name in cl: | |
bpy.data.collections.remove(bpy.data.collections[name]) | |
acs = [ac.name for ac in bpy.data.actions] | |
for name in acs: | |
bpy.data.actions.remove(bpy.data.actions[name]) | |
cms = [cm.name for cm in bpy.data.cameras] | |
for name in cms: | |
bpy.data.cameras.remove(bpy.data.cameras[name]) | |
cms = [cm.name for cm in bpy.data.curves] | |
for name in cms: | |
bpy.data.curves.remove(bpy.data.curves[name]) | |
# free the rest of the data materials, particles, textures, meshes, geo nodes | |
bpy.ops.outliner.orphans_purge( | |
do_local_ids=True, | |
do_linked_ids=True, | |
do_recursive=True, | |
) | |
def get_color_palette(): | |
# https://www.colourlovers.com/palette/92095/Giant_Goldfish | |
# palette = ["#69D2E7FF", "#E0E4CCFF", "#F38630FF", "#A7DBD8FF", "#FA6900FF"] | |
palette = [ | |
[0.41015625, 0.8203125, 0.90234375, 0.99609375], | |
[0.875, 0.890625, 0.796875, 0.99609375], | |
[0.94921875, 0.5234375, 0.1875, 0.99609375], | |
[0.65234375, 0.85546875, 0.84375, 0.99609375], | |
[0.9765625, 0.41015625, 0.0, 0.99609375], | |
] | |
return palette | |
def get_random_pallet_color(context): | |
return random.choice(context["colors"]) | |
def active_obj(): | |
""" | |
returns the active object | |
""" | |
return bpy.context.active_object | |
def time_seed(): | |
""" | |
Sets the random seed based on the time | |
and copies the seed into the clipboard | |
""" | |
seed = time.time() | |
print(f"seed: {seed}") | |
random.seed(seed) | |
bpy.context.window_manager.clipboard = str(seed) | |
return seed | |
def shade_smooth(): | |
bpy.ops.object.shade_smooth() | |
bpy.context.object.data.use_auto_smooth = True | |
def add_ico_sphere( | |
subdivisions=3, | |
radius=1, | |
location=(0, 0, 0), | |
scale=(1, 1, 1), | |
rotation=(0, 0, 0), | |
scale_factor=None, | |
apply_shade_smooth=True, | |
): | |
""" | |
Creates an ico sphere | |
""" | |
bpy.ops.mesh.primitive_ico_sphere_add( | |
subdivisions=subdivisions, | |
radius=radius, | |
enter_editmode=False, | |
align="WORLD", | |
location=location, | |
) | |
obj = active_obj() | |
if scale_factor: | |
obj.scale *= scale_factor | |
else: | |
obj.scale = scale | |
obj.rotation_euler = rotation | |
if apply_shade_smooth: | |
shade_smooth() | |
return obj | |
def add_ctrl_empty(name=None): | |
bpy.ops.object.empty_add( | |
type="PLAIN_AXES", align="WORLD", location=(0, 0, 0), scale=(1, 1, 1) | |
) | |
empty_ctrl = bpy.context.active_object | |
if name: | |
empty_ctrl.name = name | |
else: | |
empty_ctrl.name = "empty.cntrl" | |
return empty_ctrl | |
def apply_mat(material): | |
obj = bpy.context.active_object | |
obj.data.materials.append(material) | |
def make_active(obj): | |
bpy.ops.object.select_all(action="DESELECT") | |
obj.select_set(True) | |
bpy.context.view_layer.objects.active = obj | |
def make_node_tree_fcurves_linear(node_tree): | |
for fc in node_tree.animation_data.action.fcurves: | |
fc.extrapolation = "LINEAR" | |
def track_empty(cam): | |
bpy.ops.object.empty_add(type="PLAIN_AXES", align="WORLD", location=(0, 0, 0)) | |
empty = active_obj() | |
empty.name = "tracker-target" | |
make_active(cam) | |
bpy.ops.object.constraint_add(type="TRACK_TO") | |
bpy.context.object.constraints["Track To"].target = empty | |
return empty | |
def setup_camera(): | |
loc = (3.611, -3.895, 2.747) | |
rot = (1.076, 0.012, 0.746) | |
bpy.ops.object.camera_add( | |
enter_editmode=False, align="VIEW", location=loc, rotation=rot | |
) | |
cam = bpy.context.active_object | |
bpy.context.scene.camera = cam | |
bpy.context.object.data.lens = 70 | |
bpy.context.object.data.passepartout_alpha = 0.9 | |
empty = track_empty(cam) | |
empty.location.z = 0 | |
def set_scene_props(context, fps, loop_sec): | |
""" | |
Set scene properties | |
""" | |
frame_count = fps * loop_sec | |
scene = bpy.context.scene | |
scene.frame_end = frame_count | |
world = bpy.data.worlds["World"] | |
if "Background" in world.node_tree.nodes: | |
world.node_tree.nodes["Background"].inputs[0].default_value = (0.0, 0.0, 0.0, 1) | |
bpy.context.scene.render.fps = fps | |
bpy.context.scene.eevee.use_bloom = True | |
bpy.context.scene.eevee.bloom_intensity = 0.005 | |
bpy.context.scene.eevee.use_gtao = True | |
bpy.context.scene.eevee.use_ssr = True | |
bpy.context.scene.eevee.use_ssr_halfres = False | |
bpy.context.scene.eevee.ssr_quality = 1 | |
bpy.context.scene.render.resolution_x = 1080 | |
bpy.context.scene.render.resolution_y = 1080 | |
def gen_light_rig_part(light_type): | |
bpy.ops.object.empty_add( | |
type="PLAIN_AXES", align="WORLD", location=(0, 0, 0), scale=(1, 1, 1) | |
) | |
emp_tracker = active_obj() | |
bpy.ops.curve.primitive_bezier_circle_add( | |
enter_editmode=False, align="WORLD", location=(0, 0, 0), scale=(1, 1, 1) | |
) | |
rail_curve = active_obj() | |
bpy.ops.object.light_add( | |
type=light_type, radius=1, align="WORLD", location=(0, 0, 0), scale=(1, 1, 1) | |
) | |
light = active_obj() | |
bpy.ops.object.constraint_add(type="TRACK_TO") | |
bpy.context.object.constraints["Track To"].target = emp_tracker | |
bpy.context.object.constraints["Track To"].track_axis = "TRACK_NEGATIVE_Z" | |
bpy.context.object.constraints["Track To"].up_axis = "UP_Y" | |
bpy.ops.object.constraint_add(type="FOLLOW_PATH") | |
follow_path = bpy.context.object.constraints["Follow Path"] | |
bpy.context.object.constraints["Follow Path"].target = rail_curve | |
bpy.context.object.constraints["Follow Path"].forward_axis = "TRACK_NEGATIVE_X" | |
bpy.context.object.constraints["Follow Path"].up_axis = "UP_Y" | |
bpy.context.object.constraints["Follow Path"].use_fixed_location = True | |
bpy.context.object.constraints["Follow Path"].use_curve_follow = True | |
return rail_curve, emp_tracker, light, follow_path | |
def add_lights_rig(context): | |
""" | |
Setup the lights for the scene | |
""" | |
ctrl_empty = add_ctrl_empty() | |
ctrl_empty.name = "_sun_ctrl" | |
rail_curve, _, light, follow_path = gen_light_rig_part("SUN") | |
light.data.color = get_random_pallet_color(context)[:3] | |
rail_curve.scale *= 2 | |
light.data.angle = math.pi | |
light.data.energy = 0.1 | |
rail_curve.parent = ctrl_empty | |
follow_path.offset_factor = 0.5 | |
rail_curve, _, light, follow_path = gen_light_rig_part("SUN") | |
light.data.color = (1, 1, 1) | |
rail_curve.parent = ctrl_empty | |
rail_curve.scale *= 2 | |
light.data.angle = math.pi | |
light.data.energy = 5 | |
def gen_sphere_brush(context): | |
brush_instance = add_ico_sphere(subdivisions=5) | |
brush_instance.name = "brush_instance" | |
brush_instance.location.z = 10 | |
brush = add_ico_sphere(subdivisions=5) | |
brush.name = "brush_geo" | |
apply_mat(context["martial_metal"]) | |
context["point_instance_1_object"] = brush_instance | |
gen_geo_nodes_brush(context) | |
bpy.ops.object.modifier_add(type="DYNAMIC_PAINT") | |
dpaint = bpy.context.object.modifiers["Dynamic Paint"] | |
dpaint.ui_type = "BRUSH" | |
bpy.ops.dpaint.type_toggle(type="BRUSH") | |
dpaint.brush_settings.paint_wetness = 0.5 | |
dpaint.brush_settings.paint_source = "VOLUME_DISTANCE" | |
dpaint.brush_settings.paint_distance = 0.1 | |
def gen_point_distribute_0(context, node_tree): | |
point_distribute_0 = node_tree.nodes.new(type="GeometryNodePointDistribute") | |
# Attributes | |
point_distribute_0.distribute_method = "POISSON" | |
# point_distribute_0.label = "" | |
point_distribute_0.name = "Point Distribute" | |
point_distribute_0.location = mathutils.Vector((-568.904, 31.164)) | |
# Input Socket | |
point_distribute_0.inputs["Distance Min"].default_value = 0.7 # 1.169 | |
point_distribute_0.inputs["Density Max"].default_value = 9.899 | |
point_distribute_0.inputs["Seed"].default_value = 0 | |
return point_distribute_0 | |
def gen_point_instance_1(context, node_tree): | |
point_instance_1 = node_tree.nodes.new(type="GeometryNodePointInstance") | |
# Attributes | |
point_instance_1.instance_type = "OBJECT" | |
point_instance_1.use_whole_collection = True | |
point_instance_1.name = "Point Instance" | |
# point_instance_1.label = "" | |
point_instance_1.location = mathutils.Vector((91.095, -3.591)) | |
# Input Socket | |
point_instance_1.inputs["Object"].default_value = context["point_instance_1_object"] | |
# point_instance_1.inputs["Collection"].default_value = None | |
point_instance_1.inputs["Seed"].default_value = 0 | |
return point_instance_1 | |
def gen_point_scale_2(context, node_tree): | |
point_scale_2 = node_tree.nodes.new(type="GeometryNodePointScale") | |
# Attributes | |
point_scale_2.input_type = "VECTOR" | |
# point_scale_2.label = "" | |
point_scale_2.name = "Point Scale" | |
point_scale_2.location = mathutils.Vector((-348.904, 19.615)) | |
# Input Socket | |
# point_scale_2.inputs[1].default_value = "" # Factor | |
point_scale_2.inputs[2].default_value = mathutils.Vector( | |
(0.079, 0.079, 0.079) | |
) # Factor | |
point_scale_2.inputs[3].default_value = 1.0 # Factor | |
return point_scale_2 | |
def gen_transform_3(context, node_tree): | |
transform_3 = node_tree.nodes.new(type="GeometryNodeTransform") | |
# Attributes | |
# transform_3.label = "" | |
transform_3.name = "Transform" | |
transform_3.location = mathutils.Vector((-128.904, 46.269)) | |
# Input Socket | |
transform_3.inputs["Translation"].default_value = mathutils.Vector((0.0, 0.0, 0.0)) | |
transform_3.inputs["Rotation"].default_value = mathutils.Euler((0.0, 0.0, 0.0)) | |
transform_3.inputs["Scale"].default_value = mathutils.Vector((1.0, 1.0, 1.0)) | |
return transform_3 | |
def gen_combine_xyz_4(context, node_tree): | |
combine_xyz_4 = node_tree.nodes.new(type="ShaderNodeCombineXYZ") | |
# Attributes | |
# combine_xyz_4.label = "" | |
combine_xyz_4.name = "Combine XYZ" | |
combine_xyz_4.location = mathutils.Vector((-473.359, -274.076)) | |
# Input Socket | |
combine_xyz_4.inputs["X"].default_value = 0.0 | |
combine_xyz_4.inputs["Y"].default_value = 0.0 | |
combine_xyz_4.inputs["Z"].default_value = 0.0 | |
return combine_xyz_4 | |
def gen_value_5(context, node_tree): | |
value_5 = node_tree.nodes.new(type="ShaderNodeValue") | |
# Attributes | |
# value_5.label = "" | |
value_5.name = "Value" | |
value_5.location = mathutils.Vector((-1030.785, -247.654)) | |
value_5.outputs[0].default_value = 1 | |
value_5.outputs[0].keyframe_insert("default_value", frame=1) | |
value_5.outputs[0].default_value = context["frame_count"] | |
value_5.outputs[0].keyframe_insert( | |
"default_value", frame=context["frame_count_loop"] | |
) | |
# Input Socket | |
return value_5 | |
def gen_map_range_6(context, node_tree): | |
map_range_6 = node_tree.nodes.new(type="ShaderNodeMapRange") | |
# Attributes | |
map_range_6.location = mathutils.Vector((-773.472, -244.256)) | |
# map_range_6.label = "" | |
map_range_6.clamp = False | |
map_range_6.interpolation_type = "LINEAR" | |
map_range_6.name = "Map Range" | |
# Input Socket | |
map_range_6.inputs["Value"].default_value = 1.0 | |
map_range_6.inputs["From Min"].default_value = 0.0 | |
map_range_6.inputs["From Max"].default_value = 179.0 | |
map_range_6.inputs["To Min"].default_value = 0.0 | |
map_range_6.inputs["To Max"].default_value = 6.283 | |
map_range_6.inputs["Steps"].default_value = 4.0 | |
return map_range_6 | |
def gen_geo_nodes_brush(context): | |
bpy.ops.object.modifier_add(type="NODES") | |
node_tree = bpy.data.node_groups["Geometry Nodes"] | |
node_tree.nodes["Group Input"] | |
node_tree.nodes["Group Output"] | |
gen_point_distribute_0(context, node_tree) | |
gen_point_instance_1(context, node_tree) | |
gen_point_scale_2(context, node_tree) | |
gen_transform_3(context, node_tree) | |
gen_combine_xyz_4(context, node_tree) | |
gen_value_5(context, node_tree) | |
gen_map_range_6(context, node_tree) | |
# links | |
from_node = node_tree.nodes.get("Group Input") | |
to_node = node_tree.nodes.get("Point Distribute") | |
node_tree.links.new(from_node.outputs["Geometry"], to_node.inputs["Geometry"]) | |
from_node = node_tree.nodes.get("Point Distribute") | |
to_node = node_tree.nodes.get("Point Scale") | |
node_tree.links.new(from_node.outputs["Geometry"], to_node.inputs["Geometry"]) | |
from_node = node_tree.nodes.get("Point Instance") | |
to_node = node_tree.nodes.get("Group Output") | |
node_tree.links.new(from_node.outputs["Geometry"], to_node.inputs[0]) | |
from_node = node_tree.nodes.get("Point Scale") | |
to_node = node_tree.nodes.get("Transform") | |
node_tree.links.new(from_node.outputs["Geometry"], to_node.inputs["Geometry"]) | |
from_node = node_tree.nodes.get("Transform") | |
to_node = node_tree.nodes.get("Point Instance") | |
node_tree.links.new(from_node.outputs["Geometry"], to_node.inputs["Geometry"]) | |
from_node = node_tree.nodes.get("Combine XYZ") | |
to_node = node_tree.nodes.get("Transform") | |
node_tree.links.new(from_node.outputs["Vector"], to_node.inputs["Rotation"]) | |
from_node = node_tree.nodes.get("Value") | |
to_node = node_tree.nodes.get("Map Range") | |
node_tree.links.new(from_node.outputs["Value"], to_node.inputs["Value"]) | |
from_node = node_tree.nodes.get("Map Range") | |
to_node = node_tree.nodes.get("Combine XYZ") | |
node_tree.links.new(from_node.outputs["Result"], to_node.inputs["X"]) | |
make_node_tree_fcurves_linear(node_tree) | |
def gen_sphere_canvas(context): | |
canvas = add_ico_sphere(subdivisions=5) | |
canvas.name = "canvas" | |
mat, nodes = gen_mat_canvas(True) | |
color1 = get_random_pallet_color(context) | |
color2 = get_random_pallet_color(context) | |
nodes["ColorRamp"].color_ramp.elements[0].color = color1 | |
nodes["ColorRamp"].color_ramp.elements[1].color = color2 | |
nodes["ColorRamp.001"].color_ramp.elements[0].color = color2 | |
nodes["ColorRamp.001"].color_ramp.elements[1].color = color1 | |
apply_mat(mat) | |
bpy.ops.object.modifier_add(type="SUBSURF") | |
bpy.context.object.modifiers["Subdivision"].levels = 2 | |
bpy.ops.object.modifier_add(type="DYNAMIC_PAINT") | |
bpy.ops.dpaint.type_toggle(type="CANVAS") | |
bpy.ops.dpaint.output_toggle(output="A") | |
bpy.ops.dpaint.output_toggle(output="B") | |
dpaint = bpy.context.object.modifiers["Dynamic Paint"] | |
canvas = dpaint.canvas_settings.canvas_surfaces["Surface"] | |
canvas.frame_start = 1 | |
canvas.frame_end = context["frame_count"] | |
canvas.dry_speed = context["frame_count"] / 2 | |
gen_geo_nodes_canvas(context) | |
def gen_attribute_vector_math_7(context, node_tree): | |
attribute_vector_math_7 = node_tree.nodes.new( | |
type="GeometryNodeAttributeVectorMath" | |
) | |
# Attributes | |
attribute_vector_math_7.input_type_c = "ATTRIBUTE" | |
attribute_vector_math_7.input_type_b = "ATTRIBUTE" | |
attribute_vector_math_7.input_type_a = "ATTRIBUTE" | |
attribute_vector_math_7.location = mathutils.Vector((-151.983, 95.207)) | |
attribute_vector_math_7.name = "Attribute Vector Math" | |
attribute_vector_math_7.operation = "ADD" | |
# Input Socket | |
attribute_vector_math_7.inputs[1].default_value = "position" # A | |
attribute_vector_math_7.inputs[3].default_value = "wetmap_dsip" # B | |
attribute_vector_math_7.inputs[6].default_value = "" # C | |
attribute_vector_math_7.inputs["Result"].default_value = "position" | |
return attribute_vector_math_7 | |
def gen_attribute_vector_math_8(context, node_tree): | |
attribute_vector_math_001_8 = node_tree.nodes.new( | |
type="GeometryNodeAttributeVectorMath" | |
) | |
# Attributes | |
attribute_vector_math_001_8.input_type_c = "ATTRIBUTE" | |
attribute_vector_math_001_8.input_type_b = "ATTRIBUTE" | |
attribute_vector_math_001_8.input_type_a = "ATTRIBUTE" | |
attribute_vector_math_001_8.location = mathutils.Vector((-497.432, 98.182)) | |
attribute_vector_math_001_8.name = "Attribute Vector Math.001" | |
attribute_vector_math_001_8.operation = "MULTIPLY" | |
# Input Socket | |
attribute_vector_math_001_8.inputs[1].default_value = "normal" # A | |
attribute_vector_math_001_8.inputs[3].default_value = "wetmap_dsip" # B | |
attribute_vector_math_001_8.inputs[6].default_value = "" # C | |
attribute_vector_math_001_8.inputs["Result"].default_value = "wetmap_dsip" | |
return attribute_vector_math_001_8 | |
def gen_attribute_color_ramp_9(context, node_tree): | |
attribute_color_ramp_9 = node_tree.nodes.new(type="GeometryNodeAttributeColorRamp") | |
# Attributes | |
attribute_color_ramp_9.name = "Attribute Color Ramp" | |
attribute_color_ramp_9.location = mathutils.Vector((-1037.432, 97.897)) | |
attribute_color_ramp_9.color_ramp.elements[1].position = 0.824 | |
# Input Socket | |
attribute_color_ramp_9.inputs[1].default_value = "dp_wetmap" | |
attribute_color_ramp_9.inputs[2].default_value = "dp_wetmap" | |
attribute_color_ramp_9.inputs["Attribute"].default_value = "dp_wetmap" | |
attribute_color_ramp_9.inputs["Result"].default_value = "dp_wetmap" | |
return attribute_color_ramp_9 | |
def gen_attribute_math_10(context, node_tree): | |
attribute_math_10 = node_tree.nodes.new(type="GeometryNodeAttributeMath") | |
# Attributes | |
attribute_math_10.input_type_c = "ATTRIBUTE" | |
attribute_math_10.input_type_b = "FLOAT" | |
attribute_math_10.input_type_a = "ATTRIBUTE" | |
attribute_math_10.location = mathutils.Vector((-717.432, 70.917)) | |
attribute_math_10.label = "" | |
attribute_math_10.name = "Attribute Math" | |
attribute_math_10.operation = "MULTIPLY" | |
# Input Socket | |
attribute_math_10.inputs[1].default_value = "dp_wetmap" # A | |
attribute_math_10.inputs[4].default_value = -0.199 # B | |
attribute_math_10.inputs[6].default_value = 0.0 # C | |
attribute_math_10.inputs["Result"].default_value = "wetmap_dsip" | |
return attribute_math_10 | |
def gen_geo_nodes_canvas(context): | |
bpy.ops.object.modifier_add(type="NODES") | |
node_tree = bpy.data.node_groups["Geometry Nodes.001"] | |
node_tree.nodes["Group Input"] | |
node_tree.nodes["Group Output"] | |
gen_attribute_vector_math_7(context, node_tree) | |
gen_attribute_vector_math_8(context, node_tree) | |
gen_attribute_color_ramp_9(context, node_tree) | |
gen_attribute_math_10(context, node_tree) | |
# links begin | |
from_node = node_tree.nodes.get("Group Input") | |
to_node = node_tree.nodes.get("Attribute Color Ramp") | |
node_tree.links.new(from_node.outputs["Geometry"], to_node.inputs["Geometry"]) | |
from_node = node_tree.nodes.get("Attribute Vector Math") | |
to_node = node_tree.nodes.get("Group Output") | |
node_tree.links.new(from_node.outputs["Geometry"], to_node.inputs[0]) | |
from_node = node_tree.nodes.get("Attribute Vector Math.001") | |
to_node = node_tree.nodes.get("Attribute Vector Math") | |
node_tree.links.new(from_node.outputs["Geometry"], to_node.inputs["Geometry"]) | |
from_node = node_tree.nodes.get("Attribute Color Ramp") | |
to_node = node_tree.nodes.get("Attribute Math") | |
node_tree.links.new(from_node.outputs["Geometry"], to_node.inputs["Geometry"]) | |
from_node = node_tree.nodes.get("Attribute Math") | |
to_node = node_tree.nodes.get("Attribute Vector Math.001") | |
node_tree.links.new(from_node.outputs["Geometry"], to_node.inputs["Geometry"]) | |
def gen_center_piece(context): | |
gen_sphere_brush(context) | |
gen_sphere_canvas(context) | |
def gen_scene(context): | |
add_lights_rig(context) | |
gen_center_piece(context) | |
def main(): | |
""" | |
Dynamic Paint gotchas: | |
1. Make sure to save to a blend file before baking | |
(if you don't do this the Dynamic Paint modifications won't appear on the rendered frames) | |
2. If you have a Subdivision modifier make sure the Render and Viewport levels are the same | |
(You will be auto baking with the viewport level setting, so when you try to render a frame | |
the Subdivision level should be the same) | |
Tip: uncomment `bpy.ops.ptcache.bake_all(bake=True)` to automaticall bake after the script creates the scene | |
""" | |
fps = 30 | |
# bake dynamic paint from frame 1 | |
# render from frame 181 | |
loop_sec = 6 * 2 | |
frame_count = fps * loop_sec | |
seed = 1630964123.943 | |
if seed: | |
random.seed(seed) | |
else: | |
seed = time_seed() | |
delete_all_objects() | |
setup_camera() | |
context = { | |
"frame_count": frame_count, | |
"frame_count_loop": frame_count + 1, | |
"seed": seed, | |
} | |
context["colors"] = get_color_palette() | |
set_scene_props(context, fps, loop_sec) | |
context["martial_metal"], nodes = gen_martial_metal(True) | |
nodes["Principled BSDF"].inputs[0].default_value = get_random_pallet_color(context) | |
bpy.ops.ptcache.free_bake_all() | |
gen_scene(context) | |
bpy.context.scene.eevee.taa_render_samples = 64 | |
bpy.context.scene.render.image_settings.file_format = "PNG" | |
scene_num = 1 | |
day = "283" | |
bpy.context.scene.render.filepath = f"/tmp/day{day}_{scene_num}/" | |
bpy.context.scene.view_settings.look = "Very High Contrast" | |
# TODO: uncomment this to bake the Dynamic Paint (Note: The baking progress is not shown ) | |
# bpy.ops.ptcache.bake_all(bake=True) | |
bpy.context.scene.frame_start = context["frame_count"] / 2 + 1 | |
def gen_mat_canvas(return_nodes=False): | |
mat = bpy.data.materials.new(name="Material_gen_canvas") | |
mat.use_nodes = True | |
mat_output = None | |
to_rm = [] | |
for node in mat.node_tree.nodes: | |
if node.type == "OUTPUT_MATERIAL": | |
mat_output = node | |
continue | |
to_rm.append(node) | |
for node in to_rm: | |
print("Removing", node.name) | |
mat.node_tree.nodes.remove(node) | |
node_dict = dict() | |
mat_output.location = mathutils.Vector((346.016, 300.0)) | |
node_1_mapping = mat.node_tree.nodes.new(type="ShaderNodeMapping") | |
node_1_mapping.location = mathutils.Vector((-1531.177, -63.872)) | |
node_1_mapping.name = "Mapping" | |
node_1_mapping.label = "" | |
node_dict["Mapping"] = node_1_mapping | |
node_1_mapping.inputs["Vector"].default_value = (0.0, 0.0, 0.0) | |
node_1_mapping.inputs["Location"].default_value = mathutils.Vector((0.0, 0.0, 0.0)) | |
node_1_mapping.inputs["Rotation"].default_value = mathutils.Euler((0.0, 0.0, 0.0)) | |
node_1_mapping.inputs["Scale"].default_value = mathutils.Vector((1.0, 1.0, 1.0)) | |
node_1_mapping.active_preview = False | |
node_1_mapping.vector_type = "POINT" | |
node_2_tex_coord = mat.node_tree.nodes.new(type="ShaderNodeTexCoord") | |
node_2_tex_coord.location = mathutils.Vector((-1741.177, -80.872)) | |
node_2_tex_coord.name = "Texture Coordinate" | |
node_2_tex_coord.label = "" | |
node_dict["Texture Coordinate"] = node_2_tex_coord | |
node_2_tex_coord.active_preview = False | |
node_3_tex_noise = mat.node_tree.nodes.new(type="ShaderNodeTexNoise") | |
node_3_tex_noise.location = mathutils.Vector((-1241.177, -47.491)) | |
node_3_tex_noise.name = "Noise Texture" | |
node_3_tex_noise.label = "" | |
node_dict["Noise Texture"] = node_3_tex_noise | |
node_3_tex_noise.inputs["Vector"].default_value = (0.0, 0.0, 0.0) | |
node_3_tex_noise.active_preview = False | |
node_3_tex_noise.noise_dimensions = "3D" | |
node_4_val_to_rgb = mat.node_tree.nodes.new(type="ShaderNodeValToRGB") | |
node_4_val_to_rgb.location = mathutils.Vector((-691.556, -27.459)) | |
node_4_val_to_rgb.name = "ColorRamp" | |
node_4_val_to_rgb.label = "" | |
node_dict["ColorRamp"] = node_4_val_to_rgb | |
node_4_val_to_rgb.color_ramp.elements[0].color = ( | |
0.503, | |
0.120, | |
0.171, | |
0.995, | |
) | |
node_4_val_to_rgb.color_ramp.elements[0].position = 0.495 | |
node_4_val_to_rgb.color_ramp.elements[1].color = ( | |
0.550, | |
0.108, | |
0.125, | |
0.995, | |
) | |
node_4_val_to_rgb.color_ramp.elements[1].position = 0.532 | |
node_4_val_to_rgb.color_ramp.interpolation = "LINEAR" | |
node_4_val_to_rgb.active_preview = False | |
node_5_bsdf_principled = mat.node_tree.nodes.new(type="ShaderNodeBsdfPrincipled") | |
node_5_bsdf_principled.location = mathutils.Vector((-296.040, -596.632)) | |
node_5_bsdf_principled.name = "Principled BSDF.001" | |
node_5_bsdf_principled.label = "" | |
node_dict["Principled BSDF.001"] = node_5_bsdf_principled | |
node_5_bsdf_principled.inputs["Base Color"].default_value = ( | |
0.800, | |
0.800, | |
0.800, | |
1.0, | |
) | |
node_5_bsdf_principled.inputs["Subsurface Radius"].default_value = ( | |
1.0, | |
0.200, | |
0.100, | |
) | |
node_5_bsdf_principled.inputs["Subsurface Color"].default_value = ( | |
0.800, | |
0.800, | |
0.800, | |
1.0, | |
) | |
node_5_bsdf_principled.inputs["Clearcoat Roughness"].default_value = 0.028 | |
node_5_bsdf_principled.inputs["Emission"].default_value = ( | |
0.0, | |
0.0, | |
0.0, | |
1.0, | |
) | |
node_5_bsdf_principled.inputs["Normal"].default_value = (0.0, 0.0, 0.0) | |
node_5_bsdf_principled.inputs["Clearcoat Normal"].default_value = ( | |
0.0, | |
0.0, | |
0.0, | |
) | |
node_5_bsdf_principled.inputs["Tangent"].default_value = (0.0, 0.0, 0.0) | |
node_5_bsdf_principled.subsurface_method = "BURLEY" | |
node_5_bsdf_principled.distribution = "GGX" | |
node_5_bsdf_principled.active_preview = False | |
node_6_val_to_rgb = mat.node_tree.nodes.new(type="ShaderNodeValToRGB") | |
node_6_val_to_rgb.location = mathutils.Vector((-692.838, -670.351)) | |
node_6_val_to_rgb.name = "ColorRamp.001" | |
node_6_val_to_rgb.label = "" | |
node_dict["ColorRamp.001"] = node_6_val_to_rgb | |
node_6_val_to_rgb.color_ramp.elements[0].color = ( | |
0.550, | |
0.108, | |
0.125, | |
0.995, | |
) | |
node_6_val_to_rgb.color_ramp.elements[0].position = 0.495 | |
node_6_val_to_rgb.color_ramp.elements[1].color = ( | |
0.503, | |
0.120, | |
0.171, | |
0.995, | |
) | |
node_6_val_to_rgb.color_ramp.elements[1].position = 0.532 | |
node_6_val_to_rgb.color_ramp.interpolation = "LINEAR" | |
node_6_val_to_rgb.active_preview = False | |
node_7_vertex_color = mat.node_tree.nodes.new(type="ShaderNodeVertexColor") | |
node_7_vertex_color.location = mathutils.Vector((-810.627, 277.311)) | |
node_7_vertex_color.name = "Vertex Color" | |
node_7_vertex_color.label = "" | |
node_dict["Vertex Color"] = node_7_vertex_color | |
node_7_vertex_color.active_preview = False | |
node_7_vertex_color.layer_name = "dp_wetmap" | |
node_8_val_to_rgb = mat.node_tree.nodes.new(type="ShaderNodeValToRGB") | |
node_8_val_to_rgb.location = mathutils.Vector((-431.062, 263.834)) | |
node_8_val_to_rgb.name = "ColorRamp.002" | |
node_8_val_to_rgb.label = "" | |
node_dict["ColorRamp.002"] = node_8_val_to_rgb | |
node_8_val_to_rgb.color_ramp.elements[0].color = (0.0, 0.0, 0.0, 1.0) | |
node_8_val_to_rgb.color_ramp.elements[0].position = 0.0 | |
node_8_val_to_rgb.color_ramp.elements[1].color = (1.0, 1.0, 1.0, 1.0) | |
node_8_val_to_rgb.color_ramp.elements[1].position = 0.081 | |
node_8_val_to_rgb.color_ramp.interpolation = "LINEAR" | |
node_8_val_to_rgb.active_preview = False | |
node_9_mix_shader = mat.node_tree.nodes.new(type="ShaderNodeMixShader") | |
node_9_mix_shader.location = mathutils.Vector((61.969, 241.682)) | |
node_9_mix_shader.name = "Mix Shader" | |
node_9_mix_shader.label = "" | |
node_dict["Mix Shader"] = node_9_mix_shader | |
node_9_mix_shader.active_preview = False | |
bsdf = mat.node_tree.nodes.new(type="ShaderNodeBsdfPrincipled") | |
bsdf.location = mathutils.Vector((-294.757, 46.257)) | |
bsdf.name = "Principled BSDF" | |
bsdf.label = "" | |
node_dict["Principled BSDF"] = bsdf | |
bsdf.inputs["Base Color"].default_value = ( | |
0.800, | |
0.800, | |
0.800, | |
1.0, | |
) | |
bsdf.inputs["Subsurface Radius"].default_value = ( | |
1.0, | |
0.200, | |
0.100, | |
) | |
bsdf.inputs["Subsurface Color"].default_value = ( | |
0.800, | |
0.800, | |
0.800, | |
1.0, | |
) | |
bsdf.inputs["Clearcoat Roughness"].default_value = 0.028 | |
bsdf.inputs["Emission"].default_value = ( | |
0.0, | |
0.0, | |
0.0, | |
1.0, | |
) | |
bsdf.inputs["Normal"].default_value = (0.0, 0.0, 0.0) | |
bsdf.inputs["Clearcoat Normal"].default_value = ( | |
0.0, | |
0.0, | |
0.0, | |
) | |
bsdf.inputs["Tangent"].default_value = (0.0, 0.0, 0.0) | |
bsdf.subsurface_method = "BURLEY" | |
bsdf.distribution = "GGX" | |
bsdf.active_preview = False | |
node_11ShaderNodeTexVoronoi = mat.node_tree.nodes.new(type="ShaderNodeTexVoronoi") | |
node_11ShaderNodeTexVoronoi.location = mathutils.Vector((-949.015, -36.187)) | |
node_11ShaderNodeTexVoronoi.name = "Voronoi Texture" | |
node_11ShaderNodeTexVoronoi.label = "" | |
node_dict["Voronoi Texture"] = node_11ShaderNodeTexVoronoi | |
node_11ShaderNodeTexVoronoi.inputs["Vector"].default_value = (0.0, 0.0, 0.0) | |
node_11ShaderNodeTexVoronoi.inputs["Randomness"].default_value = 0.0 | |
node_11ShaderNodeTexVoronoi.distance = "CHEBYCHEV" | |
node_11ShaderNodeTexVoronoi.voronoi_dimensions = "3D" | |
node_11ShaderNodeTexVoronoi.feature = "F2" | |
node_11ShaderNodeTexVoronoi.active_preview = False | |
# links | |
from_node = mat.node_tree.nodes.get("Vertex Color") | |
to_node = mat.node_tree.nodes.get("ColorRamp.002") | |
mat.node_tree.links.new(from_node.outputs["Color"], to_node.inputs["Fac"]) | |
from_node = mat.node_tree.nodes.get("Mapping") | |
to_node = mat.node_tree.nodes.get("Noise Texture") | |
mat.node_tree.links.new(from_node.outputs["Vector"], to_node.inputs["Vector"]) | |
from_node = mat.node_tree.nodes.get("Texture Coordinate") | |
to_node = mat.node_tree.nodes.get("Mapping") | |
mat.node_tree.links.new(from_node.outputs["Generated"], to_node.inputs["Vector"]) | |
from_node = mat.node_tree.nodes.get("Noise Texture") | |
to_node = mat.node_tree.nodes.get("Voronoi Texture") | |
mat.node_tree.links.new(from_node.outputs["Color"], to_node.inputs["Vector"]) | |
from_node = mat.node_tree.nodes.get("ColorRamp") | |
to_node = mat.node_tree.nodes.get("Principled BSDF") | |
mat.node_tree.links.new(from_node.outputs["Color"], to_node.inputs["Base Color"]) | |
from_node = mat.node_tree.nodes.get("ColorRamp") | |
to_node = mat.node_tree.nodes.get("Principled BSDF") | |
mat.node_tree.links.new(from_node.outputs["Color"], to_node.inputs["Roughness"]) | |
from_node = mat.node_tree.nodes.get("ColorRamp.001") | |
to_node = mat.node_tree.nodes.get("Principled BSDF.001") | |
mat.node_tree.links.new(from_node.outputs["Color"], to_node.inputs["Base Color"]) | |
from_node = mat.node_tree.nodes.get("ColorRamp.001") | |
to_node = mat.node_tree.nodes.get("Principled BSDF.001") | |
mat.node_tree.links.new(from_node.outputs["Color"], to_node.inputs["Roughness"]) | |
from_node = mat.node_tree.nodes.get("Voronoi Texture") | |
to_node = mat.node_tree.nodes.get("ColorRamp.001") | |
mat.node_tree.links.new(from_node.outputs["Distance"], to_node.inputs["Fac"]) | |
from_node = mat.node_tree.nodes.get("Principled BSDF.001") | |
to_node = mat.node_tree.nodes.get("Mix Shader") | |
mat.node_tree.links.new(from_node.outputs["BSDF"], to_node.inputs["Shader"]) | |
from_node = mat.node_tree.nodes.get("ColorRamp.002") | |
to_node = mat.node_tree.nodes.get("Mix Shader") | |
mat.node_tree.links.new(from_node.outputs["Color"], to_node.inputs["Fac"]) | |
from_node = mat.node_tree.nodes.get("Mix Shader") | |
to_node = mat.node_tree.nodes.get("Material Output") | |
mat.node_tree.links.new(from_node.outputs["Shader"], to_node.inputs["Surface"]) | |
from_node = mat.node_tree.nodes.get("Principled BSDF") | |
to_node = mat.node_tree.nodes.get("Mix Shader") | |
mat.node_tree.links.new(from_node.outputs["BSDF"], to_node.inputs[2]) | |
from_node = mat.node_tree.nodes.get("Voronoi Texture") | |
to_node = mat.node_tree.nodes.get("ColorRamp") | |
mat.node_tree.links.new(from_node.outputs["Distance"], to_node.inputs["Fac"]) | |
if return_nodes: | |
return mat, node_dict | |
else: | |
return mat | |
def gen_martial_metal(return_nodes=False): | |
mat = bpy.data.materials.new(name="Material_gen_metal") | |
mat.use_nodes = True | |
mat_output = None | |
to_rm = [] | |
for node in mat.node_tree.nodes: | |
if node.type == "OUTPUT_MATERIAL": | |
mat_output = node | |
continue | |
to_rm.append(node) | |
for node in to_rm: | |
mat.node_tree.nodes.remove(node) | |
node_dict = dict() | |
mat_output.location = mathutils.Vector((300.0, 300.0)) | |
bsdf = mat.node_tree.nodes.new(type="ShaderNodeBsdfPrincipled") | |
bsdf.location = mathutils.Vector((10.0, 300.0)) | |
bsdf.name = "Principled BSDF" | |
bsdf.label = "" | |
node_dict["Principled BSDF"] = bsdf | |
bsdf.inputs["Base Color"].default_value = ( | |
0.925, | |
0.882, | |
0.777, | |
0.996, | |
) | |
bsdf.inputs["Subsurface Radius"].default_value = ( | |
1.0, | |
0.200, | |
0.100, | |
) | |
bsdf.inputs["Subsurface Color"].default_value = ( | |
0.800, | |
0.800, | |
0.800, | |
1.0, | |
) | |
bsdf.inputs["Metallic"].default_value = 0.996 | |
bsdf.inputs["Roughness"].default_value = 0.245 | |
bsdf.inputs["Emission"].default_value = ( | |
0.0, | |
0.0, | |
0.0, | |
1.0, | |
) | |
bsdf.inputs["Normal"].default_value = (0.0, 0.0, 0.0) | |
bsdf.inputs["Clearcoat Normal"].default_value = ( | |
0.0, | |
0.0, | |
0.0, | |
) | |
bsdf.inputs["Tangent"].default_value = (0.0, 0.0, 0.0) | |
bsdf.distribution = "GGX" | |
bsdf.active_preview = False | |
bsdf.subsurface_method = "BURLEY" | |
from_node = mat.node_tree.nodes.get("Principled BSDF") | |
to_node = mat.node_tree.nodes.get("Material Output") | |
mat.node_tree.links.new(from_node.outputs["BSDF"], to_node.inputs["Surface"]) | |
if return_nodes: | |
return mat, node_dict | |
else: | |
return mat | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment