From 1ca9456870947b13b39ad2f30393adf0acabf3fd Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sun, 12 Aug 2018 16:14:01 -0400 Subject: [PATCH] Rework camera UI to not be so monolithic --- korman/exporter/camera.py | 5 +- korman/properties/prop_camera.py | 17 +- korman/render.py | 4 - korman/ui/modifiers/region.py | 23 ++- korman/ui/ui_camera.py | 256 +++++++++++++++++++------------ 5 files changed, 198 insertions(+), 107 deletions(-) diff --git a/korman/exporter/camera.py b/korman/exporter/camera.py index 9e315a7..420ed79 100644 --- a/korman/exporter/camera.py +++ b/korman/exporter/camera.py @@ -140,7 +140,8 @@ class CameraConverter: return brain def _export_fixed_camera(self, so, bo, props): - self._exporter().animation.convert_object_animations(bo, so) + if props.anim_enabled: + self._exporter().animation.convert_object_animations(bo, so) brain = self._mgr.find_create_object(plCameraBrain1_Fixed, so=so) self._convert_brain(so, bo, props, brain) return brain @@ -185,6 +186,8 @@ class CameraConverter: else: # The animation is a loop path.flags |= plAnimPath.kWrap + if props.rail_pos == "farthest": + path.flags |= plAnimPath.kFarthest path.length = end / bpy.context.scene.render.fps rail.path = path brain.rail = rail.key diff --git a/korman/properties/prop_camera.py b/korman/properties/prop_camera.py index 6b88549..93d814a 100644 --- a/korman/properties/prop_camera.py +++ b/korman/properties/prop_camera.py @@ -159,8 +159,8 @@ class PlasmaCameraProperties(bpy.types.PropertyGroup): options=set()) circle_pos = EnumProperty(name="Position on Circle", description="The point on the circle the camera moves to", - items=[("closest", "Closest Point", "The camera moves to point on the circle closest to the Point of Attention"), - ("farthest", "Farthest Point", "The point farthest from the Point of Attention")], + items=[("closest", "Closest Point", "The camera moves to the point on the circle closest to the Point of Attention"), + ("farthest", "Farthest Point", "The camera moves to the point on the circle farthest from the Point of Attention")], options=set()) circle_velocity = FloatProperty(name="Velocity", description="Velocity of the circle camera in degrees per second", @@ -174,16 +174,29 @@ class PlasmaCameraProperties(bpy.types.PropertyGroup): min=0.0, default=8.5, options={"HIDDEN"}) # Animation + anim_enabled = BoolProperty(name="Animation Enabled", + description="Export the camera's animation", + default=True, + options=set()) start_on_push = BoolProperty(name="Start on Push", description="Start playing the camera's animation when the camera is activated", + default=True, options=set()) stop_on_pop = BoolProperty(name="Pause on Pop", description="Pauses the camera's animation when the camera is no longer activated", + default=True, options=set()) reset_on_pop = BoolProperty(name="Reset on Pop", description="Reset the camera's animation to the beginning when the camera is no longer activated", options=set()) + # Rail + rail_pos = EnumProperty(name="Position on Rail", + description="The point on the rail the camera moves to", + items=[("closest", "Closest Point", "The camera moves to the point on the rail closest to the Point of Attention"), + ("farthest", "Farthest Point", "The camera moves to the point on the rail farthest from the Point of Attention")], + options=set()) + def get_circle_radius(self, bo): """Gets the circle camera radius for this camera when it is attached to the given Object""" assert bo is not None diff --git a/korman/render.py b/korman/render.py index e51304e..393b9eb 100644 --- a/korman/render.py +++ b/korman/render.py @@ -36,10 +36,6 @@ properties_material.MATERIAL_PT_transp.COMPAT_ENGINES.add("PLASMA_GAME") properties_material.MATERIAL_PT_shadow.COMPAT_ENGINES.add("PLASMA_GAME") del properties_material -from bl_ui import properties_data_camera -properties_data_camera.DATA_PT_context_camera.COMPAT_ENGINES.add("PLASMA_GAME") -del properties_data_camera - from bl_ui import properties_data_mesh properties_data_mesh.DATA_PT_uv_texture.COMPAT_ENGINES.add("PLASMA_GAME") properties_data_mesh.DATA_PT_vertex_colors.COMPAT_ENGINES.add("PLASMA_GAME") diff --git a/korman/ui/modifiers/region.py b/korman/ui/modifiers/region.py index bbf3a07..2b18446 100644 --- a/korman/ui/modifiers/region.py +++ b/korman/ui/modifiers/region.py @@ -14,14 +14,33 @@ # along with Korman. If not, see . import bpy -from ..ui_camera import draw_camera_properties +from .. import ui_camera def camera_rgn(modifier, layout, context): layout.prop(modifier, "camera_type") if modifier.camera_type == "manual": layout.prop(modifier, "camera_object", icon="CAMERA_DATA") else: - draw_camera_properties("follow", modifier.auto_camera, layout, context, True) + cam_type = modifier.camera_type[5:] + cam_props = modifier.auto_camera + + def _draw_props(layout, cb): + for i in cb: + layout.separator() + i(layout, cam_type, cam_props) + def _draw_circle_cam_props(layout, cam_type, props): + # needs a sublayout that we can deactivate because the ui_camera + # version assumes we are most definitely a circle camera... + col = layout.column() + col.active = cam_type == "circle" + col.label("Circle Camera:") + ui_camera.draw_circle_camera_props(col, props) + + _draw_props(layout, (ui_camera.draw_camera_mode_props, + ui_camera.draw_camera_poa_props, + ui_camera.draw_camera_pos_props, + ui_camera.draw_camera_manipulation_props, + _draw_circle_cam_props)) def footstep(modifier, layout, context): layout.prop(modifier, "bounds") diff --git a/korman/ui/ui_camera.py b/korman/ui/ui_camera.py index e4ef144..56179c9 100644 --- a/korman/ui/ui_camera.py +++ b/korman/ui/ui_camera.py @@ -14,69 +14,45 @@ # along with Korman. If not, see . import bpy +from .. import helpers + +def _draw_alert_prop(layout, props, the_prop, cam_type, alert_cam="", min=None, max=None, **kwargs): + can_alert = not alert_cam or alert_cam == cam_type + if can_alert: + value = getattr(props, the_prop) + if min is not None and value < min: + layout.alert = True + if max is not None and value > max: + layout.alert = True + layout.prop(props, the_prop, **kwargs) + layout.alert = False + else: + layout.prop(props, the_prop, **kwargs) + +def _draw_gated_prop(layout, props, gate_prop, actual_prop): + row = layout.row(align=True) + row.prop(props, gate_prop, text="") + row = row.row(align=True) + row.active = getattr(props, gate_prop) + row.prop(props, actual_prop) + +def draw_camera_manipulation_props(layout, cam_type, props): + # Camera Panning + split = layout.split() + col = split.column() + col.label("Limit Panning:") + col.prop(props, "x_pan_angle") + col.prop(props, "y_pan_angle") -class CameraButtonsPanel: - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "data" - - @classmethod - def poll(cls, context): - return (context.camera and context.scene.render.engine == "PLASMA_GAME") - - -class PlasmaCameraPanel(CameraButtonsPanel, bpy.types.Panel): - bl_label = "Plasma Camera" - - def draw(self, context): - camera = context.camera.plasma_camera - layout = self.layout - - layout.prop(camera, "camera_type") - layout.separator() - draw_camera_properties(camera.camera_type, camera.settings, layout, context) - - -class PlasmaCameraTransitionPanel(CameraButtonsPanel, bpy.types.Panel): - bl_label = "Plasma Transitions" - - def draw(self, context): - pass - - -def draw_camera_properties(cam_type, props, layout, context, force_no_anim=False): - trans = props.transition - - def _draw_alert_prop(layout, props, the_prop, cam="", min=None, max=None, **kwargs): - can_alert = not cam or cam == cam_type - if can_alert: - value = getattr(props, the_prop) - if min is not None and value < min: - layout.alert = True - if max is not None and value > max: - layout.alert = True - layout.prop(props, the_prop, **kwargs) - layout.alert = False - else: - layout.prop(props, the_prop, **kwargs) - def _draw_gated_prop(layout, props, gate_prop, actual_prop): - row = layout.row(align=True) - row.prop(props, gate_prop, text="") - row = row.row(align=True) - row.active = getattr(props, gate_prop) - row.prop(props, actual_prop) - def _is_camera_animated(cam_type, props, context, force_no_anim): - if force_no_anim or cam_type == "rail": - return False - # Check for valid animation data on either the object or the camera - # TODO: Should probably check for valid FCurve channels at some point??? - for i in (props.id_data, context.object): - if i.animation_data is None: - continue - if i.animation_data.action is not None: - return True - return False + # Camera Zoom + col = split.column() + col.label("Field of View:") + col.prop(props, "fov") + _draw_gated_prop(col, props, "limit_zoom", "zoom_min") + _draw_gated_prop(col, props, "limit_zoom", "zoom_max") + _draw_gated_prop(col, props, "limit_zoom", "zoom_rate") +def draw_camera_mode_props(layout, cam_type, props): # Point of Attention split = layout.split() col = split.column() @@ -99,8 +75,10 @@ def draw_camera_properties(cam_type, props, layout, context, force_no_anim=False col.prop(props, "fast_run") col.prop(props, "ignore_subworld") +def draw_camera_poa_props(layout, cam_type, props): + trans = props.transition + # PoA Tracking - layout.separator() split = layout.split() col = split.column() col.label("Default Tracking Transition:") @@ -115,17 +93,22 @@ def draw_camera_properties(cam_type, props, layout, context, force_no_anim=False col.prop(props, "poa_offset", text="") col.prop(props, "poa_worldspace") +def draw_camera_pos_props(layout, cam_type, props): + trans = props.transition + # Position Tracking (only for follow cams) - layout.separator() split = layout.split() col = split.column() # Position Transitions col.active = cam_type != "circle" col.label("Default Position Transition:") - _draw_alert_prop(col, trans, "pos_acceleration", cam="rail", max=10.0, text="Acceleration") - _draw_alert_prop(col, trans, "pos_deceleration", cam="rail", max=10.0, text="Deceleration") - _draw_alert_prop(col, trans, "pos_velocity", cam="rail", max=10.0, text="Maximum Velocity") + _draw_alert_prop(col, trans, "pos_acceleration", cam_type, + alert_cam="rail", max=10.0, text="Acceleration") + _draw_alert_prop(col, trans, "pos_deceleration", cam_type, + alert_cam="rail", max=10.0, text="Deceleration") + _draw_alert_prop(col, trans, "pos_velocity", cam_type, + alert_cam="rail", max=10.0, text="Maximum Velocity") col.prop(trans, "pos_cut") # Position Offsets @@ -135,39 +118,116 @@ def draw_camera_properties(cam_type, props, layout, context, force_no_anim=False col.prop(props, "pos_offset", text="") col.prop(props, "pos_worldspace") - # Camera Panning - layout.separator() - split = layout.split() - col = split.column() - col.label("Limit Panning:") - col.prop(props, "x_pan_angle") - col.prop(props, "y_pan_angle") - - # Camera Zoom - col = split.column() - col.label("Field of View:") - col.prop(props, "fov") - _draw_gated_prop(col, props, "limit_zoom", "zoom_min") - _draw_gated_prop(col, props, "limit_zoom", "zoom_max") - _draw_gated_prop(col, props, "limit_zoom", "zoom_rate") - +def draw_circle_camera_props(layout, props): # Circle Camera Stuff - layout.separator() - split = layout.split() - col = split.column() - col.active = cam_type == "circle" - col.label("Circle Camera:") - col.prop(props, "circle_center", text="") - col.prop(props, "circle_pos", text="") - col.prop(props, "circle_velocity") - row = col.row(align=True) + layout.prop(props, "circle_center") + layout.prop(props, "circle_pos") + layout.prop(props, "circle_velocity") + row = layout.row(align=True) row.active = props.circle_center is None row.prop(props, "circle_radius_ui") - # Animated Camera Stuff - col = split.column() - col.active = _is_camera_animated(cam_type, props, context, force_no_anim) - col.label("Animation:") - col.prop(props, "start_on_push") - col.prop(props, "stop_on_pop") - col.prop(props, "reset_on_pop") +class CameraButtonsPanel: + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "data" + + @classmethod + def poll(cls, context): + return (context.camera and context.scene.render.engine == "PLASMA_GAME") + + +class PlasmaCameraTypePanel(CameraButtonsPanel, bpy.types.Panel): + bl_label = "" + bl_options = {"HIDE_HEADER"} + + def draw(self, context): + camera = context.camera.plasma_camera + self.layout.prop(camera, "camera_type") + + +class PlasmaCameraModePanel(CameraButtonsPanel, bpy.types.Panel): + bl_label = "Camera Tracking" + + def draw(self, context): + camera = context.camera.plasma_camera + draw_camera_mode_props(self.layout, camera.camera_type, camera.settings) + + +class PlasmaCameraAttentionPanel(CameraButtonsPanel, bpy.types.Panel): + bl_label = "Point of Attention Tracking" + + def draw(self, context): + camera = context.camera.plasma_camera + draw_camera_poa_props(self.layout, camera.camera_type, camera.settings) + + +class PlasmaCameraPositionPanel(CameraButtonsPanel, bpy.types.Panel): + bl_label = "Position Tracking" + + def draw(self, context): + camera = context.camera.plasma_camera + draw_camera_pos_props(self.layout, camera.camera_type, camera.settings) + + +class PlasmaCameraCirclePanel(CameraButtonsPanel, bpy.types.Panel): + bl_label = "Circle Camera" + + def draw(self, context): + camera = context.camera.plasma_camera + draw_circle_camera_props(self.layout, camera.settings) + + @classmethod + def poll(cls, context): + return super().poll(context) and context.camera.plasma_camera.camera_type == "circle" + + +class PlasmaCameraAnimationPanel(CameraButtonsPanel, bpy.types.Panel): + bl_label = "Camera Animation" + bl_options = {"DEFAULT_CLOSED"} + + def draw(self, context): + layout = self.layout + camera = context.camera.plasma_camera + props = camera.settings + + split = layout.split() + col = split.column() + col.label("Animation:") + col.active = props.anim_enabled and any(helpers.fetch_fcurves(context.object)) + col.prop(props, "start_on_push") + col.prop(props, "stop_on_pop") + col.prop(props, "reset_on_pop") + + col = split.column() + col.active = camera.camera_type == "rail" + col.label("Rail:") + col.prop(props, "rail_pos", text="") + + def draw_header(self, context): + self.layout.active = any(helpers.fetch_fcurves(context.object)) + self.layout.prop(context.camera.plasma_camera.settings, "anim_enabled", text="") + + +class PlasmaCameraViewPanel(CameraButtonsPanel, bpy.types.Panel): + bl_label = "Camera Lens" + + def draw(self, context): + camera = context.camera.plasma_camera + draw_camera_manipulation_props(self.layout, camera.camera_type, camera.settings) + + +class TransitionListUI(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0): + if item.camera is None: + layout.label("[Default Transition]") + else: + layout.label(item.camera.name, icon="CAMERA_DATA") + layout.prop(item, "enabled", text="") + + +class PlasmaCameraTransitionPanel(CameraButtonsPanel, bpy.types.Panel): + bl_label = "Transitions" + + def draw(self, context): + pass