Recast & Detour UI:
[blender.git] / release / scripts / startup / bl_ui / properties_game.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 import bpy
21 from bpy.types import Panel
22
23
24 class PhysicsButtonsPanel():
25     bl_space_type = 'PROPERTIES'
26     bl_region_type = 'WINDOW'
27     bl_context = "physics"
28
29
30 class PHYSICS_PT_game_physics(PhysicsButtonsPanel, Panel):
31     bl_label = "Physics"
32     COMPAT_ENGINES = {'BLENDER_GAME'}
33
34     @classmethod
35     def poll(cls, context):
36         ob = context.active_object
37         rd = context.scene.render
38         return ob and ob.game and (rd.engine in cls.COMPAT_ENGINES)
39
40     def draw(self, context):
41         layout = self.layout
42
43         ob = context.active_object
44         game = ob.game
45         soft = ob.game.soft_body
46
47         layout.prop(game, "physics_type")
48         layout.separator()
49
50         #if game.physics_type == 'DYNAMIC':
51         if game.physics_type in {'DYNAMIC', 'RIGID_BODY'}:
52             split = layout.split()
53
54             col = split.column()
55             col.prop(game, "use_actor")
56             col.prop(game, "use_ghost")
57             col.prop(ob, "hide_render", text="Invisible")  # out of place but useful
58
59             col = split.column()
60             col.prop(game, "use_material_physics_fh")
61             col.prop(game, "use_rotate_from_normal")
62             col.prop(game, "use_sleep")
63
64             layout.separator()
65
66             split = layout.split()
67
68             col = split.column()
69             col.label(text="Attributes:")
70             col.prop(game, "mass")
71             col.prop(game, "radius")
72             col.prop(game, "form_factor")
73
74             col = split.column()
75             sub = col.column()
76             sub.prop(game, "use_anisotropic_friction")
77             subsub = sub.column()
78             subsub.active = game.use_anisotropic_friction
79             subsub.prop(game, "friction_coefficients", text="", slider=True)
80
81             split = layout.split()
82
83             col = split.column()
84             col.label(text="Velocity:")
85             sub = col.column(align=True)
86             sub.prop(game, "velocity_min", text="Minimum")
87             sub.prop(game, "velocity_max", text="Maximum")
88
89             col = split.column()
90             col.label(text="Damping:")
91             sub = col.column(align=True)
92             sub.prop(game, "damping", text="Translation", slider=True)
93             sub.prop(game, "rotation_damping", text="Rotation", slider=True)
94
95             layout.separator()
96
97             split = layout.split()
98
99             col = split.column()
100             col.label(text="Lock Translation:")
101             col.prop(game, "lock_location_x", text="X")
102             col.prop(game, "lock_location_y", text="Y")
103             col.prop(game, "lock_location_z", text="Z")
104
105             col = split.column()
106             col.label(text="Lock Rotation:")
107             col.prop(game, "lock_rotation_x", text="X")
108             col.prop(game, "lock_rotation_y", text="Y")
109             col.prop(game, "lock_rotation_z", text="Z")
110
111         elif game.physics_type == 'SOFT_BODY':
112             col = layout.column()
113             col.prop(game, "use_actor")
114             col.prop(game, "use_ghost")
115             col.prop(ob, "hide_render", text="Invisible")
116
117             layout.separator()
118
119             split = layout.split()
120
121             col = split.column()
122             col.label(text="Attributes:")
123             col.prop(game, "mass")
124             col.prop(soft, "weld_threshold")
125             col.prop(soft, "location_iterations")
126             col.prop(soft, "linear_stiffness", slider=True)
127             col.prop(soft, "dynamic_friction", slider=True)
128             col.prop(soft, "collision_margin", slider=True)
129             col.prop(soft, "use_bending_constraints", text="Bending Constraints")
130
131             col = split.column()
132             col.prop(soft, "use_shape_match")
133             sub = col.column()
134             sub.active = soft.use_shape_match
135             sub.prop(soft, "shape_threshold", slider=True)
136
137             col.separator()
138
139             col.label(text="Cluster Collision:")
140             col.prop(soft, "use_cluster_rigid_to_softbody")
141             col.prop(soft, "use_cluster_soft_to_softbody")
142             sub = col.column()
143             sub.active = (soft.use_cluster_rigid_to_softbody or soft.use_cluster_soft_to_softbody)
144             sub.prop(soft, "cluster_iterations", text="Iterations")
145
146         elif game.physics_type == 'STATIC':
147             col = layout.column()
148             col.prop(game, "use_actor")
149             col.prop(game, "use_ghost")
150             col.prop(ob, "hide_render", text="Invisible")
151
152             layout.separator()
153
154             split = layout.split()
155
156             col = split.column()
157             col.label(text="Attributes:")
158             col.prop(game, "radius")
159
160             col = split.column()
161             sub = col.column()
162             sub.prop(game, "use_anisotropic_friction")
163             subsub = sub.column()
164             subsub.active = game.use_anisotropic_friction
165             subsub.prop(game, "friction_coefficients", text="", slider=True)
166
167         elif game.physics_type in {'SENSOR', 'INVISIBLE', 'NO_COLLISION', 'OCCLUDE'}:
168             layout.prop(ob, "hide_render", text="Invisible")
169
170
171 class PHYSICS_PT_game_collision_bounds(PhysicsButtonsPanel, Panel):
172     bl_label = "Collision Bounds"
173     COMPAT_ENGINES = {'BLENDER_GAME'}
174
175     @classmethod
176     def poll(cls, context):
177         game = context.object.game
178         rd = context.scene.render
179         return (game.physics_type in {'DYNAMIC', 'RIGID_BODY', 'SENSOR', 'SOFT_BODY', 'STATIC'}) and (rd.engine in cls.COMPAT_ENGINES)
180
181     def draw_header(self, context):
182         game = context.active_object.game
183
184         self.layout.prop(game, "use_collision_bounds", text="")
185
186     def draw(self, context):
187         layout = self.layout
188
189         game = context.active_object.game
190
191         layout.active = game.use_collision_bounds
192         layout.prop(game, "collision_bounds_type", text="Bounds")
193
194         row = layout.row()
195         row.prop(game, "collision_margin", text="Margin", slider=True)
196         row.prop(game, "use_collision_compound", text="Compound")
197
198 class PHYSICS_PT_game_obstacles(PhysicsButtonsPanel, Panel):
199     bl_label = "Create Obstacle"
200     COMPAT_ENGINES = {'BLENDER_GAME'}
201  
202     @classmethod
203     def poll(cls, context):
204         game = context.object.game
205         rd = context.scene.render
206         return (game.physics_type in ('DYNAMIC', 'RIGID_BODY', 'SENSOR', 'SOFT_BODY', 'STATIC'))  and (rd.engine in cls.COMPAT_ENGINES)
207
208     def draw_header(self, context):
209         game = context.active_object.game
210
211         self.layout.prop(game, "create_obstacle", text="")
212
213     def draw(self, context):
214         layout = self.layout
215
216         game = context.active_object.game
217
218         layout.active = game.create_obstacle
219
220         row = layout.row()
221         row.prop(game, "obstacle_radius", text="Radius")
222         row.label()
223
224 class RenderButtonsPanel():
225     bl_space_type = 'PROPERTIES'
226     bl_region_type = 'WINDOW'
227     bl_context = "render"
228
229     @classmethod
230     def poll(cls, context):
231         rd = context.scene.render
232         return (rd.engine in cls.COMPAT_ENGINES)
233
234
235 class RENDER_PT_game(RenderButtonsPanel, Panel):
236     bl_label = "Game"
237     COMPAT_ENGINES = {'BLENDER_GAME'}
238
239     def draw(self, context):
240         layout = self.layout
241
242         row = layout.row()
243         row.operator("view3d.game_start", text="Start")
244         row.label()
245
246
247 class RENDER_PT_game_player(RenderButtonsPanel, Panel):
248     bl_label = "Standalone Player"
249     COMPAT_ENGINES = {'BLENDER_GAME'}
250
251     def draw(self, context):
252         layout = self.layout
253
254         gs = context.scene.game_settings
255
256         layout.prop(gs, "show_fullscreen")
257
258         split = layout.split()
259
260         col = split.column()
261         col.label(text="Resolution:")
262         sub = col.column(align=True)
263         sub.prop(gs, "resolution_x", slider=False, text="X")
264         sub.prop(gs, "resolution_y", slider=False, text="Y")
265
266         col = split.column()
267         col.label(text="Quality:")
268         sub = col.column(align=True)
269         sub.prop(gs, "depth", text="Bit Depth", slider=False)
270         sub.prop(gs, "frequency", text="FPS", slider=False)
271
272         # framing:
273         col = layout.column()
274         col.label(text="Framing:")
275         col.row().prop(gs, "frame_type", expand=True)
276         if gs.frame_type == 'LETTERBOX':
277             col.prop(gs, "frame_color", text="")
278
279
280 class RENDER_PT_game_stereo(RenderButtonsPanel, Panel):
281     bl_label = "Stereo"
282     COMPAT_ENGINES = {'BLENDER_GAME'}
283
284     def draw(self, context):
285         layout = self.layout
286
287         gs = context.scene.game_settings
288         stereo_mode = gs.stereo
289
290         # stereo options:
291         layout.prop(gs, "stereo", expand=True)
292
293         # stereo:
294         if stereo_mode == 'STEREO':
295             layout.prop(gs, "stereo_mode")
296             layout.prop(gs, "stereo_eye_separation")
297
298         # dome:
299         elif stereo_mode == 'DOME':
300             layout.prop(gs, "dome_mode", text="Dome Type")
301
302             dome_type = gs.dome_mode
303
304             split = layout.split()
305
306             if dome_type == 'FISHEYE' or \
307                dome_type == 'TRUNCATED_REAR' or \
308                dome_type == 'TRUNCATED_FRONT':
309
310                 col = split.column()
311                 col.prop(gs, "dome_buffer_resolution", text="Resolution", slider=True)
312                 col.prop(gs, "dome_angle", slider=True)
313
314                 col = split.column()
315                 col.prop(gs, "dome_tesselation", text="Tesselation")
316                 col.prop(gs, "dome_tilt")
317
318             elif dome_type == 'PANORAM_SPH':
319                 col = split.column()
320
321                 col.prop(gs, "dome_buffer_resolution", text="Resolution", slider=True)
322                 col = split.column()
323                 col.prop(gs, "dome_tesselation", text="Tesselation")
324
325             else:  # cube map
326                 col = split.column()
327                 col.prop(gs, "dome_buffer_resolution", text="Resolution", slider=True)
328
329                 col = split.column()
330
331             layout.prop(gs, "dome_text")
332
333
334 class RENDER_PT_game_shading(RenderButtonsPanel, Panel):
335     bl_label = "Shading"
336     COMPAT_ENGINES = {'BLENDER_GAME'}
337
338     def draw(self, context):
339         layout = self.layout
340
341         gs = context.scene.game_settings
342
343         layout.prop(gs, "material_mode", expand=True)
344
345         if gs.material_mode == 'GLSL':
346             split = layout.split()
347
348             col = split.column()
349             col.prop(gs, "use_glsl_lights", text="Lights")
350             col.prop(gs, "use_glsl_shaders", text="Shaders")
351             col.prop(gs, "use_glsl_shadows", text="Shadows")
352             col.prop(gs, "use_glsl_color_management", text="Color Management")
353
354             col = split.column()
355             col.prop(gs, "use_glsl_ramps", text="Ramps")
356             col.prop(gs, "use_glsl_nodes", text="Nodes")
357             col.prop(gs, "use_glsl_extra_textures", text="Extra Textures")
358
359
360 class RENDER_PT_game_performance(RenderButtonsPanel, Panel):
361     bl_label = "Performance"
362     COMPAT_ENGINES = {'BLENDER_GAME'}
363
364     def draw(self, context):
365         layout = self.layout
366
367         gs = context.scene.game_settings
368         col = layout.column()
369         row = col.row()
370         row.prop(gs, "use_frame_rate")
371         row.prop(gs, "use_display_lists")
372
373         col.prop(gs, "restrict_animation_updates")
374
375
376 class RENDER_PT_game_display(RenderButtonsPanel, Panel):
377     bl_label = "Display"
378     COMPAT_ENGINES = {'BLENDER_GAME'}
379
380     def draw(self, context):
381         layout = self.layout
382
383         gs = context.scene.game_settings
384         flow = layout.column_flow()
385         flow.prop(gs, "show_debug_properties", text="Debug Properties")
386         flow.prop(gs, "show_framerate_profile", text="Framerate and Profile")
387         flow.prop(gs, "show_physics_visualization", text="Physics Visualization")
388         flow.prop(gs, "use_deprecation_warnings")
389         flow.prop(gs, "show_mouse", text="Mouse Cursor")
390         
391 class SceneButtonsPanel():
392     bl_space_type = 'PROPERTIES'
393     bl_region_type = 'WINDOW'
394     bl_context = "scene"
395         
396 class SCENE_PT_game_navmesh(SceneButtonsPanel, bpy.types.Panel):
397     bl_label = "Navigation mesh"
398     bl_default_closed = True
399     COMPAT_ENGINES = {'BLENDER_GAME'}
400     
401     @classmethod
402     def poll(cls, context):
403         scene = context.scene
404         return (scene and scene.render.engine in cls.COMPAT_ENGINES)
405
406     def draw(self, context):
407         layout = self.layout
408
409         rd = context.scene.game_settings.recast_data
410
411         layout.operator("object.create_navmesh", text='Build navigation mesh')
412
413         col = layout.column()
414         col.label(text="Rasterization:")
415         row = col.row()
416         row.prop(rd, "cell_size")
417         row.prop(rd, "cell_height")
418
419         col = layout.column()
420         col.label(text="Agent:")
421         split = col.split()
422
423         col = split.column()
424         col.prop(rd, "agent_height", text="Height")
425         col.prop(rd, "agent_radius", text="Radius")
426
427         col = split.column()
428         col.prop(rd, "max_slope")
429         col.prop(rd, "max_climb")
430
431         col = layout.column()
432         col.label(text="Region:")
433         row = col.row()
434         row.prop(rd, "region_min_size")
435         row.prop(rd, "region_merge_size")
436
437         col = layout.column()
438         col.label(text="Polygonization:")
439         split = col.split()
440         
441         col = split.column()
442         col.prop(rd, "edge_max_len")
443         col.prop(rd, "edge_max_error")
444
445         split.prop(rd, "verts_per_poly")
446         
447         col = layout.column()
448         col.label(text="Detail Mesh:")
449         row = col.row()
450         row.prop(rd, "sample_dist")
451         row.prop(rd, "sample_max_error")
452
453
454 class WorldButtonsPanel():
455     bl_space_type = 'PROPERTIES'
456     bl_region_type = 'WINDOW'
457     bl_context = "world"
458
459
460 class WORLD_PT_game_context_world(WorldButtonsPanel, Panel):
461     bl_label = ""
462     bl_options = {'HIDE_HEADER'}
463     COMPAT_ENGINES = {'BLENDER_GAME'}
464
465     @classmethod
466     def poll(cls, context):
467         rd = context.scene.render
468         return (context.scene) and (rd.use_game_engine)
469
470     def draw(self, context):
471         layout = self.layout
472
473         scene = context.scene
474         world = context.world
475         space = context.space_data
476
477         split = layout.split(percentage=0.65)
478         if scene:
479             split.template_ID(scene, "world", new="world.new")
480         elif world:
481             split.template_ID(space, "pin_id")
482
483
484 class WORLD_PT_game_world(WorldButtonsPanel, Panel):
485     bl_label = "World"
486     COMPAT_ENGINES = {'BLENDER_GAME'}
487
488     @classmethod
489     def poll(cls, context):
490         scene = context.scene
491         return (scene.world and scene.render.engine in cls.COMPAT_ENGINES)
492
493     def draw(self, context):
494         layout = self.layout
495
496         world = context.world
497
498         row = layout.row()
499         row.column().prop(world, "horizon_color")
500         row.column().prop(world, "ambient_color")
501
502
503 class WORLD_PT_game_mist(WorldButtonsPanel, Panel):
504     bl_label = "Mist"
505     COMPAT_ENGINES = {'BLENDER_GAME'}
506
507     @classmethod
508     def poll(cls, context):
509         scene = context.scene
510         return (scene.world and scene.render.engine in cls.COMPAT_ENGINES)
511
512     def draw_header(self, context):
513         world = context.world
514
515         self.layout.prop(world.mist_settings, "use_mist", text="")
516
517     def draw(self, context):
518         layout = self.layout
519
520         world = context.world
521
522         layout.active = world.mist_settings.use_mist
523
524         row = layout.row()
525         row.prop(world.mist_settings, "start")
526         row.prop(world.mist_settings, "depth")
527
528
529 class WORLD_PT_game_physics(WorldButtonsPanel, Panel):
530     bl_label = "Physics"
531     COMPAT_ENGINES = {'BLENDER_GAME'}
532
533     @classmethod
534     def poll(cls, context):
535         scene = context.scene
536         return (scene.world and scene.render.engine in cls.COMPAT_ENGINES)
537
538     def draw(self, context):
539         layout = self.layout
540
541         gs = context.scene.game_settings
542
543         layout.prop(gs, "physics_engine")
544         if gs.physics_engine != 'NONE':
545             layout.prop(gs, "physics_gravity", text="Gravity")
546
547             split = layout.split()
548
549             col = split.column()
550             col.label(text="Physics Steps:")
551             sub = col.column(align=True)
552             sub.prop(gs, "physics_step_max", text="Max")
553             sub.prop(gs, "physics_step_sub", text="Substeps")
554             col.prop(gs, "fps", text="FPS")
555
556             col = split.column()
557             col.label(text="Logic Steps:")
558             col.prop(gs, "logic_step_max", text="Max")
559
560             col = layout.column()
561             col.prop(gs, "use_occlusion_culling", text="Occlusion Culling")
562             sub = col.column()
563             sub.active = gs.use_occlusion_culling
564             sub.prop(gs, "occlusion_culling_resolution", text="Resolution")
565
566         else:
567             split = layout.split()
568
569             col = split.column()
570             col.label(text="Physics Steps:")
571             col.prop(gs, "fps", text="FPS")
572
573             col = split.column()
574             col.label(text="Logic Steps:")
575             col.prop(gs, "logic_step_max", text="Max")
576
577 class WORLD_PT_game_physics_obstacles(WorldButtonsPanel, Panel):
578     bl_label = "Obstacle simulation"
579     COMPAT_ENGINES = {'BLENDER_GAME'}
580     
581     @classmethod
582     def poll(cls, context):
583         scene = context.scene
584         return (scene.world and scene.render.engine in cls.COMPAT_ENGINES)
585
586     def draw(self, context):
587         layout = self.layout
588
589         gs = context.scene.game_settings
590
591         layout.prop(gs, "obstacle_simulation", text = "Type")
592         if gs.obstacle_simulation != 'NONE':
593             layout.prop(gs, "level_height")
594             layout.prop(gs, "show_obstacle_simulation")
595
596 if __name__ == "__main__":  # only for live edit.
597     bpy.utils.register_module(__name__)