【maxscript】矩阵对齐-武器残影

最简单的矩阵对齐,

复制代码
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
相关推荐
mortimer2 小时前
Python 异常处理进阶:从 `traceback` 细节到稳健的多语言处理器
python
和鲸社区2 小时前
四大经典案例,入门AI算法应用,含分类、回归与特征工程|2025人工智能实训季初阶赛
人工智能·python·深度学习·算法·机器学习·分类·回归
piaopiaolanghua3 小时前
PyCharm旧版本下载地址
ide·python·pycharm
云天徽上3 小时前
【数据可视化-111】93大阅兵后的军费开支情况———2024年全球军费开支分析:用Python和Pyecharts打造炫酷可视化大屏
开发语言·python·信息可视化·pyecharts
胖达不服输3 小时前
「日拱一码」087 机器学习——SPARROW
人工智能·python·机器学习·sparrow
GilgameshJSS5 小时前
【学习K230-例程21】GT6700-UDP-Client
网络·python·单片机·网络协议·学习·udp
FriendshipT5 小时前
Nuitka 将 Python 脚本封装为 .pyd 或 .so 文件
开发语言·python
她说人狗殊途5 小时前
动态代理1
开发语言·python
Yvonne爱编码5 小时前
后端编程开发路径:从入门到精通的系统性探索
java·前端·后端·python·sql·go