cleanup: pep8
[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, Menu
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         physics_type = game.physics_type
51
52         if physics_type == 'CHARACTER':
53             layout.prop(game, "use_actor")
54             layout.prop(ob, "hide_render", text="Invisible")  # out of place but useful
55             layout.prop(game, "step_height", slider=True)
56             layout.prop(game, "jump_speed")
57             layout.prop(game, "fall_speed")
58
59         elif physics_type in {'DYNAMIC', 'RIGID_BODY'}:
60             split = layout.split()
61
62             col = split.column()
63             col.prop(game, "use_actor")
64             col.prop(game, "use_ghost")
65             col.prop(ob, "hide_render", text="Invisible")  # out of place but useful
66
67             col = split.column()
68             col.prop(game, "use_material_physics_fh")
69             col.prop(game, "use_rotate_from_normal")
70             col.prop(game, "use_sleep")
71
72             layout.separator()
73
74             split = layout.split()
75
76             col = split.column()
77             col.label(text="Attributes:")
78             col.prop(game, "mass")
79             col.prop(game, "radius")
80             col.prop(game, "form_factor")
81
82             col = split.column()
83             sub = col.column()
84             sub.prop(game, "use_anisotropic_friction")
85             subsub = sub.column()
86             subsub.active = game.use_anisotropic_friction
87             subsub.prop(game, "friction_coefficients", text="", slider=True)
88
89             split = layout.split()
90
91             col = split.column()
92             col.label(text="Velocity:")
93             sub = col.column(align=True)
94             sub.prop(game, "velocity_min", text="Minimum")
95             sub.prop(game, "velocity_max", text="Maximum")
96
97             col = split.column()
98             col.label(text="Damping:")
99             sub = col.column(align=True)
100             sub.prop(game, "damping", text="Translation", slider=True)
101             sub.prop(game, "rotation_damping", text="Rotation", slider=True)
102
103             layout.separator()
104
105             split = layout.split()
106
107             col = split.column()
108             col.label(text="Lock Translation:")
109             col.prop(game, "lock_location_x", text="X")
110             col.prop(game, "lock_location_y", text="Y")
111             col.prop(game, "lock_location_z", text="Z")
112
113             col = split.column()
114             col.label(text="Lock Rotation:")
115             col.prop(game, "lock_rotation_x", text="X")
116             col.prop(game, "lock_rotation_y", text="Y")
117             col.prop(game, "lock_rotation_z", text="Z")
118
119         elif physics_type == 'SOFT_BODY':
120             col = layout.column()
121             col.prop(game, "use_actor")
122             col.prop(game, "use_ghost")
123             col.prop(ob, "hide_render", text="Invisible")
124
125             layout.separator()
126
127             split = layout.split()
128
129             col = split.column()
130             col.label(text="Attributes:")
131             col.prop(game, "mass")
132             # disabled in the code
133             # col.prop(soft, "weld_threshold")
134             col.prop(soft, "location_iterations")
135             col.prop(soft, "linear_stiffness", slider=True)
136             col.prop(soft, "dynamic_friction", slider=True)
137             col.prop(soft, "collision_margin", slider=True)
138             col.prop(soft, "use_bending_constraints", text="Bending Constraints")
139
140             col = split.column()
141             col.prop(soft, "use_shape_match")
142             sub = col.column()
143             sub.active = soft.use_shape_match
144             sub.prop(soft, "shape_threshold", slider=True)
145
146             col.separator()
147
148             col.label(text="Cluster Collision:")
149             col.prop(soft, "use_cluster_rigid_to_softbody")
150             col.prop(soft, "use_cluster_soft_to_softbody")
151             sub = col.column()
152             sub.active = (soft.use_cluster_rigid_to_softbody or soft.use_cluster_soft_to_softbody)
153             sub.prop(soft, "cluster_iterations", text="Iterations")
154
155         elif physics_type == 'STATIC':
156             col = layout.column()
157             col.prop(game, "use_actor")
158             col.prop(game, "use_ghost")
159             col.prop(game, "use_record_animation")
160             col.prop(ob, "hide_render", text="Invisible")
161
162             layout.separator()
163
164             split = layout.split()
165
166             col = split.column()
167             col.label(text="Attributes:")
168             col.prop(game, "radius")
169
170             col = split.column()
171             sub = col.column()
172             sub.prop(game, "use_anisotropic_friction")
173             subsub = sub.column()
174             subsub.active = game.use_anisotropic_friction
175             subsub.prop(game, "friction_coefficients", text="", slider=True)
176
177         elif physics_type == 'SENSOR':
178             col = layout.column()
179             col.prop(game, "use_actor", text="Detect Actors")
180             col.prop(ob, "hide_render", text="Invisible")
181
182         elif physics_type in {'INVISIBLE', 'NO_COLLISION', 'OCCLUDE'}:
183             layout.prop(ob, "hide_render", text="Invisible")
184
185         elif physics_type == 'NAVMESH':
186             layout.operator("mesh.navmesh_face_copy")
187             layout.operator("mesh.navmesh_face_add")
188
189             layout.separator()
190
191             layout.operator("mesh.navmesh_reset")
192             layout.operator("mesh.navmesh_clear")
193
194         if physics_type not in {'NO_COLLISION', 'OCCLUDE'}:
195             layout.separator()
196             split = layout.split()
197
198             col = split.column()
199             col.prop(game, "collision_group")
200             col = split.column()
201             col.prop(game, "collision_mask")
202
203
204 class PHYSICS_PT_game_collision_bounds(PhysicsButtonsPanel, Panel):
205     bl_label = "Collision Bounds"
206     COMPAT_ENGINES = {'BLENDER_GAME'}
207
208     @classmethod
209     def poll(cls, context):
210         game = context.object.game
211         rd = context.scene.render
212         return (game.physics_type in {'DYNAMIC', 'RIGID_BODY', 'SENSOR', 'SOFT_BODY', 'STATIC', 'CHARACTER'}) and (rd.engine in cls.COMPAT_ENGINES)
213
214     def draw_header(self, context):
215         game = context.active_object.game
216
217         self.layout.prop(game, "use_collision_bounds", text="")
218
219     def draw(self, context):
220         layout = self.layout
221
222         game = context.active_object.game
223
224         layout.active = game.use_collision_bounds
225         layout.prop(game, "collision_bounds_type", text="Bounds")
226
227         row = layout.row()
228         row.prop(game, "collision_margin", text="Margin", slider=True)
229         row.prop(game, "use_collision_compound", text="Compound")
230
231
232 class PHYSICS_PT_game_obstacles(PhysicsButtonsPanel, Panel):
233     bl_label = "Create Obstacle"
234     COMPAT_ENGINES = {'BLENDER_GAME'}
235
236     @classmethod
237     def poll(cls, context):
238         game = context.object.game
239         rd = context.scene.render
240         return (game.physics_type in {'DYNAMIC', 'RIGID_BODY', 'SENSOR', 'SOFT_BODY', 'STATIC'}) and (rd.engine in cls.COMPAT_ENGINES)
241
242     def draw_header(self, context):
243         game = context.active_object.game
244
245         self.layout.prop(game, "use_obstacle_create", text="")
246
247     def draw(self, context):
248         layout = self.layout
249
250         game = context.active_object.game
251
252         layout.active = game.use_obstacle_create
253
254         row = layout.row()
255         row.prop(game, "obstacle_radius", text="Radius")
256         row.label()
257
258
259 class RenderButtonsPanel:
260     bl_space_type = 'PROPERTIES'
261     bl_region_type = 'WINDOW'
262     bl_context = "render"
263
264     @classmethod
265     def poll(cls, context):
266         rd = context.scene.render
267         return (rd.engine in cls.COMPAT_ENGINES)
268
269
270 class RENDER_PT_embedded(RenderButtonsPanel, Panel):
271     bl_label = "Embedded Player"
272     COMPAT_ENGINES = {'BLENDER_GAME'}
273
274     def draw(self, context):
275         layout = self.layout
276
277         rd = context.scene.render
278
279         row = layout.row()
280         row.operator("view3d.game_start", text="Start")
281         row.label()
282         row = layout.row()
283         row.label(text="Resolution:")
284         row = layout.row(align=True)
285         row.prop(rd, "resolution_x", slider=False, text="X")
286         row.prop(rd, "resolution_y", slider=False, text="Y")
287
288
289 class RENDER_PT_game_player(RenderButtonsPanel, Panel):
290     bl_label = "Standalone Player"
291     COMPAT_ENGINES = {'BLENDER_GAME'}
292
293     def draw(self, context):
294         import sys
295         layout = self.layout
296         not_osx = sys.platform != "darwin"
297
298         gs = context.scene.game_settings
299
300         row = layout.row()
301         row.operator("wm.blenderplayer_start", text="Start")
302         row.label()
303
304         row = layout.row()
305         row.label(text="Resolution:")
306         row = layout.row(align=True)
307         row.active = not_osx or not gs.show_fullscreen
308         row.prop(gs, "resolution_x", slider=False, text="X")
309         row.prop(gs, "resolution_y", slider=False, text="Y")
310         row = layout.row()
311         col = row.column()
312         col.prop(gs, "show_fullscreen")
313
314         if not_osx:
315             col = row.column()
316             col.prop(gs, "use_desktop")
317             col.active = gs.show_fullscreen
318
319         col = layout.column()
320         col.label(text="Quality:")
321         col.prop(gs, "samples")
322         col = layout.column(align=True)
323         col.prop(gs, "depth", text="Bit Depth", slider=False)
324         col.prop(gs, "frequency", text="Refresh Rate", slider=False)
325
326
327 class RENDER_PT_game_stereo(RenderButtonsPanel, Panel):
328     bl_label = "Stereo"
329     COMPAT_ENGINES = {'BLENDER_GAME'}
330
331     def draw(self, context):
332         layout = self.layout
333
334         gs = context.scene.game_settings
335         stereo_mode = gs.stereo
336
337         # stereo options:
338         layout.prop(gs, "stereo", expand=True)
339
340         # stereo:
341         if stereo_mode == 'STEREO':
342             layout.prop(gs, "stereo_mode")
343             layout.prop(gs, "stereo_eye_separation")
344
345         # dome:
346         elif stereo_mode == 'DOME':
347             layout.prop(gs, "dome_mode", text="Dome Type")
348
349             dome_type = gs.dome_mode
350
351             split = layout.split()
352
353             if dome_type in {'FISHEYE', 'TRUNCATED_REAR', 'TRUNCATED_FRONT'}:
354                 col = split.column()
355                 col.prop(gs, "dome_buffer_resolution", text="Resolution", slider=True)
356                 col.prop(gs, "dome_angle", slider=True)
357
358                 col = split.column()
359                 col.prop(gs, "dome_tessellation", text="Tessellation")
360                 col.prop(gs, "dome_tilt")
361
362             elif dome_type == 'PANORAM_SPH':
363                 col = split.column()
364                 col.prop(gs, "dome_buffer_resolution", text="Resolution", slider=True)
365
366                 col = split.column()
367                 col.prop(gs, "dome_tessellation", text="Tessellation")
368
369             else:  # cube map
370                 col = split.column()
371                 col.prop(gs, "dome_buffer_resolution", text="Resolution", slider=True)
372
373                 col = split.column()
374
375             layout.prop(gs, "dome_text")
376
377
378 class RENDER_PT_game_shading(RenderButtonsPanel, Panel):
379     bl_label = "Shading"
380     COMPAT_ENGINES = {'BLENDER_GAME'}
381
382     def draw(self, context):
383         layout = self.layout
384
385         gs = context.scene.game_settings
386
387         layout.prop(gs, "material_mode", expand=True)
388
389         if gs.material_mode == 'GLSL':
390             split = layout.split()
391
392             col = split.column()
393             col.prop(gs, "use_glsl_lights", text="Lights")
394             col.prop(gs, "use_glsl_shaders", text="Shaders")
395             col.prop(gs, "use_glsl_shadows", text="Shadows")
396             col.prop(gs, "use_glsl_color_management", text="Color Management")
397
398             col = split.column()
399             col.prop(gs, "use_glsl_ramps", text="Ramps")
400             col.prop(gs, "use_glsl_nodes", text="Nodes")
401             col.prop(gs, "use_glsl_extra_textures", text="Extra Textures")
402
403
404 class RENDER_PT_game_system(RenderButtonsPanel, Panel):
405     bl_label = "System"
406     COMPAT_ENGINES = {'BLENDER_GAME'}
407
408     def draw(self, context):
409         layout = self.layout
410
411         gs = context.scene.game_settings
412         col = layout.column()
413         row = col.row()
414         col = row.column()
415         col.prop(gs, "use_frame_rate")
416         col.prop(gs, "use_restrict_animation_updates")
417         col.prop(gs, "use_material_caching")
418         col = row.column()
419         col.prop(gs, "use_display_lists")
420         col.active = gs.raster_storage != 'VERTEX_BUFFER_OBJECT'
421
422         row = layout.row()
423         row.prop(gs, "vsync")
424
425         row = layout.row()
426         row.prop(gs, "raster_storage")
427
428         row = layout.row()
429         row.label("Exit Key")
430         row.prop(gs, "exit_key", text="", event=True)
431
432
433 class RENDER_PT_game_display(RenderButtonsPanel, Panel):
434     bl_label = "Display"
435     COMPAT_ENGINES = {'BLENDER_GAME'}
436
437     def draw(self, context):
438         layout = self.layout
439
440         gs = context.scene.game_settings
441
442         layout.prop(context.scene.render, "fps", text="Animation Frame Rate", slider=False)
443
444         flow = layout.column_flow()
445         flow.prop(gs, "show_debug_properties", text="Debug Properties")
446         flow.prop(gs, "show_framerate_profile", text="Framerate and Profile")
447         flow.prop(gs, "show_physics_visualization", text="Physics Visualization")
448         flow.prop(gs, "use_deprecation_warnings")
449         flow.prop(gs, "show_mouse", text="Mouse Cursor")
450
451         col = layout.column()
452         col.label(text="Framing:")
453         col.row().prop(gs, "frame_type", expand=True)
454         if gs.frame_type == 'LETTERBOX':
455             col.prop(gs, "frame_color", text="")
456
457
458 class SceneButtonsPanel:
459     bl_space_type = 'PROPERTIES'
460     bl_region_type = 'WINDOW'
461     bl_context = "scene"
462
463
464 class SCENE_PT_game_navmesh(SceneButtonsPanel, Panel):
465     bl_label = "Navigation mesh"
466     bl_options = {'DEFAULT_CLOSED'}
467     COMPAT_ENGINES = {'BLENDER_GAME'}
468
469     @classmethod
470     def poll(cls, context):
471         scene = context.scene
472         return (scene and scene.render.engine in cls.COMPAT_ENGINES)
473
474     def draw(self, context):
475         layout = self.layout
476
477         rd = context.scene.game_settings.recast_data
478
479         layout.operator("mesh.navmesh_make", text="Build navigation mesh")
480
481         col = layout.column()
482         col.label(text="Rasterization:")
483         row = col.row()
484         row.prop(rd, "cell_size")
485         row.prop(rd, "cell_height")
486
487         col = layout.column()
488         col.label(text="Agent:")
489         split = col.split()
490
491         col = split.column()
492         col.prop(rd, "agent_height", text="Height")
493         col.prop(rd, "agent_radius", text="Radius")
494
495         col = split.column()
496         col.prop(rd, "slope_max")
497         col.prop(rd, "climb_max")
498
499         col = layout.column()
500         col.label(text="Region:")
501         row = col.row()
502         row.prop(rd, "region_min_size")
503         row.prop(rd, "region_merge_size")
504
505         col = layout.column()
506         col.label(text="Polygonization:")
507         split = col.split()
508
509         col = split.column()
510         col.prop(rd, "edge_max_len")
511         col.prop(rd, "edge_max_error")
512
513         split.prop(rd, "verts_per_poly")
514
515         col = layout.column()
516         col.label(text="Detail Mesh:")
517         row = col.row()
518         row.prop(rd, "sample_dist")
519         row.prop(rd, "sample_max_error")
520
521
522 class RENDER_PT_game_sound(RenderButtonsPanel, Panel):
523     bl_label = "Sound"
524     COMPAT_ENGINES = {'BLENDER_GAME'}
525
526     def draw(self, context):
527         layout = self.layout
528
529         scene = context.scene
530
531         layout.prop(scene, "audio_distance_model")
532
533         col = layout.column(align=True)
534         col.prop(scene, "audio_doppler_speed", text="Speed")
535         col.prop(scene, "audio_doppler_factor")
536
537
538 class WorldButtonsPanel:
539     bl_space_type = 'PROPERTIES'
540     bl_region_type = 'WINDOW'
541     bl_context = "world"
542
543
544 class WORLD_PT_game_context_world(WorldButtonsPanel, Panel):
545     bl_label = ""
546     bl_options = {'HIDE_HEADER'}
547     COMPAT_ENGINES = {'BLENDER_GAME'}
548
549     @classmethod
550     def poll(cls, context):
551         rd = context.scene.render
552         return (context.scene) and (rd.use_game_engine)
553
554     def draw(self, context):
555         layout = self.layout
556
557         scene = context.scene
558         world = context.world
559         space = context.space_data
560
561         split = layout.split(percentage=0.65)
562         if scene:
563             split.template_ID(scene, "world", new="world.new")
564         elif world:
565             split.template_ID(space, "pin_id")
566
567
568 class WORLD_PT_game_world(WorldButtonsPanel, Panel):
569     bl_label = "World"
570     COMPAT_ENGINES = {'BLENDER_GAME'}
571
572     @classmethod
573     def poll(cls, context):
574         scene = context.scene
575         return (scene.world and scene.render.engine in cls.COMPAT_ENGINES)
576
577     def draw(self, context):
578         layout = self.layout
579
580         world = context.world
581
582         row = layout.row()
583         row.column().prop(world, "horizon_color")
584         row.column().prop(world, "ambient_color")
585
586
587 class WORLD_PT_game_mist(WorldButtonsPanel, Panel):
588     bl_label = "Mist"
589     COMPAT_ENGINES = {'BLENDER_GAME'}
590
591     @classmethod
592     def poll(cls, context):
593         scene = context.scene
594         return (scene.world and scene.render.engine in cls.COMPAT_ENGINES)
595
596     def draw_header(self, context):
597         world = context.world
598
599         self.layout.prop(world.mist_settings, "use_mist", text="")
600
601     def draw(self, context):
602         layout = self.layout
603
604         world = context.world
605
606         layout.active = world.mist_settings.use_mist
607
608         layout.prop(world.mist_settings, "falloff")
609
610         row = layout.row(align=True)
611         row.prop(world.mist_settings, "start")
612         row.prop(world.mist_settings, "depth")
613
614         layout.prop(world.mist_settings, "intensity", text="Minimum Intensity")
615
616
617 class WORLD_PT_game_physics(WorldButtonsPanel, Panel):
618     bl_label = "Physics"
619     COMPAT_ENGINES = {'BLENDER_GAME'}
620
621     @classmethod
622     def poll(cls, context):
623         scene = context.scene
624         return (scene.world and scene.render.engine in cls.COMPAT_ENGINES)
625
626     def draw(self, context):
627         layout = self.layout
628
629         gs = context.scene.game_settings
630
631         layout.prop(gs, "physics_engine", text="Engine")
632         if gs.physics_engine != 'NONE':
633             layout.prop(gs, "physics_gravity", text="Gravity")
634
635             split = layout.split()
636
637             col = split.column()
638             col.label(text="Physics Steps:")
639             sub = col.column(align=True)
640             sub.prop(gs, "physics_step_max", text="Max")
641             sub.prop(gs, "physics_step_sub", text="Substeps")
642             col.prop(gs, "fps", text="FPS")
643
644             col = split.column()
645             col.label(text="Logic Steps:")
646             col.prop(gs, "logic_step_max", text="Max")
647
648             col = layout.column()
649             col.label(text="Physics Deactivation:")
650             sub = col.row(align=True)
651             sub.prop(gs, "deactivation_linear_threshold", text="Linear Threshold")
652             sub.prop(gs, "deactivation_angular_threshold", text="Angular Threshold")
653             sub = col.row()
654             sub.prop(gs, "deactivation_time", text="Time")
655
656             col = layout.column()
657             col.prop(gs, "use_occlusion_culling", text="Occlusion Culling")
658             sub = col.column()
659             sub.active = gs.use_occlusion_culling
660             sub.prop(gs, "occlusion_culling_resolution", text="Resolution")
661
662         else:
663             split = layout.split()
664
665             col = split.column()
666             col.label(text="Physics Steps:")
667             col.prop(gs, "fps", text="FPS")
668
669             col = split.column()
670             col.label(text="Logic Steps:")
671             col.prop(gs, "logic_step_max", text="Max")
672
673
674 class WORLD_PT_game_physics_obstacles(WorldButtonsPanel, Panel):
675     bl_label = "Obstacle simulation"
676     COMPAT_ENGINES = {'BLENDER_GAME'}
677
678     @classmethod
679     def poll(cls, context):
680         scene = context.scene
681         return (scene.world and scene.render.engine in cls.COMPAT_ENGINES)
682
683     def draw(self, context):
684         layout = self.layout
685
686         gs = context.scene.game_settings
687
688         layout.prop(gs, "obstacle_simulation", text="Type")
689         if gs.obstacle_simulation != 'NONE':
690             layout.prop(gs, "level_height")
691             layout.prop(gs, "show_obstacle_simulation")
692
693
694 class DataButtonsPanel:
695     bl_space_type = 'PROPERTIES'
696     bl_region_type = 'WINDOW'
697     bl_context = "data"
698
699
700 class DATA_PT_shadow_game(DataButtonsPanel, Panel):
701     bl_label = "Shadow"
702     COMPAT_ENGINES = {'BLENDER_GAME'}
703
704     @classmethod
705     def poll(cls, context):
706         COMPAT_LIGHTS = {'SPOT', 'SUN'}
707         lamp = context.lamp
708         engine = context.scene.render.engine
709         return (lamp and lamp.type in COMPAT_LIGHTS) and (engine in cls.COMPAT_ENGINES)
710
711     def draw_header(self, context):
712         lamp = context.lamp
713
714         self.layout.prop(lamp, "use_shadow", text="")
715
716     def draw(self, context):
717         layout = self.layout
718
719         lamp = context.lamp
720
721         layout.active = lamp.use_shadow
722
723         split = layout.split()
724
725         col = split.column()
726         col.prop(lamp, "shadow_color", text="")
727
728         col = split.column()
729         col.prop(lamp, "use_shadow_layer", text="This Layer Only")
730         col.prop(lamp, "use_only_shadow")
731
732         col = layout.column()
733         col.label("Buffer Type:")
734         col.prop(lamp, "ge_shadow_buffer_type", text="", toggle=True)
735         col.label("Quality:")
736         col = layout.column(align=True)
737         col.prop(lamp, "shadow_buffer_size", text="Size")
738         col.prop(lamp, "shadow_buffer_bias", text="Bias")
739         col.prop(lamp, "shadow_buffer_bleed_bias", text="Bleed Bias")
740
741         row = layout.row()
742         row.label("Clipping:")
743         row = layout.row(align=True)
744         row.prop(lamp, "shadow_buffer_clip_start", text="Clip Start")
745         row.prop(lamp, "shadow_buffer_clip_end", text="Clip End")
746
747         if lamp.type == 'SUN':
748             row = layout.row()
749             row.prop(lamp, "shadow_frustum_size", text="Frustum Size")
750
751
752 class ObjectButtonsPanel:
753     bl_space_type = 'PROPERTIES'
754     bl_region_type = 'WINDOW'
755     bl_context = "object"
756
757
758 class OBJECT_MT_lod_tools(Menu):
759     bl_label = "Level Of Detail Tools"
760
761     def draw(self, context):
762         layout = self.layout
763
764         layout.operator("object.lod_by_name", text="Set By Name")
765         layout.operator("object.lod_generate", text="Generate")
766         layout.operator("object.lod_clear_all", text="Clear All", icon='PANEL_CLOSE')
767
768
769 class OBJECT_PT_levels_of_detail(ObjectButtonsPanel, Panel):
770     bl_label = "Levels of Detail"
771     COMPAT_ENGINES = {'BLENDER_GAME'}
772
773     @classmethod
774     def poll(cls, context):
775         return context.scene.render.engine in cls.COMPAT_ENGINES
776
777     def draw(self, context):
778         layout = self.layout
779         ob = context.object
780
781         col = layout.column()
782
783         for i, level in enumerate(ob.lod_levels):
784             if i == 0:
785                 continue
786             box = col.box()
787             row = box.row()
788             row.prop(level, "object", text="")
789             row.operator("object.lod_remove", text="", icon='PANEL_CLOSE').index = i
790
791             row = box.row()
792             row.prop(level, "distance")
793             row = row.row(align=True)
794             row.prop(level, "use_mesh", text="")
795             row.prop(level, "use_material", text="")
796
797         row = col.row(align=True)
798         row.operator("object.lod_add", text="Add", icon='ZOOMIN')
799         row.menu("OBJECT_MT_lod_tools", text="", icon='TRIA_DOWN')
800
801
802 if __name__ == "__main__":  # only for live edit.
803     bpy.utils.register_module(__name__)