вторник, 18 октября 2016 г.

Расстановка книг.

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

Ниже сам скрипт с небольшим описанием:
Заготовка из книг
Расположение пивота(точки опоры) книги
Сплайны по которым будет расстановка книг
При выделенном сплайне выполняем этот код:
-----------------------------
main_spl = $
arrSplineData = #()
for numS = 1 to (numSplines  main_spl) do
    (     arS = #()
        for k =0.0 to 1.0 by  0.0001 do append arS (pathInterp main_spl numS k)
        append arrSplineData arS
    )
-----------------------------
Происходит считывание позиций со сплайна с шагом 0.0001 от длины элемента сплайна. Запись данных в массив происходит также поэлементно.
После записи arrSplineData = #(#(pos1, pos2, pos3),#(pos1, pos2, pos3),#(pos1, pos2, pos3).....)
Затем выделяем заготовку книг и выполняем запись книг в массив:

--------------------------------
arrBooks = selection as array
--------------------------------
Далее с помощью функции newBook из массива книг arrBooks выбираем произвольную книгу и определяем ее габариты, в моем случае, по оси Y! 
------------------------------
fn newBook arB =  #(theB = arB[random 1 arB.count], theB.max.y - theB.min.y)
NB = newBook arrBooks    
------------------------------

С помощью следующей части кода определяем массив newBooks - куда будут записаны все созданные книги и обращаясь к уже созданному массиву arrSplineData производим расстановку книг:
------------------------------
 newBooks = #()
for k = 1 to arrSplineData.count do(
    s = 0
    thePos = arrSplineData[k][1]
    for i = 2 to arrSplineData[k].count do(
        if s == 0 do thePos = arrSplineData[k][i-1]
        s = s + (distance arrSplineData[k][i-1] arrSplineData[k][i])
        if s >= NB[2] do (
            nbi = instance NB[1]
            nbi.wirecolor = NB[1].wirecolor
            theY = normalize (arrSplineData[k][i-1] - arrSplineData[k][i])
            theZ = [0,0,1]
            theX = cross theY theZ
            nbi.transform = matrix3 theX theY theZ thePos
          
            append newBooks nbi
            NB = newBook arrBooks  
            s = 0
            )
        )
    )
------------------------------

После первой расстановки возможно понадобится сделать реверс некоторым элементам сплайна.

Для этого удалим массив newBooks:
--------------------------------
delete newBooks
--------------------------------

Получается сырой, полуавтоматический способ расстановки для больших библиотек без изысков.
Ниже целиком весь код:
---------------------------------
 main_spl = $
arrSplineData = #()
for numS = 1 to (numSplines  main_spl) do
    (     arS = #()
        for k =0.0 to 1.0 by  0.0001 do append arS (pathInterp main_spl numS k)
        append arrSplineData arS
    ) -- END for numS = 1 to (numSplines  theMainShape) do
      
arrBooks = selection as array

fn newBook arB =  #(theB = arB[random 1 arB.count], theB.max.y - theB.min.y)
NB = newBook arrBooks  

newBooks = #()
for k = 1 to arrSplineData.count do(
    s = 0
    thePos = arrSplineData[k][1]
    for i = 2 to arrSplineData[k].count do(
        if s == 0 do thePos = arrSplineData[k][i-1]
        s = s + (distance arrSplineData[k][i-1] arrSplineData[k][i])
        if s >= NB[2] do (
            nbi = instance NB[1]
            nbi.wirecolor = NB[1].wirecolor
            theY = normalize (arrSplineData[k][i-1] - arrSplineData[k][i])
            theZ = [0,0,1]
            theX = cross theY theZ
            nbi.transform = matrix3 theX theY theZ thePos
          
            append newBooks nbi
            NB = newBook arrBooks  
            s = 0
            )
        )
    )

delete newBooks
---------------------------------

Это финальный результат:

Похожие скрипты:
bookmanager на scriptspot
bookscatter на scriptspot



пятница, 7 октября 2016 г.

Сложное копирование.

