You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
4.3 KiB
184 lines
4.3 KiB
"""
|
|
|
|
Script DAT Callbacks
|
|
|
|
me - this DAT
|
|
|
|
scriptOp - the OP which is cooking
|
|
|
|
"""
|
|
|
|
|
|
import math
|
|
|
|
|
|
def onCook(scriptOp):
|
|
|
|
"""
|
|
Called when the Script DAT needs to cook.
|
|
|
|
"""
|
|
|
|
scriptOp.clear()
|
|
scriptOp.appendRow(['id', 'P(X)', 'P(Y)', 'P(Z)', 'Rot(x)', 'Rot(y)', 'Rot(z)'])
|
|
|
|
|
|
|
|
# 1. Fetch Constants
|
|
const_op = op('constant_camera')
|
|
camera_y_offset = 0
|
|
camera_y_far = 0
|
|
|
|
|
|
|
|
# Default rotation and timing values
|
|
rx, ry, rz = 0.0, 0.0, 0.0
|
|
duration_per_step = 3.0
|
|
|
|
|
|
|
|
if const_op:
|
|
|
|
# Distance Offsets
|
|
target_chan = const_op.chan('camera_distance_y')
|
|
if target_chan is not None:
|
|
camera_y_offset = target_chan.eval()
|
|
|
|
|
|
|
|
far_chan = const_op.chan('camera_distance_y_far')
|
|
if far_chan is not None:
|
|
camera_y_far = far_chan.eval()
|
|
|
|
|
|
|
|
# Rotation Values
|
|
|
|
rx_chan = const_op.chan('rot_x')
|
|
ry_chan = const_op.chan('rot_y')
|
|
rz_chan = const_op.chan('rot_z')
|
|
|
|
if rx_chan is not None: rx = rx_chan.eval()
|
|
if ry_chan is not None: ry = ry_chan.eval()
|
|
if rz_chan is not None: rz = rz_chan.eval()
|
|
|
|
|
|
|
|
# Duration / Speed
|
|
|
|
dur_chan = const_op.chan('duration_per_seconds')
|
|
if dur_chan is not None:
|
|
duration_per_step = max(0.1, dur_chan.eval()) # Prevent division by zero
|
|
|
|
|
|
|
|
# 2. Check for specific ID selection
|
|
|
|
const_select = op('SelectId')
|
|
select_id = -1
|
|
|
|
if const_select:
|
|
select_chan = const_select.chan('id')
|
|
if select_chan is not None:
|
|
select_id = int(select_chan.eval())
|
|
|
|
|
|
|
|
# 3. Input Validation
|
|
|
|
if len(scriptOp.inputs) == 0:
|
|
return
|
|
|
|
|
|
|
|
inputDat = scriptOp.inputs[0]
|
|
|
|
|
|
|
|
# CASE A: Tracking a specific single ID
|
|
|
|
if select_id >= 0:
|
|
# Ensure the target CHOP exists
|
|
target_path = 'grid_text' + str(select_id + 1) + '/OUT'
|
|
out = op(target_path)
|
|
rx=0
|
|
ry=0
|
|
rz=0
|
|
|
|
|
|
|
|
if out:
|
|
try:
|
|
px_val = out['px'].eval()
|
|
py_val = out['py'].eval()
|
|
pz_val = out['pz'].eval() - camera_y_offset
|
|
scriptOp.appendRow([select_id, px_val, py_val, pz_val, rx, ry, rz])
|
|
except:
|
|
# Handle cases where channels might be missing
|
|
pass
|
|
return
|
|
|
|
# CASE B: Panning sequence (Average -> P1 -> P2 -> P3 -> P4)
|
|
else:
|
|
raw_points = []
|
|
sum_x, sum_y, sum_z = 0.0, 0.0, 0.0
|
|
|
|
# Gather up to 4 points from the input table
|
|
for i in range(1, min(5, inputDat.numRows)):
|
|
try:
|
|
px = float(inputDat[i, 1] or 0)
|
|
py = float(inputDat[i, 2] or 0)
|
|
pz = float(inputDat[i, 3] or 0)
|
|
raw_points.append((px, py, pz))
|
|
|
|
sum_x += px
|
|
sum_y += py
|
|
sum_z += pz
|
|
|
|
except (ValueError, TypeError):
|
|
continue
|
|
|
|
|
|
|
|
if not raw_points:
|
|
return
|
|
|
|
|
|
|
|
# Calculate Average Position
|
|
num_raw = len(raw_points)
|
|
avg_pos = (sum_x / num_raw, sum_y / num_raw, sum_z / num_raw)
|
|
|
|
# Build the sequence: [Average, P1, P2, P3, P4]
|
|
sequence = [avg_pos] + raw_points
|
|
num_steps = len(sequence)
|
|
|
|
if num_steps == 1:
|
|
p = sequence[0]
|
|
scriptOp.appendRow(['average_only', p[0], p[1], p[2] - camera_y_far, rx, ry, rz])
|
|
else:
|
|
total_time = absTime.seconds
|
|
|
|
# Calculate current segment and interpolation factor
|
|
progress = (total_time / duration_per_step) % num_steps
|
|
idx1 = int(progress)
|
|
idx2 = (idx1 + 1) % num_steps
|
|
alpha = progress - idx1
|
|
# Smooth transition (Cosine ease)
|
|
alpha = (1 - math.cos(alpha * math.pi)) / 2
|
|
# Perform Linear Interpolation
|
|
p1 = sequence[idx1]
|
|
p2 = sequence[idx2]
|
|
|
|
ax = p1[0] + (p2[0] - p1[0]) * alpha
|
|
ay = p1[1] + (p2[1] - p1[1]) * alpha
|
|
az = p1[2] + (p2[2] - p1[2]) * alpha
|
|
label = "avg" if (idx2 == 0) else str(idx2)
|
|
scriptOp.appendRow([label, ax, ay, az - camera_y_far, rx, ry, rz])
|
|
return
|
|
|
|
|
|
|
|
def onGetCookLevel(scriptOp):
|
|
return CookLevel.AUTOMATIC
|
|
|
|
|