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