最简单的矩阵对齐,
m = matrix3 1
-- 应用旋转
m = rotate m (at time timeValue in coordsys world ($'Bip001 Prop1'.rotation))
-- 应用位置
m = translate m (at time timeValue ($'Bip001 Prop1'.pos ))
-- 应用到武器
targetObj.transform = m
精确版的矩阵对齐
python
-- 1. 获取sourceBip的位置(矩阵第4列是位置信息)
local sourcePos = sourceBip.transform[4] -- 等价于 sourceBip.pos
-- 2. 获取sourceBip的X轴方向(矩阵第1列)
local sourceX = sourceBip.transform[1] -- X轴方向向量
-- 3. 构建目标旋转矩阵(Z轴对齐sourceX)
local targetZ = normalize sourceX -- 目标Z轴 = sourceBip的X轴
local worldUp = [0,1,0] -- 参考上方向(可改为sourceBip的Y轴)
local targetX = normalize (cross worldUp targetZ) -- 计算X轴(与Z轴正交)
local targetY = cross targetZ targetX -- 计算Y轴(确保三轴正交)
-- 4. 构建完整变换矩阵(包含旋转+位移)
-- 矩阵结构:[X轴, Y轴, Z轴, 位置]
local finalMatrix = matrix3 targetX targetY targetZ sourcePos
-- 5. 应用矩阵到目标对象
targetObj.transform = finalMatrix
麻烦帮解释下,如何做到targetObj的z轴对齐sourceBip的x轴的?
这段代码通过矩阵变换实现了让targetObj
的 Z 轴与sourceBip
的 X 轴对齐,核心原理如下:
获取源对象的 X 轴方向
python
local sourceX = sourceBip.transform[1] -- X轴方向向量
这里从sourceBip
的变换矩阵中提取了第一列,这列数据代表了sourceBip
在世界空间中的 X 轴方向向量
将源 X 轴设为目标 Z 轴
local targetZ = normalize sourceX -- 目标Z轴 = sourceBip的X轴
直接将sourceBip
的 X 轴方向向量归一化后,作为targetObj
的 Z 轴方向向量,这一步是实现对齐的关键
构建完整的正交坐标系
因为 3D 空间中的三个轴需要相互正交,所以需要基于已确定的 Z 轴计算出对应的 X 轴和 Y 轴:
local targetX = normalize (cross worldUp targetZ) -- 计算X轴(与Z轴正交)
local targetY = cross targetZ targetX -- 计算Y轴(确保三轴正交)
通过叉乘运算确保三个轴两两垂直,形成一个标准的三维坐标系
构建变换矩阵并应用
最后将计算出的三个轴向量和源对象的位置组合成变换矩阵,应用到目标对象:
local finalMatrix = matrix3 targetX targetY targetZ sourcePos
targetObj.transform = finalMatrix
通过这个过程,targetObj
的 Z 轴就精确地指向了sourceBip
的 X 轴方向,同时保持了自身坐标系的正交性。
要根据单选按钮的选择来决定targetObj
的哪个轴对齐到sourceBip
的 X 轴,你可以这样修改代码:
python
-- 获取单选按钮的选择状态(1=x, 2=y, 3=z)
local selectedAxis = ChangeAxis.selected
-- 1. 获取源对象的位置和X轴方向
local sourcePos = sourceBip.transform[4] -- 位置信息
local sourceX = normalize sourceBip.transform[1] -- 源对象X轴方向(归一化)
-- 2. 定义世界上方向向量
local worldUp = [0,1,0]
-- 3. 根据选择确定目标轴与源X轴对齐
local targetX, targetY, targetZ
case selectedAxis of
(
1: -- 目标X轴对齐源X轴
(
targetX = sourceX
targetZ = normalize (cross worldUp targetX)
targetY = cross targetZ targetX
)
2: -- 目标Y轴对齐源X轴
(
targetY = sourceX
targetX = normalize (cross targetY worldUp)
targetZ = cross targetX targetY
)
3: -- 目标Z轴对齐源X轴(原始逻辑)
(
targetZ = sourceX
targetX = normalize (cross worldUp targetZ)
targetY = cross targetZ targetX
)
)
-- 4. 构建完整的变换矩阵
local finalMatrix = matrix3 targetX targetY targetZ sourcePos
-- 5. 应用变换矩阵到目标对象
targetObj.transform = finalMatrix
加入缩放
python
-- 获取单选按钮状态
local selectedAxis = ChangeAxis.state
-- 1. 分解源对象的变换信息
local sourceTM = sourceBip.transform -- 完整变换矩阵
local sourcePos = sourceTM[4] -- 位置
local sourceScale = sourceBip.scale -- 缩放
-- 兼容旧版本:从矩阵中提取纯旋转部分(移除缩放影响)
local rotOnlyTM = sourceTM
-- 归一化矩阵的三个轴,消除缩放影响,得到纯旋转矩阵
rotOnlyTM[1] = normalize rotOnlyTM[1]
rotOnlyTM[2] = normalize rotOnlyTM[2]
rotOnlyTM[3] = normalize rotOnlyTM[3]
local sourceX = rotOnlyTM[1] -- 仅用旋转信息计算X轴方向
-- 2. 定义世界上方向
local worldUp = [0,1,0]
-- 3. 计算目标对象的轴向(仅处理旋转,不包含缩放)
local targetX, targetY, targetZ
case selectedAxis of
(
1: -- 目标X轴对齐源X轴
(
targetX = sourceX
targetZ = normalize (cross worldUp targetX)
targetY = cross targetZ targetX
)
2: -- 目标Y轴对齐源X轴
(
targetY = sourceX
targetX = normalize (cross targetY worldUp)
targetZ = cross targetX targetY
)
3: -- 目标Z轴对齐源X轴
(
targetZ = sourceX
targetX = normalize (cross worldUp targetZ)
targetY = cross targetZ targetX
)
)
-- 4. 构建包含正确缩放的变换矩阵
local rotMatrix = matrix3 targetX targetY targetZ [0,0,0]
local myscaleMatrix = scaleMatrix sourceScale -- 单独的缩放矩阵
local finalRotScale = rotMatrix * myscaleMatrix -- 旋转×缩放
finalRotScale[4] = sourcePos -- 设置位置
-- 5. 应用最终变换矩阵
targetObj.transform = finalRotScale
完整版
python
if(addghost!=undefined )do( DestroyDialog addghost )
rollout addghost "武器残影25.09.05" width:200 height:100 rolledup:false
(
-- UI控件定义
pickbutton 'pickobj' "拾取跟随骨骼" pos:[90,5] width:100 height:25 align:#center -- 按钮文本更明确
checkbox 'chk1' "创建dummy" pos:[5,5] width:80 height:26 tooltip:"翻转轴向."
-- 新增:偏移量控制
label 'offsetLabel' "偏移比例:" pos:[100,40] width:60 height:16
spinner 'spinBaseOffset' "" pos:[150,40] width:40 height:16 range:[-100000, 100000, 0.5] type:#float step:0.1
button 'CollapseMartixAlign' "打印矩阵约束" pos:[5,35] width:90 height:25 enabled:true
radiobuttons 'ChangeAxis' "" pos:[14,75] width:97 height:16 labels:#("x", "y","z") default:1 columns:3 align:#left
local weapons = #()
local lastPositions = #()
local weaponsname = #("weapon001", "weapon002", "weapon003", "weapon004")
fn followAndAlign targetObj sourceBip = (
if (targetObj != undefined and sourceBip != undefined) then (
-- 获取单选按钮状态
local selectedAxis = ChangeAxis.state
-- 1. 分解源对象的变换信息
local sourceTM = sourceBip.transform -- 完整变换矩阵
local sourcePos = sourceTM[4] -- 位置
local sourceScale = sourceBip.scale -- 缩放
-- 兼容旧版本:从矩阵中提取纯旋转部分(移除缩放影响)
local rotOnlyTM = sourceTM
-- 归一化矩阵的三个轴,消除缩放影响,得到纯旋转矩阵
rotOnlyTM[1] = normalize rotOnlyTM[1]
rotOnlyTM[2] = normalize rotOnlyTM[2]
rotOnlyTM[3] = normalize rotOnlyTM[3]
local sourceX = rotOnlyTM[1] -- 仅用旋转信息计算X轴方向
-- 2. 定义世界上方向
local worldUp = [0,1,0]
-- 3. 计算目标对象的轴向(仅处理旋转,不包含缩放)
local targetX, targetY, targetZ
case selectedAxis of
(
1: -- 目标X轴对齐源X轴
(
targetX = sourceX
targetZ = normalize (cross worldUp targetX)
targetY = cross targetZ targetX
)
2: -- 目标Y轴对齐源X轴
(
targetY = sourceX
targetX = normalize (cross targetY worldUp)
targetZ = cross targetX targetY
)
3: -- 目标Z轴对齐源X轴
(
targetZ = sourceX
targetX = normalize (cross worldUp targetZ)
targetY = cross targetZ targetX
)
)
-- 4. 构建包含正确缩放的变换矩阵
local rotMatrix = matrix3 targetX targetY targetZ [0,0,0]
local myscaleMatrix = scaleMatrix sourceScale -- 单独的缩放矩阵
local finalRotScale = rotMatrix * myscaleMatrix -- 旋转×缩放
finalRotScale[4] = sourcePos -- 设置位置
-- 5. 应用最终变换矩阵
targetObj.transform = finalRotScale
)
)
-- 计算当前帧与前一帧的位置差
fn getPositionDeltaAndDir sourceBip = (
if currentTime > animationrange.start then (
local currentPos = sourceBip.pos
local prevPos = at time (currentTime - 1) sourceBip.pos
local delta = currentPos - prevPos -- 位置差向量
local distance = length delta -- 距离
local dir = if distance > 0 then normalize delta else [0,0,0] -- 归一化方向向量
return [distance, dir.x,dir.y,dir.z] -- 返回[距离, 方向向量]
)else(
return [0.0,0.0,0.0,0.0] -- 第一帧返回默认值
)
)
-- 自动偏移函数:仅使用倍数偏移逻辑,结合物理位置差
fn autoOffsetWeapons sourceObj = (
local baseDist = spinBaseOffset.value -- 基础偏移值(用户设置)
local deltaData = getPositionDeltaAndDir sourceObj
local distance = deltaData[1]
local moveDir = [deltaData[2],deltaData[3],deltaData[4]] -- 归一化的移动方向向量
local index = 1 -- 武器序号
for weapon in weapons do (
if weapon != undefined do (
-- 计算偏移量:基础距离 × 序号 × 移动方向
local offsetVec = baseDist * index * moveDir
-- 获取源对象当前位置
local sourcePos = sourceObj.pos
-- 计算最终位置:源位置 + 偏移向量
local finalPos = sourcePos + offsetVec
-- 设置武器在当前帧的位置
at time currentTime (
weapon.pos = finalPos
)
index += 1
)
)
)
on pickobj picked obj do(
if (obj!=undefined) then
(
pickobj.text = obj.name
)
if (chk1.checked)then(
weapons = #() -- 清空现有列表
for weapon in weaponsname do (
if weapon != undefined then (
local dummyObj = Dummy name:weapon
dummyObj.pos = [0,0,0]
dummyObj.boxsize =[10,10,10]
appendIfUnique weapons dummyObj
)
)
)else(weapons = #($weapon001, $weapon002, $weapon003, $weapon004))
)
on pickobj rightclick do(
pickobj.text="拾取跟随骨骼"
weapons = #()
)
on CollapseMartixAlign pressed do(
local obj = getnodebyname pickobj.text
if obj == undefined then (
print "请先拾取跟随骨骼"
)
slidertime = animationrange.start
-- 创建动画并应用偏移
with redraw off(
set animate on
max tool animmode
for t = animationrange.start to animationrange.end do (
at time t (
for weapon in weapons do(
followAndAlign weapon obj
)
-- 自动应用倍数偏移
autoOffsetWeapons obj
)
)
set animate off
max tool animmode
)
)
)
-- 创建浮动窗口并添加rollout
createDialog addghost