motiv-ground

Übersicht: Python & Blender


Lernziel

a

Was kann modelliert werden? Das wohl häufigste Motiv sind Landschaften mit den folgenden Objekten, die wir zum Teil auch erzeugen wollen:

  • Wolken
  • Steine
  • Bäume
  • Berge
  • Gras
  • Wasser
  • Berge/Untergund

Handlungsanweisungen

Aufgaben:
  1. Ändere die Parameter!
  2. Wie würde eine Vertiefung mit Wasser generiert werden?
  3. Wie kann ein Teil des Berges mit einer eisbedeckten Spitze versehen werden?

Ein Beispiel:

Wie kann per Skript generiert werden, was im Bild gelb bzw. orange wie der Uluṟu aussieht. Bald sind wir schlauer...

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

Das Gerüst

Wieder ist das Gerüst einer Klasse der Ausgangspunkt.

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

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


class Landscape():
    """Create a landscape as low poly"""

    def __init__(self):
        pass

    def setColor(self):
        pass

    def new(self):
        """ constuction of a new landscape """
        pass

    def remove(self):
        """ Delete a landscape or all"""
        pass

if __name__ == "__main__":

    # switch to object mode, if nessasary
    if bpy.ops.object.mode_set.poll():
        bpy.ops.object.mode_set(mode='OBJECT')
    ground = Landscape()

Die Grundplatte

Sie wird aus einer plane erzeugt und anschließend auf 100x100 Einheiten vergrößert.

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

        """ constuction of a new landscape """
        scn = bpy.context.scene
        bpy.ops.mesh.primitive_plane_add(location=(0, 0, 0))
        obj = bpy.context.object
        obj.scale[0] = 100
        obj.scale[1] = 100
        obj.name = "ground"

Ein kleines Erdbeben

Wir teilen die Grundplatte mehrfach und damit es nicht zu rechteckig wird, verwenden wir den Parameter fractal. Es is nur ein kleines Erdbeben, denn wir heben einzelne Punkte nur minimal an.

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

        val = 1
        for i in range(1, 5):
            val = val + random.randint(1, 3)
            bpy.ops.mesh.subdivide(fractal=val)

Der Brocken

Ein kleiner Teil der Grundplatte soll sich deutlich vom normalen Niveau abheben, was in mehrerern Schritten erfolgt. Verwendung findet ein bmesh, die neuere Version zur Verwaltung von Koordinaten.

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

        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.select_all(action='TOGGLE')
        bpy.ops.object.editmode_toggle()
        me = obj.data
        i = random.randint(0, len(me.vertices))
        obj.data.vertices[i].select = True
        bpy.ops.object.mode_set(mode='EDIT')
        for i in range(5):
            bpy.ops.mesh.select_more()

        bm = bmesh.from_edit_mesh(obj.data)
        for vertex in bm.verts:
            if vertex.select:
                vertex.co.z += 11

        bmesh.update_edit_mesh(obj.data)
        bpy.ops.transform.resize(value=(.9, .9, 0))

        for vertex in bm.verts:
            if vertex.select:
                vertex.co.z += 15

        bmesh.update_edit_mesh(obj.data)
        bpy.ops.transform.resize(value=(.8, .8, 0))

Das Skript als Ganzes

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

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

class Landscape():
    """Create a landscape as low poly"""

    def __init__(self):
        self.top = 0

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

    def new_area(self):
        """ constuction of a new landscape """
        scn = bpy.context.scene
        bpy.ops.mesh.primitive_plane_add(location=(0, 0, 0))
        obj = bpy.context.object
        obj.scale[0] = 100
        obj.scale[1] = 100
        obj.name = "ground"
        bpy.ops.object.mode_set(mode='EDIT')
        val = 1
        for i in range(1, 5):
            val = val + random.randint(1, 3)
            bpy.ops.mesh.subdivide(fractal=val)
        bpy.ops.object.mode_set(mode='OBJECT')
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.select_all(action='TOGGLE')
        bpy.ops.object.editmode_toggle()
        me = obj.data
        i = random.randint(0, len(me.vertices))
        obj.data.vertices[i].select = True
        bpy.ops.object.mode_set(mode='EDIT')
        for i in range(5):
            bpy.ops.mesh.select_more()

        bm = bmesh.from_edit_mesh(obj.data)
        for vertex in bm.verts:
            if vertex.select:
                vertex.co.z += 11

        bmesh.update_edit_mesh(obj.data)
        bpy.ops.transform.resize(value=(.9, .9, 0))

        for vertex in bm.verts:
            if vertex.select:
                vertex.co.z += 15

        bmesh.update_edit_mesh(obj.data)
        bpy.ops.transform.resize(value=(.8, .8, 0))

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

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

        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')
    ground = Landscape()
    ground.remove()
    ground.new_area()

Kommentare