Finally got it! There were a few problems with my earlier attempts. The one that was throwing me off the most was incorrect position (because the rotations never looked consistently right with a wrong position)
It turns out I had the position wrong because the +Y in Blender is actually -Z in a Y-Up RH coordinate system. Or in other words when converting the code is:
posX = lightObj.matrix_world[0][3]
posY = lightObj.matrix_world[2][3]
posZ = -lightObj.matrix_world[1][3] # note the negative
(swizzle YZ and negate Z)
Another problem I encountered... It turns out there is a special case when exporting lights, which I didn't realize earlier. In Blender, the cone of a spotlight with zero rotations aims straight down, where in Maya, Houdini, and my game engine, spotlights face down the -Z. (If blender were to match this it would aim down the +Y)
So with these in mind, I came to this for rotations:
# for objects
obj = bpy.data.objects['objName']
mm = bpy_extras.io_utils.axis_conversion(from_forward='Y', from_up='Z', to_forward='-Z', to_up='Y')
om = obj.matrix_world.to_3x3()
t = mm * om
v = t.to_euler('XYZ')
print('pos:(%s, %s, %s)' % (obj.matrix_world.translation.x, obj.matrix_world.translation.z, -obj.matrix_world.translation.y))
print('rot:(%s, %s, %s)' % (degrees(v.x) + 90.0, degrees(v.y), degrees(v.z)))
# not totally sure why we need the +90 in X, but assume it has something to do with compensating for the axis_conversion
# for spot lights (they face straight down in Blender)
obj = bpy.data.objects['spotLightName']
mm = bpy_extras.io_utils.axis_conversion(from_forward='Y', from_up='Z', to_forward='-Z', to_up='Y')
om = obj.matrix_world.to_3x3()
t = mm * om
v = t.to_euler('XZY') # not sure why this needs to be XZY for spot lights and XYZ for objects
print('pos:(%s, %s, %s)' % (obj.matrix_world.translation.x, obj.matrix_world.translation.z, -obj.matrix_world.translation.y))
print('rot:(%s, %s, %s)' % (degrees(v.x), degrees(v.y), degrees(v.z)))
# note: there is no need for the axis_conversion rotation compensation due to the spotlight facing down already
As you can see in the comments, there are still things I am not clear on and not sure I am doing right, so please feel free to comment/correct me where wrong.
I have a feeling something might be off with my rotation order stuff, but I don't know what it should be. All I know intuitively is that it makes sense to use XZY since that is what I am swizzling to, that should match the post-swizzled rotation order so it is XYZ in both systems right?
X > Z, Y > X, Z > Y. – gandalf3 Apr 29 '14 at 22:30newMat = orientMat * obj.matrix_world.to_3x3()I think it must be thenewMat.to_euler('XYZ')that is messing me up. All I can figure is thatto_euler()might still assume things are in Z up... or I am doing something else wrong. Again an example of a working implementation would be ideal. – PolyMesh Apr 30 '14 at 17:35