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