# Is there a better way than dupliverts to make these scripted monkeys always face “out”?

In another question I ask How to make these scripted duplivert monkeys always face “out” using duplicates? Here I would like to ask if there is a better (or at least alternate) way to do this besides using duplicates or "dupliverts".

Here's an example using dupliverts - the same as in the other question and so far I haven't figured out there how to get the monkeys placed on the icosphere's vertices or it's faces to face "out".

Is there a different way to do this?

note: I will not always use monkeys on icospheres - this is an abstraction. I'm working towards correctly animating this constellation of 4,425 Earth-facing satellites where I'll have roughly eighty rotating circles with sixty "monkeys" each. However I will also be using 2D meshes in a second project (rather than circles), so I really want to understand the underlying logic/math of the orientations, both on 1D (circles) and 2D meshes.

edit: Here is one of the images from the linked satellite question to help illustrate why I want to create "monkeys" (satellites) in groups, and not create and keyframe four thousand individuals:

# monkey.rotation_euler = 0, 0, pi   # ??

bpy.ops.object.parent_set(type='OBJECT', keep_transform=True) # keep_transform ??

bpy.context.object.use_dupli_vertices_rotation = True  # ??


Ok here is a basic script:

import bpy, math

pi = math.pi
loco_f, loco_v = (-5, 0, 5), (+5, 0, 5)

bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)

shape_mesh_f = bpy.context.active_object

shape_mesh_v = bpy.context.active_object

monkey_f = bpy.context.active_object
# monkey.rotation_euler = 0, 0, pi

monkey_v = bpy.context.active_object
# monkey.rotation_euler = 0, 0, pi

bpy.ops.object.select_all(action='DESELECT')

monkey_f.select = True
bpy.context.scene.objects.active = shape_mesh_f

bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
bpy.context.object.dupli_type = 'FACES'
bpy.context.object.use_dupli_vertices_rotation = True

bpy.ops.object.select_all(action='DESELECT')

monkey_v.select = True
bpy.context.scene.objects.active = shape_mesh_v

bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
bpy.context.object.dupli_type = 'VERTS'
bpy.context.object.use_dupli_vertices_rotation = True


Instead of dupliverts, we can create it all in one mesh using bmesh.ops.create_monkey().

import bpy
import bmesh
import mathutils

loco_f, loco_v = (-5, 0, 5), (+5, 0, 5)

bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)

shape_mesh_v = bpy.context.active_object

bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.delete(type='EDGE_FACE')
bpy.ops.object.mode_set(mode='OBJECT')

bm = bmesh.new()
bm.from_mesh(shape_mesh_v.data)

src = [v for v in bm.verts]

for v in src:
loc = mathutils.Matrix.Translation(v.co)
rot = v.normal.to_track_quat('-Y','Z')
mat = mathutils.Matrix.Identity(3)
mat.rotate(rot)
mat = loc * mat.to_4x4()
bmesh.ops.create_monkey(bm, matrix=mat)

bm.to_mesh(shape_mesh_v.data)
bm.free()


So the key point you are looking for is v.normal.to_track_quat('-Y','Z') to get a quaternion rotation matrix from the vertex normal.

For another way to approach this, you could try animation nodes.

Here we make a list of the location and normal of each vert in an icosphere, we use that list to replicate the suzanne mesh.

EDIT: After some experimenting with the aim of creating your final goal, I found creating one orbital plane at a time as a unique object seems to be the fastest way, seems to find a balance between object and vertex count. Once they are all created they can all be merged into one mesh for simplicity. While creating each plane, an armature bone can be added and aligned with each orbit allowing easy animation. This is the script I came up with, a simple proof of concept that needs better positioning and timing to be complete.

• Thanks, but as mentioned in the question, I will have 4,425 satellites. So I really want to learn to use dupliverts, that's why I've carefully worded the question and added the script to explain exactly what I would like to do. Then when I animate them, I just rotate appropriate meshes and the duplicates move without individual keyframings. For background and timings related to thousands of mesh objects, see the extensive comments and answers to this question. Usually it is advised to avoid calling ops when possible, especially in high volume. – uhoh Nov 20 '16 at 10:48
• However the rot = v.normal.to_track_quat('-Y','Z') seems to be an interesting lead, I'll try to start learning about what normal_to_track_quat actually means. and WOW!! Animation nodes - that sounds really interesting!! – uhoh Nov 20 '16 at 10:54
• Aha! It turns out it was you who introduced me to dupliverts!! – uhoh Nov 20 '16 at 10:59
• Normal operators add overhead that can kill performance, bmesh operators don't do the updates that normal operators do so they don't get the same performance penalty. An all in one mesh should perform as good as dupliverts. to_track_quat turns the vector from the normals into an axis based rotation that you can use. Animation nodes will let you add rotation changes during your animation, add a time info node to use the frame in calculating to animate loc/rot. – sambler Nov 20 '16 at 13:33
• Ah - bmesh.ops not bpy.ops!! I misread, which then sent me off in the wrong direction - my mistake. Actually the all-in-one mesh is a really interesting solution if the shape mesh is permanent, I like it very much! But this lacks the flexibility of being able to animate the shape mesh, unless somehow store (or calculate) the group of vertices corresponding to each repeat and then.. dunno. I like this solution but I really do want to learn how to do what I've actually asked also. I can split the question into two: "how to duplivert?" & "a better way than duplivert?" – uhoh Nov 20 '16 at 15:28