diff --git a/korman/exporter/camera.py b/korman/exporter/camera.py index 7524e70..7efffb6 100644 --- a/korman/exporter/camera.py +++ b/korman/exporter/camera.py @@ -28,8 +28,6 @@ class CameraConverter: trans_props = camera_props.transition brain.poaOffset = hsVector3(*camera_props.poa_offset) - if isinstance(brain, plCameraBrain1_Avatar): - brain.offset = hsVector3(*camera_props.pos_offset) if camera_props.poa_type == "object": brain.subject = self._mgr.find_create_key(plSceneObject, bl=camera_props.poa_object) @@ -84,6 +82,55 @@ class CameraConverter: # TODO: do we need to do something about animations here? return mod + def _export_circle_camera(self, so, bo, props): + brain = self._mgr.find_create_object(plCameraBrain1_Circle, so=so) + self._convert_brain(so, bo, props, brain) + + # Circle Camera specific stuff ahoy! + if props.poa_type == "avatar": + brain.circleFlags |= plCameraBrain1_Circle.kCircleLocalAvatar + elif props.poa_type == "object": + brain.poaObject = self._mgr.find_create_key(plSceneObject, bl=props.poa_object) + else: + self._report.warn("Circle Camera '{}' has no Point of Attention. Is this intended?", bo.name, indent=3) + if props.circle_pos == "farthest": + brain.circleFlags |= plCameraBrain1_Circle.kFarthest + + # If no center object is specified, we will use the camera object's location. + # We will use a simple vector for this object to make life simpler, however, + # specifying an actual center allows you to do interesting things like animate the center... + # Fascinating! Therefore, we will expose the Plasma Object... + if props.circle_center is None: + brain.center = hsVector3(*bo.location) + else: + brain.centerObject = self._mgr.find_create_key(plSceneObject, bl=props.circle_center) + # This flag has no effect in CWE, but I'm using it for correctness' sake + brain.circleFlags |= plCameraBrain1_Circle.kHasCenterObject + + # PlasmaMAX forces these values so the circle camera is rather slow, which is somewhat + # sensible to me. Fast circular motion seems like a poor idea to me... If we want these + # values to be customizable, we probably want add some kind of range limitation or at least + # a flashing red light in the UI... + brain.acceleration = 10.0 + brain.deceleration = 10.0 + brain.velocity = 15.0 + + # Related to above, circle cameras use a slightly different velocity method. + # This is stored in Plasma as a fraction of the circle's circumference. It makes + # more sense to me to present it in terms of degrees per second, however. + # NOTE that Blender returns radians!!! + brain.cirPerSec = props.circle_velocity / (2 * math.pi) + + # I consider this clever... If we have a center object, we use the magnitude of the displacement + # of the camera's object to the center object (at frame 0) to determine the radius. If no center + # object is specified, we allow the user to specify the radius. + # Well, it's clever until you realize it's the same thing Cyan did in PlasmaMAX... But it's harder + # here because Blendsucks. + brain.radius = props.get_circle_radius(bo) + + # Done already? + return brain + def _export_fixed_camera(self, so, bo, props): brain = self._mgr.find_create_object(plCameraBrain1_Fixed, so=so) self._convert_brain(so, bo, props, brain) @@ -93,6 +140,9 @@ class CameraConverter: def _export_follow_camera(self, so, bo, props): brain = self._mgr.find_create_object(plCameraBrain1_Avatar, so=so) self._convert_brain(so, bo, props, brain) + + # Follow camera specific stuff ahoy! + brain.offset = hsVector3(*camera_props.pos_offset) return brain @property diff --git a/korman/properties/modifiers/region.py b/korman/properties/modifiers/region.py index dfff86a..8d3d6c7 100644 --- a/korman/properties/modifiers/region.py +++ b/korman/properties/modifiers/region.py @@ -69,7 +69,8 @@ class PlasmaCameraRegion(PlasmaModifierProperties): camera_type = EnumProperty(name="Camera Type", description="What kind of camera should be used?", - items=[("auto", "Auto Follow Camera", "Automatically generated camera"), + items=[("auto_follow", "Auto Follow Camera", "Automatically generated follow camera"), + ("auto_circle", "Auto Circle Camera", "Automatically generated circle camera"), ("manual", "Manual Camera", "User specified camera object")], options=set()) camera_object = PointerProperty(name="Camera", @@ -86,9 +87,12 @@ class PlasmaCameraRegion(PlasmaModifierProperties): camera_so_key = exporter.mgr.find_create_key(plSceneObject, bl=self.camera_object) camera_props = self.camera_object.data.plasma_camera.settings else: + assert self.camera_type[:4] == "auto" + # Wheedoggy! We get to export the doggone camera now. camera_props = self.auto_camera - exporter.camera.export_camera(so, bo, "follow", camera_props) + camera_type = self.camera_type[5:] + exporter.camera.export_camera(so, bo, camera_type, camera_props) # Setup physical stuff phys_mod = bo.plasma_modifiers.collision diff --git a/korman/properties/prop_camera.py b/korman/properties/prop_camera.py index fd5b7fe..d637ce3 100644 --- a/korman/properties/prop_camera.py +++ b/korman/properties/prop_camera.py @@ -17,8 +17,9 @@ import bpy from bpy.props import * import math -camera_types = [("follow", "Follow Camera", "Camera follows an object."), - ("fixed", "Fixed Camera", "Camera is fixed in one location.")] +camera_types = [("circle", "Circle Camera", "The camera circles a fixed point"), + ("follow", "Follow Camera", "The camera follows an object"), + ("fixed", "Fixed Camera", "The camera is fixed in one location")] class PlasmaTransition(bpy.types.PropertyGroup): poa_acceleration = FloatProperty(name="PoA Acceleration", @@ -142,6 +143,43 @@ class PlasmaCameraProperties(bpy.types.PropertyGroup): description="The camera should be considered the Age's primary camera.", options=set()) + # Cricle Camera + def _get_circle_radius(self): + # This is coming from the UI, so we need to get the active object from + # Blender's context and pass that on to the actual getter. + return self.get_circle_radius(bpy.context.object) + def _set_circle_radius(self, value): + # Don't really care about error checking... + self.circle_radius_value = value + + circle_center = PointerProperty(name="Center", + description="Center of the circle camera's orbit", + type=bpy.types.Object, + 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")], + options=set()) + circle_velocity = FloatProperty(name="Velocity", + description="Velocity of the circle camera in degrees per second", + min=0.0, max=math.radians(360.0), precision=0, default=math.radians(36.0), + subtype="ANGLE", options=set()) + circle_radius_ui = FloatProperty(name="Radius", + description="Radius at which the circle camera should orbit the Point of Attention", + min=0.0, get=_get_circle_radius, set=_set_circle_radius, options=set()) + circle_radius_value = FloatProperty(name="INTERNAL: Radius", + description="Radius at which the circle camera should orbit the Point of Attention", + min=0.0, default=8.5, options={"HIDDEN"}) + + 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 + if self.circle_center is not None: + vec = bo.location - self.circle_center.location + return vec.magnitude + return self.circle_radius_value + def harvest_actors(self): if self.poa_type == "object": return set((self.poa_object.name),) diff --git a/korman/ui/ui_camera.py b/korman/ui/ui_camera.py index 43a3e71..cf74f6d 100644 --- a/korman/ui/ui_camera.py +++ b/korman/ui/ui_camera.py @@ -59,7 +59,7 @@ def draw_camera_properties(cam_type, props, layout, context): col = split.column() col.label("Camera Mode:") col = col.column() - col.alert = cam_type == "follow" and props.poa_type == "none" + col.alert = cam_type != "fixed" and props.poa_type == "none" col.prop(props, "poa_type", text="") col.alert = False row = col.row() @@ -98,6 +98,7 @@ def draw_camera_properties(cam_type, props, layout, context): col = split.column() # Position Transitions + col.active = cam_type != "circle" col.label("Default Position Transition:") col.prop(trans, "pos_acceleration", text="Acceleration") col.prop(trans, "pos_deceleration", text="Deceleration") @@ -126,3 +127,16 @@ def draw_camera_properties(cam_type, props, layout, context): _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") + + # 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) + row.active = props.circle_center is None + row.prop(props, "circle_radius_ui")