motiv-tree

Übersicht: Python & Blender


Objectives

a

What can be modeled? The most common motif are landscapes with the following objects that we want to create in some cases:

  • Clouds
  • Stones
  • Trees
  • Gras
  • Water
  • Mountains/landscape
Tasks:
  1. Copy the script from the stone generator (last station).
  2. What will change when an out of the cloud a tree top and additionally complete tree is to be generated?
  3. As the tree top the every tribe different forms.
  4. What kind of trees there. Look it up in books and on websites after.
  5. Add one or more branches to the trunk.
  6. Plant a tree!

Tree Top and Strain

Tree tops and clouds look very similar, so let's use the class for clouds. New is the construction of the strain. This is explained here step by step. Anyone who wants to generate a real tree, will find an extension for Blender: Tree plugin and a video Video on how to use the plugin More about the theorie behind trees: Lindenmayer-System or L-System

Cloud Script as a Template

Create a copy of the script and start with renaming methods and objects.

use-cases/art/low-poly/cloud-e.py (Source)

#!bpy
"""
Name: 'Cloud-Generator'
Blender: 2.7x
Group: 'Low poly'
Tooltip: 'Part of a Low-Poly-Skripts collection'
"""
import bpy
import random

class Cloud():
    """Create a cloud as low poly"""

    def __init__(self):
        pass

    def setColor(self, obj, material, color):
        material.diffuse_color = color
        material.specular_hardness = 200
        obj.data.materials.append(material)

    def new(self, min=0, max=1):
        """ constuction of a new cloud """
        bpy.ops.mesh.primitive_ico_sphere_add(location=(0, 0, 0))
        obj = bpy.context.object
        obj.name = "cloud"
        obj.location.x = int(random.uniform(min +10 * -1, max + 10))
        obj.location.y = int(random.uniform(min, max)) * 10
        obj.location.z = int(random.uniform(min, max +3 )) * 10

        obj.scale[0] = int(random.uniform(min + 1, max + 3))
        obj.scale[1] = int(random.uniform(min + 1, max + 3))
        obj.scale[2] = int(random.uniform(min + 1, max + 4))

        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()
        bpy.ops.object.editmode_toggle()

        bpy.ops.object.modifier_add(type='DISPLACE')
        STUCCI_TEX = bpy.data.textures.new("Stucci tex", type="STUCCI")
        obj.modifiers["Displace"].texture = STUCCI_TEX
        obj.modifiers["Displace"].strength = .2

        MATERIAL_CLOUD = bpy.data.materials.new('cloud')
        self.setColor(obj,
                      MATERIAL_CLOUD,
                      (random.uniform(0, .3),
                       random.uniform(0, .3),
                       random.uniform(0.3, 1)))

    def remove(self, name=None):
        """ Delete a cloud or all"""

        if name:
            bpy.ops.object.select_pattern(pattern=name)
        else:
            bpy.ops.object.select_pattern(pattern="cloud*")

        bpy.ops.object.delete()


if __name__ == "__main__":

    # switch to object mode, if nessasary
    if bpy.ops.object.mode_set.poll():
        bpy.ops.object.mode_set(mode='OBJECT')
    cloud = Cloud()
    cloud.remove()
    for i in range(5):
        cloud = Cloud()
        cloud.new()

The Strain

The strain is not made by a tube, that would be ok for a spruce. We create a more asymmetrically stukture for the strain. The same technique can be used branches on the tree. The former will be described here. How to use the technique for the branches is an exercise for the reader :-)

Here are an example to get the idea:

/use-cases/art/low-poly/trees.png

We are starting at zero point of the coordinate system. As so often an obj variable is created and name the new object.

use-cases/art/low-poly/tree.py (Source)

    def tree_trank(self):
        """ Constuction of a new tree top """

        # we are starting with a circle
        bpy.ops.mesh.primitive_circle_add(vertices=8,
                                          radius=0.1,
                                          location=(0, 0, 0))
        obj = bpy.context.object
        obj.name = "tree_trank"

We are starting with a circle and extrude it (exacly five times). We logging the valus, because we don't know where the strain is ending. We need the values of x and z, so we can use the values later on. The variables are prepaird in the __init__ method of the class. Such variables are named instance variables.

The extended __init__ method

use-cases/art/low-poly/tree.py (Source)

    def __init__(self):
        self.top = 0
        self.x = 0

Extrude with edit mode

Only in the Edit Mode can be extracted. Again we use random values to a predetermined value in a heritage Reich Vary displacement in the x-axis and the growth in the height to be able to make should not strain the other same.

