пятница, 10 июня 2016 г.

Вращение колес посредством maxscript.

Данный скрипт предназначен для анимации сразу большого массива колес!

Если Вам надо заригить колеса в одном авто, лучше пройти по ссылкам ниже.  Данный случай постараюсь рассмотреть позже.

Для корректной работы колесам необходимо применить 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
------------------------------------------------------------

Clear undefined Bitmaps

----------------------------------------------------

fn clearUndefinedBitMaps mm = (
  numSTM = getNumSubTexmaps  mm
  if numSTM != undefined and numSTM != 0 do  (
   for i = 1 to numSTM do  (
    BMAP = getSubTexmap  mm i
       if BMAP != undefined do(
                                        bm =  getclassinstances Bitmaptexture target:BMAP
                                        getUnd = false
                                        if bm.count != 0 do for t in bm do if t.filename == "" or t.filename == undefined  do getUnd = true
                                          
                                        if getUnd do(
                                            format "%, % - clear\n" mm.name BMAP.name
                                            setSubTexmap mm i undefined
                                            )
                                    )
                                )  
                            )
 )


fn theAllMat = (
  local allMat = #()
  join allMat (getclassinstances CoronaMtl)
  join allMat (getclassinstances VRayMtl)
  join allMat (getclassinstances Standardmaterial)
   allMat
 )

for m in theAllMat() do clearUndefinedBitMaps m
for m in sceneMaterials do clearUndefinedBitMaps m
for i = 1 to meditmaterials.count do if classOf meditmaterials[i] == BitmapTexture do if meditmaterials[i].filename == "" or meditmaterials[i].filename == undefined do (mn = meditmaterials[i].name; format "% - replase Standardmaterial in meditmaterials\n" mn; meditmaterials[i] = Standardmaterial name:mn;)
----------------------------------------------------

четверг, 9 июня 2016 г.

Выделить задублированные объекты в сцене.

Если вдруг выяснилось, что по ошибке произошло задвоение объектов в сцене и объекты абсолютно идентичные, с одним материалом  и т.п.. В общем, единственный способ их убрать - это вручную "протыкивать" , то  на помощь может прийти этот скрипт:

------------------------------------------------------------
 fn round_to val n =
(
    local mult = 10.0 ^ n
        (floor ((val * mult) + 0.5)) / mult
)
arPos = #()
arObj = #()

ar = selection as array

for i in ar do(
    rp = [round_to i.pos.x 1,round_to i.pos.y 1, round_to i.pos.z 1]
  
    if findItem arPos rp == 0 do(
        append arPos rp
        append arObj i
        )
    )
select arObj
------------------------------------------------------------


Функция округления взята отсюда: http://forums.cgsociety.org - Rounding a number, How?
 Похожие скрипты: http://www.scriptspot.com/ - Duplicate object finder/selecter


вторник, 7 июня 2016 г.

Расстановка объектов разной детализации в зависимости от расстояния до камеры.

Есть массив штор (быстро расставлена базовая заготовка).

Теперь надо поменять эту заготовку на высоко и низко детализированные шторы, в зависимости от расстояния до камеры:
Если расстояние от шторы до камеры меньше 2000 см, то - Hi, если больше - Mi шторы.

---------------------------------------------------------
 arBaseCur = selection as array

arHi = selection as array
arMi = selection as array

fn theDistAllFrames obj c = (
    b = false
    for j= animationRange.start to animationRange.end do(
       at time j (
           p1 = obj.pos
           pc = c.pos
           dist = length (p1 - pc)
           if dist <= 2000 do b = true
           )
    )
b
)

delAr = #()
nAr = #()
for i in arBaseCur do (
    _cam = for i in objects where classOf i == Targetcamera collect i
    if theDistAllFrames i _cam[1] then(
        RselFromAr = arHi[random 1 arHi.count]
        nobj = instance RselFromAr
        nobj.transform = i.transform
        nObj.wirecolor = RselFromAr.wirecolor
        append delAr i
        append nAr nobj
        )else (
                RselFromAr = arMi[random 1 arMi.count]
                nobj = instance RselFromAr
                nobj.transform = i.transform
                nObj.wirecolor = RselFromAr.wirecolor
                append delAr i
                append nAr nobj
        )
    )
delete delAr
select nAr
---------------------------------------------------------














понедельник, 6 июня 2016 г.

Скрипт убирает лишние материалы из Multi/Sub-Object.

Вот пример. В объекте используется всего два материала, остальные идут в виде бороды от старых сцен:
Есть несколько способов сделать это ручками стандартными средствами макса, но скриптом проще и быстрее:



-------------------------------------------------------------

-- work olny selection geometry
-- collapse modifier stack
fn GetOriginObj ar = (
    arrAL = #()
    arComp = #()
    for i in ar do(
        if findItem arComp i == 0 do(
            if classOf i.baseobject == Editable_Poly or classOf i.baseobject == Editable_mesh do(
                arPart = #()
                InstanceMgr.GetInstances i &allObjInst
                join arPart allObjInst
                join arComp arPart
                append arrAL arPart
                )
            )
    )
    arFin = #()
    aC = arrAL.count
    for i = 1 to aC do append arFin arrAL[i][1]
    arFin
    )

fn getArID obj =(
    arID = #()
    maxOps.CollapseNodeTo obj 1 true
    case classOf obj of (
                (Editable_Poly):(
                    nf = obj.numfaces
                    arID = makeUniqueArray(for f=1 to nf collect  polyop.getFaceMatId obj f)
                    )
                (Editable_Mesh):(
                    nf = obj.numfaces
                    arID = makeUniqueArray(for f=1 to nf collect  getFaceMatID obj f)
                    )
                default: (false)
            )
    )

fn setID obj arMat =(
    case classOf obj of (
                (Editable_Poly):(
                    nf = obj.numfaces
                    for f=1 to nf do(
                        _id = polyop.getFaceMatId obj f
                        for m in arMat do if _id == m[3]  do polyOp.setFaceMatID obj f m[4]
                        )
                    )
                (Editable_Mesh):(
                    nf = obj.numfaces
                    for f=1 to nf do(
                        _id = getFaceMatId obj f
                        for m in arMat do if _id == m[3]  do setFaceMatID obj f m[4]
                        )
                    )
                default: (false)
            )
    )  

arrObj = GetOriginObj (selection as array)  
select arrObj
  
progressstart "Optimization ID-materials..."  
arNum = arrObj.count
num = 1
for o in arrObj do(
    m = o.material
    arMat = #()
    if m != undefined and classOf m == Multimaterial do(
        arID = getArID o
        ac = arID.count
        if m.numsubs > ac do(
                if arID != false and ac > 1 do for i = 1 to ac do append arMat #(m[arID[i]], m.names[arID[i]], m.materialIDList[arID[i]], i)
                setID o arMat
                if setID != false do(
                            nMat = Multimaterial  numsubs:arMat.count name:("m-" + o.name)
                            for i = 1 to arMat.count do(
                                nMat.materialList[i] = arMat[i][1]
                                nMat.names[i] = arMat[i][2]
                                nMat.materialIDList[i] = arMat[i][4]
                                )
                              
                            update o  
                            --get instances obj
                            InstanceMgr.GetInstances o &allObjInst
                            for i in allObjInst    do i.material = nMat  

                            format "%, %" o.name allObjInst.count
                        )
                    progressupdate (num*100.0/arNum)
                    num +=1  
            ) -- end if m.numsubs > ac
        ) -- end if m != undefined and classOf m == Multimaterial
    ) -- end for o in arrObj
  
progressend ()
-------------------------------------------------------------

четверг, 2 июня 2016 г.

Удаление лишних вершин на ребрах в Editable Poly.


Понадобилось почистить кирпичную кладку от "лишних вершин" после булевых операций:




Ссылки:
Vertex Cleaner
How do I remove mid-edge EPoly Vertices?
VGSelectBadVertices
111_selectBadVertices

   После исследования скриптов по ссылкам, стало понятно, что они немного не подходят для поставленной задачи.


   В скрипте ниже, по сути, тоже самое, только сбоку.
   Добавил возможность работать с группой объектов, чтобы не перещелкивать каждый в больших сценах и постарался немного ускорить. Скрипт работает только с Editable Poly объектами!:
tr - threshold = 0.7 подбирается опытным путем.


---------------------------------------------------------------------
try destroyDialog RemoveMiVertex catch()
--function
fn getMiVertexPoly obj tr = (   
    arrVertInd = #()
    for v in obj.verts  do (
        arEdge=(PolyOp.getEdgesUsingVert obj #(v.index)) as array
        if arEdge.count==2 do (
                edgeVerts = (polyop.getVertsUsingEdge obj arEdge) as array
                p1 = polyop.getVert obj edgeVerts[1]
                p2 = polyop.getVert obj edgeVerts[2]
                p3 = polyop.getVert obj edgeVerts[3]
                v1 = p1 - p2
                v2 = p2 - p3
                v3 = length (cross (normalize v1) (normalize v2))
                if v3 <= tr do append arrVertInd v.index
        )

    )
format "%; % - removed vertices\n" obj.name arrVertInd.count
arrVertInd
)   
--Interface
rollout RemoveMiVertex "RemoveMiVertex"
(   
spinner tresh "Threshold:" range:[0,100,0.7] type:#float width:120 align:#left --offset:[10,0] enabled:false --across:2
checkbox onlySel "Only select (don't remove)" checked:false align:#left --offset:[10,0]
button theGo "Remove" width:175 height:30  align:#left  --across:2
    on theGo pressed do(
    arSel = for i in selection where classOf i.baseobject == Editable_Poly collect i
   
    max modify mode--switch to modify panel  --set the base object as current level:   
        for obj in arSel do(
            select obj--select it
            modPanel.setCurrentObject obj.baseobject
            subObjectLevel = 1--set sub-*object level to vertex level
            polyOp.setVertSelection obj (getMiVertexPoly obj (tresh.value/100.0))
            update obj   
            --pick button "Remove"   
            if not onlySel.state do (
                    h = windows.getChildHWND #max "Remove"
                    hc=windows.getChildrenHwnd h[2]
                    UIAccessor.PressButton hc[1][1]  --button  "Fit"  
                )
            -----------------------
            subobjectLevel = 0
            update obj   
            )
    ) -- end on theGo pressed do
) --END rollout
createDialog RemoveMiVertex width:200
---------------------------------------------------------------------