--------------------------------------------
arZamenit = selection as array
arNaEto = selection as array

for i in arZamenit do(
    maxops.clonenodes arNaEto[random 1 arNaEto.count] cloneType:#instance expandHierarchy:true newNodes:&nnl
    nnl[1].transform = i.transform
     )
delete arZamenit
--------------------------------------------

Функция  maxops.clonenodes позволяет копировать объекты с прилинкованными объектами.
Например, если надо боксы-заготовки поменять на машины с людьми. При этом машины  в виде группы, то данный способ подойдет как нельзя кстати.

среда, 14 сентября 2016 г.

Выделение инстансных объектов.

 ------------------------------------------------------
arInst = #()
arSel = selection as array
if arSel.count != 0 do(
    for i in arSel do(
        InstanceMgr.GetInstances i &rptInstances
        join arInst rptInstances
        )
    select arInst
    )
 ------------------------------------------------------
Источники:
ersindemir
forums.cgsociety
forums.cgsociety
docs.autodesk.com

Декодирование mse скриптов.

1. Запустить quickbms.exe  и  выбрать Mse_Decrypter.txt

2. Выбрать mse

3. Указать путь для сохранения декодированного скрипта.

Источник:
http://www.scriptsays.com/2016/04/3dmax-mse-script-source-code-decryption-tool/

Правда этот способ подходит не для всех mse файлов. 
 

вторник, 13 сентября 2016 г.

Работа с именами текстур.

Бывает так, что, к примеру, в папке wood - куча текстур с непонятными названиями, вроде - 01, asdfasdf, и т.п. .Скрипт ниже переименовывает текстуры по названию папки + произвольное шестизначное число. Сразу обновляет путь битмапы. На примере папки wood текстуры будут иметь следующие имена  - wood_126523 и т.п.
-----------------------------------------------------------
 (
local albm = getclassinstances bitmapTexture, arPath = #()
  
fn randName num = (
local rN = ""
for i = 1 to num do rN+= (random 1 num) as string
rN
)

fn getFileFolderName pf = (
    fsp = filterString pf "\\"
    fsp[fsp.count-1]
    )

for n in albm do
(
    if n.filename != undefined do(
        if doesFileExist n.filename then(
                appendIfUnique arPath n.filename
                nn =(getFilenamePath  n.filename)+(getFileFolderName  n.filename)+"_"+(randName 6)+(getFilenameType n.filename)
                renameFile n.filename nn
                n.filename = nn
                appendIfUnique arPath n.filename
            ) else(
                if (findItem  arPath n.filename) != 0 do n.filename = arPath[(findItem  arPath n.filename)+1]
                )
        )
    )
)   
-----------------------------------------------------------

среда, 7 сентября 2016 г.

Поиск и замена одинаковых текстур с разными именами.

Иногда бывает так, что в сложных сценах могут появится битмапы с одинаковыми текстурами, но с разными путями к ним, соответственно 3ds max может при просчете несколько раз загружать одну и ту же текстуру, в пустую расходуя память. Чтобы этого избежать и был написан следующий скрипт.

  ---------------------------------------------------
(
arBMT = getclassinstances bitmapTexture
   
fn unicImageData w_bmp = (
            arData = #()
            unicAr = #()
            work_bmp = openbitmap w_bmp.filename
            bmp_w = work_bmp.width
            bmp_h = work_bmp.height
            for i = 0.1 to 0.9 by 0.1 do(
                for j = 0.1 to 0.9 by 0.1 do(
                    p = (getpixels work_bmp [(i*bmp_w) as  integer,(j*bmp_h) as  integer] 1)[1]
                    append unicAr p
                    )
                )
            close work_bmp       
            join arData  #(bmp_w, bmp_h, GetFileSize w_bmp.filename, unicAr, getFilenameType w_bmp.filename)
            arData
    )
   
fn CompareArrays a b = if a.count == b.count AND (for i = 1 to a.count where a[i]!=b[i] collect i).count == 0 then return true else return false
   
fn CompareImages bm1 bm2 =(
            local a = unicImageData bm1
            local b = unicImageData bm2
            if a[1] ==b[1] and a[2] == b[2] and a[3] == b[3] and CompareArrays a[4] b[4] == true and a[5] == b[5] then return true else return false
    )

progressstart "Search and replacement..."
num = 1
nc = arBMT.count
    for i in arBMT do(
        progressupdate (num*100.0/nc)
        num+=1
       
        for j in arBMT do(
            if i.filename != j.filename do(
                if i.filename != undefined and j.filename != undefined do(
                    if doesFileExist i.filename and doesFileExist j.filename do(
                        if CompareImages i j do (print (j.filename+"    --->    "+i.filename);j.filename = i.filename;)
                        )
                    )
                )
            ) -- for j in arBMT do
           
        )--for i in arBMT do
progressend ()    

)
---------------------------------------------------

