Welcome to mirror list, hosted at ThFree Co, Russian Federation.

object.py « op « scripts « release - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: c510981ebc253c5532ccfd722fc2a9736945ca53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ##### END GPL LICENSE BLOCK #####

# <pep8 compliant>

import bpy
from bpy.props import *


class SelectPattern(bpy.types.Operator):
    '''Select object matching a naming pattern.'''
    bl_idname = "object.select_pattern"
    bl_label = "Select Pattern"
    bl_register = True
    bl_undo = True

    pattern = StringProperty(name="Pattern", description="Name filter using '*' and '?' wildcard chars", maxlen=32, default="*")
    case_sensitive = BoolProperty(name="Case Sensitive", description="Do a case sensitive compare", default=False)
    extend = BoolProperty(name="Extend", description="Extend the existing selection", default=True)

    def execute(self, context):

        import fnmatch

        if self.properties.case_sensitive:
            pattern_match = fnmatch.fnmatchcase
        else:
            pattern_match = lambda a, b: fnmatch.fnmatchcase(a.upper(), b.upper())

        obj = context.object
        if obj and obj.mode == 'POSE':
            items = obj.data.bones
        elif obj and obj.type == 'ARMATURE' and obj.mode == 'EDIT':
            items = obj.data.edit_bones
        else:
            items = context.visible_objects

        # Can be pose bones or objects
        for item in items:
            if pattern_match(item.name, self.properties.pattern):
                item.selected = True
            elif not self.properties.extend:
                item.selected = False

        return {'FINISHED'}

    def invoke(self, context, event):
        wm = context.manager
        # return wm.invoke_props_popup(self, event)
        wm.invoke_props_popup(self, event)
        return {'RUNNING_MODAL'}

    def draw(self, context):
        layout = self.layout
        props = self.properties

        layout.prop(props, "pattern")
        row = layout.row()
        row.prop(props, "case_sensitive")
        row.prop(props, "extend")


class SubdivisionSet(bpy.types.Operator):
    '''Sets a Subdivision Surface Level (1-5)'''

    bl_idname = "object.subdivision_set"
    bl_label = "Subdivision Set"
    bl_register = True
    bl_undo = True

    level = IntProperty(name="Level",
            default=1, min=-100, max=100, soft_min=-6, soft_max=6)

    relative = BoolProperty(name="Relative", description="Apply the subsurf level as an offset relative to the current level", default=False)

    def poll(self, context):
        obs = context.selected_editable_objects
        return (obs is not None)

    def execute(self, context):
        level = self.properties.level
        relative = self.properties.relative

        if relative and level == 0:
            return {'CANCELLED'} # nothing to do

        def set_object_subd(obj):
            for mod in obj.modifiers:
                if mod.type == 'MULTIRES':
                    if level <= mod.total_levels:
                        if obj.mode == 'SCULPT':
                            if relative:
                                mod.sculpt_levels += level
                            else:
                                if mod.sculpt_levels != level:
                                    mod.sculpt_levels = level
                        elif obj.mode == 'OBJECT':
                            if relative:
                                mod.levels += level
                            else:
                                if mod.levels != level:
                                    mod.levels = level
                    return
                elif mod.type == 'SUBSURF':
                    if relative:
                        mod.levels += level
                    else:
                        if mod.levels != level:
                            mod.levels = level

                    return

            # adda new modifier
            mod = obj.modifiers.new("Subsurf", 'SUBSURF')
            mod.levels = level

        for obj in context.selected_editable_objects:
            set_object_subd(obj)

        return {'FINISHED'}


class Retopo(bpy.types.Operator):
    '''TODO - doc'''

    bl_idname = "object.retopology"
    bl_label = "Retopology from Grease Pencil"
    bl_register = True
    bl_undo = True

    def execute(self, context):
        import retopo
        retopo.main()
        return {'FINISHED'}


