Improved cache baking operators which make use of threaded depsgraph
[blender-addons-contrib.git] / object_physics_meadow / ui.py
1 ### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 import bpy, os
22 from bpy.types import Operator, Panel
23 from bpy.props import *
24
25 from object_physics_meadow import meadow, settings as _settings, patch, blob, physics
26 from object_physics_meadow.settings import find_meadow_object
27 from object_physics_meadow.util import *
28 from object_physics_meadow import progress
29
30 # default progress reports
31 def progress_default():
32     progress.show_progress_bar = True
33     progress.show_stdout = True
34 # XXX baking: wm.progress updates are disabled for now,
35 # because the bake operator overrides this with it's own progress numbers ...
36 def progress_baking():
37     progress.show_progress_bar = False
38     progress.show_stdout = True
39
40 class OBJECT_PT_Meadow(Panel):
41     """Settings for meadow components"""
42     bl_label = "Meadow"
43     bl_idname = "OBJECT_PT_meadow"
44     bl_space_type = 'PROPERTIES'
45     bl_region_type = 'WINDOW'
46     bl_context = "object"
47     
48     @classmethod
49     def poll(cls, context):
50         ob = context.object
51         if not ob:
52             return False
53         return True
54     
55     def draw(self, context):
56         settings = _settings.get(context)
57         groundob = find_meadow_object(context, 'GROUND')
58         has_samples = blob.object_has_blob_data(groundob) if groundob else False
59         ob = context.object
60         meadow = ob.meadow
61         layout = self.layout
62         
63         layout.prop(meadow, "type", expand=True)
64         
65         layout.separator()
66         
67         if meadow.type == 'TEMPLATE':
68             row = layout.row()
69             if groundob:
70                 row.prop_search(meadow, "density_vgroup_name", groundob, "vertex_groups", text="Density Vertex Group")
71             else:
72                 row.active = False
73                 row.prop(meadow, "density_vgroup_name", text="Density Vertex Group")
74             
75             row = layout.row()
76             row.prop(meadow, "use_as_dupli")
77             sub = row.row()
78             sub.enabled = meadow.use_as_dupli
79             sub.prop(meadow, "use_centered")
80             row = layout.row()
81             row.prop(meadow, "hide")
82         
83         elif meadow.type == 'GROUND':
84             box = layout.box()
85             
86             sub = box.column()
87             # this politely prevents users from changing settings unwantedly,
88             # they have to delete the samples first
89             sub.enabled = not has_samples
90             sub.prop(meadow, "seed")
91             col = sub.column(align=True)
92             col.prop(meadow, "patch_radius")
93             col.prop(meadow, "max_patches")
94             sub.prop(meadow, "sampling_levels")
95             
96             if has_samples:
97                 box.operator("meadow.delete_blobs", icon='X', text="Delete Samples")
98             else:
99                 box.operator("meadow.make_blobs", icon='STICKY_UVS_DISABLE', text="Create Samples")
100         
101         layout.separator()
102         
103         col = layout.column()
104         col.enabled = has_samples
105         col.operator("meadow.make_patches", icon='PARTICLE_PATH', text="Update Patches")
106         
107         row = col.row()
108         row.operator("meadow.bake_physics", icon='MOD_PHYSICS')
109         row.operator("meadow.free_physics", icon='X')
110
111         row = layout.row()
112         row.prop(groundob.meadow, "use_layers")
113         sub = row.row()
114         if groundob.meadow.use_layers:
115             sub.template_layers(groundob.meadow, "layers", groundob.meadow, "used_layers", -1)
116         else:
117             sub.enabled = False
118             sub.template_layers(groundob, "layers", groundob.meadow, "used_layers", -1)
119
120
121 class MeadowOperatorBase():
122     def verify_cache_dir(self):
123         if not bpy.data.is_saved:
124             self.report({'ERROR'}, "File must be saved for generating external cache directory")
125             return False, ""
126         
127         cache_dir = bpy.path.abspath("//meadow_cache")
128         if os.path.exists(cache_dir):
129             if not os.path.isdir(cache_dir):
130                 self.report({'ERROR'}, "%s is not a directory" % cache_dir)
131                 return False, ""
132         else:
133             try:
134                 os.mkdir(cache_dir)
135             except OSError as err:
136                 self.report({'ERROR'}, "{0}".format(err))
137                 return False, ""
138         return True, cache_dir
139
140
141 class MakeBlobsOperator(MeadowOperatorBase, Operator):
142     """Generate Blob objects storing dupli distribution"""
143     bl_idname = "meadow.make_blobs"
144     bl_label = "Make Blobs"
145     bl_options = {'REGISTER', 'UNDO'}
146     
147     def execute(self, context):
148         scene = context.scene
149         settings = _settings.get(context)
150         
151         #cache_ok, cache_dir = self.verify_cache_dir()
152         #if not cache_ok:
153         #    return {'CANCELLED'}
154         if not settings.blob_group(context):
155             bpy.data.groups.new(settings.blob_groupname)
156         
157         groundob = find_meadow_object(context, 'GROUND')
158         if not groundob:
159             self.report({'ERROR'}, "Could not find meadow Ground object")
160             return {'CANCELLED'}
161         blobgridob = find_meadow_object(context, 'BLOBGRID')
162         if not blobgridob:
163             self.report({'ERROR'}, "Could not find meadow Blob Grid object")
164             return {'CANCELLED'}
165         
166         with ObjectSelection():
167             progress_default()
168             meadow.make_blobs(context, blobgridob, groundob)
169         
170         return {'FINISHED'}
171
172
173 class DeleteBlobsOperator(MeadowOperatorBase, Operator):
174     """Delete Blob objects storing dupli distribution"""
175     bl_idname = "meadow.delete_blobs"
176     bl_label = "Delete Blobs"
177     bl_options = {'REGISTER', 'UNDO'}
178     
179     def execute(self, context):
180         scene = context.scene
181         settings = _settings.get(context)
182         
183         groundob = find_meadow_object(context, 'GROUND')
184         
185         with ObjectSelection():
186             progress_default()
187             meadow.delete_blobs(context, groundob)
188         
189         return {'FINISHED'}
190
191
192 class MakePatchesOperator(MeadowOperatorBase, Operator):
193     """Make Patch copies across the grid for simulation and set up duplis"""
194     bl_idname = "meadow.make_patches"
195     bl_label = "Make Patches"
196     bl_options = {'REGISTER', 'UNDO'}
197     
198     def execute(self, context):
199         scene = context.scene
200         settings = _settings.get(context)
201         
202         if not settings.patch_group(context):
203             bpy.data.groups.new(settings.patch_groupname)
204         if not settings.blob_group(context):
205             bpy.data.groups.new(settings.blob_groupname)
206         
207         groundob = find_meadow_object(context, 'GROUND')
208         if not groundob:
209             self.report({'ERROR'}, "Could not find meadow Ground object")
210             return {'CANCELLED'}
211         blobgridob = find_meadow_object(context, 'BLOBGRID')
212         if not blobgridob:
213             self.report({'ERROR'}, "Could not find meadow Blob Grid object")
214             return {'CANCELLED'}
215         
216         with ObjectSelection():
217             progress_default()
218             meadow.make_patches(context, blobgridob, groundob)
219         
220         return {'FINISHED'}
221
222
223 # Combines blob + patches operator for menu entry
224 class MakeMeadowOperator(MeadowOperatorBase, Operator):
225     """Make blobs and patches based on designated meadow objects"""
226     bl_idname = "meadow.make_meadow"
227     bl_label = "Make Meadow"
228     bl_options = {'REGISTER', 'UNDO'}
229     
230     def execute(self, context):
231         result = bpy.ops.meadow.make_blobs()
232         if 'FINISHED' not in result:
233             return result
234         result = bpy.ops.meadow.make_patches()
235         if 'FINISHED' not in result:
236             return result
237         
238         return {'FINISHED'}
239
240
241 class MEADOW_OT_BakePhysics(MeadowOperatorBase, Operator):
242     """Bake all physics caches"""
243     bl_idname = "meadow.bake_physics"
244     bl_label = "Bake Physics"
245     bl_options = {'REGISTER', 'UNDO'}
246     
247     def execute(self, context):
248         with ObjectSelection():
249             progress_baking()
250             physics.scene_bake_all(context)
251         return {'FINISHED'}
252
253
254 class MEADOW_OT_FreePhysics(MeadowOperatorBase, Operator):
255     """Free all physics caches"""
256     bl_idname = "meadow.free_physics"
257     bl_label = "Free Physics"
258     bl_options = {'REGISTER', 'UNDO'}
259     
260     def execute(self, context):
261         with ObjectSelection():
262             progress_baking()
263             physics.scene_free_all(context)
264         return {'FINISHED'}
265
266
267 def menu_generate_meadow(self, context):
268     self.layout.operator("meadow.make_meadow", icon='PARTICLE_PATH')
269
270 def register():
271     bpy.utils.register_class(OBJECT_PT_Meadow)
272     
273     bpy.utils.register_class(MakeBlobsOperator)
274     bpy.utils.register_class(DeleteBlobsOperator)
275     bpy.utils.register_class(MakePatchesOperator)
276     bpy.utils.register_class(MakeMeadowOperator)
277     bpy.utils.register_class(MEADOW_OT_BakePhysics)
278     bpy.utils.register_class(MEADOW_OT_FreePhysics)
279     bpy.types.INFO_MT_add.append(menu_generate_meadow)
280
281 def unregister():
282     bpy.utils.unregister_class(OBJECT_PT_Meadow)
283     
284     bpy.types.INFO_MT_add.remove(menu_generate_meadow)
285     bpy.utils.unregister_class(MakeBlobsOperator)
286     bpy.utils.unregister_class(DeleteBlobsOperator)
287     bpy.utils.unregister_class(MakePatchesOperator)
288     bpy.utils.unregister_class(MakeMeadowOperator)
289     bpy.utils.unregister_class(MEADOW_OT_BakePhysics)
290     bpy.utils.unregister_class(MEADOW_OT_FreePhysics)