Функция unicImageData получает информацию о текстуре в виде массива #(ширина, высота, размер текстуры, RGB 100 точек с шагом 10% , разрешение текстуры)

Функция CompareArrays просто сравнивает два массива. Источник.

Функция CompareImages сравнивает две битмапы.

Далее, собственно, идет двойной перебор и сравнение битмапов в проекте с выводом результата в листенер.
На последнем проекте было сэкономлено не много, конечно, но около 10 мегабайт.

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

Полуавтоматический способ импорта и подготовки чертежей

Это что-то вроде заготовки для импорта и подготовки чертежей. В теле скрипта необходимо указать путь к dwg файлам,  также можно удалить указанные слои.
Функция ObjLayerName выполняет роль фильтра - не рассматриваются сплайны из указанных в функции слоев.
 --------------------------------------------------------
--mP = "23"

importFile ("path.dwg") #noPrompt

delete (for o in objects where superclassOf o == shape and (o.layer.name == "name layer 01" or o.layer.name == "name layer 02") collect o)

fn ObjLayerName obj = (
    temp = true
    if obj.layer.name == "acad-K2-fas" then temp = false
    if obj.layer.name == "acad-K1-pl" then temp = false
    if obj.layer.name == "acad-K2-pl" then temp = false
    if obj.layer.name == "acad-K3-pl" then temp = false
    if obj.layer.name == "acad-K4-pl" then temp = false
    if obj.layer.name == "acad-K5-pl" then temp = false
    if obj.layer.name == "acad-K6-pl" then temp = false
    temp
    )

arrShape = for o in objects where superclassOf o == shape and ObjLayerName o collect o
arrShapeC = arrShape.count

arByName = #()
for i = 1 to arrShapeC do if findItem arbyName arrShape[i].name ==  0 do append arByName arrShape[i].name
   
arSplineByName = #()
for n in arByName do(
    arNT = #()
    for j in arrShape do if j.name == n do append arNT j
    append arSplineByName arNT
    )
   
for s in arSplineByName do(
        convertToSplineShape s[1]
        for i=2 to s.count do(
            convertToSplineShape s[i]
            addAndWeld s[1] s[i] -1
            )
        updateShape s[1]
    )
   
arrShape = for o in objects where superclassOf o == shape and ObjLayerName o collect o
arrShapeC = arrShape.count
ms = arrShape[1]

convertToSplineShape ms
for i = 2 to arrShapeC do(
        convertToSplineShape arrShape[i]
        addAndWeld ms arrShape[i] -1
            )
updateShape ms

--delete blocks           
delete (for o in objects where superclassOf o != shape collect o)

--delete empty layers
--set 0-layer to be current
(layermanager.getlayer 0).current = true
for i = Layermanager.count-1 to 1 by-1 do(
(layermanager.getLayer i).nodes &theNodes
if thenodes.count== 0 do LayerManager.deleteLayerbyname (layermanager.getLayer i).name
)

select ms
ms.name = "K6_floor_"+mP

------add newObj to layer
theLayer = layermanager.getLayerFromName $K6_floor_01.layer.name
theLayer.addNode ms
 --------------------------------------------------------