Render.ru

Работа с VrayProxy.

Александр Якушев

Активный участник
Рейтинг
7
#1
Скрипт должен уметь находить, коллектить и переключать врэй прокси.
Потестируйте, пожалуйста. Может быть поможете улучшить.

Код:
(--------------- start ProxySimplifier
--ScriptProxySimplifier_v1
--tested in Max 2013
rollout ProxySimplifier "ProxySimplifier"
( --start rollout ProxySimplifier
group "Finder:"
	(
	editText thePathText text:"Path is undefined" width:208 align:#left readOnly:true 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:"Path is undefined" width:120 align:#center readOnly:true across: 4
	button btn_browse2 "<---- Browse" width:100  height:18 align:#center 
	Checkbox chBox_update "Relink" width:60 align:#center checked:true
	button DoIt2 "Collect Proxy..." width:120  height:18 align:#center color:red	
	)
group "Switcher:"
	(
		button btn3 "On" width:100  height:18 align:#center across: 3
		button btn4 "Off" width:100  height:18 align:#center
		Checkbox chBox2 "Selected only" width:60 align:#center checked:true
		)

local theFolderPathCollector = undefined
local misProxyPathes = #()
local all_dir = #()
local theFolderPath = undefined
local theBooleanSwitch = true
	
	
--fn
fn exist_f the_path = try((getFiles the_path).count == 1)catch(undefined)

fn getFolderPathes root = 
		(	
			local dir_array = GetDirectories (root+"*")
			join all_dir dir_array
			
			if dir_array.count != 0 do
			(
				for the_path in dir_array do
					(
						join dir_array (GetDirectories the_path)
						getFolderPathes the_path
						)
				)
			return all_dir
			)	
			
fn KillMisProxies theProxy	=
(
	local	theProxyPath = try(theProxy.filename)catch(undefined)
	if theProxyPath != undefined and exist_f theProxyPath == false do 
	(
		local theProxyName = fileNameFromPath theProxyPath
		for folder in all_dir do
		(
			local theFiles = getFiles (folder + "*")
			for file in theFiles do if (fileNameFromPath file) == theProxyName do theProxy.filename = file
			)
		)		
	)
--fn
--Switcher
on btn3 pressed do	
(
	if chBox2.state == true then for i in selected where classOf i == VrayProxy do (i.display = 1) else for i in objects where classOf i == VrayProxy do (i.display = 1)
	)
on btn4 pressed do	
(
	if chBox2.state == true then for i in selected where classOf i == VrayProxy do (i.display = 0) else for i in objects where classOf i == VrayProxy do (i.display = 0)
	)	
--Switcher	
	
--collector
on btn_browse2 pressed do
	(
		theFolderPathCollector= getSavePath  caption: "Pick folder" initialDir:"C:\\"
		thePathText2.text = theFolderPathCollector as string
		)		
	
on DoIt2 pressed do
	(--start DoIt pressed
			local theAllGoodProxy = #()

			for i in objects do if classOf i == VRayProxy do
			(
				if exist_f i.filename == true do append theAllGoodProxy i 
				)
			
			if theFolderPathCollector != undefined then
			(
				for i in theAllGoodProxy do
				(
					local theP1 = i.filename
					local theP2 =theFolderPathCollector + "\\" + (filenameFromPath theP1)
					try(copyFile theP1 theP2) catch(print ("don't copy - " + i.filename as string) )
					if chBox_update.state == true do try(i.filename = theP2)catch()
					)
				)else messageBox "Please specify path."
		)--end DoIt pressed
--end collector	
	
on btn_browse pressed do
	(
		theFolderPath = getSavePath  caption: "Pick folder" initialDir:"C:/"
		thePathText.text = theFolderPath as string
		)			

on findMisProxy pressed do
(
	misProxyPathes = #()
	
	lbl_01.text = "Reading proxy..."
	for i in objects do if classOf i == VRayProxy do
	(
		if exist_f i.filename == false do	append misProxyPathes i.filename
		try(lbl_01.text = i.name)catch()	
		)
	mP.items = misProxyPathes
	lbl_01.text = "please use DoubleClick for getting proxy"
	)
	
on mP doubleClicked itm do	
(
	local the_obj = #(), the_array = #()
	print misProxyPathes[itm] as string
	for i in objects do if classOf i == VRayProxy do
	(
		if exist_f i.filename == false and misProxyPathes[itm] == i.filename do append the_obj i
		try(lbl_01.text = i.name)catch()	
		)
	select the_obj
	lbl_01.text = "please use DoubleClick for getting proxy"
	)	
	
on DoIt pressed do
(
	all_dir = #()
	if theFolderPath != undefined then
	(
			lbl_01.text = "Reading pathes..."
			getFolderPathes theFolderPath
			for i in objects do
				(
					KillMisProxies i
					try(lbl_01.text = i.name)catch()
					)
			lbl_01.text = "please use DoubleClick for getting proxy"
		)
		else messageBox "Please pick path..."
	)
	
)--end rollout ProxySimplifier
CreateDialog ProxySimplifier 500 350
)------------- end ProxySimplifier
 

