Advertisement
nux95

Cinema 4D - PoseGhost Script 1.5

Sep 22nd, 2012
129
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.58 KB | None | 0 0
  1. # coding: UTF-8
  2. #
  3. # Copyright (C) 2012, Athanasios Pozantzis
  4. # Copyright (C) 2012, Niklas Rosenstein (rewrite)
  5. #
  6. # Changelog (newest at top)
  7. # -------------------------
  8. #
  9. # v1.5 September 22, 2012 (Niklas Rosenstein):
  10. #   - Complete rewrite
  11. #   - Abstraction of components
  12. #   - A bug has been fixed, where a second Sketch & Toon video post was created
  13. #     when the avaialble video post was not the first element in the list.
  14. #   - HyperNURBS are now reactivated if they were activated when running the
  15. #     script. Turned off HyperNURBS will stay turned off.
  16. #   - The whole script is now merged into a single undo-step.
  17. #   - Hidden objects are no more included in the resulting ghost-object.
  18. #
  19. # v1.4 September 22, 2012 (Athanasios Pozantzis):
  20. #   - Initial version
  21.  
  22. import c4d
  23.  
  24. # Missing Symbols in the c4d module.
  25. VPsketchandtoon = 1011015
  26. Tsketchandtoon = 1011012
  27. Msketchandtoon = 1011014
  28. COMMAND_CONNECTANDDELETE = 16768
  29.  
  30. def collect_objects(root, doc=None, callback=lambda x: True):
  31.     """ *Generator*. Collects all objects starting from *root* recursively that
  32.        match by the passed callback. If root is None, the document must have
  33.        been passed to start from the top-level of the hierarchy. The passed
  34.        object for *root* can be a `c4d.BaseList2D` instance, but if it is None
  35.        and the document is used, it will search for the objects in the Object
  36.        Manager. """
  37.  
  38.     def procedure(object, callback):
  39.         if callback(object): yield object
  40.         for child in object.GetChildren():
  41.             for sub in procedure(child, callback):
  42.                 yield sub
  43.  
  44.     if root:
  45.         for sub in procedure(root, callback):
  46.             yield sub
  47.     else:
  48.         for object in doc.GetObjects():
  49.             for sub in procedure(object, callback):
  50.                 yield sub
  51.  
  52. def find_object(root, callback):
  53.     """ Finds an object in the hierarchy of *root* that matches the passed
  54.        callback. """
  55.     if not root: return None
  56.  
  57.     def procedure(object):
  58.         if callback(object): return object
  59.         for child in object.GetObjects():
  60.             child = find_object(child)
  61.             if child: return child
  62.         return None
  63.  
  64.     return procedure(root)
  65.  
  66. def filter_hierarchy(root, callback, rwnull=True):
  67.     """ Filters a hierarchy, which means to remove all objects that do not
  68.        match the passed callback. *rwnull* stands for "replace with null".
  69.        If this parameter is True (by default) and an object is about to be
  70.        removed from the hierarchy, it is replaced by a Null object in case
  71.        it has children. The passed callback must return True to notify
  72.        an object needs to be removed. """
  73.     children = root.GetChildren()
  74.     if callback(root):
  75.         if not children:
  76.             root.Remove()
  77.             return
  78.         null = c4d.BaseObject(c4d.Onull)
  79.         null.SetName(root.GetName())
  80.         for child in children:
  81.             child.InsertUnder(null)
  82.         if root.GetUp() or root.GetPred() or root.GetNext():
  83.             null.InsertAfter(root)
  84.         root.Remove()
  85.         root = null
  86.  
  87.     for child in children:
  88.         filter_hierarchy(child, callback)
  89.  
  90. def delete_tags(object, keep=lambda x: False, delete=lambda x: True):
  91.     """ Iterates over all tags of the passed *object* and determines wether
  92.        the tag should be kept or not. This is determined by calling the passed
  93.        *keep* and *delete* functions. When the former returns True, the tag
  94.        is kept. If not, the latter must return True to delete it. Both
  95.        functions are passed a `c4d.BaseTag` instance. """
  96.  
  97.     tag = object.GetFirstTag()
  98.     while tag:
  99.         next = tag.GetNext()
  100.         if not keep(tag) and delete(tag):
  101.             tag.Remove()
  102.         tag = next
  103.  
  104. def polygonize_objects(objects, hierarchy=True):
  105.     """ Polygonizes the hierarchy of the passed `c4d.BaseObject` instances
  106.        with a little trick using a temporary `c4d.BaseDocument`. The
  107.        full hierarchies are used, except *hierarchy* is set to False. """
  108.  
  109.     flags = c4d.COPYFLAGS_NO_ANIMATION | c4d.COPYFLAGS_NO_BITS | c4d.COPYFLAGS_NO_MATERIALPREVIEW
  110.     if not hierarchy:
  111.         flags |= c4d.COPYFLAGS_NO_HIERARCHY
  112.  
  113.     # Create a new temporary document and insert a clone of the passed objects
  114.     # into it.
  115.     doc = c4d.documents.BaseDocument()
  116.     for object in objects:
  117.         if object.GetDocument():
  118.             object = object.GetClone(flags)
  119.         doc.InsertObject(object)
  120.  
  121.     # Polygonize the document, obtain the objects, release them from the
  122.     # document and return them.
  123.     new_doc = doc.Polygonize()
  124.     objects = new_doc.GetObjects()
  125.  
  126.     # Release all original objects from the temporary document.
  127.     for object in doc.GetObjects():
  128.         object.Remove()
  129.  
  130.     # Release all polygonized objects from the polygonized document.
  131.     for object in objects:
  132.         object.Remove()
  133.  
  134.     return objects
  135.  
  136. def join_hierarchy(object):
  137.     """ Joins the hierarchy of the passed object into a single object. A
  138.        callback function can be passed to point out what objects to exclude
  139.        (returning False will exclude the object). The passed objects
  140.        hierarchy should be fully polygonized. """
  141.  
  142.     # Check if the object is already in a document.
  143.     if object.GetDocument():
  144.         object = object.GetClone(c4d.COPYFLAGS_NO_ANIMATION | c4d.COPYFLAGS_NO_BITS | c4d.COPYFLAGS_NO_MATERIALPREVIEW)
  145.  
  146.     # Create a temporary document and insert it into the Cinema 4D document
  147.     # list.
  148.     prevdoc = c4d.documents.GetActiveDocument()
  149.     doc = c4d.documents.BaseDocument()
  150.     doc.InsertObject(object)
  151.     c4d.documents.InsertBaseDocument(doc)
  152.     c4d.documents.SetActiveDocument(doc)
  153.  
  154.     # Select all objects in the hierarchy. We will use the `collect_object`
  155.     # function for this, because it iterates recursively over all objects
  156.     # in the passed hierarchy, so we can apply our function for it. :)
  157.     # The passed lambda-function includes the `and False`, because when
  158.     # the function returns False, the object the function is currently at
  159.     # will not be yielded. This way, we have no overhead from this.
  160.     # The call must be included in any kind of iteration (for-loop, conversion
  161.     # to a tuple/list) to make the generator actually execute. However,
  162.     # as stated before, the resulting iteration will end immediately because
  163.     # no objects are yielded.
  164.     tuple(collect_objects(object, callback=lambda x: (x.SetBit(c4d.BIT_ACTIVE) and False)))
  165.  
  166.     # Invoke the CallCommand to connect and delete the selected objects.
  167.     c4d.CallCommand(COMMAND_CONNECTANDDELETE)
  168.  
  169.     # Obtain the new object from the document.
  170.     new_object = doc.GetFirstObject()
  171.     new_object.Remove()
  172.  
  173.     # Release the object and document.
  174.     object.Remove()
  175.     c4d.documents.KillDocument(doc)
  176.     c4d.documents.SetActiveDocument(prevdoc)
  177.  
  178.     return new_object
  179.  
  180. def apply_options(doc, poly):
  181.     """ Applies the general options for the Ghost object. """
  182.     dtag = poly.MakeTag(c4d.Tdisplay)
  183.     dtag[c4d.DISPLAYTAG_AFFECT_DISPLAYMODE] = True
  184.     dtag[c4d.DISPLAYTAG_SDISPLAYMODE] = c4d.DISPLAYTAG_SDISPLAY_HIDDENLINE
  185.  
  186.     def apply_snt(doc, poly):
  187.         # Change an option on the display-tag.
  188.         dtag[c4d.DISPLAYTAG_WDISPLAYMODE] = c4d.DISPLAYTAG_WDISPLAY_SKELETON
  189.  
  190.         # Find the first Sketch & Toon Video Post effect, or insert one if it does
  191.         # not exist.
  192.         rd = doc.GetActiveRenderData()
  193.         vp = find_object(rd.GetFirstVideoPost(), lambda x: x.CheckType(VPsketchandtoon))
  194.  
  195.         if not vp:
  196.             vp = c4d.BaseList2D(VPsketchandtoon)
  197.             rd.InsertVideoPostLast(vp)
  198.  
  199.         # Create a new material for the Sketch & Toon editor outline.
  200.         name = poly.GetName()
  201.         mat = c4d.BaseMaterial(Msketchandtoon)
  202.         mat.SetName(name + " Material")
  203.         doc.InsertMaterial(mat)
  204.         tag = poly.MakeTag(Tsketchandtoon)
  205.         tag.SetName(name + " Tag")
  206.         tag[c4d.OUTLINEMAT_LINE_DEFAULT_MAT_V] = mat
  207.  
  208.         # Set the required information.
  209.         vp[c4d.OUTLINEMAT_EDLINES_SHOWLINES] = True
  210.         vp[c4d.OUTLINEMAT_EDLINES_REDRAW_FULL] = True
  211.         vp[c4d.OUTLINEMAT_EDLINES_LINE_OBJECTS_MODE] = 0 #include
  212.         vp[c4d.OUTLINEMAT_EDLINES_LINE_OUTLINE] = False
  213.         vp[c4d.OUTLINEMAT_EDLINES_LINE_CREASE] = False
  214.         vp[c4d.OUTLINEMAT_EDLINES_LINE_MATERIAL] = False
  215.         vp[c4d.OUTLINEMAT_EDLINES_LINE_INTERSECTION] = False
  216.         vp[c4d.OUTLINEMAT_EDLINES_LINE_BORDER] = False
  217.         vp[c4d.OUTLINEMAT_EDLINES_LINE_EDGES] = False
  218.         vp[c4d.OUTLINEMAT_EDLINES_LINE_FOLD] = True
  219.         vp[c4d.OUTLINEMAT_SCENE_AA] = 0
  220.  
  221.         lst = vp[c4d.OUTLINEMAT_EDLINES_LINE_OBJECTS] or c4d.InExcludeData()
  222.         lst.InsertObject(poly, 0)
  223.         vp[c4d.OUTLINEMAT_EDLINES_LINE_OBJECTS] = lst
  224.  
  225.     def apply_alt(doc, poly):
  226.         poly[c4d.ID_BASEOBJECT_XRAY] = True
  227.  
  228.     if c4d.modules.CheckSketch():
  229.         apply_snt(doc, poly)
  230.     else:
  231.         apply_alt(doc, poly)
  232.  
  233. def main():
  234.     """ The final procedure. """
  235.  
  236.     if not op:
  237.         c4d.gui.MessageDialog('Please select exactly one object.')
  238.         return False
  239.  
  240.     # Information for future use.
  241.     fps = doc.GetFps()
  242.     time = doc.GetTime()
  243.     frame = time.GetFrame(fps)
  244.  
  245.     # This list will be filled with all HyperNURBS objects contained in the
  246.     # hierarchy of the selected object. They need to be turned off before
  247.     # converting the hierarchy to a ghost.
  248.     hypernurbs_objects = []
  249.  
  250.     # Fill the HyperNURBS-objects list and turn off all of them.
  251.     for hn in collect_objects(op, callback=lambda x: x.CheckType(c4d.Osds)):
  252.         hypernurbs_objects.append([hn, hn.GetDeformMode()])
  253.         hn.SetDeformMode(False)
  254.  
  255.     # Clone the hierarchy and sort out unnecessary objects.
  256.     object = op.GetClone()
  257.     filter_hierarchy(object, lambda x: x.GetEditorMode() == c4d.MODE_OFF)
  258.     poly = polygonize_objects([object])[0]
  259.  
  260.     # Join the hierarchy, etc.
  261.     poly = join_hierarchy(poly)
  262.     poly.SetName("%s Ghost(%d)" % (poly.GetName()[:-2], frame))
  263.     poly.SetRenderMode(c4d.MODE_OFF)
  264.     delete_tags(poly, keep=lambda x: any(map(x.CheckType, [c4d.Tpoint, c4d.Tpolygon])))
  265.     doc.InsertObject(poly)
  266.     doc.AddUndo(c4d.UNDOTYPE_NEW, poly)
  267.     doc.SetActiveObject(poly)
  268.  
  269.     # Invoke the procedure for applying the options for Sketch & Toon.
  270.     apply_options(doc, poly)
  271.  
  272.     # Reactivate all HyperNURBS objects to their original state.
  273.     for hn, state in hypernurbs_objects:
  274.         hn.SetDeformMode(state)
  275.  
  276.     # Tell Cinema 4D to update its interface.
  277.     c4d.EventAdd()
  278.  
  279. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement