Данный скрипт предназначен для анимации сразу большого массива колес!
Если Вам надо заригить колеса в одном авто, лучше пройти по ссылкам ниже. Данный случай постараюсь рассмотреть позже.
Для корректной работы колесам необходимо применить ResetXForm.
Также, в зависимости от способа копирования колес (mirror или instance с разворотом), возможно понадобится отдельно настраивать вращение для левых и правых колес (по часовой стрелке или против).
Сразу про недостатки:
- покадровая анимация
- морока с настройкой колес
- наверняка можно оптимизировать, но такая задача не ставилась. Главное, что более менее корректно работал.
---------------------------------------------------------------
-- tested in 3DMax 2014
try destroyDialog WheelAnimator catch()
global arrFrontWheel, arrBackWheel, arAllData = #()
--function
fn sumArData ar = (
sum = 0
for i in ar do sum+=i
sum
)
fn wheelRotationFlipZ_180 arW fb = if arW.count != 0 do for i in arW do if fb then rotate i (angleaxis 180 [0,0,1]) else rotate i (angleaxis -180 [0,0,1])
-- arWheel - array wheels, wheelRadius - value, theAxis - axis rotation(1 - "X" or 2-"Y"), AngFlip - clockwise(counterclockwise) (true or false)
fn wheelWriteDataAnimation arW wheelRadius theAxis AngFlip FB = (
if arWeel.count != 0 do(
arAllData = #()
for w in arW do(
case FB of(
("FrontWheel"): (
local arTransform = #(), arAng = #()
--weelRadius
if wheelRadius == 0 do wheelRadius = (w.max.z - w.min.z)/2.0
--delete old keys
sliderTime = animationRange.start
deleteKeys w #allKeys
--write data
for t = animationRange.start to animationRange.end do (
at time t (
p0 = at time t w.position; p1 = at time (t+1) w.position;
dif = p1-p0; len = Length(dif); vec = normalize dif; v2 = normalize (cross vec [0,0,1]);
theAng = (if AngFlip then 1 else -1)*(180.0*len)/(wheelRadius*pi) -- AngFlip +1 or -1
append arAng theAng; ang = sumArData arAng;
cm = if theAxis == 1 then matrix3 v2 vec (cross v2 vec) p0 else matrix3 vec v2 (cross vec v2) p0
append arTransform ((rotateXmatrix ang)*cm)
) -- end at time j
) -- end for j = AnimStart to AnimEnd do
append arAllData #(w, arTransform)
)
("BackWheel"):(
local arTransform = #(), arAng = #()
--weelRadius
if wheelRadius == 0 do wheelRadius = (w.max.z - w.min.z)/2.0
--delete old keys
sliderTime = animationRange.start
deleteKeys w #allKeys
--write data
for t = animationRange.start to animationRange.end do (
at time t (
p0 = at time t w.position; p1 = at time (t+1) w.position;
dif = p1-p0; len = Length(dif); vec = normalize dif; v2 = normalize (cross vec [0,0,1]);
theAng = (if AngFlip then 1 else -1)*(180.0*len)/(wheelRadius*pi) -- AngFlip +1 or -1
append arAng theAng; ang = sumArData arAng;
cm = w.transform
append arTransform ((rotateXmatrix ang)*cm)
) -- end at time j
) -- end for j = AnimStart to AnimEnd do
append arAllData #(w, arTransform)
)
)
)
)--end if arWeel.count != 0
)
-- animation wheel by array data
fn wheelAnimation =(
if arAllData.count != 0 do(
for w in arAllData do(
local num = 0
with animate on(
for t = animationRange.start to animationRange.end do at time t w[1].transform = w[2][num+=1]
)
--deleting unnecessary keys
selectKeys w[1].pos.controller animationRange.start animationRange.end
deleteKeys w[1].pos.controller #selection
selectKeys w[1].scale.controller animationRange.start animationRange.end
deleteKeys w[1].scale.controller #selection
)--for i in arWeel do
)--if arWeel.count != 0 do(
)--fn weelAnimation w arW =(
--Interface
rollout WheelAnimator "WheelAnimator"
(
group "Selection:"
(
listbox ListFrontWheel "" height:5
button addToListFrontWheel "Add front wheels" width:175
listbox ListBackWheel "" height:5
button addToListBackWheel "Add back wheels" width:175
)
group "Parameters:"
(
spinner radW "Wheel radius:" range:[0,100000, 0] type:#float width:100 offset:[30,0] enabled:false across:2
checkbox theAuto "Auto" checked:true align:#center offset:[10,0]
label lbl1 "Orientation: " align:#left across:2
radiobuttons rb1 "" labels:#("X", "Y") default:1 align:#left offset:[-15,0] --across:2
checkbox theFlip "Flip (turn 180 deg around Z-ax)" checked:true align:#left
checkbox theCW "Clockwise (or not)" checked:true align:#left
)
button theGo "Animate" width:175 height:30 align:#left --across:2
on theAuto changed theState do(
radW.enabled = not theAuto.state
if radW.enabled == false do radW.value = 0
)
on addToListFrontWheel pressed do(
local SelFW = selection as array, ListName = #()
arrFrontWheel = #()
if SelFW.count != 0 then(
for i in SelFW do (append ListName i.name; append arrFrontWheel i;)
ListFrontWheel.items = ListName
) else ListFrontWheel.items = #()
)
on addToListBackWheel pressed do(
local SelFW = selection as array, ListName = #()
arrBackWheel = #()
if SelFW.count != 0 then(
for i in SelFW do(append ListName i.name; append arrBackWheel i;)
ListBackWheel.items = ListName
) else ListBackWheel.items = #()
)
on theFlip changed theState do (wheelRotationFlipZ_180 arrFrontWheel theFlip.state; wheelRotationFlipZ_180 arrBackWheel theFlip.state;)
on rb1 changed state do (
wheelWriteDataAnimation arrFrontWheel radW.value rb1.state theCW.state "FrontWheel"
wheelAnimation()
wheelWriteDataAnimation arrBackWheel radW.value rb1.state theCW.state "BackWheel"
wheelAnimation()
)
on theGo pressed do (
wheelWriteDataAnimation arrFrontWheel radW.value rb1.state theCW.state "FrontWheel"
wheelAnimation()
wheelWriteDataAnimation arrBackWheel radW.value rb1.state theCW.state "BackWheel"
wheelAnimation()
)
on theCW changed theState do (
wheelWriteDataAnimation arrFrontWheel radW.value rb1.state theCW.state "FrontWheel"
wheelAnimation()
wheelWriteDataAnimation arrBackWheel radW.value rb1.state theCW.state "BackWheel"
wheelAnimation()
)
) --END rollout
createDialog WheelAnimator width:200
---------------------------------------------------------------
Ссылки по теме:
MAXScript Tutorials: Quaternions 1/2
Rolling Ball
111_carWheel_v0.3
3DS MAX - CG Academy - The Matrix Continued
CGTalk - Wheel rig script!
Rotating the Wheels
Если необходимо просто провращать колеса без подруливания, то это можно сделать этим скриптом:
------------------------------------------------------------
-- tested in 3DMax 2014
try destroyDialog SimpleWheelAnimator catch()
--fn
fn swa arW wr st cw rb = ( -- arW: array wheels [#()], wr: wheel radius[float], st: step [integer], cw: Clockwise[false-true], rb: radiobuttons [integer]
if arW.count != 0 do(
for w in arW do(
deleteKeys w #allKeys
for t = animationRange.start to animationRange.end by st do(
p0 = at time (t-st) w.position; p1 = at time t w.position; d = length (p1-p0); u += (if cw then 1 else -1)*180.0*d/(wr*pi);
case rb of(
(1):(addnewkey w.rotation.controller.X_Rotation.controller t).value = u
(2):(addnewkey w.rotation.controller.Y_Rotation.controller t).value = u
(3):(addnewkey w.rotation.controller.Z_Rotation.controller t).value = u
)
)
)--for w in ws do(
)--if ws.count != 0 do(
)-- end fn
--Interface
rollout SimpleWheelAnimator "SimpleWheelAnimator"(
group "Parameters:"(
label lbl1 "Rotation axis - " align:#left across:2; radiobuttons rb1 "" labels:#("X", "Y", "Z") default:1 align:#left columns:3;
checkbox theCW "Clockwise" checked:true align:#left across:2; spinner sf "Step:" range:[1,100000, 20] type:#integer width:60 align:#left;
spinner radW "Wheel radius:" range:[0,100000, 40] type:#float width:100 align:#left --enabled:false across:2; checkbox theAuto "Auto" checked:true align:#center offset:[10,0]
)
button theGo "Animate selection!" width:175 height:30 align:#left --across:2
on theAuto changed theState do(
radW.enabled = not theAuto.state
if radW.enabled == false do radW.value = 0
)
on theGo pressed do swa (selection as array) radW.value sf.value theCW.state rb1.state
on theCW changed theState do swa (selection as array) radW.value sf.value theCW.state rb1.state
on rb1 changed state do swa (selection as array) radW.value sf.value theCW.state rb1.state
) --END rollout
createDialog SimpleWheelAnimator width:200
------------------------------------------------------------
Если Вам надо заригить колеса в одном авто, лучше пройти по ссылкам ниже. Данный случай постараюсь рассмотреть позже.
Для корректной работы колесам необходимо применить ResetXForm.
Также, в зависимости от способа копирования колес (mirror или instance с разворотом), возможно понадобится отдельно настраивать вращение для левых и правых колес (по часовой стрелке или против).
Сразу про недостатки:
- покадровая анимация
- морока с настройкой колес
- наверняка можно оптимизировать, но такая задача не ставилась. Главное, что более менее корректно работал.
---------------------------------------------------------------
-- tested in 3DMax 2014
try destroyDialog WheelAnimator catch()
global arrFrontWheel, arrBackWheel, arAllData = #()
--function
fn sumArData ar = (
sum = 0
for i in ar do sum+=i
sum
)
fn wheelRotationFlipZ_180 arW fb = if arW.count != 0 do for i in arW do if fb then rotate i (angleaxis 180 [0,0,1]) else rotate i (angleaxis -180 [0,0,1])
-- arWheel - array wheels, wheelRadius - value, theAxis - axis rotation(1 - "X" or 2-"Y"), AngFlip - clockwise(counterclockwise) (true or false)
fn wheelWriteDataAnimation arW wheelRadius theAxis AngFlip FB = (
if arWeel.count != 0 do(
arAllData = #()
for w in arW do(
case FB of(
("FrontWheel"): (
local arTransform = #(), arAng = #()
--weelRadius
if wheelRadius == 0 do wheelRadius = (w.max.z - w.min.z)/2.0
--delete old keys
sliderTime = animationRange.start
deleteKeys w #allKeys
--write data
for t = animationRange.start to animationRange.end do (
at time t (
p0 = at time t w.position; p1 = at time (t+1) w.position;
dif = p1-p0; len = Length(dif); vec = normalize dif; v2 = normalize (cross vec [0,0,1]);
theAng = (if AngFlip then 1 else -1)*(180.0*len)/(wheelRadius*pi) -- AngFlip +1 or -1
append arAng theAng; ang = sumArData arAng;
cm = if theAxis == 1 then matrix3 v2 vec (cross v2 vec) p0 else matrix3 vec v2 (cross vec v2) p0
append arTransform ((rotateXmatrix ang)*cm)
) -- end at time j
) -- end for j = AnimStart to AnimEnd do
append arAllData #(w, arTransform)
)
("BackWheel"):(
local arTransform = #(), arAng = #()
--weelRadius
if wheelRadius == 0 do wheelRadius = (w.max.z - w.min.z)/2.0
--delete old keys
sliderTime = animationRange.start
deleteKeys w #allKeys
--write data
for t = animationRange.start to animationRange.end do (
at time t (
p0 = at time t w.position; p1 = at time (t+1) w.position;
dif = p1-p0; len = Length(dif); vec = normalize dif; v2 = normalize (cross vec [0,0,1]);
theAng = (if AngFlip then 1 else -1)*(180.0*len)/(wheelRadius*pi) -- AngFlip +1 or -1
append arAng theAng; ang = sumArData arAng;
cm = w.transform
append arTransform ((rotateXmatrix ang)*cm)
) -- end at time j
) -- end for j = AnimStart to AnimEnd do
append arAllData #(w, arTransform)
)
)
)
)--end if arWeel.count != 0
)
-- animation wheel by array data
fn wheelAnimation =(
if arAllData.count != 0 do(
for w in arAllData do(
local num = 0
with animate on(
for t = animationRange.start to animationRange.end do at time t w[1].transform = w[2][num+=1]
)
--deleting unnecessary keys
selectKeys w[1].pos.controller animationRange.start animationRange.end
deleteKeys w[1].pos.controller #selection
selectKeys w[1].scale.controller animationRange.start animationRange.end
deleteKeys w[1].scale.controller #selection
)--for i in arWeel do
)--if arWeel.count != 0 do(
)--fn weelAnimation w arW =(
--Interface
rollout WheelAnimator "WheelAnimator"
(
group "Selection:"
(
listbox ListFrontWheel "" height:5
button addToListFrontWheel "Add front wheels" width:175
listbox ListBackWheel "" height:5
button addToListBackWheel "Add back wheels" width:175
)
group "Parameters:"
(
spinner radW "Wheel radius:" range:[0,100000, 0] type:#float width:100 offset:[30,0] enabled:false across:2
checkbox theAuto "Auto" checked:true align:#center offset:[10,0]
label lbl1 "Orientation: " align:#left across:2
radiobuttons rb1 "" labels:#("X", "Y") default:1 align:#left offset:[-15,0] --across:2
checkbox theFlip "Flip (turn 180 deg around Z-ax)" checked:true align:#left
checkbox theCW "Clockwise (or not)" checked:true align:#left
)
button theGo "Animate" width:175 height:30 align:#left --across:2
on theAuto changed theState do(
radW.enabled = not theAuto.state
if radW.enabled == false do radW.value = 0
)
on addToListFrontWheel pressed do(
local SelFW = selection as array, ListName = #()
arrFrontWheel = #()
if SelFW.count != 0 then(
for i in SelFW do (append ListName i.name; append arrFrontWheel i;)
ListFrontWheel.items = ListName
) else ListFrontWheel.items = #()
)
on addToListBackWheel pressed do(
local SelFW = selection as array, ListName = #()
arrBackWheel = #()
if SelFW.count != 0 then(
for i in SelFW do(append ListName i.name; append arrBackWheel i;)
ListBackWheel.items = ListName
) else ListBackWheel.items = #()
)
on theFlip changed theState do (wheelRotationFlipZ_180 arrFrontWheel theFlip.state; wheelRotationFlipZ_180 arrBackWheel theFlip.state;)
on rb1 changed state do (
wheelWriteDataAnimation arrFrontWheel radW.value rb1.state theCW.state "FrontWheel"
wheelAnimation()
wheelWriteDataAnimation arrBackWheel radW.value rb1.state theCW.state "BackWheel"
wheelAnimation()
)
on theGo pressed do (
wheelWriteDataAnimation arrFrontWheel radW.value rb1.state theCW.state "FrontWheel"
wheelAnimation()
wheelWriteDataAnimation arrBackWheel radW.value rb1.state theCW.state "BackWheel"
wheelAnimation()
)
on theCW changed theState do (
wheelWriteDataAnimation arrFrontWheel radW.value rb1.state theCW.state "FrontWheel"
wheelAnimation()
wheelWriteDataAnimation arrBackWheel radW.value rb1.state theCW.state "BackWheel"
wheelAnimation()
)
) --END rollout
createDialog WheelAnimator width:200
---------------------------------------------------------------
Ссылки по теме:
MAXScript Tutorials: Quaternions 1/2
Rolling Ball
111_carWheel_v0.3
3DS MAX - CG Academy - The Matrix Continued
CGTalk - Wheel rig script!
Rotating the Wheels
Если необходимо просто провращать колеса без подруливания, то это можно сделать этим скриптом:
------------------------------------------------------------
-- tested in 3DMax 2014
try destroyDialog SimpleWheelAnimator catch()
--fn
fn swa arW wr st cw rb = ( -- arW: array wheels [#()], wr: wheel radius[float], st: step [integer], cw: Clockwise[false-true], rb: radiobuttons [integer]
if arW.count != 0 do(
for w in arW do(
deleteKeys w #allKeys
for t = animationRange.start to animationRange.end by st do(
p0 = at time (t-st) w.position; p1 = at time t w.position; d = length (p1-p0); u += (if cw then 1 else -1)*180.0*d/(wr*pi);
case rb of(
(1):(addnewkey w.rotation.controller.X_Rotation.controller t).value = u
(2):(addnewkey w.rotation.controller.Y_Rotation.controller t).value = u
(3):(addnewkey w.rotation.controller.Z_Rotation.controller t).value = u
)
)
)--for w in ws do(
)--if ws.count != 0 do(
)-- end fn
--Interface
rollout SimpleWheelAnimator "SimpleWheelAnimator"(
group "Parameters:"(
label lbl1 "Rotation axis - " align:#left across:2; radiobuttons rb1 "" labels:#("X", "Y", "Z") default:1 align:#left columns:3;
checkbox theCW "Clockwise" checked:true align:#left across:2; spinner sf "Step:" range:[1,100000, 20] type:#integer width:60 align:#left;
spinner radW "Wheel radius:" range:[0,100000, 40] type:#float width:100 align:#left --enabled:false across:2; checkbox theAuto "Auto" checked:true align:#center offset:[10,0]
)
button theGo "Animate selection!" width:175 height:30 align:#left --across:2
on theAuto changed theState do(
radW.enabled = not theAuto.state
if radW.enabled == false do radW.value = 0
)
on theGo pressed do swa (selection as array) radW.value sf.value theCW.state rb1.state
on theCW changed theState do swa (selection as array) radW.value sf.value theCW.state rb1.state
on rb1 changed state do swa (selection as array) radW.value sf.value theCW.state rb1.state
) --END rollout
createDialog SimpleWheelAnimator width:200
------------------------------------------------------------