import bpy from random import randint, random from mathutils import Vector from math import pi, sin, cos bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete(use_global=False) def new_tree(): verts = [] faces = [] face_uvs = [] cylinder_sections = 7 # Should this take an old ring to connect to? def draw_branch_section(start, end, radius): middle_vector = end - start point_one = middle_vector.cross((1, 0, 0)).normalized() * radius point_two = middle_vector.cross(point_one).normalized() * radius for s in range(cylinder_sections + 1): angle_a = s * 2 * pi / cylinder_sections # angle_b = (s+1) * 2 * pi / cylinder_sections p_a = cos(angle_a) * point_one + sin(angle_a) * point_two + start # p_b = cos(angle_b) * point_one + sin(angle_b) * point_two + start # p_c = p_b + middle_vector p_d = p_a + middle_vector # Now hopefully p_a, p_b, p_c, and p_d form a cylinder panel # Optimization: We can save shared vertices if s < cylinder_sections: faces.append([n + len(verts) for n in [0, 1, 3, 2]]) face_uvs.append(((s / cylinder_sections, 0), (s/cylinder_sections, 1))) verts.extend([p_a, p_d]) # run this AFTER the tree mesh is created def add_texture_coordinates(tree_mesh): uv_layer = tree_mesh.uv_layers.new(name="Bark") for mpoly, fuv in zip(tree_mesh.polygons, face_uvs): uv_layer.data[mpoly.loop_indices[0]].uv = fuv[0] uv_layer.data[mpoly.loop_indices[1]].uv = fuv[1] # For the next: 3, than 2 uv_layer.data[mpoly.loop_indices[3]].uv = (fuv[0][0] + (1/cylinder_sections), fuv[0][1]) uv_layer.data[mpoly.loop_indices[2]].uv = (fuv[1][0] + (1/cylinder_sections), fuv[1][1]) # This is where we left off in class # Important question: How we avoid re-using a little slice of our texture? # Options: Have draw_branch_section output a y offset, have draw_branch_section support curves, etc def draw_branch(start, end, radius, prev_pos, subdivisions): draw_branch_section(start, end, radius) # curve? # for i in range(subdivisions): # Call draw_branch_section on one subdivision # params? def make_tree(pos, calls, prev_pos, stop_adjustment = 1): # Will we stop? if random() > stop_adjustment /calls: # Leaves? return branches = randint(1, 5) for i in range(branches): new_endpoint = Vector((pos.x + 3 * (random() - 0.5), pos.y + 3 * (random() - 0.5), pos.z + 1 + random())) make_tree(new_endpoint, calls + 1, pos) # Draw branch to new endpoint draw_branch(pos, new_endpoint, .3/calls, prev_pos, 1) # Text code to make one cylinder, with a texture to test it out make_tree(Vector((0, 0, 0)), 1, Vector((0, 0, -2)), 1000) tree_mesh = bpy.data.meshes.new(name="TreeMesh") tree_mesh.from_pydata(vertices=verts, edges=[], faces=faces) add_texture_coordinates(tree_mesh) tree_mesh.validate(verbose=True) # I think this prints out errors if we have any tree_object = bpy.data.objects.new(name="Tree", object_data=tree_mesh) bpy.context.collection.objects.link(tree_object) material = bpy.data.materials.new(name="TreeBark") material.use_nodes = True tree_object.data.materials.append(material) bsdf_node= material.node_tree.nodes.get("Principled BSDF") texture_node = material.node_tree.nodes.new(type="ShaderNodeTexImage") texture_node.image = bpy.data.images.load("bark.jpg") material.node_tree.links.new(texture_node.outputs["Color"], bsdf_node.inputs["Base Color"]) return tree_object new_tree()