Александр Якушев

Активный участник
Рейтинг
7
#2
Нечто подобное есть и для текстур. Столкнулся с такой проблемой - немного долго производит перебор материалов или объектов(особенно в больших проектах). Возможно ли как-то ускорить этот процесс? И возможно ли убрать "зависание" 3d макса во время поиска или перебора?
 

Александр Якушев

Активный участник
Рейтинг
7
#3
Немного поменял функцию получения массива путей к папкам по указанному пути:
Код:
fn getFolderPathes root = 
(
	local dir_array = GetDirectories (root+"*")
	if dir_array.count != 0 do
	(
		for i in dir_array do
		(
			append all_dir i
			getFolderPathes i
			)
		)
	return all_dir
	)
Не подскажете, может быть можно как-то ускорить процесс сканирования и получения массива папок?
 

Александр Якушев

Активный участник
Рейтинг
7
#4
http://files.mail.ru/0A7F1D559CDC4147A77BF130348A940E - много ошибок нашел, поправил, здесь последняя тестовая, вроде рабочая, версия.
Помогите, пожалуйста, ускорить перебор и замену путей в блоке:
Код:
on DoIt pressed do
(
	all_dir = #()
	if theFolderPath != undefined then
	(
			lbl_01.text = "Reading pathes..."
			getFolderPathes theFolderPath
			for i in objects do
			(
				if classOf i == VrayProxy do
				(
					if i.filename != undefined and i.filename != getFiles (i.filename) do 
					(
						local theProxyName = fileNameFromPath i.filename
						for p in all_dir do
						(
							local theFiles = getFiles (p + "*")
							for f in theFiles do 
							(
								if theProxyName == (fileNameFromPath f) do i.filename = f
								) 
							)
						)		
						
					try(lbl_01.text = i.name)catch()
					)
				)
			lbl_01.text = "please use DoubleClick for getting proxy"
		)
		else messageBox "Please pick path..."
	)
Уж больно долго, особенно, если в сцене несколько тысяч проксей. И еще, после N-го объекта макс виснет и перестает отображать текст
Код:
 try(lbl_01.text = i.name)catch()
, как можно это побороть без использования прогресс бара?
 
