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