Chapter 2. Creating and Editing Objects

In a way, meshes are the most essential type of objects in a 3D application. They form the basis of most visible objects and are the raw material that might get rigged and animated further down the line. This chapter deals with the creation of meshes and with ways to manipulate a mesh object, both as a whole and as the individual entities it consists of—the vertices, edges, and faces.

Creating and Editing Objects

In this chapter, you will learn:

  • How to create configurable mesh objects
  • How to design a graphical user interface
  • How to make your script store user choices for later reuse
  • How to select vertices and faces in a mesh
  • How to parent an object to another
  • How to create groups
  • How to modify meshes
  • How to run Blender from the command line and render in the background
  • How to process command-line parameters

Creepy crawlies—a GUI to configure objects

Instantiating a single copy of a one-off Blender object (like we did in the "hello world" example inChapter 1, Extending Blender with Python) might be a good programming exercise, but an object creation script really comes into its own when built-in methods such as copying objects, or modifiers such as the array modifier, are not sufficient.

A good example is where we want to create one or many object variants and these variants need to be easy to configure for the end user. For example, nuts and bolts come in many shapes and sizes so Blender comes included with a script to create them. Many more scripts are available on the Web to create anything from mechanical gears to stairs, from trees to church domes.

In this section, we show how to build a small application that can create all sorts of bug-like creatures and comes with a simple but effective GUI to set the many configurable parameters. This application also stores the user preferences for later reuse.

Building the user interface

Designing, building, and testing a graphical user interface can be a formidable task, but the Blender API provides us with tools to make this task a lot easier. The Blender.Draw module provides simple, but often used and easy to configure components to quickly put a user interface together. The Blender.BGL module gives access to all the nuts and bolts to design a graphical user interface from scratch. We will mostly use the former because it is almost everything we need but we give an example of the latter as well to design a simple error pop up. Our main user interface will look like this:

Building the user interface

When we invoke our script from the Add Menu (normally accessible from the menu bar at the top of the screen or by pressing the spacebar in the 3D view), the previous menu will pop up and the user can tweak the parameters to his or her liking. When the OK button is pressed the script generates an insect-like mesh. The pop up can also be exited by pressing Esc, in which case the script terminates without generating a mesh.

Creating bugs—some assembly required

Our mission is to create simple creatures from a small sample of building blocks that may be chained together. The outline for our script is this:

  1. Import the building blocks for our creatures.
  2. Draw a user interface.
  3. Assemble the creature mesh from building blocks as defined by the user.
  4. Insert the mesh as an object into the scene.

We go through the script step-by-step showing the relevant parts in detail. (The full script is available as creepycrawlies.py.) The first step involves creating body parts that are suitable for assembling together. This means we have to model these parts in Blender, defining suitable joints and marking those joints as vertex groups. Then we export these meshes as Python code by using a script that we encounter again in the next chapter as it deals with vertex groups.

For now, we use this generated Python code simply as a module containing several lists of vertices defining each body part. We have to make sure that this module is somewhere in the Python path, for example, .blenderscriptspymodules would be a logical choice or alternatively the user scriptdir. The Python file with the mesh building blocks is called mymesh.py so the first part of our code contains the following import statement:

import mymesh

Creating a user interface

Drawing a simple user interface is a matter of using Draw.Create() to create the required buttons and assembling and initializing these buttons with Draw.PupBlock()

This is somewhat limited compared to full-fledged libraries available for some programming languages, but very easy to use. The basic idea is to create interactive objects, such as buttons, and then assemble them in a dialog box to display to the user. At the same time, the dialog box states some of the limitations on the values the button may produce. The dialog or pop up will be shown at the position of the cursor. Blender is capable of producing a more sophisticated user interface, but for now we stick to the basics.

Although Draw.Create() can produce toggle buttons and input buttons for strings as well, for our application we need input buttons only for integer values and floating point values. The type of the variable (for example a floating point value or an integer) is determined by the type of the default value given to Draw.Create(). The OK button is automatically displayed by Draw.PupBlock(). This function takes a list of tuples as an argument with each tuple defining a button to display. Each tuple consists of a text to display on the button, a button object created with Draw.Create(), minimum and maximum allowable values, and a tooltip text to show when hovering above the button.

Draw = Blender.Draw
THORAXSEGMENTS = Draw.Create(3)
TAILSEGMENTS = Draw.Create(5)
LEGSEGMENTS = Draw.Create(2)
WINGSEGMENTS = Draw.Create(2)
EYESIZE = Draw.Create(1.0)
TAILTAPER = Draw.Create(0.9)
if not Draw.PupBlock('Add CreepyCrawly', [
('Thorax segments:' , THORAXSEGMENTS, 2, 50, 'Number of thorax segments'),
('Tail segments:' , TAILSEGMENTS, 0, 50, 'Number of tail segments'),
('Leg segments:' , LEGSEGMENTS, 2, 10, 'Number of thorax segments with legs'),
('Wing segments:' , WINGSEGMENTS, 0, 10, 'Number of thorax segments with wings'),
('Eye size:' , EYESIZE, 0.1,10, 'Size of the eyes'),
('Tail taper:' , TAILTAPER, 0.1,10, 'Taper fraction of each tail segment'),]):
return

As you can see, we limit the possible values of our input buttons to a reasonable range (up to 50 for the thorax and tail segments) to prevent unwanted results (huge values might cripple your system if memory or processing power is scarce).

Remembering choices

It would be very convenient if we could remember the user's choices so that we could present the last settings when the script is run again, but in Blender each script is run in isolation and all information within the script is lost once the script ends. Therefore, we need some mechanism to store information in a persistent way. For this purpose, the Blender API has the Registry module that allows us to keep values in memory (and on disk as well) indexed by an arbitrary key.

