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