3
$\begingroup$

I am learning how to create mesh objects using python. The math will be complicated (fsolve and other special functions in Scipy), but I've simplified the problem to just four sides of a box. The first set of code does not run in blender 2.70a installed on my macbook air.

The error message in this case is only "Python script fail, look in the console for now..."

However, if I run the math part in python 2.7.6 first (second code segment below), then paste the output back into blender (third code segment), it works and draws four sides of a box.

This doesn't work:

import bpy
import numpy as np
from math import pi

mesh = bpy.data.meshes.new("Cubit")
object = bpy.data.objects.new("Cubit", mesh)

object.location = (0,0,0)  # bpy.context.scene.cursor_location
bpy.context.scene.objects.link(object)

mesh.from_pydata(verts,[],faces)
mesh.update(calc_edges=True)

verts = []
faces = []

n_side = 4
n_seg = 1

i_seg = 0
while (i_seg < n_seg+1):
    i_vert = 0
    zv = 2. * i_seg
    while (i_vert < n_side):
        xv = np.cos(0.5 * pi * i_vert)
        yv = np.sin(0.5 * pi * i_vert)
        vert = (xv,yv,zv)
        verts.append(vert)
        i_vert = i_vert + 1
    i_seg = i_seg + 1

i_seg = 0
while (i_seg < n_seg):
    i_vert = 0
    while (i_vert < n_side):
        iv1 = i_seg*n_side + i_vert
        iv2 = i_seg*n_side + np.mod(i_vert + 1, n_side)
        iv3 = (i_seg + 1)*n_side + np.mod(i_vert + 1, n_side)
        iv4 = (i_seg + 1)*n_side + i_vert
        face = (iv1,iv2,iv3,iv4)
        faces.append(face)
        i_vert = i_vert + 1
    i_seg = i_seg + 1

mesh = bpy.data.meshes.new("Cubit")
object = bpy.data.objects.new("Cubit", mesh)

object.location = (0,0,0)  #bpy.context.scene.cursor_location
bpy.context.scene.objects.link(object)

mesh.from_pydata(verts,[],faces)
mesh.update(calc_edges=True)

But if I run this in Python IDLE

import numpy as np
from math import pi

verts = []
faces = []

n_side = 4
n_seg= 1

i_seg = 0
while (i_seg < n_seg+1):
    i_vert = 0
    zv = 2. * i_seg
    while (i_vert < n_side):
        xv = np.cos(0.5 * pi * i_vert)
        yv = np.sin(0.5 * pi * i_vert)
        vert = (xv,yv,zv)
        verts.append(vert)
        i_vert = i_vert + 1
    i_seg = i_seg + 1

i_seg = 0
while (i_seg < n_seg):
    i_vert = 0
    while (i_vert < n_side):
        iv1 = i_seg*n_side + i_vert
        iv2 = i_seg*n_side + np.mod(i_vert + 1, n_side)
        iv3 = (i_seg + 1)*n_side + np.mod(i_vert + 1, n_side)
        iv4 = (i_seg + 1)*n_side + i_vert
        face = (iv1,iv2,iv3,iv4)
        faces.append(face)
        i_vert = i_vert + 1
    i_seg = i_seg + 1

print ' verts = ', verts
print ' faces = ', faces

...and then paste the (ugly) output into blender like this, it works fine and draws four sides of a box.

import bpy
import numpy as np
from math import pi

verts =  [(1.0, 0.0, 0.0), (6.123233995736766e-17, 1.0, 0.0), (-1.0, 1.2246467991473532e-16, 0.0), (-1.8369701987210297e-16, -1.0, 0.0), (1.0, 0.0, 2.0), (6.123233995736766e-17, 1.0, 2.0), (-1.0, 1.2246467991473532e-16, 2.0), (-1.8369701987210297e-16, -1.0, 2.0)]
faces =  [(0, 1, 5, 4), (1, 2, 6, 5), (2, 3, 7, 6), (3, 0, 4, 7)]

mesh = bpy.data.meshes.new("Cubit")
object = bpy.data.objects.new("Cubit", mesh)

object.location = (0,0,0)  #bpy.context.scene.cursor_location
bpy.context.scene.objects.link(object)

mesh.from_pydata(verts,[],faces)
mesh.update(calc_edges=True)
$\endgroup$
  • $\begingroup$ I've had further trouble - see this question $\endgroup$ – uhoh Aug 9 '15 at 12:16
2
$\begingroup$

Your script has two minor issues

Line 11,12 should be removed

#mesh.from_pydata(verts,[],faces)
#mesh.update(calc_edges=True)

The console window from Menu:Window/Toggle System Console shows a traceback:

  File "\Text", line 51, in <module>
  File "C:\Program Files\Blender Foundation\Blender\2.70\scripts\modules\bpy_typ
es.py", line 418, in from_pydata
    p.vertices = f
TypeError: bpy_struct: item.attr = val: expected sequence items of type int, not
 numpy.int32

this means that for some reason the face's values are of type numpy.int32 instead of the expected int type.

This is easy to fix. Just add casts to int type in line 40:

face = (int(iv1),int(iv2),int(iv3),int(iv4))
$\endgroup$
  • $\begingroup$ Great! The int() solves the problem. Line 12 seems unnecessary. but line 11 still seems to be required, otherwise only a dot appears. $\endgroup$ – uhoh Jul 24 '14 at 15:07
  • $\begingroup$ I'll also have to read further about the console, it seems some homework is required to make it happen in OSX. Thank you!!! $\endgroup$ – uhoh Jul 24 '14 at 15:08
  • $\begingroup$ To debug python running in blender in OSX, I followed the procedure outlined in wiki.blender.org/index.php/Doc:2.6/Manual/Interface/… In my case I open a Terminal from Applications, then typed the line "/Applications/BlenderApplication/blender.app/Contents/MacOS/blender" which reflects the name as shown in my Applications folder. This starts Blender from the terminal, and the python errors appear here. $\endgroup$ – uhoh Jul 24 '14 at 16:06
2
$\begingroup$

Just to add some things. If you're making scripts in Blender you definitely need to have the console open, so you can see the errors. You already discovered how to do that in OSX.

Furthermore I'm guessing you have a background in another programming language. The while loops you're using are very unpythonic. You can use simple for loops here, which looks cleaner and even saves you a little bit of typing ;).

The whole part between the import statements and verts = [] can be removed. You add the mesh and object at the end, so you absolutely don't need this.

I thinks it's better if you first create the mesh from_pydata, then create the object and then link the object to the scene. This way you're sure that any needed updates to the scene will happen. I also tested it in the order you have and it worked for me (even without mesh.update(calc_edges=True). So it might be just my preference.

Here is a simplified version. For simplicities sake I removed the dependency on numpy, but of course you can add it back to suit your needs. Only remember to convert any numpy values to int(), float() or whatever it should be, before passing it to Blender (like stacker mentioned).

import bpy
from math import pi, sin, cos

verts = []
faces = []

n_side = 4
n_seg = 1

for i_seg in range(n_seg + 1):
    zv = 2 * i_seg
    for i_vert in range(n_side):
        xv = cos(0.5 * pi * i_vert)
        yv = sin(0.5 * pi * i_vert)
        vert = (xv, yv, zv)
        verts.append(vert)

for i_seg in range(n_seg):
    for i_vert in range(n_side):
        iv1 = i_seg * n_side + i_vert
        iv2 = i_seg * n_side + (i_vert + 1) % n_side
        iv3 = (i_seg + 1) * n_side + (i_vert + 1) % n_side
        iv4 = (i_seg + 1) * n_side + i_vert
        face = (iv1, iv2, iv3, iv4)
        faces.append(face)

mesh = bpy.data.meshes.new("Cubit")
mesh.from_pydata(verts, [], faces)

object = bpy.data.objects.new("Cubit", mesh)
object.location = (0, 0, 0)  # bpy.context.scene.cursor_location

bpy.context.scene.objects.link(object)
$\endgroup$
  • $\begingroup$ Thanks also to jasperge, indeed I've just started to learn python and blender and forget Matlab and a whole series of previous languages. The clean-up and style suggestions are very helpful! $\endgroup$ – uhoh Jul 27 '14 at 14:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.