I've been working on a large city in CityEngine, and I've imported the roads into blender. The roads have street lamps along them, however they are part of the road mesh and not their own object; therefore they all have their own mesh data, and do not share it.

I have separated the lamps from the street, and they are all individual objects with their own positions, however, the rotation is 0,0,0 for all of them; so I can't simply loop through them and replace the lamps with instances as it would lose the wanted rotation.

I believe I need to loop through the original lamps and find how the mesh data of a new instance needs to be rotated to fit the rotation of the original lamp; then replace and set the rotation.

The issue is I don't know the maths I need to look into or if this has been done before.


1 Answer 1


You don't show a specific example, but in general the problem comes down to:

  1. Iterating over all objects of a specific type, maybe you can just select the objects you want to modify, and iterate over context.selected_objects.
  2. Finding a unique face, ideally a face that you want to face X or Y axis (be parallel to XZ or YZ plane), maybe because it always has the same index regardless of the applied rotation, or maybe it always has a particular area etc.
  3. Calculating the rotation difference between that face and current vector facing the axis of choice.
  4. Setting the rotation to the object.
  5. Applying counter-rotation to the mesh.
  6. Now all meshes should be the same, so you can just use a single mesh for all the objects, this means p. 5 has to be applied only to a single object, unless, of course, one of the object/mesh pairs was already "good", meaning the rotation calculated for it in p. 4 would be 0.
import bpy
from bpy import context as C, data as D
from mathutils import Vector

if C.mode != 'EDIT_MESH':
    raise Exception("Go to Edit Mode and select the face(s) to point towards XZ plane")

alphaape = C.object
indices = [f.index for f in alphaape.data.polygons if f.select]

def get_rot(mesh, indices):
    normals = [mesh.polygons[i].normal for i in indices]

    # no need to divide by len(normals) or normalize
    averaged_dir = sum(normals, start=Vector()) 
    averaged_dir.z = 0  # this way the normal is parallel to Z=0 plane and
                        # hopefully perpendicular to Y=0 plane
    rot = averaged_dir.to_track_quat('-Y', 'Z').to_euler()
    return rot

apes = (o for o in D.objects if o.name.startswith('Suzanne'))
for monke in apes:
    if monke is alphaape:
    dupmesh = monke.data
    rot = get_rot(dupmesh, indices)
    monke.data = alphaape.data
    D.meshes.remove(dupmesh)  # WARNING! I'm not checking for other users!
                              # - it's safer to not remove and make it an orphan
    monke.rotation_euler = rot

enter image description here

  • 1
    $\begingroup$ Absolute HERO, I'm still new to blender so this means a lot! $\endgroup$ Commented Sep 16, 2022 at 21:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .