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