четверг, 12 мая 2016 г.

Возможности Boolean в 3ds Max 2017.


Похоже булевы операции в 2017 максе стали работать гораздо лучше, чем в предыдущих версиях.
Появилась возможность изменять параметры исходных объектов, менять тип операций и многое другое.

Скачать сцену можно по этой ссылке.

среда, 27 апреля 2016 г.

Callback функции.

Пример на illusioncatalyst.com

Хороший урок от Bobo


General Event Callback Mechanism

Ниже простой пример вызова функции при выделении нужного модификатора.


----------------------------
fn checkModName theName =(
    r = false
    try(if (modPanel.getCurrentObject()).name == theName then r = true) catch()
    r
)

txt = "try(if checkModName \"UVW Map\" then print \"you have selected UVW Map modifier\" \n"
txt += "else print \"you have selected a different modifier\")catch()"

callbacks.addscript #modPanelObjPostChange txt id:#abrakadabra

------------------------------------------------------------
--callbacks.removeScripts id:#abrakadabra
эта строка для выгрузки функции из памяти.
----------------------------

четверг, 21 апреля 2016 г.

Просто и быстро заменить одни объекты другими (например hi польные на low польные, включая инстансные...). Или про функцию instancereplace.

Есть сцена, в которой понадобилось заменить высокополигональных людей на те же модели, но оптимизированные.
Делается этим скриптом, но в определенной последовательности!
-----------------------------------------start
  arLow = selection as array
 arHi = selection as array

 for i in arLow do(
    for j in arHi do(
         if i.name == j.name and i.material.name == j.material.name do(
                print i.name
                instancereplace j i
             )
        )
     )
-----------------------------------------end

Выделяем первую группу, например высокополигональных людей и выполняем строку (shift+enter):

arHi = selection as array
 создаем ,тем самым, массив hipoly
 Тоже для lowpoly группы:
arLow = selection as array


 Только теперь можно выполнить код:
--
 for i in arLow do(
    for j in arHi do(
         if i.name == j.name and i.material.name == j.material.name do(
                print i.name
                instancereplace j i
             )
        )
     )
 --
Благодаря использованию функции instancereplace в сцене должны поменяться все инстансные (зависимые) высокополигональные люди. 





понедельник, 28 марта 2016 г.

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

Очень редко, но требуется точно выровнять одинаковые объекты со сбитыми пивотами и с разной трансформацией.


Это можно сделать этим скриптом:

--------------------------------------------------------
(  
fn AlignPivotTo Obj Trgt =
(
    -- Current offset transform matrix
    TmScale = scaleMatrix Obj.objectOffsetScale
    TmRot = Obj.objectOffsetRot as matrix3
    TmPos = transMatrix Obj.objectOffsetPos
    TmOffset = TmScale * TmRot * TmPos
    -- New offset transform matrix
    TmOffset *= obj.transform * inverse Trgt
    -- Apply matrix
    Obj.transform = Trgt
    -- Restore offsets
    Obj.objectOffsetPos = TmOffset.translation
    Obj.objectOffsetRot = TmOffset.rotation
    Obj.objectOffsetScale = TmOffset.scale
)

fn localMatrix p1 p2 p3 =
(
    v1 = p1 - p3
    v2 = p2 - p3
    e1 = normalize( cross v1 v2)
    e2 = normalize( cross e1 v1)
    e3 = normalize( cross e1 e2)
    mat3 = matrix3 1
    mat3[1] = e1
    mat3[2] = e2
    mat3[3] = e3
    mat3[4] = p1
    return mat3
    )
   
   
fn AlignObjects mainObj tempObj numberFace =
(
--zapishem masshtab objectov
ScaleMainObj = mainObj.scale
ScaleTempObj = tempObj.scale
--vistavim 100pr masshtab
mainObj.scale = [1,1,1]
tempObj.scale = [1,1,1]   
   
   
VertUseFacesT = ((meshop.getVertsUsingFace tempObj numberFace) as array)
VertUseFacesM = ((meshop.getVertsUsingFace mainObj numberFace) as array)
----------------------------------------------   
NumV = tempObj.numverts
t1 = VertUseFacesT[1]
t2 = VertUseFacesT[2]
t3 = VertUseFacesT[3]
m1 = VertUseFacesM[1]
m2 = VertUseFacesM[2]
m3 = VertUseFacesM[3]
----------------------------------------------
theMt = localMatrix (getVert tempObj t1) (getVert tempObj t2) (getVert tempObj t3)
theMm = localMatrix (getVert mainObj m1) (getVert mainObj m2) (getVert mainObj m3)
AlignPivotTo tempObj theMt
tempObj.transform = theMm
tempObj.pivot = mainObj.pivot

--vistavim stariy masshtab
mainObj.scale = ScaleMainObj
tempObj.scale = ScaleTempObj
    )

   
fn roundNum A B  =     floor (10000.0*sqrt((A-B)^2))
   
   
fn compareObjByAreas MainObj CompareObj =
(
--zapishem masshtab objectov
ScaleMainObj = MainObj.scale
ScaleCompareObj = CompareObj.scale
--vistavim 100pr masshtab
MainObj.scale = [1,1,1]
CompareObj.scale = [1,1,1]   

tempMainObj = snapshot  MainObj  --zaskrinshotim objecty
tempCompObj = snapshot  CompareObj
   
numFacesM = tempMainObj.numFaces   
numFacesC = tempCompObj.numFaces

if numFacesM == numFacesC then
(
    local theSummAreas = 0.0
    for i = 1 to 10 do --viberem 10 proizvolnih fasov dlya sravneniya
    (
        randFace = random 1 numFacesM
        Sqare_mf = meshop.getFaceArea tempMainObj randFace
        Sqare_cf = meshop.getFaceArea tempCompObj randFace
        theSummAreas = theSummAreas + (Sqare_mf - Sqare_cf)
        )
    if theSummAreas <= 0.1 then CompareIndex = 1 else CompareIndex = 0
    ) else CompareIndex = 0

delete tempMainObj
delete tempCompObj

--vistavim stariy masshtab
MainObj.scale = ScaleMainObj
CompareObj.scale = ScaleCompareObj
   
return  CompareIndex   
    )   
   
   
   
   
if (compareObjByAreas $1 $2) == 1 do AlignObjects $1 $2 1   
   
    )
--------------------------------------------------------


Для выполнения необходимо базовый объект переименовать в "1", а совмещаемый в "2".

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


среда, 23 марта 2016 г.

Скрипты для упрощения работы с VrayProxy и CoronaProxy.

Скрипты умеют:
-  искать proxy по указанному пути;
- отображать список ненайденных proxy;
-  составлять список proxy в сцене;
- собирать proxyв указанную папку;
- менять способ отображения в сцене;
- перебивать пути (только для CoronaProxy).






Код для VrayProxy:

------------------------------------------------start
 --------------- start ProxySimplifier
--ScriptProxySimplifier_v3
--tested in Max 2013

-- optimized version 2013-05-23 (Black Sphinx)