Рейтинг
31
#6
Александр, я сделал кое-какую оптимизацию твоего скрипта (версия из поста #4). Результат можно забрать здесь. Вроде стало работать немного побыстрее. К сожалению у меня сцены с "несколько тысяч проксей", а делать специально - лениво. Так что протестируй по скорости и отпишись по результатам - мне самому интересно.

Мои комментарии на английском во избежание глюков. Английский наверное плохой, но транслит я вообще не перевариваю :confused:

Несколько попутных замечаний по скрипту.

1. Я бы ещё поработал над общей логикой. Некоторые вещи не очевидны. Всё становится понятным только после анализа кода, а это неправильно.

2. Не надо без особой нужды использовать try...catch. Это дорогая конструкция в плане производительности.

3. При анализе программы мне очень не хватало комментариев. Комментарии - это благо.
 

Александр Якушев

Активный участник
Рейтинг
7
#7
Black Sphinx спасибо!
Не ожидал такой прыти, но реальное ускорение в 10-ки раз!!! Старая версия вешает макс, примерно, на 15-20 мин. Твоя оптимизированная - за минуту! Поиск проблемных прокси тоже ускорился, стал, считай, мгновенным, даже в тяжелых проектах.
Спасибо за советы!
 
Рейтинг
31
#8
Нашёл ещё одну оптимизацию:
Код:
	/*
	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"
		)
	)
В предыдущей версии просматривался весь список папок в поисках меш-файла для прокси и в прокси подставлялся последний найденный.
Здесь поиск идёт только до первого найденного файла.

Кстати, здесь имеется и потенциальная засада. Скрипт (ни в том, ни в другом варианте) не отрабатывает ситуацию, когда в дереве папок имеется несколько одноимённых меш-файлов, которые могут быть различными. Какова должна быть логика работы в этом случае - решать тебе.

Можно сделать ещё одну оптимизацию, при условии, что дерево папок для поиска меш-файлов не изменяется в течение рабочей сессии 3ds max. (А дерево папок может измениться вследствие внешних, по отношению к Максу, причин.) Поскольку массив all_dir у тебя объявлен глобальным, то он существует от первого вызова этого скрипта до выхода из Макса. Можно его не обновлять каждый раз при нажатии на кнопку "Find Proxy", а использовать существующий. Понятно, что при изменении корня дерева папок, массив придётся сбросить и перечитать информацию с диска заново.

Есть в этом скрипте одна пакость, которую, видимо, просто побороть не удастся. Если в качестве корня для Finder'а указать, например, "C:\", то Макс виснет наглухо. Похоже, что скрипт при заполнении массива all_dir полностью расходует всю доступную ему память, после чего Макс виснет.
 

Александр Якушев

Активный участник
Рейтинг
7
#9
Есть в этом скрипте одна пакость, которую, видимо, просто побороть не удастся. Если в качестве корня для Finder'а указать, например, "C:\", то Макс виснет наглухо. Похоже, что скрипт при заполнении массива all_dir полностью расходует всю доступную ему память, после чего Макс виснет.
Да тоже замечал нечто подобное. Кстати, не знаете как можно эту самую память увеличить?

Спасибо за советы. Сейчас немного занят, но обязательно постараюсь доделать и отписаться. К тому же тут друзья посоветовали бесплатный скрипт relink bitmaps - он оказывается и прокси умеет находить. Очень шустрый, как говорится лучшее враг хорошего). Дело только за коллектором прокси, но это уже можно архивом создавать, потом поиском по типу выбирать.

Есть еще одна идея оптимизации - использовать collect для выборки проблемных проксей и потом работать ТОЛЬКО с ними. Т.е. избавится от перебора ВСЕХ объектов и проверки classOf i == VrayProxy. Но это тоже попозже, сейчас по работе загрузили.

Может быть потом нечто подобное и для поинт кэшей реализовать и прочего)
 
Рейтинг
31
#10
Кстати, не знаете как можно эту самую память увеличить?
Почитай в справке по МаксСкрипту раздел Memory Allocation and Garbage Collection.

К тому же тут друзья посоветовали бесплатный скрипт relink bitmaps - он оказывается и прокси умеет находить.
Есть и встроенная фича. Asset Tracking называется (Application Menu > References > Asset Tracking).

Есть еще одна идея оптимизации - использовать collect для выборки проблемных проксей и потом работать ТОЛЬКО с ними. Т.е. избавится от перебора ВСЕХ объектов и проверки classOf i == VrayProxy.
Я собственно это и сделал, если ты анализировал код скрипта после оптимизации. Только без применения collect, поскольку в этом же цикле делается ещё одно действие.
 

Александр Якушев

Активный участник
Рейтинг
7
#11
Я собственно это и сделал, если ты анализировал код скрипта после оптимизации. Только без применения collect, поскольку в этом же цикле делается ещё одно действие.
Код только смотрел, но не анализировал) Может быть ошибаюсь, но хочу попробовать использовать преимущественно collect. В начале коллектом создать массив данных, убрать все лишнее и только потом работать без лишних действий. Опят же повторюсь, код еще не анализировал, может быть ваша оптимизация лучшая. Тот же релинк битмапс работает на тех же проектах все-таки быстрее, есть куда стремиться) Думаю еще таймстампом время выполнения сравнивать. Потом буду анализировать подробнее и отпишусь.

Спасибо, но встроенную фичу тоже юзаем и ресурколлектором пользуемся и прочее. Asset Tracking не умеет искать. Ручками перебивать в нем неудобно.
 

Александр Якушев

Активный участник
Рейтинг
7
#12
Проанализировал. Спасибо у вас очень круто написано. По скорости collect проигрывает.
Единственное внес небольшое изменение в логику. Поменял функцию:
вер 3-01:
Код:
	fn findMissingProxy = (
		misProxyPathes = #()
		wrongProxyObjects = #()
		
		lbl_01.text = "Reading proxy..."
		for i in objects where classOf i == VRayProxy do (
			if doesFileExist i.filename == false do (
				appendIfUnique misProxyPathes i.filename
				append wrongProxyObjects i
			-- lbl_01.text = i.name -- comment for performanse reasons
			)
		)
		sort misProxyPathes
		mP.items = misProxyPathes
		lbl_01.text = "please use DoubleClick for getting proxy"
	)
новая версия:
Код:
	fn findMissingProxy = (
		misProxyPathes = #()
		wrongProxyObjects = #()
		
		lbl_01.text = "Reading proxy..."
			 for i in objects where classOf i == VrayProxy  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"
	)
На больших проектах, более 7000 тысяч объектов удалось время сократить более чем в двое (с ~14 сек до ~6). Смысл в том, что отсекаются из перебора инстансные прокси с одинаковыми путями.

Есть в этом скрипте одна пакость, которую, видимо, просто побороть не удастся. Если в качестве корня для Finder'а указать, например, "C:\", то Макс виснет наглухо. Похоже, что скрипт при заполнении массива all_dir полностью расходует всю доступную ему память, после чего Макс виснет.
Это да. У меня тоже, если, к примеру, целиком папку с проектами указать, то виснет напрочь. Пока не удалось решить проблему. Что удивительно, тот же релинк битмапс, прекрасно папку с проектами переваривает целиком и довольно шустро.
 

Александр Якушев

Активный участник
Рейтинг
7
#13
Если отдельно функцию getFolderPathes тестить, то прекрасно и быстро работает :)
Код:
clearlistener()
all_dir = #()

fn getFolderPathes root = 
	(
		dir_array = #(root)
		for d in dir_array do
			join dir_array (getDirectories (d+"/*"))
		dir_array
	)
	
start = timestamp()
--test code
all_dir = getFolderPathes ("C:\\")
--test code
end = timestamp()

format " % seconds\n" ((end-start)/1000.0)
Собственно, 44805 папок за 38.9 сек - неплохо и без зависаний.
Собака, наверное, где-то в последующем коде зарыта.
 
Рейтинг
31
#14
Смысл в том, что отсекаются из перебора инстансные прокси с одинаковыми путями.
Согласен. Про инстансные прокси я как-то вообще не подумал. Единственно, мне не очень нравится использование функции findItem с массивом строк. Есть у меня подозрение, что она работает не очень быстро. Впрочем, я могу ошибаться. А если не ошибаюсь - это еще один путь к оптимизации ))
Замена использования в цикле appendIfUnique на makeUniqueArray после цикла дала выигрыш? Я колебался какую функцию использовать.

Что удивительно, тот же релинк битмапс, прекрасно папку с проектами переваривает целиком и довольно шустро.
А посмотреть что у релинк битмапс в нутре никак не получается?

Если отдельно функцию getFolderPathes тестить, то прекрасно и быстро работает
Я тоже пробовал отдельно тестить эту функцию. Код почти полностью аналогичен твоему. Но у меня папок на c:\ почти 80 000. Виснет, подлюка. Точнее говоря, task manager показывает некую активность (порядка 13% загрузки CPU у Макса и постоянное увеличение занимаемой памяти), но сам Макс признаков жизни в течение 10-12 минут не подаёт. В результате чего был убиваем из того же task manager.

Есть у меня, правда, подозрение, что функция спотыкается о линки типа Application Data или, возможно, на циклических линках. (Имеется ввиду, что Win7 установлена на C:). Но это надо отдельно исследовать.
 
Рейтинг
31
#15
Нашёл я багу в функции обхода дерева папок. Причём бага оказалась в MaxScript. В функции getDirectories.

Оказалось, что если папка-1 содержит папку с именем вида {12345678-1234-1234-1234-1234567890ab}, т.е. GUID в фигурных скобках, то функция getDirectories возвращает в массиве имя родительской папки: #("диск:\путь\папка-1\") вместо #("диск:\путь\папка-1\{12345678-1234-1234-1234-1234567890ab}\").

В результате в функции getFolderPathes получается мёртвый цикл.
 

Александр Якушев

Активный участник
Рейтинг
7
#16
Извините, не мог ответить.
Замена использования в цикле appendIfUnique на makeUniqueArray после цикла дала выигрыш? Я колебался какую функцию использовать.
Непосредственно не тестил, просто подумал, что каждый раз проверять на уникальность возможно дольше, чем в конце разом.

А посмотреть что у релинк битмапс в нутре никак не получается?
Неее никак. Закодировано... - если что, вот здесь можно скачать http://www.colinsenner.com/scripts/relink-bitmaps.

Нашёл я багу в функции обхода дерева папок.
Спасибо! Тоже потестил, - напрочь зависает, даже предусмотренный ESC не срабатывает. Вы не придумали способ обойти этот баг?
Что-то у меня пока не получается(
 
Рейтинг
31
#17
Есть одно решение на счёт getDirectories. Но через жопу, медленно и неэффективно.
Код:
fn getDirs root = (
	tmpfile = sysInfo.tempdir + "hiddencmdout.tmp"
	dirs = #()
	HiddenDOSCommand (@"dir /b/o:n/a:d > " + tmpfile) startpath:root exitCode:&exitcode
	if exitcode == 0 do (
		fs = openFile tmpfile mode:"rt"
		while not eof fs do (
			dirname = readline fs
			append dirs (root + dirname + "\\")
		)
		close fs
	)
	dirs
)

fn getFolderPathes root = 
(
	dir_array = #(root)
	d_arr = #()
	for d in dir_array do (
		join dir_array (getDirs d)
	)
	dir_array
)

root = @"E:\"

start = timestamp() 
da = getFolderPathes root
end = timestamp() 

format " % seconds\n" ((end-start)/1000.0) 
for d in da do print d
format " % seconds\n" ((end-start)/1000.0)
2021 папка за 38.411 секунды. Не быстро однако ((

В принципе на эту тему можно ещё подумать в направлении: (1) читать список папок через getDirectories, (2) поэлементно сравнить возвращённый список с папкой-аргументом, (3) если нет совпадений, то продолжаем, (4) если есть совпадение, то для этой папки применяем функцию getDirs и продолжаем. Может получиться побыстрее, чем только через getDirs.
 
Рейтинг
31
#18
Неее никак. Закодировано... - если что, вот здесь можно скачать http://www.colinsenner.com/scripts/relink-bitmaps.
Скачал. Не так уж там и закодировано )) Но разбираться пока времени нет - там больше 50 килобайт исходников.
Архивчик выложил сюда. Будет жить до 10 июня. Обязательно посмотри.
А mzp - это обычный zip-архив. Просто расширение поменяли.
 

Александр Якушев

Активный участник
Рейтинг
7
#19
А mzp - это обычный zip-архив.
Круто!!!) Спасибо.
Есть одно решение на счёт getDirectories.
Спасибо! Хотя бы такое. Код для меня сложный, не могу сходу разобраться. Попозже обязательно разберусь, может быть удастся ускорить. К тому же релинк битмапс открыли)
 
Рейтинг
31
#20
Код для меня сложный, не могу сходу разобраться.
Нет там ничего сложного. В двух словах:

1. Вызов системной getDirectories заменён на вызов getDirs.

2. В getDirs вызывается досовская команда dir с ключами (вывод только папок в коротком формате в алфавитном порядке), вывод которой записывается во временный файл в каталоге %temp%. Этот файл тут же открывается, считывается построчно и каждая папка (т.е. считанная строка) добавляется к массиву.

У меня очень сильное подозрение, что Relink Bitmaps на папке вида {12345678-1234-1234-1234-1234567890ab} тоже подохнет.
 
Сверху