How to fix the bug of objects position in script
-
Guys! Please, help. I cannot fix this problem.
The main idea of this script is:
- add selected objects and materials of these objects to the layer
- insert all objects under null.
But position of objects changes. How to fix it?
: video
import c4d def main(): doc.StartUndo() layer = c4d.documents.LayerObject() # Set layer name (another option tp set the name) # Get the invisible root layer layerRoot = doc.GetLayerObjectRoot() # Insert the layer under the parent layer.InsertUnder(layerRoot) name = layer.GetName() rvalue = c4d.gui.RenameDialog(name) # rename print(rvalue) layer [c4d.ID_BASELIST_NAME] = rvalue #layer.DelBit(c4d.BIT_ACTIVE) null_obj = c4d.BaseObject(c4d.Onull) null_obj [c4d.ID_LAYER_LINK] = layer null_obj [c4d.ID_BASELIST_NAME] = rvalue doc.InsertObject(null_obj) doc.AddUndo(c4d.UNDOTYPE_NEWOBJ, layer) doc.AddUndo(c4d.UNDOTYPE_NEWOBJ, null_obj) s = doc.GetSelection() vp = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_NONE) for obj in vp: print obj obj [c4d.ID_LAYER_LINK] = layer obj.InsertUnder(null_obj) obj_mat_tags = [t for t in obj.GetTags() if t.IsInstanceOf(c4d.Ttexture)] for tag in obj_mat_tags: mat = tag.GetMaterial() print mat mat [c4d.ID_LAYER_LINK] = layer doc.EndUndo() c4d.EventAdd() if __name__ == '__main__': main()
I use r21 version of c4d.
-
Hello @roman,
Thank you for reaching out to us. R21 has left the SDK support cycle, which primarily means we will not debug again such versions anymore and also do not fix bugs for it anymore. I have provided below a solution for your problem which has been tested with R25 because of that, but it should run fine in R21.
Your problem primarily is rooted in you simply reparenting the objects. Objects store their transformation - their position, scale and orientation - as a matrix relative to their parent. So, when you have the objects
a, b, c
with the local positions(0, 100, 0), (0, 50, 0), (0, 0, 0)
andb
being parented toa
, then the effective global position ofb
is(0, 100, 0) + (0, 50, 0) = (0, 150, 0)
. When you then parentb
toc
, its position will change from(0, 150, 0)
to(0, 50, 0)
sincec
only contributes the null vector to the position of its children. The same principle applies to the scale and orientation stored in the local transform matrix of an object. You were also missing some undo-steps, at least I assumed you did not skip them intentionally.The topic is also covered in the Python API Matrix Manual.
Cheers,
FerdinandThe result:
The script:
"""Moves the selected objects to a new layer and parent object. Your problem primarily is rooted in you simply reparenting the objects. Objects store their transformation - their position, scale and orientation - as a matrix relative to their parent. So, when you have the objects `a, b, c` with the local positions `(0, 100, 0), (0, 50, 0), (0, 0, 0)` and `b` being parented to `a`, then the effective global position of `b` is `(0, 100, 0) + (0, 50, 0) = (0, 150, 0)`. When you then parent `b` to `c`, its position will change from `(0, 150, 0)` to `(0, 50, 0)` since `c` only contributes the null vector to the position of its children. The same principle applies to the scale and orientation stored in the local transform matrix of an object. You were also missing some undo-steps, at least I assumed you did not skip them intentionally. """ import c4d def main(): """Entry point. """ # Start an undo stack item. doc.StartUndo() # Add a new top-level layer newLayer = c4d.documents.LayerObject() layerRoot = doc.GetLayerObjectRoot() newLayer.InsertUnder(layerRoot) doc.AddUndo(c4d.UNDOTYPE_NEWOBJ, newLayer) # Let the user name the new layer newName = c4d.gui.RenameDialog(newLayer.GetName()) newLayer [c4d.ID_BASELIST_NAME] = newName # Create a null object to parent the objects moved to the new layer to. newLayerNull = c4d.BaseObject(c4d.Onull) newLayerNull [c4d.ID_LAYER_LINK] = newLayer newLayerNull [c4d.ID_BASELIST_NAME] = newName doc.InsertObject(newLayerNull) doc.AddUndo(c4d.UNDOTYPE_NEWOBJ, newLayerNull) # Iterate over the selected objects and attach them both to the new layer # and layer null-object. for item in doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_NONE): # Set the layer of the current item, detach it from its previous # parent and store its global matrix. doc.AddUndo(c4d.UNDOTYPE_CHANGE, item) item [c4d.ID_LAYER_LINK] = newLayer itemMg = item.GetMg() item.Remove() # Attach the object to the null and set its global matrix to the old # value. item.SetMg(itemMg) item.InsertUnder(newLayerNull) # Get all materials attached with material tags to the item. isTex = lambda item: item.IsInstanceOf(c4d.Ttexture) for material in [t.GetMaterial() for t in item.GetTags() if isTex(t)]: doc.AddUndo(c4d.UNDOTYPE_CHANGE, material) material[c4d.ID_LAYER_LINK] = newLayer # Close the undo item and push an update event to Cinema 4D. doc.EndUndo() c4d.EventAdd() if __name__ == '__main__': main()