class ShapeTransfer(bpy.types.Operator):
    '''Copy the active objects current shape to other selected objects with the same number of verts'''

    bl_idname = "object.shape_key_transfer"
    bl_label = "Transfer Shape Key"
    bl_register = True
    bl_undo = True

    mode = EnumProperty(items=(
                        ('OFFSET', "Offset", "Apply the relative positional offset"),
                        ('RELATIVE_FACE', "Relative Face", "Calculate the geometricly relative position (using faces)."),
                        ('RELATIVE_EDGE', "Relative Edge", "Calculate the geometricly relative position (using edges).")),
                name="Transformation Mode",
                description="Method to apply relative shape positions to the new shape",
                default='OFFSET')

    use_clamp = BoolProperty(name="Clamp Offset",
                description="Clamp the transformation to the distance each vertex moves in the original shape.",
                default=False)

    def _main(self, ob_act, objects, mode='OFFSET', use_clamp=False):

        def me_nos(verts):
            return [v.normal.copy() for v in verts]

        def me_cos(verts):
            return [v.co.copy() for v in verts]

        def ob_add_shape(ob):
            C_tmp = {"object": ob}
            me = ob.data
            if me.shape_keys is None: # add basis
                bpy.ops.object.shape_key_add(C_tmp)
            bpy.ops.object.shape_key_add(C_tmp)
            ob.active_shape_key_index = len(me.shape_keys.keys) - 1
            ob.shape_key_lock = True

        from Geometry import BarycentricTransform
        from Mathutils import Vector

        if use_clamp and mode == 'OFFSET':
            use_clamp = False

        me = ob_act.data

        orig_shape_coords = me_cos(ob_act.active_shape_key.data)

        orig_normals = me_nos(me.verts)
        orig_coords = me_cos(me.verts)

        for ob_other in objects:
            me_other = ob_other.data
            if len(me_other.verts) != len(me.verts):
                self.report({'WARNING'}, "Skipping '%s', vertex count differs" % ob_other.name)
                continue

            target_normals = me_nos(me_other.verts)
            target_coords = me_cos(me_other.verts)

            ob_add_shape(ob_other)

            # editing the final coords, only list that stores wrapped coords
            target_shape_coords = [v.co for v in ob_other.active_shape_key.data]

            median_coords = [[] for i in range(len(me.verts))]

            # Method 1, edge
            if mode == 'OFFSET':
                for i, vert_cos in enumerate(median_coords):
                    vert_cos.append(target_coords[i] + (orig_shape_coords[i] - orig_coords[i]))

            elif mode == 'RELATIVE_FACE':
                for face in me.faces:
                    i1, i2, i3, i4 = face.verts_raw
                    if i4 != 0:
                        pt = BarycentricTransform(orig_shape_coords[i1],
                            orig_coords[i4], orig_coords[i1], orig_coords[i2],
                            target_coords[i4], target_coords[i1], target_coords[i2])
                        median_coords[i1].append(pt)

                        pt = BarycentricTransform(orig_shape_coords[i2],
                            orig_coords[i1], orig_coords[i2], orig_coords[i3],
                            target_coords[i1], target_coords[i2], target_coords[i3])
                        median_coords[i2].append(pt)

                        pt = BarycentricTransform(orig_shape_coords[i3],
                            orig_coords[i2], orig_coords[i3], orig_coords[i4],
                            target_coords[i2], target_coords[i3], target_coords[i4])
                        median_coords[i3].append(pt)

                        pt = BarycentricTransform(orig_shape_coords[i4],
                            orig_coords[i3], orig_coords[i4], orig_coords[i1],
                            target_coords[i3], target_coords[i4], target_coords[i1])
                        median_coords[i4].append(pt)

                    else:
                        pt = BarycentricTransform(orig_shape_coords[i1],
                            orig_coords[i3], orig_coords[i1], orig_coords[i2],
                            target_coords[i3], target_coords[i1], target_coords[i2])
                        median_coords[i1].append(pt)

                        pt = BarycentricTransform(orig_shape_coords[i2],
                            orig_coords[i1], orig_coords[i2], orig_coords[i3],
                            target_coords[i1], target_coords[i2], target_coords[i3])
                        median_coords[i2].append(pt)

                        pt = BarycentricTransform(orig_shape_coords[i3],
                            orig_coords[i2], orig_coords[i3], orig_coords[i1],
                            target_coords[i2], target_coords[i3], target_coords[i1])
                        median_coords[i3].append(pt)

            elif mode == 'RELATIVE_EDGE':
                for ed in me.edges:
                    i1, i2 = ed.verts
                    v1, v2 = orig_coords[i1], orig_coords[i2]
                    edge_length = (v1 - v2).length
                    n1loc = v1 + orig_normals[i1] * edge_length
                    n2loc = v2 + orig_normals[i2] * edge_length


                    # now get the target nloc's
                    v1_to, v2_to = target_coords[i1], target_coords[i2]
                    edlen_to = (v1_to - v2_to).length
                    n1loc_to = v1_to + target_normals[i1] * edlen_to
                    n2loc_to = v2_to + target_normals[i2] * edlen_to

                    pt = BarycentricTransform(orig_shape_coords[i1],
                        v2, v1, n1loc,
                        v2_to, v1_to, n1loc_to)
                    median_coords[i1].append(pt)

                    pt = BarycentricTransform(orig_shape_coords[i2],
                        v1, v2, n2loc,
                        v1_to, v2_to, n2loc_to)
                    median_coords[i2].append(pt)

            # apply the offsets to the new shape
            from functools import reduce
            VectorAdd = Vector.__add__

            for i, vert_cos in enumerate(median_coords):
                if vert_cos:
                    co = reduce(VectorAdd, vert_cos) / len(vert_cos)

                    if use_clamp:
                        # clamp to the same movement as the original
                        # breaks copy between different scaled meshes.
                        len_from = (orig_shape_coords[i] - orig_coords[i]).length
                        ofs = co - target_coords[i]
                        ofs.length = len_from
                        co = target_coords[i] + ofs

                    target_shape_coords[i][:] = co

        return {'FINISHED'}

    def execute(self, context):
        C = bpy.context
        ob_act = C.active_object
        objects = [ob for ob in C.selected_editable_objects if ob != ob_act]
        return self._main(ob_act, objects, self.properties.mode, self.properties.use_clamp)


bpy.types.register(SelectPattern)
bpy.types.register(SubdivisionSet)
bpy.types.register(Retopo)
bpy.types.register(ShapeTransfer)