rollout ProxySimplifier "ProxySimplifier"
( --start rollout ProxySimplifier

    local pathPrompt =  "Select path please..."
    local wrongProxyObjects = #()    -- array for collect proxy objects with broken links

    group "Finder:"
    (
        button ListMisProxy "List Missing Proxies" width:230  height:18 align:#right color:red across:2
        button ListProxy "List Proxies" width:230  height:18 align:#right color:red
        label lbl_01 "------------------------------------------------------------" align:#centere
        listbox mP "List proxies:"  height:10
        editText thePathText text:pathPrompt width:120 align:#left readOnly:true across:3
        button btn_browse "<---- Browse" width:100  height:18 align:#right
        button DoIt "Find Proxy..." width:120  height:18 align:#center
    )
    group "Collector:"
    (
        editText thePathText2 text:pathPrompt width:120 align:#center readOnly:true across: 4
        button btn_browse2 "<---- Browse" width:100  height:18 align:#center
        button DoIt2 "Collect Proxy..." width:120  height:18 align:#center color:red  
        Checkbox chBox3 "Selected only" width:60 align:#center checked:true
        )
    group "Display:"
    (
        button btn3 "Preview from file" width:100  height:18 align:#center across: 3
        button btn4 "Bounding box" width:100  height:18 align:#center
        Checkbox chBox2 "Selected only" width:60 align:#center checked:true
    )
  
  
    local theFolderPathCollector = undefined
    local ProxyPathes = #()
    local all_dir = #()
    local theFolderPath = undefined
    local theBooleanSwitch = true
    local wrongProxyObjects = #()
    local ProxyObjects = #()
  
  
    /*
    Return string array of root folder and all it's subfolders
    */
    fn getFolderPathes root =
    (
        dir_array = #(root)
        for d in dir_array do
            join dir_array (getDirectories (d+"/*"))
        dir_array
    )

    -- Display group section
  
    /*
    Function setProxyDisplay set display mode for proxies.
        sel - when true apply to selection. Otherwise to all proxies in scene
        disp_mode - display mode: 0 - boundary box, 1 - preview from file, 2 - point, 3 - preview from file (faces)
    */
    fn setProxyDisplay sel disp_mod = (
        if sel then
            objSet = selection
        else
            objSet = objects
      
        for i in objSet where classOf i == VrayProxy do (
                i.display = disp_mod
            )
    )
      
    on btn3 pressed do    (
        setProxyDisplay chBox2.state 1
    )

    on btn4 pressed do    (
        setProxyDisplay chBox2.state 0
    )  
    -- end Display section


    -- Collector group section
  
    on btn_browse2 pressed do (
        theFolderPathCollector = getSavePath  caption: "Pick folder" initialDir:"C:\\"
        if theFolderPathCollector != undefined then
            thePathText2.text = theFolderPathCollector as string
        else
            thePathText2.text = pathPrompt
    )      
      
    /*
    Copy all linked mesh files to folder theFolderPathCollector.
    After successful copying link in proxy updated by new file location.
    */
    on DoIt2 pressed do
    (
      
        local theAllGoodProxy = #()
      
        if chBox3.state == true then
        (
                for i in selection where classOf i == VRayProxy do if doesFileExist i.filename  do appendIfUnique theAllGoodProxy i
            )
        else (for i in objects where classOf i == VRayProxy do if doesFileExist i.filename  do appendIfUnique theAllGoodProxy i )
      
      
        local theP1 = undefined
        local theP2 = undefined
      
        if theFolderPathCollector != undefined then (
            for i in theAllGoodProxy do (
                theP1 = i.filename
                theP2 = theFolderPathCollector + "\\" + (filenameFromPath theP1)
                if (copyFile theP1 theP2) then
                    i.filename = theP2
                else
                    print ("don't copy - " + i.filename as string)
            )
        )
        else
            messageBox "Please specify path."
    )    -- end DoIt2 pressed

    --end Collector section
      
    -- Finder group section
          
    on btn_browse pressed do (
        theFolderPath = getSavePath  caption: "Pick folder" initialDir:"C:\\"
        if theFolderPath != undefined then
            thePathText.text = theFolderPath as string
        else
            thePathText.text = pathPrompt
    )          

    /*
    findMissingProxy
    Collect proxy objects with broken links (the mesh file not exists).
    Array misProxyPathes contains only broken paths and use for listbox "Wrong proxies".
    Array wrongProxyObjects use for further work.
    */
    fn findMissingProxy = (
        ProxyPathes = #()
        wrongProxyObjects = #()
      
        lbl_01.text = "Reading proxy..."
             for i in objects where classOf i == VrayProxy  do
             (    
                 if findItem ProxyPathes i.filename == 0 and doesFileExist i.filename == false do
                 (
                        append wrongProxyObjects i
                        append ProxyPathes  i.filename
                     )
                )
        --makeUniqueArray misProxyPathes
        --sort misProxyPathes
        mP.items = ProxyPathes
        lbl_01.text = "please use DoubleClick for getting proxy"
    )
  
    fn findProxy = (
        ProxyPathes = #()
        ProxyObjects = #()
      
        lbl_01.text = "Reading proxy..."
            local arObj = objects as array
            local arObjCount = arObj.count
            progressstart ("Reading proxy...")
             for i = 1 to arObjCount where classOf arObj[i] == VrayProxy  do
             (    
                 progressupdate (i*100.0/arObjCount)
                 if findItem ProxyPathes arObj[i].filename == 0 and doesFileExist arObj[i].filename == true do
                 (
                        append ProxyObjects arObj[i]
                        append ProxyPathes  arObj[i].filename
                     )
                )
      
        --makeUniqueArray ProxyPathes
        --sort ProxyPathes
        mP.items = ProxyPathes
        lbl_01.text = "please use DoubleClick for getting proxy"
        progressend ()
    )  
  
    on ListMisProxy pressed do findMissingProxy()
    on ListProxy pressed do findProxy()
    /*
    mP doubleClicked
    Select in scene proxy objects with mesh file name that double clicked in "Wrong proxies" listbox.
    */
    on mP doubleClicked itm do     (
                                                local theAllProxy = for i in objects where classOf i == VrayProxy collect i
                                                try( select (for i in theAllProxy where ProxyPathes[itm] == i.filename collect i) )catch()
                                                lbl_01.text = "please use DoubleClick for getting proxy"
                                            )  
  
    /*
    Subroutine finds missing mesh file for all proxies in array wrongProxyObjects in folder tree started from theFolderPath.
    */
on DoIt pressed do (
    if wrongProxyObjects.count != 0 do (

            if theFolderPath == undefined do (
            messageBox "Please pick path..."
            return 0
            )

            lbl_01.text = "Reading pathes..."
            all_dir = getFolderPathes theFolderPath

            local testFileName = undefined
            local theProxyName = undefined

            lbl_01.text = "Finding mesh files..."
            fileNotFound = true
          
                    for i in wrongProxyObjects do (
                    theProxyName = fileNameFromPath i.filename
                    fileNotFound = true
                            for p in all_dir while fileNotFound do (
                            testFileName = p + "\\" + theProxyName
                                    if doesFileExist testFileName do (
                                    i.filename = testFileName
                                    fileNotFound = false
                                    )
                            )
                    -- lbl_01.text = i.name -- comment for performanse reasons
                    )
            findMissingProxy() -- refresh array wrongProxyObjects and listbox "Wrong proxies"
            lbl_01.text = "please use DoubleClick for getting proxy"
    )
)
    --end finder
      
)--end rollout ProxySimplifier

