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() |