use-cases/art/low-poly/tree.py (Source)

        # switch to EDIT mode
        bpy.ops.object.mode_set(mode='EDIT')
        # start extruding the circle
        for i in range(5):
            length = random.uniform(0, .5)
            self.top = self.top + length
            bpy.ops.mesh.extrude_region_move(
                MESH_OT_extrude_region={"mirror": False},
                TRANSFORM_OT_translate={"value": (0,
                                                  0,
                                                  length)})
            new_x = random.uniform(.1, .2) * random.choice([1, -1])
            self.x = self.x + new_x
            bpy.ops.transform.translate(value=((new_x,
                                                0,
                                                0)))

Bend the trunk

Of course, the trunk is a little bent, and also receives a special color.

use-cases/art/low-poly/tree.py (Source)

        bpy.ops.object.mode_set(mode='OBJECT')
        bpy.ops.object.modifier_add(type='DECIMATE')
        obj.modifiers["Decimate"].ratio = 0.114

        MATERIAL_TREE_TRANK = bpy.data.materials.new('Tree trank')

        self.setColor(obj,
                      MATERIAL_TREE_TRANK, (random.uniform(0, .3),
                                            random.uniform(0, .3),
                                            0))

Reassemble the tree

The individual parts of the tree can now already, each part for itself, be generated. The parts should also be connected. We use the method new to achieve this.

use-cases/art/low-poly/tree.py (Source)

    def new(self):

        # get the context
        scn = bpy.context.scene

        parts = ['tree_trank', 'tree_top', 'tree']

        self.tree_trank()
        self.tree_top()
        obj = bpy.context.object
        scn.objects[parts[0]].select = True
        scn.objects[parts[1]].select = True
        bpy.ops.object.join()
        obj.name = parts[2]

The methods trunk and new

use-cases/art/low-poly/tree.py (Source)

    def tree_trank(self):
        """ Constuction of a new tree top """

        # we are starting with a circle
        bpy.ops.mesh.primitive_circle_add(vertices=8,
                                          radius=0.1,
                                          location=(0, 0, 0))
        obj = bpy.context.object
        obj.name = "tree_trank"

        # switch to EDIT mode
        bpy.ops.object.mode_set(mode='EDIT')
        # start extruding the circle
        for i in range(5):
            length = random.uniform(0, .5)
            self.top = self.top + length
            bpy.ops.mesh.extrude_region_move(
                MESH_OT_extrude_region={"mirror": False},
                TRANSFORM_OT_translate={"value": (0,
                                                  0,
                                                  length)})
            new_x = random.uniform(.1, .2) * random.choice([1, -1])
            self.x = self.x + new_x
            bpy.ops.transform.translate(value=((new_x,
                                                0,
                                                0)))

        bpy.ops.object.mode_set(mode='OBJECT')
        bpy.ops.object.modifier_add(type='DECIMATE')
        obj.modifiers["Decimate"].ratio = 0.114

        MATERIAL_TREE_TRANK = bpy.data.materials.new('Tree trank')

        self.setColor(obj,
                      MATERIAL_TREE_TRANK, (random.uniform(0, .3),
                                            random.uniform(0, .3),
                                            0))

    def new(self):

        # get the context
        scn = bpy.context.scene

        parts = ['tree_trank', 'tree_top', 'tree']

        self.tree_trank()
        self.tree_top()
        obj = bpy.context.object
        scn.objects[parts[0]].select = True
        scn.objects[parts[1]].select = True
        bpy.ops.object.join()
        obj.name = parts[2]

Nn addendum

Who is watching the console carefully, has noticed the following warnings:

convertViewVec: called in an invalid context
convertViewVec: called in an invalid context
convertViewVec: called in an invalid context
convertViewVec: called in an invalid context
convertViewVec: called in an invalid context
convertViewVec: called in an invalid context
convertViewVec: called in an invalid context
convertViewVec: called in an invalid context
convertViewVec: called in an invalid context
convertViewVec: called in an invalid context

This message can be ignored or turned off, as suggested in a forum post. Probably it is better, to look for an alternative solution and this alternative is called BMesh.

Transformation with a bmesh

Das BMesh ist ein neuer Datentyp und enthält die Struktur eines Körpers. Ein Beispiel für die Neuanlage findet man in der Station.

This time the object is however already produced and is now retrospectively to be changed.

These three steps are necessary:

  1. convert the object into a bmesh
  2. Manipulate the coordinates
  3. Save the new coordinates

About the steps 1 to 3

Provided there is a bit marked and the edit mode is activated.

bm = bmesh.from_edit_mesh(obj.data)
# more manipulations at this point
bmesh.update_edit_mesh(obj.data)
/use-cases/art/low-poly/tree2.jpg
Help: The script is generating a tree, but not finished yet! Help is appreciated.

Comments