escapeEnable = true

CreateDialog ProxySimplifier 500 370
------------- end ProxySimplifier
--By Alex Yakushev
------------------------------------------------end




Код для CoronaProxy:

----------------------------------------------start
 --------------- start ProxySimplifier
--ScriptProxySimplifier_v3
--tested in Max 2013

-- optimized version 2013-05-23 (Black Sphinx)

rollout ProxySimplifierC "ProxySimplifierCorona"
( --start rollout ProxySimplifier

    local pathPrompt =  "Select path please..."
    local wrongProxyObjects = #()    -- array for collect proxy objects with broken links

    group "Finder:"
    (
        editText thePathText text:pathPrompt width:208 align:#left across:3
        button btn_browse "<---- Browse" width:100  height:18 align:#right
        button findMisProxy "Find Missing Proxies..." width:150  height:18 align:#right color:red
        listbox mP "Wrong proxies:"  height:10
        label lbl_01 "------------------------------------------------------------" align:#center
        button DoIt "Find Proxy..." width:150  height:18 align:#center
    )
    group "Collector:"
    (
        editText thePathText2 text:pathPrompt width:120 align:#center  across: 4
        button btn_browse2 "<---- Browse" width:100  height:18 align:#center
        button DoIt2 "Collect Proxy..." width:100  height:18 align:#center
        button DoIt3 "Change Pathes..." width:100  height:18 align:#center  
        )
      
      
    --group "Display:"
    --(
    --    button btn3 "Preview from file" width:100  height:18 align:#center across: 2
    --    button btn4 "Bounding box" width:100  height:18 align:#center
    --)
  
  
    local theFolderPathCollector = undefined
    local misProxyPathes = #()
    local all_dir = #()
    local theFolderPath = undefined
    local theBooleanSwitch = true
    local wrongProxyObjects = #()
  
  
    /*
    Return string array of root folder and all it's subfolders
    */
    fn getFolderPathes root =
    (
        dir_array = #(root)
        for d in dir_array do
            join dir_array (getDirectories (d+"/*"))
        dir_array
    )

    -- Display group section
      
    --on btn3 pressed do    for i in selection where classOf i == CoronaProxy do i.previzType = 3
    --on btn4 pressed do    for i in selection where classOf i == CoronaProxy do i.previzType = 0
    -- end Display section


    -- Collector group section
  
    on btn_browse2 pressed do
        (
            if pathPrompt == "Select path please..." then
            (
                theFolderPathCollector = getSavePath  caption: "Pick folder" initialDir:"C:\\"
                if theFolderPathCollector != undefined do
                    (
                    thePathText2.text = theFolderPathCollector as string
                    pathPrompt = theFolderPathCollector as string
                        )
                )else
                (
                    theFolderPathCollector = getSavePath  caption: "Pick folder" initialDir:pathPrompt
                    if theFolderPathCollector != undefined do
                        (
                        thePathText2.text = theFolderPathCollector as string
                        pathPrompt = theFolderPathCollector as string
                            )
                    )
            )  
      
    /*
    Copy all linked mesh files to folder theFolderPathCollector.
    After successful copying link in proxy updated by new file location.
    */
    on DoIt3 pressed do
    (
        if theFolderPathCollector != undefined do
        (
          
            for i in (getClassInstances CoronaProxy) do
                    (
                        local theP1 = i.filename
                        i.filename = (theFolderPathCollector + "\\" + (filenameFromPath theP1))
                        )          
            )
        )
  
    on DoIt2 pressed do (
        local theAllGoodProxy = #()

        for i in objects where classOf i == CoronaProxy do (
            if doesFileExist i.filename  do appendIfUnique theAllGoodProxy i
        )
      
        local theP1 = undefined
        local theP2 = undefined
      
        if theFolderPathCollector != undefined then (
            for i in theAllGoodProxy do (
                local theP1 = i.filename
                local theP2 = theFolderPathCollector + "\\" + (filenameFromPath theP1)
                if (copyFile theP1 theP2) then
                    i.filename = theP2
                else
                    print ("don't copy - " + i.filename as string)
            )
        )
        else
            messageBox "Please specify path."
    )    -- end DoIt2 pressed

    --end Collector section
      
    -- Finder group section
          
                  
    on btn_browse pressed do
        (
            if pathPrompt == "Select path please..." then
            (
                theFolderPath = getSavePath  caption: "Pick folder" initialDir:"C:\\"
                if theFolderPath != undefined do
                    (
                    thePathText.text = theFolderPath as string
                    pathPrompt = theFolderPath as string
                        )
                )else
                (
                    theFolderPath = getSavePath  caption: "Pick folder" initialDir:pathPrompt
                    if theFolderPath != undefined do
                        (
                        thePathText.text = theFolderPath as string
                        pathPrompt = theFolderPath as string
                            )
                    )
            )      
                  

    /*
    findMissingProxy
    Collect proxy objects with broken links (the mesh file not exists).
    Array misProxyPathes contains only broken paths and use for listbox "Wrong proxies".
    Array wrongProxyObjects use for further work.
    */
    fn findMissingProxy = (
        misProxyPathes = #()
        wrongProxyObjects = #()
      
        lbl_01.text = "Reading proxy..."
             for i in objects where classOf i == CoronaProxy  do
             (    
                 if findItem misProxyPathes i.filename == 0 and doesFileExist i.filename == false do
                 (
                        append wrongProxyObjects i
                        append misProxyPathes  i.filename
                     )
                )
        makeUniqueArray misProxyPathes
        sort misProxyPathes
        mP.items = misProxyPathes
        lbl_01.text = "please use DoubleClick for getting proxy"
    )
  
    on findMisProxy pressed do (
        findMissingProxy()
    )
  
    /*
    mP doubleClicked
    Select in scene proxy objects with mesh file name that double clicked in "Wrong proxies" listbox.
    */
    on mP doubleClicked itm do     (
      
        if misProxyPathes.count == 0 do
            return 0
      
        the_obj = #()
        -- print misProxyPathes[itm] as string -- comment for performanse reasons
        for i in wrongProxyObjects do (
            if misProxyPathes[itm] == i.filename do
                append the_obj i
            -- lbl_01.text = i.name -- comment for performanse reasons
        )
        select the_obj
        lbl_01.text = "please use DoubleClick for getting proxy"
    )  
  
    /*
    Subroutine finds missing mesh file for all proxies in array wrongProxyObjects in folder tree started from theFolderPath.
    */
on DoIt pressed do (
    if wrongProxyObjects.count != 0 do (

            if theFolderPath == undefined do (
            messageBox "Please pick path..."
            return 0
            )

            lbl_01.text = "Reading pathes..."
            all_dir = getFolderPathes theFolderPath

            local testFileName = undefined
            local theProxyName = undefined

            lbl_01.text = "Finding mesh files..."
            fileNotFound = true
          
                    for i in wrongProxyObjects do (
                    theProxyName = fileNameFromPath i.filename
                    fileNotFound = true
                            for p in all_dir while fileNotFound do (
                            testFileName = p + "\\" + theProxyName
                                    if doesFileExist testFileName do (
                                    i.filename = testFileName
                                    fileNotFound = false
                                    )
                            )
                    -- lbl_01.text = i.name -- comment for performanse reasons
                    )
            findMissingProxy() -- refresh array wrongProxyObjects and listbox "Wrong proxies"
            lbl_01.text = "please use DoubleClick for getting proxy"
    )
)
    --end finder
      
)--end rollout ProxySimplifier