Our GUI initialization code changes little in itself if we want to add this functionality, but is prepended by code retrieving remembered values (if they are present) and followed by code saving the user's choices:

reg = Blender.Registry.GetKey('CreepyCrawlies',True)
try:
nthorax=reg['ThoraxSegments']
except:
nthorax=3
try:
ntail=reg['TailSegments']
except:
ntail=5
... <similar code for other parameters> ...
Draw = Blender.Draw
THORAXSEGMENTS = Draw.Create(nthorax)
TAILSEGMENTS = Draw.Create(ntail)
LEGSEGMENTS = Draw.Create(nleg)
WINGSEGMENTS = Draw.Create(nwing)
EYESIZE = Draw.Create(eye)
TAILTAPER = Draw.Create(taper)
if not Draw.PupBlock('Add CreepyCrawly', [
... <identical code as in previous example> ...
return
reg={'ThoraxSegments':THORAXSEGMENTS.val, 'TailSegments' :TAILSEGMENTS.val, 'LegSegments' :LEGSEGMENTS.val, 'WingSegments' :WINGSEGMENTS.val, 'EyeSize' :EYESIZE.val, 'TailTaper':TAILTAPER.val}
Blender.Registry.SetKey('CreepyCrawlies',reg,True)

The actual reading and writing of our registry entry is highlighted. The True argument indicates that we want to retrieve our data from disk if it is not available in memory, or write it to disk as well when saving so that our script can access this saved information even if we stop Blender and restart it later. The actual registry entry received or written is a dictionary that can hold whatever data we want. Of course, there might not yet be a registry entry present, in which case we get a None value—a situation taken care of by the try … except … statements.

The full power of Blender graphics

A pop-up dialog is sufficient for many applications but if it does not fit your requirements, Blender's Draw module has many more building blocks to create a user interface but these building blocks require a little bit more effort to glue them together in a working application.

We will use these building blocks to create an error pop up. This pop up merely shows a message on an alarmingly colored background but illustrates nicely how user actions (such as key presses or button clicks) are linked to the graphical elements.

from Blender import Window,Draw,BGL
def event(evt, val):
if evt == Draw.ESCKEY:
Draw.Exit() # exit when user presses ESC
return
def button_event(evt):
if evt == 1:
Draw.Exit()
return
def msg(text):
w = Draw.GetStringWidth(text)+20
wb= Draw.GetStringWidth('Ok')+8
BGL.glClearColor(0.6, 0.6, 0.6, 1.0)
BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
BGL.glColor3f(0.75, 0.75, 0.75)
BGL.glRecti(3,30,w+wb,3)
Draw.Button("Ok",1,4,4,wb,28)
Draw.Label(text,4+wb,4,w,28)
def error(text):
Draw.Register(lambda:msg(text), event, button_event)

The error() function is where it all starts and ends for the user; it tells Blender what to draw, where to send events such as button clicks, where to send key presses, and starts the interaction. The lambda function is necessary as the function that we pass to Draw.Register() to draw things cannot take an argument, yet we want to pass a different text argument every time we call error(). The lambda function basically defines a new function without arguments but with the text enclosed.

The msg() function is responsible for drawing all of the elements on the screen. It draws a colored backdrop with the BGL.glRecti() function, a label with the text to display (with Draw.Label()), and an OK button that is assigned an event number of 1 (with Draw.Button()). When the user clicks the OK button, this event number is sent to the event handler—the button_event() function that we passed to Draw.Register(). All that the event handler does when it is called with this event number of 1 is to terminate the Draw.Register() function by calling Draw.Exit(), so our error() function may return.

Creating a new Mesh object

Once we have retrieved our lists of vertex co-ordinates and face indices from the mymesh module, we need some manner to create a new Mesh object in our scene and add MVert and MFace objects to this mesh. This might be implemented like this:

me=Blender.Mesh.New('Bug')
me.verts.extend(verts)
me.faces.extend(faces)
scn=Blender.Scene.GetCurrent()
ob=scn.objects.new(me,'Bug')
scn.objects.active=ob
me.remDoubles(0.001)
me.recalcNormals()

The first line creates a new Mesh object with the name Bug. It will contain no vertices, edges, or faces and will not be embedded in a Blender object nor connected to any Scene yet. If the name of the mesh already exists, it is appended with a unique numerical suffix (for example, Bug.001).

The next two lines actually create geometry inside the mesh. The verts attribute is where our list of MVert objects is referenced. It has a method extend() that will take a list of tuples, each containing the x, y, and z coordinates of the vertices to create. Likewise, the extend() method of the faces attribute will take a list of tuples, each containing three or more indices into the vertex list that together define a face. Order is important here: we need to add new vertices first; otherwise newly-created faces cannot refer to them. It is not necessary to define any edges, as adding faces will also create implied edges that are not already present.

A mesh in itself is not yet an object that can be manipulated by the user, so in the next few lines (highlighted) we retrieve the current scene and add a new object to the scene. The arguments to new() are the Mesh object that we created earlier and the name we want to give to the object. The name given to the object might be the same as the one given to the mesh, as mesh names and object names live in different namespaces. As with meshes, an existing name will be made unique by adding a suffix. If the name is left out, the new object will have the type of its argument as a default name (Mesh in this case).

A newly-created object will be selected but not active so we correct that by assigning our object to scene.objects.active .

As we add together our mesh from various collections of vertices the result might not be as clean as we would like and therefore, the final two actions make sure we do not have any vertices that occupy almost the same location in space and that all face normals consistently point outward.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.75.165