mirror of https://github.com/H-uru/korman.git
13 changed files with 1032 additions and 38 deletions
@ -0,0 +1,225 @@
|
||||
# This file is part of Korman. |
||||
# |
||||
# Korman is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# Korman is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with Korman. If not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
import bpy |
||||
import math |
||||
from PyHSPlasma import * |
||||
import weakref |
||||
|
||||
from .explosions import * |
||||
from .. import helpers |
||||
from . import utils |
||||
|
||||
class CameraConverter: |
||||
def __init__(self, exporter): |
||||
self._exporter = weakref.ref(exporter) |
||||
|
||||
def _convert_brain(self, so, bo, camera_props, brain): |
||||
trans_props = camera_props.transition |
||||
|
||||
brain.poaOffset = hsVector3(*camera_props.poa_offset) |
||||
if camera_props.poa_type == "object": |
||||
brain.subject = self._mgr.find_create_key(plSceneObject, bl=camera_props.poa_object) |
||||
|
||||
brain.xPanLimit = camera_props.x_pan_angle / 2.0 |
||||
brain.zPanLimit = camera_props.y_pan_angle / 2.0 |
||||
brain.panSpeed = camera_props.pan_rate |
||||
if camera_props.limit_zoom: |
||||
brain.setFlags(plCameraBrain1.kZoomEnabled, True) |
||||
brain.zoomMax = camera_props.zoom_max * (4.0 / 3.0) |
||||
brain.zoomMin = camera_props.zoom_min * (4.0 / 3.0) |
||||
brain.zoomRate = camera_props.zoom_rate |
||||
|
||||
brain.acceleration = trans_props.pos_acceleration |
||||
brain.deceleration = trans_props.pos_deceleration |
||||
brain.velocity = trans_props.pos_velocity |
||||
brain.poaAcceleration = trans_props.poa_acceleration |
||||
brain.poaDeceleration = trans_props.poa_deceleration |
||||
brain.poaVelocity = trans_props.poa_velocity |
||||
|
||||
if isinstance(brain, plCameraBrain1_Avatar): |
||||
brain.setFlags(plCameraBrain1.kCutPos, trans_props.pos_cut) |
||||
brain.setFlags(plCameraBrain1.kCutPOA, trans_props.poa_cut) |
||||
else: |
||||
brain.setFlags(plCameraBrain1.kCutPos, True) |
||||
brain.setFlags(plCameraBrain1.kCutPOA, True) |
||||
if camera_props.poa_type == "avatar": |
||||
brain.setFlags(plCameraBrain1.kFollowLocalAvatar, True) |
||||
if camera_props.maintain_los: |
||||
brain.setFlags(plCameraBrain1.kMaintainLOS, True) |
||||
if camera_props.poa_worldspace: |
||||
brain.setFlags(plCameraBrain1.kWorldspacePOA, True) |
||||
if camera_props.pos_worldspace: |
||||
brain.setFlags(plCameraBrain1.kWorldspacePos, True) |
||||
if camera_props.ignore_subworld: |
||||
brain.setFlags(plCameraBrain1.kIgnoreSubworldMovement, True) |
||||
if camera_props.fall_vertical: |
||||
brain.setFlags(plCameraBrain1.kVerticalWhenFalling, True) |
||||
if camera_props.fast_run: |
||||
brain.setFlags(plCameraBrain1.kSpeedUpWhenRunning, True) |
||||
|
||||
def export_camera(self, so, bo, camera_type, camera_props, camera_trans=[]): |
||||
brain = getattr(self, "_export_{}_camera".format(camera_type))(so, bo, camera_props) |
||||
mod = self._export_camera_modifier(so, bo, camera_props, camera_trans) |
||||
mod.brain = brain.key |
||||
|
||||
def _export_camera_modifier(self, so, bo, props, trans): |
||||
mod = self._mgr.find_create_object(plCameraModifier, so=so) |
||||
|
||||
# PlasmaMAX allows the user to specify the horizontal OR vertical FOV, but not both. |
||||
# We only allow setting horizontal FOV (how often do you look up?), however. |
||||
# Plasma assumes 4:3 aspect ratio.. |
||||
fov = props.fov |
||||
mod.fovW, mod.fovH = math.degrees(fov), math.degrees(fov * (3.0 / 4.0)) |
||||
|
||||
# This junk is only valid for animated cameras... |
||||
# Animation exporter should have already run by this point :) |
||||
if mod.animated: |
||||
mod.startAnimOnPush = props.start_on_push |
||||
mod.stopAnimOnPop = props.stop_on_pop |
||||
mod.resetAnimOnPop = props.reset_on_pop |
||||
|
||||
for manual_trans in trans: |
||||
if not manual_trans.enabled or manual_trans.mode == "auto": |
||||
continue |
||||
cam_trans = plCameraModifier.CamTrans() |
||||
if manual_trans.camera: |
||||
cam_trans.transTo = self._mgr.find_create_key(plCameraModifier, bl=manual_trans.camera) |
||||
cam_trans.ignore = manual_trans.mode == "ignore" |
||||
|
||||
trans_info = manual_trans.transition |
||||
cam_trans.cutPos = trans_info.pos_cut |
||||
cam_trans.cutPOA = trans_info.poa_cut |
||||
cam_trans.accel = trans_info.pos_acceleration |
||||
cam_trans.decel = trans_info.pos_deceleration |
||||
cam_trans.velocity = trans_info.pos_velocity |
||||
cam_trans.poaAccel = trans_info.poa_acceleration |
||||
cam_trans.poaDecel = trans_info.poa_deceleration |
||||
cam_trans.poaVelocity = trans_info.poa_velocity |
||||
mod.addTrans(cam_trans) |
||||
|
||||
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): |
||||
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 |
||||
|
||||
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(*props.pos_offset) |
||||
return brain |
||||
|
||||
def _export_rail_camera(self, so, bo, props): |
||||
brain = self._mgr.find_create_object(plCameraBrain1_Fixed, so=so) |
||||
self._convert_brain(so, bo, props, brain) |
||||
|
||||
rail = self._mgr.find_create_object(plRailCameraMod, so=so) |
||||
rail.followFlags |= plLineFollowMod.kForceToLine |
||||
if props.poa_type == "object": |
||||
rail.followMode = plLineFollowMod.kFollowObject |
||||
rail.refObj = self._mgr.find_create_key(plSceneObject, bl=props.poa_object) |
||||
else: |
||||
rail.followMode = plLineFollowMod.kFollowLocalAvatar |
||||
if bo.parent: |
||||
rail.pathParent = self._mgr.find_create_key(plSceneObject, bl=bo.parent) |
||||
|
||||
# The rail is defined by a position controller in Plasma. Cyan uses a separate |
||||
# path object, but it makes more sense to me to just animate the camera with |
||||
# the details of the path... |
||||
pos_fcurves = tuple(i for i in helpers.fetch_fcurves(bo, False) if i.data_path == "location") |
||||
pos_ctrl = self._exporter().animation.convert_transform_controller(pos_fcurves, bo.matrix_basis) |
||||
if pos_ctrl is None: |
||||
raise ExportError("'{}': Rail Camera lacks appropriate rail keyframes".format(bo.name)) |
||||
path = plAnimPath() |
||||
path.controller = pos_ctrl |
||||
path.affineParts = utils.affine_parts(bo.matrix_local) |
||||
begin, end = bo.animation_data.action.frame_range |
||||
for fcurve in pos_fcurves: |
||||
f1, f2 = fcurve.evaluate(begin), fcurve.evaluate(end) |
||||
if abs(f1 - f2) > 0.001: |
||||
break |
||||
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 |
||||
|
||||
return brain |
||||
|
||||
@property |
||||
def _mgr(self): |
||||
return self._exporter().mgr |
||||
|
||||
@property |
||||
def _report(self): |
||||
return self._exporter().report |
@ -0,0 +1,246 @@
|
||||
# This file is part of Korman. |
||||
# |
||||
# Korman is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# Korman is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with Korman. If not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
import bpy |
||||
from bpy.props import * |
||||
import math |
||||
|
||||
from .. import idprops |
||||
|
||||
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"), |
||||
("rail", "Rail Camera", "The camera follows an object by moving along a line")] |
||||
|
||||
class PlasmaTransition(bpy.types.PropertyGroup): |
||||
poa_acceleration = FloatProperty(name="PoA Acceleration", |
||||
description="Rate the camera's Point of Attention tracking velocity increases in feet per second squared", |
||||
min=-100.0, max=100.0, precision=0, default=60.0, |
||||
unit="ACCELERATION", options=set()) |
||||
poa_deceleration = FloatProperty(name="PoA Deceleration", |
||||
description="Rate the camera's Point of Attention tracking velocity decreases in feet per second squared", |
||||
min=-100.0, max=100.0, precision=0, default=60.0, |
||||
unit="ACCELERATION", options=set()) |
||||
poa_velocity = FloatProperty(name="PoA Velocity", |
||||
description="Maximum velocity of the camera's Point of Attention tracking", |
||||
min=-100.0, max=100.0, precision=0, default=60.0, |
||||
unit="VELOCITY", options=set()) |
||||
poa_cut = BoolProperty(name="Cut", |
||||
description="The camera immediately begins tracking the Point of Attention", |
||||
options=set()) |
||||
|
||||
pos_acceleration = FloatProperty(name="Position Acceleration", |
||||
description="Rate the camera's positional velocity increases in feet per second squared", |
||||
min=-100.0, max=100.0, precision=0, default=60.0, |
||||
unit="ACCELERATION", options=set()) |
||||
pos_deceleration = FloatProperty(name="Position Deceleration", |
||||
description="Rate the camera's positional velocity decreases in feet per second squared", |
||||
min=-100.0, max=100.0, precision=0, default=60.0, |
||||
unit="ACCELERATION", options=set()) |
||||
pos_velocity = FloatProperty(name="Position Max Velocity", |
||||
description="Maximum positional velocity of the camera", |
||||
min=-100.0, max=100.0, precision=0, default=60.0, |
||||
unit="VELOCITY", options=set()) |
||||
pos_cut = BoolProperty(name="Cut", |
||||
description="The camera immediately moves to its new position", |
||||
options=set()) |
||||
|
||||
|
||||
class PlasmaManualTransition(bpy.types.PropertyGroup): |
||||
camera = PointerProperty(name="Camera", |
||||
description="The camera from which this transition is intended", |
||||
type=bpy.types.Object, |
||||
poll=idprops.poll_camera_objects, |
||||
options=set()) |
||||
transition = PointerProperty(type=PlasmaTransition, options=set()) |
||||
mode = EnumProperty(name="Transition Mode", |
||||
description="Type of transition that should occur between the two cameras", |
||||
items=[("ignore", "Ignore Camera", "Ignore this camera and do not transition"), |
||||
("auto", "Auto", "Auto transition as defined by the two cameras' properies"), |
||||
("manual", "Manual", "Manually defined transition")], |
||||
default="auto", |
||||
options=set()) |
||||
enabled = BoolProperty(name="Enabled", |
||||
description="Export this transition", |
||||
default=True, |
||||
options=set()) |
||||
|
||||
|
||||
class PlasmaCameraProperties(bpy.types.PropertyGroup): |
||||
# Point of Attention |
||||
poa_type = EnumProperty(name="Point of Attention", |
||||
description="The point of attention that this camera tracks", |
||||
items=[("avatar", "Track Local Player", "Camera tracks the player's avatar"), |
||||
("object", "Track Object", "Camera tracks an object in the scene"), |
||||
("none", "Don't Track", "Camera does not track anything")], |
||||
options=set()) |
||||
poa_object = PointerProperty(name="PoA Object", |
||||
description="Object the camera should track as its Point of Attention", |
||||
type=bpy.types.Object, |
||||
options=set()) |
||||
poa_offset = FloatVectorProperty(name="PoA Offset", |
||||
description="Offset from the point of attention's origin to track", |
||||
soft_min=-50.0, soft_max=50.0, |
||||
size=3, default=(0.0, 0.0, 3.0), |
||||
options=set()) |
||||
poa_worldspace = BoolProperty(name="Worldspace Offset", |
||||
description="Point of Attention Offset is in worldspace coordinates", |
||||
options=set()) |
||||
|
||||
# Position Offset |
||||
pos_offset = FloatVectorProperty(name="Position Offset", |
||||
description="Offset the camera's position", |
||||
soft_min=-50.0, soft_max=50.0, |
||||
size=3, default=(0.0, 10.0, 3.0), |
||||
options=set()) |
||||
pos_worldspace = BoolProperty(name="Worldspace Offset", |
||||
description="Position offset is in worldspace coordinates", |
||||
options=set()) |
||||
|
||||
# Default Transition |
||||
transition = PointerProperty(type=PlasmaTransition, options=set()) |
||||
|
||||
# Limit Panning |
||||
x_pan_angle = FloatProperty(name="X Degrees", |
||||
description="Maximum camera pan angle in the X direction", |
||||
min=0.0, max=math.radians(180.0), precision=0, default=math.radians(90.0), |
||||
subtype="ANGLE", options=set()) |
||||
y_pan_angle = FloatProperty(name="Y Degrees", |
||||
description="Maximum camera pan angle in the Y direction", |
||||
min=0.0, max=math.radians(180.0), precision=0, default=math.radians(90.0), |
||||
subtype="ANGLE", options=set()) |
||||
pan_rate = FloatProperty(name="Pan Velocity", |
||||
description="", |
||||
min=0.0, precision=1, default=50.0, |
||||
unit="VELOCITY", options=set()) |
||||
|
||||
# Zooming |
||||
fov = FloatProperty(name="Default FOV", |
||||
description="Horizontal Field of View angle", |
||||
min=0.0, max=math.radians(180.0), precision=0, default=math.radians(70.0), |
||||
subtype="ANGLE") |
||||
limit_zoom = BoolProperty(name="Limit Zoom", |
||||
description="The camera allows zooming per artist limitations", |
||||
options=set()) |
||||
zoom_max = FloatProperty(name="Max FOV", |
||||
description="Maximum camera FOV when zooming", |
||||
min=0.0, max=math.radians(180.0), precision=0, default=math.radians(120.0), |
||||
subtype="ANGLE", options=set()) |
||||
zoom_min = FloatProperty(name="Min FOV", |
||||
description="Minimum camera FOV when zooming", |
||||
min=0.0, max=math.radians(180.0), precision=0, default=math.radians(35.0), |
||||
subtype="ANGLE", options=set()) |
||||
zoom_rate = FloatProperty(name="Zoom Velocity", |
||||
description="Velocity of the camera's zoom in degrees per second", |
||||
min=0.0, max=180.0, precision=0, default=90.0, |
||||
unit="VELOCITY", options=set()) |
||||
|
||||
# Miscellaneous Movement Props |
||||
maintain_los = BoolProperty(name="Maintain LOS", |
||||
description="The camera should maintain line-of-sight with the object it's tracking", |
||||
options=set()) |
||||
fall_vertical = BoolProperty(name="Fall Camera", |
||||
description="The camera will orient itself vertically when the local player begins falling", |
||||
options=set()) |
||||
fast_run = BoolProperty(name="Faster When Falling", |
||||
description="The camera's velocity will have a floor when the local player is falling", |
||||
options=set()) |
||||
ignore_subworld = BoolProperty(name="Ignore Subworld Movement", |
||||
description="The camera will not be parented to any subworlds", |
||||
options=set()) |
||||
|
||||
# Core Type Properties |
||||
primary_camera = BoolProperty(name="Primary Camera", |
||||
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 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", |
||||
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"}) |
||||
|
||||
# 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 |
||||
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),) |
||||
return set() |
||||
|
||||
|
||||
class PlasmaCamera(bpy.types.PropertyGroup): |
||||
camera_type = EnumProperty(name="Camera Type", |
||||
description="", |
||||
items=camera_types, |
||||
options=set()) |
||||
settings = PointerProperty(type=PlasmaCameraProperties, options=set()) |
||||
transitions = CollectionProperty(type=PlasmaManualTransition, |
||||
name="Transitions", |
||||
description="", |
||||
options=set()) |
||||
active_transition_index = IntProperty(options={"HIDDEN"}) |
@ -0,0 +1,272 @@
|
||||
# This file is part of Korman. |
||||
# |
||||
# Korman is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# Korman is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with Korman. If not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
import bpy |
||||
|
||||
from .. import helpers |
||||
from . import ui_list |
||||
|
||||
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") |
||||
|
||||
# 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() |
||||
col.label("Camera Mode:") |
||||
col = col.column() |
||||
col.alert = cam_type != "fixed" and props.poa_type == "none" |
||||
col.prop(props, "poa_type", text="") |
||||
col.alert = False |
||||
row = col.row() |
||||
row.active = props.poa_type == "object" |
||||
row.prop(props, "poa_object", text="") |
||||
col.separator() |
||||
col.prop(props, "primary_camera") |
||||
|
||||
# Miscellaneous |
||||
col = split.column() |
||||
col.label("Tracking Settings:") |
||||
col.prop(props, "maintain_los") |
||||
col.prop(props, "fall_vertical") |
||||
col.prop(props, "fast_run") |
||||
col.prop(props, "ignore_subworld") |
||||
|
||||
def draw_camera_poa_props(layout, cam_type, props): |
||||
trans = props.transition |
||||
|
||||
# PoA Tracking |
||||
split = layout.split() |
||||
col = split.column() |
||||
col.label("Default Tracking Transition:") |
||||
col.prop(trans, "poa_acceleration", text="Acceleration") |
||||
col.prop(trans, "poa_deceleration", text="Deceleration") |
||||
col.prop(trans, "poa_velocity", text="Maximum Velocity") |
||||
col = col.column() |
||||
col.active = cam_type == "follow" |
||||
col.prop(trans, "poa_cut", text="Cut Animation") |
||||
|
||||
# PoA Offset |
||||
col = split.column() |
||||
col.label("Point of Attention Offset:") |
||||
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) |
||||
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_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 = col.column() |
||||
col.active = cam_type == "follow" |
||||
col.prop(trans, "pos_cut", text="Cut Animation") |
||||
|
||||
# Position Offsets |
||||
col = split.column() |
||||
col.active = cam_type == "follow" |
||||
col.label("Position Offset:") |
||||
col.prop(props, "pos_offset", text="") |
||||
col.prop(props, "pos_worldspace") |
||||
|
||||
def draw_circle_camera_props(layout, props): |
||||
# Circle Camera Stuff |
||||
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") |
||||
|
||||
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): |
||||
layout = self.layout |
||||
camera = context.camera.plasma_camera |
||||
|
||||
ui_list.draw_list(layout, "TransitionListUI", "camera", camera, "transitions", |
||||
"active_transition_index", rows=3, maxrows=4) |
||||
|
||||
try: |
||||
item = camera.transitions[camera.active_transition_index] |
||||
trans = item.transition |
||||
except: |
||||
pass |
||||
else: |
||||
layout.separator() |
||||
box = layout.box() |
||||
box.prop(item, "camera", text="Transition From") |
||||
box.prop(item, "mode") |
||||
|
||||
box.separator() |
||||
split = box.split() |
||||
split.active = item.mode == "manual" |
||||
|
||||
col = split.column() |
||||
col.label("Tracking Transition:") |
||||
col.prop(trans, "poa_acceleration", text="Acceleration") |
||||
col.prop(trans, "poa_deceleration", text="Deceleration") |
||||
col.prop(trans, "poa_velocity", text="Maximum Velocity") |
||||
col.prop(trans, "poa_cut", text="Cut Transition") |
||||
|
||||
col = split.column() |
||||
col.label("Position Transition:") |
||||
col.prop(trans, "pos_acceleration", text="Acceleration") |
||||
col.prop(trans, "pos_deceleration", text="Deceleration") |
||||
col.prop(trans, "pos_velocity", text="Maximum Velocity") |
||||
col.prop(trans, "pos_cut", text="Cut Transition") |
Loading…
Reference in new issue