escapeEnable = true

CreateDialog ProxySimplifierC width:500
------------- end ProxySimplifier
--By Alex Yakushev
----------------------------------------------end





вторник, 22 марта 2016 г.

Текстурирование плоскостей (например жалюзи в большом жилом здании). Texturing blinds in the building.

 Есть заготовка для жалюзи в виде цельной геометрии:

 Разбиваем все на элементы:







Затем, если необходимо, выравниваем пивоты и масштабируем в произвольном порядке:

for i in selection do (i.pivot = i.center; i.pivot.z = i.max.z)
for i in selection do scale i [1,1,random 0.25 1]





 Выравниваем UVW модификатор по нормали:
Источник здесь:
How do I align the UVW_Modifier's Gizmo to a selected face?

Это выжимка из источника:
 --------------------------------------------------start
for i in selection do(
    local theMod = uvwmap()
    theMod.maptype = 0
    addModifier i theMod
)

fn alignUVGizmo theObj =(
faceNormal = getFaceNormal theObj 1
rightVector = normalize (cross [0,0,1] faceNormal)
upVector = normalize ( cross rightVector faceNormal )

theObj.modifiers[1].gizmo.transform = matrix3 rightVector upVector faceNormal [0,0,0]
)

for i in selection do alignUVGizmo i
--------------------------------------------------end
Это результат, на примере одной плоскости:

Осталось выровнять UVW по вершинам.
Для этого можно воспользоваться готовой функцией:

HOW TO ... FIT THE UVW MAPPING GIZMO
Выполняем ее:
for i in selection do UVWMapFit i  i.modifiers[1] useSel:#none





Это результат:


То же можно было бы применить и для штор, но функция подгонки текстурных координат дает немного некорректный результат:


Можно обойтись и без нее. Вот финальный код, в котором просто нажимается кнопка "Fit" для каждого объекта (в нашем случае этого более чем достаточно):


--------------------------start
for i in selection do(
    local theMod = uvwmap()
    theMod.maptype = 0
    addModifier i theMod
)

fn alignUVGizmo theObj =(
faceNormal = getFaceNormal theObj 1
rightVector = normalize (cross [0,0,-1] faceNormal)
upVector = normalize ( cross rightVector faceNormal )
theObj.modifiers[1].gizmo.transform = matrix3 rightVector upVector faceNormal [0,0,0]
)
for i in selection do alignUVGizmo i

max modify mode
arrPlane = selection as array
for i in arrPlane do (
    select i
    h = windows.getChildHWND #max "Fit"
    hc=windows.getChildrenHwnd h[2]
    UIAccessor.PressButton hc[54][1]  --button  "Fit"  
    )
--------------------------end


Работает такой способ, конечно дольше и для нескольких тысяч объектов может занять приличное время.
Параметром 1 или -1 в строке "rightVector = normalize (cross [0,0,-1] faceNormal)" можно менять направление по z.