반응형

가끔 아니 어쩌면 자주 3D 공간안에서 물체의 위치를 지정할때 이 물체가 저쪽에 닿는 면에 딱 위치하고 싶은데... 할때가 있죠? 

예를 들면 나무나 돌덩이 이런걸 랜덤하게 막 생성한 다음 굴곡진 지형에 모두 딱 안착시키고 싶다거나요.

또는 어떤 물체를 기준으로 어떤 방향으로 뻗어 나갈때 무엇인가 닿는 물체가 있는지 검사하고 싶을때도 있지요.

이때 사용되는 개체가 바로 Ray 입니다.

실제로 Ray를 알기 전과 알고난 뒤는 정말 다른 세계라고 할수 있겠습니다.

 

자 오늘은 Ray 에 대하여 알아보도록 하겠습니다.

3D 는 정말 놀라울 정도로 기하학적인 수학의 결정체라 할 수 있겠습니다. 이를 구현하기 위하여 정말 얼마만큼의 수학적 노력이 있었는지 상상이 안될 정도 입니다.

오늘은 그 결정체중의 하나인 Ray에 대하여 알아볼텐데요. 3DS MAX 에서 Ray 는 두가지 명령어를 통해 사용됩니다.

intersectRay 와 intersectRayEx, intersectRayScene 이렇게 세가지 입니다. 사용하는 목적에 따라 구분하여 사용하시면 되는데요. 각각은 아래와 같습니다.

  • intersectRay : Ray 와 대상물체를 지정한 뒤 부딧히는 위치를 알아낸다.
  • intersectRayEx : 위와 동일하며 부딪힌 Face 정보와 부딪힌 면의 normal 정보를 돌려준다
  • intersectRayScene : 대상 개체를 지정하지 않고 Scene 내의 어떤 물체든 부딪힌 정보를 돌려준다. (느림)

사용하는 방법은 간단한데요, 먼저 Ray 를 생성해주고 필요하면 Ray의 출발 위치(pos)와 방향(dir)을 변경할 수 있습니다.

myRay = ray [0,0,0] [0,0,-1]

ray 를 생성하는 구문 인데요, [0,0,0] 위치를 출발점으로 하고 [0,0,-1] 을 방향으로 하는 ray 가 만들어 지게 됩니다. [0,0,-1] 이라 하면 z 축을 기준으로 아래쪽을 바라보는 방향이라고 보면 되겠죠? 

 

이렇게 만들어진 Ray를 위의 명령어와 함께 사용해볼까요?

임시로 테스트용 Scene 을 만들어 봄

먼저 위와 같은 Scene 을 만들어서 테스트 해보려 합니다. 노란색 공이 지면에 부딪히는 위치로 이동 시킬거에요. 중간에 네모 박스가 있지만 네모박스는 무시하고 지면에 위치시켜야 한다면 아래와 같이 intersectray 를 이용하여 위치시킬 수 있습니다.

-- ray 를 생성해 준다
myRay = ray [0,0,0] [0,0,-1]

-- 이동시킬 물체를 지정하자
obj = $Sphere001

-- ray 의 출발 위치를 물체의 중심으로 설정해보자
myRay.pos = obj.pos

-- 위치 검출에 사용될 지형을 지정해보자
tern = $terrain_01

-- intersectray 를 이용하여 지형과 부딪히는 위치를 찾아내자
interResult = intersectray tern myRay

-- 어떤 결과가 나왔는지 print 를 이용해서 살펴보자
print(interResult)

-- 부딪힌 위치에 오브젝트를 위치시키자
obj.pos = interResult.pos

요렇게 하면 어떻게 되냐면요

지면에 정확하게 안착함

요렇게 노란 공이 지면에 달라 붙게 됩니다. 코드에 주석을 달아 놓았으니 따로 설명은 안드려도 될 것 같은데요. 한가지만 설명해 드리자면 intersectray의 결과값을 보면 (ray [-1.62331,11.1008,1.22501] [-0.407511,-0.107746,0.906822]) 요런 식으로 ray 의 형식으로 결과가 나옵니다. ray 는 pos 와 dir 의 속성이 있지 않습니까?? 여기서 결과값의 pos 정보가 바로 부딧힌 면의 위치 정보가 됩니다. 그 뒤에 나오는 [-0.407511,-0.107746,0.906822] 는 부딪힌 면의 normal 값으로 만약 공이 부딪힌 다음 튕겨나간다거나 하는 다음 동작이 필요하다면 반드시 필요한 정보이지요.

어쨌든 intersectray 만 보자면 부딪힌 위치와 각도를 ray 의 형태로 돌려준다는 것을 알 수 있었습니다. 특징이라 하면 , 특정 개체를 대상으로 하기 때문에 속도가 매우 빠릅니다. 사실상 성능을 고려하지 않아도 되는 함수죠.

그런데 만약 부딪힌 면이 어디인지 알아야 하고 부딪힌 개체에도 뭔가 추가적인 액션이 필요할 수 있겠습니다. 사람일은 모르는거자나요? 누군가는 저 지형개체에 어떤 face에 물체가 부딪혔는지를 알아야 할수도 있지 않겠습니까? 이 때 intersectrayex 라는 함수를 이용하게 됩니다.

이번에는 intersectray 대신에 intersectrayex 를 사용해 볼 겁니다. 사용방법은 동일하고요. intersectrayEx [node] [ray] 와 같이 사용해 주면 됩니다.

-- intersectray 를 이용하여 지형과 부딪히는 위치를 찾아내자
interResult = intersectrayex tern myRay

-- 어떤 결과가 나왔는지 print 를 이용해서 살펴보자
print(interResult)

-- 결과는? 
#((ray [-1.62331,11.1008,1.22501] [-0.382998,-0.128105,0.914823]), 240, [0.383149,0.282177,0.334673])

아까와는 결과가 조금 다르죠? #(oo,oo,oo) 형태로 나온걸 보면 배열로 결과가 나온걸 알 수 있습니다. 총 3개의 리턴값이 나왔는데요.

  • (ray [-1.62331,11.1008,1.22501] [-0.382998,-0.128105,0.914823])
  • 240
  • [0.383149,0.282177,0.334673]

intersectray 와 마찬가지로 첫번째 결과에는 부딪힌 면의 위치, 방향을 담는 ray 정보가, 두번째에는 어떤 숫자가 나왔고요, 세번째에는 point3 형식으로 좌표가 나왔는데, 딱보니 위에 ray 에 부딪힌 면의 방향이라는 것을 알 수 있겠습니다. 두번째 숫자는 뭐냐고요?

부딪힌 face 의 index 임을 알 수 있다.

바로 부딪힌 mesh 의 face 인덱스 입니다. 240 번이죠? 바로 저부분에 부딪힌 것 입니다. (이번에는 이동은 하지 않았기 때문에..) 만약 동일하게 이동해야 한다면 배열 안의 개체에서 위치 정보를 뽑아내야 하므로 interResult[1].pos 와 같은 방법으로 부딪힌 면의 위치를 가져오면 됩니다.

이런 기능을 이용해서 부딪힌 면의 색상을 바꾸어 준다거나 해당 face를 삭제하는 등의 기능을 넣을 수 있겠지요? 뭐 레이저총에 맞은 face 가 폭파되며 사라지는 등의...  네.. 그럴때 꼭 필요한 훌륭한 명령어 입니다.

참고로 intersectray 나 intersectrayex 는 mesh 오브젝트에서 정확한 정보를 받아 올 수 있습니다. 가끔 기본 primatine 에 modifier 로 edit_poly 같은것을 씌운 경우에는 동작하지 않을 수 있으니 만약 결과값이 undefined 와 같은 값이 나온다면 검출에 사용된 모델링을 mesh 로 변경한 뒤 (convertTomesh obj) 검사를 진행하시면 됩니다.

 

끝으로 intersectRayScene 은 위와는 다르게 Ray 가 어떤 개체에 부딪히는지 알수 없을 때 사용합니다.

모두 동일하고 해당 명령만 intersectRayScene 으로 변경해서 돌려볼까요?

interResult = intersectrayScene myRay

요렇게 말이죠. 이번에는 대상을 특정하지 않기 때문에 함수 뒤에 인자가 ray 개체 하나만 들어갔습니다. 

결과를 볼까요? 

#(#($Editable_Mesh:terrain_01 @ [-4.616531,-2.612816,0.000000], (ray [-1.62331,11.1008,1.22501] [-0.382998,-0.128105,0.914823])), #($Sphere:Sphere001 @ [-1.623309,11.100846,65.260162], (ray [-1.62331,11.1008,61.9389] [0,0,-1])), #($Box:Box001 @ [15.646946,18.567043,19.666662], (ray [-1.62331,11.1008,26.4499] [0,0,1])))

뭔가 무시무시하게 길게 나왔죠?

이녀석도 역시 배열 개체이므로 하나씩 보면 

#($Editable_Mesh:terrain_01 @ [-4.616531,-2.612816,0.000000], (ray [-1.62331,11.1008,1.22501] [-0.382998,-0.128105,0.914823]))
#($Sphere:Sphere001 @ [-1.623309,11.100846,65.260162], (ray [-1.62331,11.1008,61.9389] [0,0,-1]))
#($Box:Box001 @ [15.646946,18.567043,19.666662], (ray [-1.62331,11.1008,26.4499] [0,0,1]))

요렇게 3개의 개체와 부딪혔다는 정보를 돌려준 것을 알 수 있습니다. 심지어는 본인(sphere) 에도 부딪힌 정보를 돌려 받았다는 것을 알 수 있네요 ㄷㄷㄷ. (저걸 원한건 아닌데) 부딪힌 정보를 보면 역시 내부 정보 역시 배열인데요, 첫번째는 부딪힌 대상 개체, 두번째는 부딪힌 위치정보 (ray 속성) 라는걸 알 수 있습니다.

만약 개체에 어떤 특정할만한 속성이 있다면 해당 개체에 물체를 위치 시킬 수 있습니다.

-- ray 를 생성해 준다
myRay = ray [0,0,0] [0,0,-1]

-- 이동시킬 물체를 지정하자
obj = $Sphere001

-- ray 의 출발 위치를 물체의 중심으로 설정해보자
myRay.pos = obj.pos

-- 위치 검출에 사용될 지형을 지정해보자
tern = $terrain_01

-- intersectray 를 이용하여 지형과 부딪히는 위치를 찾아내자
interResult = intersectrayScene myRay

-- 어떤 결과가 나왔는지 print 를 이용해서 살펴보자
print(interResult)

for i = 1 to interResult.count do
(
	testObj = interResult[i][1]
	if (findString testObj.name "Box") != undefined then
	(
		obj.pos = interResult[i][2].pos
	)
)

요렇게요. 

코드에서는 Box 라는 이름을 포함하는 개체를 만나면 해당 개체에 위치하도록 하는 코드 입니다.

이게 사실 한두개일 때는 간단한 이야기 인데 지면에 위치 시켜야 하는 개체가 수십개 ~ 수백개가 되면 일이 커집니다. 그럴때는 스크립트가 필요한 시점인거죠.

예를 들면 이렇게요..

이 많은 공들을 지면에 붙이라고?

 

모두 sphere 라는 이름을 갖고 있기때문에 간단하게 처리할 수 있겠네요. 지면에 닿는 개체는 지면에 닿도록 하고 box 에 닿는 개체는 box 에 위치시키도록 하겠습니다. 

sphere 의 중심을 기준으로 하다보니 약간 파뭍히는 대상이 있었는데요, sphere 의 반지름 만큼 위쪽으로 위치하도록 코드를 짜 보겠습니다.

-- ray 를 생성해 준다
myRay = ray [0,0,0] [0,0,-1]

-- 이름이 sphere 로 시작되는 모든 개체를 변수 objs 에 담자.
objs = execute("$Sphere*")

for obj in objs do
(
	-- 각 sphere 의 중심점으로 ray 의 위치를 지정함
	myRay.pos = obj.pos
	
	-- intersectray 를 이용하여 지형과 부딪히는 위치를 찾아내자
	interResult = intersectrayScene myRay


	for i = 1 to interResult.count do
	(
		testObj = interResult[i][1]
		if (findString testObj.name "Box") != undefined then
		(
			obj.pos = interResult[i][2].pos
			-- 파묻히지 않도록 반지름 만큼 위로 올림
			obj.pos.z += obj.radius
		)
		else if (findString testObj.name "terrain") != undefined then
		(
			--- 부딪힌 정보가 terrain 일때 해당 위치로 이동 시킴 
			obj.pos = interResult[i][2].pos
		-- 파묻히지 않도록 반지름 만큼 위로 올림
			obj.pos.z += obj.radius
		)
	)
)

 

뭐 별거 없죠? 위에서 설명한 것을 반복문만 추가했을 뿐 입니다.

결과는요?

한번에 모두 지면에 안착 성공

요렇게 예쁘게 안착된 것을 확인 할 수 있습니다.

물론 저는 본 기능을 설명 드리기 위해 위와 같은 예제를 들었지만요. 실제 제가 사용하는 함수에는 몇초만에 수천번 이상 위치를 검출하며 ray 가 사용되는 케이스가 허다분 합니다.

저는 주로 자동으로 모델링을 할때 모델링의 위치, 크기, 배치, 부딪힘, 확장 범위 등을 검사할때 사용하는데요, 이런 검사들을 통해 사람이 손을 데지 않고도 아주 정교한 모델링을 할 수 있는 근간이 됩니다.

참고로 intersectRayScene 의 경우 scene 의 모든 개체에 대하여 검사가 이루어지기 때문에 Scene 내에 아주 많은(수만개 이상의) node 들이 자리잡고 있는 경우에는 속도가 매우 느려질 수 있습니다. 그래서 시작 위치를 기준으로 반경내에 들어오는 대상을 먼저 골라낸 다음 속도가 빠른 intersectRay 를 이용하여 각각 부딪힌 위치를 검출하는 방식으로 사용하고 있습니다. 프로그래밍이라는게 그렇자나요. ㅎ 높은 성능과 효율성을 위해 다양하고 창의적인 방법으로 접근해 가는게 바로 프로그래밍의 묘미이지요.

오늘은 이만 마칩니다. 내용이 길어기니 힘드네요 ㅋ

2021.03.24 - [DEV/MAX SCRIPT] - 3DS MAX 스크립트 레퍼런스 (헬프파일) 다운 받기

 

3DS MAX 스크립트 레퍼런스 (헬프파일) 다운 받기

스크립트 초급자든 중급자든 반드시 필요한 것 하나를 꼽으라면 저는 레퍼런스라고 생각합니다. 기존에는 3DS MAX 설치 시에 함께 설치되어 스크립트 작성 창에서 F1 키를 누르면 chm 파일 형식의

diy-dev-design.tistory.com

2021.02.24 - [DEV/MAX SCRIPT] - 3da max script 점과 점 사이의 거리 구하기

 

3da max script 점과 점 사이의 거리 구하기

간단하게 포스팅을 남기려고 합니다. 제목 그대로 버텍스 간 거리 구하기 입니다. 아니 정확히는 두개의 point3 사이의 거리를 구하는 방법을 소개해 드릴까 합니다. 그리고 용용 편으로 두 점사

diy-dev-design.tistory.com

2020.04.29 - [DEV/MAX SCRIPT] - 3DS MAX 스크립트로 개체 선택하기

 

3DS MAX 스크립트로 개체 선택하기

Max Script 를 이용하여 무엇인가를 하려면 필수 요소라고 할 수 있는 것 중의 하나가 개체를 선택하거나 지정하는 방법입니다. 여기서 개체는 3D Scene 안의 오브젝트, 스플라인, 헬퍼 등과 같은 사

diy-dev-design.tistory.com

 

반응형
반응형

간단하게 포스팅을 남기려고 합니다. 

제목 그대로 버텍스 간 거리 구하기 입니다. 아니 정확히는 두개의 point3 사이의 거리를 구하는 방법을 소개해 드릴까 합니다. 

그리고 용용 편으로 두 점사이의 어떤 특정 위치의 점의 좌표를 구하는 방법도 함께 알려 드리도록 하겠습니다.

어떤 물체가 점 A 로 부터 점 B 까지 움직이거나 A,B 사이의 특정위치를 알기 위한 계산을 하려면 필수적인 연산인데요.

예를들면 점 A 와 점 B 의 한 가운데가 되는 위치, 또는 점 A 로부터 점 B 까지의 0.2 정도 의 위치를 계산 하는 방법을 소개해 드릴까 합니다.

 

 

점과 점사이의 거리 구하기

점과 점 사이의 거리는 max script 에서는 아주 간단하게 구할 수 있답니다. 기본 명령어가 있기 때문인데요.

 

그림과 같이 박스 2개가 있는 경우 정 가운데 새로운 박스를 하나 만들어야 할 경우 사용하면 됩니다.

물론 저는 예를 box 로 들었지만 개체 A 의 어떤 점과 개체 B 의 어떤 점 사이의 위치를 구할때 동일하게 사용할 수 있는 예제 입니다.

boxA = $Box01
boxB = $Box02

ptA = boxA.center
ptB = boxB.center

distAB = distance ptA ptB -- 두점 사이의 거리를 계산한다.

간단하져?

distance 라는 명령을 이용하여 간단하게 두 점 사이의 거리를 구할 수 있습니다.

 

이제 정 가운데의 위치를 알아 볼까요?

공간상에서 위치를 이동시킨 값을 얻는 방법은 아래와 같습니다.

  • 최초 시작위치 + (이동할 방향 * 이동할 거리)

여기서 이동할 방향은 vector 라는 생소한 개념인데요. 나중에 설명을 드리도록 하겠습니다. 일단 vector 라고 하면 방향+거리 라고 간단하게 생각하시면 되겠습니다.

위의 예제를 활용하여 정중앙의 위치를 찾아보겠습니다.

boxA = $Box01
boxB = $Box02

ptA = boxA.center -- boxA 의 중앙 위치 (point3)
ptB = boxB.center -- boxB 의 중앙 위치 (point3)

distAB = distance ptA ptB -- 두점 사이의 거리를 계산한다.

-- 두점 사이의 정 가운데 위치를 찾아보자.
-- ptA --> pt2 방향으로 distAB / 2 한만큼 이동한 위치를 찾아보자

vector = ptB - ptA -- ptA -> ptB 방향 : ptB - ptA

vector = normalize(vector) -- normalize 를 이용하여 벡터(방향+거리) 에서 거리 개념을 제거한다.

ptC = ptA + (vector * (distAB / 2)) -- 시작위치 + (방향 * 이동할 거리)
	
boxC = box()
	
boxC.pos = ptC -- 새로 만들어진 boxC 의 위치를 구해진 정중앙으로 변경

 

요렇게 실행해 보면 아래와 같이 새로운 박스가 생성 됩니다.

가운데 녹색 박스가 두개의 박스 정 중앙에 생성되었다.

 

네 간단하죠? 실제로 제가 아주 많이 사용하는 계산 식으로 사용자 함수로 만들어 놓고 사용한답니다. 물론 정중앙이 아닌 1/3 지점, 아니면 어떤 위치도 설정이 가능합니다.

-- ptA --> pt2 방향으로 ratio 한만큼 이동한 위치를 돌려주는 함수
fn getPointBy2Point ptA ptB ratio =
(
	distAB = distance ptA ptB
	vector = normalize(ptB - ptA) -- 벡터(방향+거리) 에서 거리 개념을 제거한다.
	ptC = ptA + (vector * (distAB / 2)) -- 시작위치 + (방향 * 이동할 거리)		
	return ptC
)

 

이런식으로 만들어서 사용하는데요. 실제 사용할 때는 아래와 같이 사용하게 됩니다.

-- ptA --> pt2 방향으로 ratio 한만큼 이동한 위치를 돌려주는 함수
fn getPointBy2Point p1 p2 ratio =
(
	distAB = distance p1 p2
	vector = normalize(p2 - p1) -- 벡터(방향+거리) 에서 거리 개념을 제거한다.
	p3 = p1 + (vector * (distAB * ratio)) -- 시작위치 + (방향 * 이동할 거리)		
	return p3
)


boxA = $Box01
boxB = $Box02

ptA = boxA.center
ptB = boxB.center

ptC = getPointBy2Point ptA ptB 0.75 -- 위에서 작성된 함수를 이용하여 간단히 위치 계산

boxC = box()
boxC.pos = ptC

실행해 보면 두 박스 사이의 0.75 정도의 위치에 새로운 박스가 생성된 것을 알 수 있습니다.

 

위에서 만들어 놓은 함수를 이용하여 점 A 로부터 점 B 방향으로 1.0 이 넘으로 B 보다 더 멀리에 위치한 점을 얻을 수 있고요. 코드를 조금 수정하면 ratio 대신 직접 거리를 넣는 것도 간단히 구현할 수 있습니다.

 

공간상의 자동화 작업을 할 때 아주 자주 사용되는 기능이므로 저처럼 함수로 만들어 놓고 사용하셔도 좋겠습니다.

 

이만 오늘의 포스팅을 마치도록 하겠습니다.

 

 

반응형
반응형

오늘은 3DS MAX script 를 개발하면서 이해하면 도움이 될 용어 두가지를 설명 드릴까 합니다.

바로 property 와 method 입니다. 프라퍼티와 매소드 정도로 발음하시면 됩니다.

도대체 이게 뭐고 왜 이런걸 설명하는 걸까요? 

사실 몰라도 그만이기는 하지만 본격적으로 스크립트 개발을 하시려면 이해하고 넘어가시면 도움이 될 것 같아 작성하기로 마음 먹었습니다.

 

Property - 속성, 어떤 개체가 가지고 있는 속성을 가져오거나 변경한다.

Method - 명령, 어떤 개체에 대하여 일반적으로 필요한 여러가지 기능을 수행하도록 명령을 보낸다.

이라고 쉽게 생각하시면 됩니다. 실제 의미와는 조금 차이가 있긴 하지만 어쨌든 위와 같이 이해하시면 크게 다르지는 않습니다.

 

Property

일반적으로 프라퍼티는 어떤 클래스의 속성을 정의하는 지시어 입니다. 예를 들면

obj = $Box01

obj.Height -- 모델링의 높이, 읽고 쓰기 가능
obj.Width -- 모델링의 가로 폭, 읽고 쓰기 가능
obj.Length -- 모델링의 세로 폭, 읽고 쓰기 가능

obj.pos.x -- 모델링의 x 위치 , 읽고 쓰기 가능
obj.pos.y -- 모델링의 y 위치 , 읽고 쓰기 가능
obj.pos.z -- 모델링의 z 위치 , 읽고 쓰기 가능

obj.max.x -- 모델링의 바운드 박스 (공간 영역)중 가장 큰 x 위치 , 읽기만 가능
obj.max.y -- 모델링의 바운드 박스 (공간 영역)중 가장 큰 y 위치 , 읽기만 가능
obj.max.z -- 모델링의 바운드 박스 (공간 영역)중 가장 큰 z 위치 , 읽기만 가능

obj.min.x -- 모델링의 바운드 박스 (공간 영역)중 가장 작은 x 위치 , 읽기만 가능
obj.min.y -- 모델링의 바운드 박스 (공간 영역)중 가장 작은 y 위치 , 읽기만 가능
obj.min.z -- 모델링의 바운드 박스 (공간 영역)중 가장 작은 z 위치 , 읽기만 가능

와 같이 정의 했을 때 Height 나 Width, Length 는 Box() 라는 클래스(엄연히 말하면 pos, max 는 node 클래스의 프라퍼티 임)의 속성(프라퍼티)이 됩니다. 대부분의 경우 read/wright 가 지원되지만 경우에 따라서는 read 만 지원되는 경우도 있습니다. 무슨 말인가 하면 read 만 지원된다 하면 해당 프라퍼티를 불러올 수 는 있지만 사용자가 변경하지는 못한다는 뜻입니다. Height 나 width, length 는 변경이 가능하죠? 한번 해보세요.

 

 

Methode

method 는 명령이라고 말씀 드렸는데요. 보통 클래스를 만들때 해당 클래스를 제어할 수 있는 여러가지 기능을 한꺼번에 만들게 됩니다. 맥스 스크립트에서는 프라퍼티와 매소드를 굳이 크게 구분하고 있지는 않지만 엄연히 개념적으로 다르므로 이해하시면 좀 더 복잡한 스크립트 코딩에 도움이 됩니다.

대표적으로 editable_poly  같은 클래스를 컨트롤 하기 위한 polyop 라는 오퍼레이션 전용 클래스가 있는데 해당 클래스에 getvert 와 setvert 가 있습니다.
getvert 는 polygon 모델링의 특정 vertex 의 위치를 받아오는 기능이며 setvert 는 특정 버텍스의 위치를 설정(변경) 해주는 기능입니다. getvert 는 사실 프라퍼티에 가까운 기능이지만 setvert 는 분명 매소드처럼 동작 되는 것이죠.
맥스스크립트에서는 get/set 이 대부분 쌍으로 존재합니다.

obj = $ -- editabel poly

pt = polyop.getvert obj 1 -- 1번 버텍스의 위치를 가져온다.
pt.z += 10 -- 가져온 위치의 z 값을 +10 해준다
polyop.setvert obj 1 pt -- +10 된 위치를 다시 원래 버텍스에 세팅한다.

위의 예제는 polyop 라는 폴리곤 전용 오퍼레이션 매소드를 이용하여 버텍스의 좌표를 변경하는 대표적인 간단한 사례 입니다.

for 구분과 연계하여 아래와 같이 할 수도 있죠.

obj = $ -- editabel poly

vNum = polyop.getnumverts obj -- 버텍스의 개수를 가져온다.

for i = 1 to vNum do
(
	pt = polyop.getvert obj i -- i 번쨰 버텍스의 위치를 가져온다.
	pt.z += 10 -- 가져온 위치의 z 값을 +10 해준다
	polyop.setvert obj i pt -- +10 된 위치를 다시 원래 버텍스에 세팅한다.
)

 

물론 editable poly 자체가 가지고 있는 method 와 property 도 있습니다.

obj = $ -- editabel poly

obj.selectByMaterial  1 -- select by material id 1

위의 코드는 머테리얼 ID 1번인 모든 폴리곤을 선택해주는 기능(method) 가 되겠습니다. 뒤의 숫자를 바꿔주면 해당 머테리얼 ID 가 적용된 모든 폴리곤이 선택이 되겠죠.

 

 

맥스 스크립트에서 언급되는 개체는 상위 / 하위 개념을 가지고 있습니다. 이는 일반적으로 계층구조의 프로그래밍을 할때 보여지는 구조인데요. 상위 클래스 > 하위 클래스와 같은 형태인데 하위 클래스는 상위 클래스의 프라퍼티와 매소드를 상속 받아 동일하게 사용할 수 있습니다.

 

맨위의 사례에서 보여드린 .pos 라든가 .max .min 같은 프라퍼티가 대표적인 예인데요, 이는 node 라는 3ds max 의 최상위 클래스를 상속했기 때문입니다.

node 라는 클래스는 우리가 알고 있는 선택가능한 거의 모든 대상이 포함되며 이는 geometry 뿐만 아니라 shape 이나 helper, camera 까지도 모두 node 의 하위 클래스 입니다.

즉 node 클래스에서 정의하고 있는 모든 속성 및 명령을 해당 하위 클래스에서도 사용이 가능한 것입니다.

스크립트 개발하실때 가장 많이 참고하는 것이 바로 help 파일인데요. help 파일 (3ds max script reference)의 특정 개체에 대한 페이지 (예를 들면 editable mesh) 에 나와 있지 않더라도 해당 개체가 node 클래스의 하위 클래스라면 node 클래스에서 정의한 모든 속성과 기능을 사용할 수 있다는 것이죠.

 

How to use

프라퍼티나 매소드는 보통은 아래와 같이 사용됩니다.

클래스.명령어 -- 위에서 보여드렸죠?

명령어 <클래스> -- 실제로는 이런 경우가 많습니다.

위에서 말씀드린 node 클래스의 property/methode 들을 볼까요? 

F1 키를 누르신 뒤 검색 창에 node 를 쳐보시면 node 개체에 대하여 상세하게 설명이 되어 있습니다.

move <node> <point3> -- mapped
scale <node> <point3> -- mapped
rotate <node> <angle> <axis_point3> -- mapped -- angle in degrees
rotate <node> <quat> -- mapped
rotate <node> <eulerangles> -- mapped

이런식으로 사용되는 것이죠. 

앞의 move, scale 등은 node 에 사용할 수 있는 method 라고 할 수 있겠습니다.

말하자면 

명령어 <오브젝트> 옵션

이런 경우가 대부분이고 자주 사용됩니다.

 

 

자 간단하게 설명을 드렸지만 개념에 대한 부분이므로 어려울 수 있습니다.

솔직히 몰라도 스크립트 개발에는 큰 영향은 없습니다. 하지만 개자이너로 거듭나기 위하여 이정도 개념은 가지고 있어야 하지 않겠습니까? ㅋ 개발자들 눈이 동그라미가 될거에요.

끝으로 맥스 스크립트의 node 계층 구조를 간단하게 보여드리겠습니다. reference 에서 Hierarchy를 검색 하시면 max script 의 모든 계층 구조를 보실 수 있답니다.

Value
--MAXWrapper
----node   
------camera  
--------Freecamera 
--------Missing_Camera 
--------Targetcamera 
------GeometryClass  
--------Apollo_Param_Container 
--------apolloParamContainer 
--------Blizzard 
--------BoneGeometry 
--------BoneObj 
--------Boolean2 
--------Box 
--------C_Ext 
--------Capsule 
--------ChamferBox 
--------ChamferCyl 
--------Cone 
--------Conform 
--------Connect 
--------ControlContainer 
--------CV_Surf 
--------Cylinder 
--------Damper 
--------Editable_mesh 
--------Editable_Patch 
--------Editable_Poly 
--------EditablePolyMesh 
--------Gengon 
--------GeoSphere 
--------Hedra 
--------Hose 
--------L_Ext 
--------Loft 
--------LoftObject 
--------Mesher 
--------meshGrid 
--------Missing_GeomObject 
--------Morph 
--------Nurbs 
--------NURBS_Imported_Objects 
--------NURBSSurf 
--------OilTank 
--------OldBoolean 
--------PArray 
--------particleMesher 
--------PCloud 
--------Plane 
--------Point_Surf 
--------Point_SurfGeometry 
--------PolyMeshObject 
--------Prism 
--------Pyramid 
--------Quadpatch 
--------RingWave 
--------RmModel 
--------RmModelGeometry 
--------Scatter 
--------ShapeMerge 
--------SlidingDoor 
--------SlidingWindow 
--------Snow 
--------Sphere 
--------Spindle 
--------Spray 
--------Spring 
--------SuperSpray 
--------Targetobject 
--------Teapot 
--------Terrain 
--------Torus 
--------Torus_Knot 
--------TriMeshGeometry 
--------Tripatch 
--------Tube 
------helper  
--------Anchor 
--------AudioClip 
--------Background 
--------Billboard 
--------Bone 
--------BoxGizmo 
--------CamPoint 
--------Compass 
--------Cone_Angle 
--------ConeAngleManip 
--------CylGizmo 
--------Dummy 
--------Falloff_Manipulator 
--------FalloffManip 
--------FogHelper 
--------grid 
--------Hotspot_Manip 
--------HotspotManip 
--------IK_Chain_Object 
--------IK_Swivel_Manip 
--------IKSwivelManip 
--------Inline 
--------LOD 
--------Missing_Helper 
--------NavInfo 
--------Plane_Angle 
--------PlaneAngleManip 
--------Point 
--------PointHelperObj 
--------Position_Manip 
--------PositionManip 
--------Protractor 
--------ProxSensor 
--------radiusManip 
--------Reactor_Angle_Manip 
--------Reactor_Vector_Handle_Manip 
--------ReactorAngleManip 
--------ReactorVectorHandleManip 
--------Rotation_--
--------Rotation_Valuehelper 
--------RotationValueManip 
--------Slider_Manip 
--------SliderManip 
--------sliderManipulator 
--------Sound 
--------SphereGizmo 
--------Tape 
--------TimeSensor 
--------TouchSensor 
--------uvwMappingHeightManip 
--------uvwMappingLengthManip 
--------uvwMappingUTileManip 
--------uvwMappingVTileManip 
--------uvwMappingWidthManip 
------light  
--------Directionallight 
--------freeSpot 
--------Missing_Light 
--------Omnilight 
--------TargetDirectionallight 
--------targetSpot 
------NodeObject  
------shape  
--------Arc 
--------Circle 
--------CV_Curve 
--------CV_Curveshape 
--------Donut 
--------Ellipse 
--------Helix 
--------line 
--------LinearShape 
--------Lines 
--------Missing_Shape 
--------Ngon 
--------NURBSCurveShape 
--------Point_Curve 
--------Point_Curveshape 
--------Rectangle 
--------section 
--------Simple_Shape 
--------Simple_Spline 
--------SplineShape 
--------Star 
--------text 
------SpacewarpObject  
--------BendModWSM 
--------Bomb 
--------CameraMapSpaceWarp 
--------ConformSpaceWarp 
--------Deflector 
--------Drag 
--------gravity 
--------MapScalerSpaceWarp 
--------Missing_WSM_Object 
--------Motor 
--------Path_Follow 
--------PathDeformSpaceWarp 
--------PBomb 
--------PDynaFlect 
--------PDynaflector 
--------POmniFlect 
--------PushSpaceWarp 
--------SDeflector 
--------SDynaFlect 
--------SDynaflector 
--------SOmniFlect 
--------SpaceBend 
--------Spacedisplace 
--------SpaceFFDBox 
--------SpaceFFDCyl 
--------SpaceNoise 
--------Spaceripple 
--------SpaceSkew 
--------SpaceStretch 
--------SpaceTaper 
--------SpaceTwist 
--------Spacewave 
--------UDeflector 
--------UDynaDeflector 
--------UDynaFlect 
--------UOmniFlect 
--------Vortex 
--------Wind 
------System  
--------Bones 
--------Missing_System 
--------Ring_Array 
--------Sunlight 
--------XRefObject 

그럼 이만 오늘 포스팅을 마칩니다.

감사합니다.

 

2020/04/29 - [DEV/MAX SCRIPT] - 3DS MAX 스크립트로 개체 선택하기

2019/12/11 - [DEV/MAX SCRIPT] - 3DS MAX 스크립트로 초간단 다이얼로그 창 , UI 버튼 만들기

2019/06/01 - [DEV/MAX SCRIPT] - 3DS MAX Script 에 대하여

반응형
반응형

Max Script 를 이용하여 무엇인가를 하려면 필수 요소라고 할 수 있는 것 중의 하나가 개체를 선택하거나 지정하는 방법입니다. 여기서 개체는 3D Scene 안의 오브젝트, 스플라인, 헬퍼 등과 같은 사용자가 직접 선택할 수 있는 개체를 말합니다. 물론 오늘 강좌에서의 선택은 마우스를 클릭하여 선택하는 select의 개념만을 말하는 것은 아닙니다. 

예를 들어 A 라는 모델링 개체의 스케일을 2배로 변경한다고 했을 때 일반적인 사용자의 입장에서는 마우스로 해당 모델링을 클릭하거나 레이어 리스트에서 선택하거나 개체 리스트에서 해당 모델링을 선택 (select) 한 뒤 속성 등을 변경하게 되는데요. 스크립트에서는 굳이 select 의 상태를 만들 필요는 없습니다. 물론 동일하게 select 상태를 만들 수 있긴 하지만 성능 관점에서는 굳이 select 상태여야 할 필요가 없다면 바로 진행을 하면 됩니다.

참고로 본 강좌 진행을 위해 알아야 할 것은 이제부터는 개체를 노드(Node)라고 부를 것 입니다. 3DS MAX Script 에서 공식적으로 개체를 Node 라고 지정하였기 때문이고요. 레퍼런스 등을 보실때도 그렇게 이해하고 계신 것이 혼선이 없을 것 입니다.

우선 기본적인 방식을 살펴 보겠습니다.

  • selection 상태를 만드는 방법
  • 현재 선택된 개체를 가져오는 방법
  • selection 상태가 아닌 상태에서 개체를 지정하는 방법
    • 지정된 이름을 기준으로 지정하는 방법 (단수 개체)
    • 이름의 일부를 기준으로 지정하는 방법 (복수 개체)
    • 레이어 단위로 지정하는 방법
    • 모델링 클래스 단위로 지정하는 방법 (geometry, spline, ...)

 

이런 것들을 소개해 드릴 까 합니다.

이번 강좌의 내용만 잘 이해하셔도 3D Scene 내의 대부분의 개체를 선택하거나 지정하는 것이 가능합니다.

 

 

Selection 상태를 만드는 방법

먼저 기본형은 Select <Node> 의 형태로 되어 있습니다.

Node 는 $ 표시를 붙인 뒤에 개체의 이름을 적어주면 해당 노드를 지정하게 됩니다.

만약 $Box01 이라 하면 "Box01" 이라는 이름을 갖는 노드(개체)를 지칭하게 됩니다. 이녀석을 선택 (Select) 상태로 만드시려면 아래와 같은 방법을 사용하면됩니다.

Select $Box01

요 코드를 스크립트 에디터 또는 리스너(F11) 창 아래 하얀색 창에 적어주고 실행하면 "Box01" 이라는 이름을 갖는 모델링이 선택된 상태가 됩니다. 물론 Box01 이라는 개체가 존재해야 하지요.

A 라는 오브젝트를 선택한 뒤 B 라는 오브젝트를 추가로 선택하고 싶으신 경우 아래와 같이 코드를 작성하시면 됩니다.

select $Box01
selectmore $Box02

selectmore 라는 명령어를 이용하여 셀렉션을 추가할 수 있습니다.

 

 

 

현재 선택된 개체를 가져오는 방법

코드 작성을 하다보면 모든 기능이 시작부터 끝까지 자동화가 어려운 경우가 있습니다. 그런 경우 사용자에 의해 특정 개체를 선택한 뒤 부터 코드가 실행이 된다거나 하는 경우가 종종 있을 수 있습니다. 이럴때 현재 선택된 개체를 가져오는 스크립트가 필요한데요. 바로 아래와 같이 사용하시면 됩니다.

myObject = getCurrentSelection()

이렇게 하면 현재 선택된 개체들을 myObject 라는 변수로 가져올 수 있는데요. 아래와 같이 실재로 사용할 수 있습니다.

myObject = getCurrentSelection()

for obj in myObject do
(
	-- 선택된 개체에 대하여 수행해야 하는 코드를 작성한다.
    print (obj.name)
)

-- 또는

for i = 1 to myObject.count do
(
	obj = myObject[i]
    print (obj.name)
)

 


selection 상태가 아닌 상태에서 개체를 지정하는 방법

스크립트를 이용하여 자동화 업무를 하다보면 항상 선택이라는 액션이 발생되는 것은 아닙니다. 선택이라는 액션이 이루어 지는동안 속도가 많이 저하되기 때문에 자동화의 대상이 많을 수록 선택 과 같은 액션은 생략하는 것이 좋습니다. 개체가 선택될 때 마다 맥스에서는 UI 를 업데이트 해주어야 하는데요, 이때 성능이 많이 느려지게 됩니다. 아래의 내용에서는 선택 과정 없이 특정 개체에 직접 접근하는 간단한 사례를 설명하겠습니다.

 

지정된 이름을 기준으로 지정하는 방법 (단수 개체)

오브젝트의 이름을 알고 있는 경우 가장 손쉽게 개체를 지정할 수 있습니다. 보통은 코드 작성의 편의를 위하여 변수에 담아서 사용하는것이 일반적인 방법입니다. 위에 select 예제와 유사한 방식으로 지정하게 됩니다.

obj = $Box01
print (obj.name)

Box01 이라는 모델링 개체의 이름을 프린트 창에 출력하는 코드죠. 위와 같이 첫줄을 적어주면 obj 라는 개체가 갖는 다양한 속성들이나 명령들을 적용할 수 있게 됩니다. 예를 들어 해당 개체가 Box 라는 class 인 경우 아래와 같이 속성을 변경할 수 있게 됩니다.

obj = $Box01
print (obj.name)

-- 박스의 크기를 현재의 두배로 만들자
obj.width = obj.width * 2
obj.height = obj.height * 2
obj.length = obj.length * 2

-- 높이를 50 만큼 올려보자
obj.pos.z += 50 

 

요런식으로 사용하는 것이지요.

 

 

 

이름의 일부를 기준으로 지정하는 방법 (복수 개체)

이번에는 이름의 일부분만을 이용하여 개체를 지정하는 방법을 소개해보겠습니다.

예를 들어 "box01" , "box02",... "box99" 이런식으로 동일한 이름 구조를 갖는 복수개의 개체가 있는 경우에 해당됩니다. 

objs = execute ("$box*")

print objs.count

위에 보시면 execute 라는 명령어가 있는데요. 뒤쪽에 나오는 문자열을 실행 가능한 문장처럼 해석하는 것입니다. 문자열에는 "$box*" 이라는 형태로 씌어져 있는데 여기서 * 은 와일드 카드라고 불리는 글자 입니다. 다시말하면 box + 어떤 것이든 해당되는 문자열 이라고 보시면 됩니다. scene 내에 box 로 시작하는 모든 개체가 objs 라는 변수에 담기는 것이죠. objs 는 배열형 변수가 되어 실제 사용하실 때는 아래와 같이 하나씩 꺼내 쓸 수 있게 됩니다.

objs = execute ("$box*")

print objs.count -- 전체 담겨져 있는 개체의 수를 출력한다

for o in objs do
(
	print o.name -- 개체의 이름을 출력한다.
    -- 여기서 개체별로 하고 싶은 액션을 작성하면 됩니다.
)

-- 다른 방법으로는
for i = 1 to objs.count do
(
	print objs[i].name

)

 

위와 같은 형식으로 각각의 개체에 대하여 어떤 작업을 하는 것이 가능합니다.

 

 

레이어 단위로 지정하는 방법

그럼 이번에는 레이어 단위로 개체를 지정하는 방법을 알아보겠습니다. 

3DS MAX 작업을 하다보면 레이어를 기준으로 개체를 생성하고 관리하는 경우가 종종 있습니다. 물론 레이어 창을 열어서 해당 레이어 안의 개체를 선택해 줄 수 있겠지만 레이어가 많은 경우, 또는 특정 레이어에 항상 같은 작업이 반복되는 경우 레이어의 개체를 지정하여 액션을 만들어 주는 것이 유용할 때가 있습니다.

레이어 이름을 아는 경우 아주 간단하게 적용이 가능합니다.

myLayer = LayerManager.getLayerFromName "myObjects"

myLayer.nodes &objs

for  o in objs do
(
	print o.name
)

myObjects 라는 이름을 갖는 레이어의 모든 개체를 objs 라는 변수에 담는 코드 입니다. 

레이어.nodes 라는 명령어가 뒤에 따라오는 참조형 변수에 해당 레이어의 모든 노드를 담아주는 기능을 수행합니다. 

참조형 변수는 반드시 & 기호를 붙여 주어야 하며 미리 정의를 해줄 필요는 없습니다.

 

 

모델링 클래스 단위로 지정하는 방법 (geometry, spline, ...)

scene 내에 위에서 정의한 방식 외에도 여러 레이어에 포함되어 있고 다양한 이름으로 존재하는 개체를 지정해야 하는 경우도 많이 있습니다. 예를 들면 scene 내의 모든 geometry 나 spline, helper 등을 선택해야 할 때도 있기 마련입니다.

이럴 때 사용할 수 있는 간단한 방법이 있습니다.

for  o in objects do
(
	print o.name
)

scene 내의 모든 오브젝트에 대한 처리가 가능하게 하는 간단한 방법입니다.

여기서 사용된  objects 와 같은 개체를 오브젝트 셋이라 부를수 있는데요 다음과 같은 종류가 있습니다.

objects  --all the objects
geometry  --the standard 3ds Max categories... 
lights
cameras
helpers
shapes
systems
spacewarps
selection  --the current selection

어떤 변수에 위에 해당되는 셋을 선언해주면 scene 내의 모든 해당 클래스의 개체가 대상이 됩니다.

아주 간단하지만 강력한 기능이죠.

 

이렇게 다양한 방법으로 화면 내의 개체를 지정하고 지정된 개체에 대하여 필요한 어떤 작업을 수행하는 것이 가능합니다. 어렵지 않죠?

다음번에는 어떤 조건을 붙여서 개체를 지정하거나 피하는 방법을 소개해 드리겠습니다.

 

그럼 이만~

2022.05.31 - [DEV/MAX SCRIPT] - [3ds max script] ray 에 대하여 알아보기

 

[3ds max script] ray 에 대하여 알아보기

가끔 아니 어쩌면 자주 3D 공간안에서 물체의 위치를 지정할때 이 물체가 저쪽에 닿는 면에 딱 위치하고 싶은데... 할때가 있죠? 예를 들면 나무나 돌덩이 이런걸 랜덤하게 막 생성한 다음 굴곡진

diy-dev-design.tistory.com

2019/12/11 - [DEV/MAX SCRIPT] - 3DS MAX 스크립트로 초간단 다이얼로그 창 , UI 버튼 만들기

 

3DS MAX 스크립트로 초간단 다이얼로그 창 , UI 버튼 만들기

3DS MAX 스트립트를 이용하여 스크립팅을 하기 시작하면 MAX 로 할 수 있는 일이 너무나 많고 빠르게 진행된다는 것을 알 수 있게 됩니다. 이 놀라운 기능들을 스크립트를 매번 실행시키는 것이 번�

diy-dev-design.tistory.com

 

2019/12/16 - [DEV/MAX SCRIPT] - 3DS MAX SCRIPT 강좌?? 무작정 시작하기 - 디자이너 추천

 

3DS MAX SCRIPT 강좌?? 무작정 시작하기 - 디자이너 추천

개발이라 하면 두드러기가 나는 디자이너 들이 있습니다. 아니 대부분 그렇죠. 이글을 읽고 계신 본인도 그러하다면, 그런데 스크립트로 무언가를 하고 싶다면 어쩌겠습니까.. 배워야지요. 그래

diy-dev-design.tistory.com

 

반응형
반응형

3DS MAX 스트립트를 이용하여 스크립팅을 하기 시작하면 MAX 로 할 수 있는 일이 너무나 많고 빠르게 진행된다는 것을 알 수 있게 됩니다. 이 놀라운 기능들을 스크립트를 매번 실행시키는 것이 번거롭고 여러가지 기능을 한번에 수행하거나 관리할 수 있게끔 만들고 싶은 욕심이 생길 때가 있습니다. 그럴때 그런 기능들을 한곳에 모아 실행하기 위하여는 마치 어떤 툴이나 프로그램 처럼 UI 가 있는 것이 실행하기도 편하고 보기에도 좋을 것입니다.

그래서 오늘은 3DS MAX Script 를 이용하여 다이얼로드 창을 만드는 방법, 그리고 간단한 UI 를 적용하는 방법을 알아 보겠습니다.

  • 다이얼로그 창 만들기 / 버튼 만들어 기능 연결하기
  • 롤아웃 플로터 만들기

먼저 3DS MAX 에서 다이얼로그 창을 만들어 보겠습니다.

다이얼로그 는 윈도우에서 예/아니오 경고창과 같이 간단한 타이틀바와 닫기 버튼, 내부에 간단한 UI 들을 담을 수 있는데요. 기본적으로는 rollout 을 만들어 rollout 내부에 UI 를 담고 만들어진 rollout 을 다이얼로그창의 형식으로 보여주는 형태로 되어 있습니다.

 

다이얼로그 창 만들기 / 버튼 만들어 기능 연결하기

가장 기본형은 아래와 같습니다.

rollout myFirstRollout "my menu 01"
(
	button bt_myButton "press me!"
	
	on bt_myButton pressed do
	(
		messageBox "You got it!"
	)
	
)

createDialog myFirstRollout

먼저 롤아웃을 선언해 준 뒤 그안에 버튼을 하나 만들고 버튼을 눌렀을때 특정한 메시지를 팝업으로 보여주는 다이얼로그입니다.

바로 요렇게 만들어 지게 됩니다.

심플한 다이얼로그

가운데 있는 버튼을 눌러주면 아래와 같이 팝업으로 메시지가 출력이 됩니다.

messagebox 가 뜬 모습

 

자 이게 기본은 했고요. 다이얼로그의 크기, 위치, 버튼의 크기나 위치등을 지정하지 않았기 때문에 위와 같이 작게 표시가 되었는데요. 여기서 그 크기위 위치를 지정하는 방법을 알아보도록 하겠습니다.

제일 마지막 줄에 createDialog 라는 명령으로 만들어준 롤아웃을 다이얼로그라는 형태로 보여주었는데요. 이부분에서 다이얼로그릐 크기, 위치를 지정할 수 있습니다. 마지막 줄의 내용을 아래와 같이 수정해 보겠습니다.

createDialog myFirstRollout 300 200 100 100

요런 식으로 정의 할 수 있는데요. 4개의 숫자를 뒤에 연속해서 써주게 되는데

[가로길이] [세로길이] [윈도우상의 가로위치] [윈도우상의 세로위치]

의 순서로 적어주면 되며 뒤의 두가지 숫자를 생략하게 되면 화면의 중앙에 다이얼로그가 생성이 됩니다.

위의 코드로 수정한 뒤 실행하게 되면 아래와 같이 윈도우 좌측 상단에 가로 300 세로 200 픽셀의 다이얼로그 창이 만들어 지게 됩니다.

화면의 좌상단에서 열린 다이얼로그 창

버튼을 하나 추가해 볼까요?

rollout myFirstRollout "my menu 01"
(
	button bt_myButton1 "press me!"
	button bt_myButton2 "don't press me!"
	
	on bt_myButton1 pressed do
	(
		messageBox "You got it!"
	)
	
	on bt_myButton2 pressed do
	(
		destroyDialog myFirstRollout
	)
	
)

createDialog myFirstRollout 300 200 100 100

자 이제 버튼이 두개가 되었습니다. 두번째 버튼을 누르게 되면 다이얼 로그 창이 닫히는 그런 코드가 실행이 됩니다.

스크립트를 실행해 보면 아래와 같이 버튼이 두개가 표시됩니다.

다이얼로그에 버튼이 두개가 되었다.

그런데 저는 저 두개의 버튼을 가로로 배치하고 싶어서 코드를 약간 더 손을 보았습니다. 레이아웃을 조정하는 것이죠.

rollout myFirstRollout "my menu 01"
(
	button bt_myButton1 "press me!" pos:[10,10] width:130 height:30
	button bt_myButton2 "don't press me!" pos:[150,10] width:130 height:30
	
	on bt_myButton1 pressed do
	(
		messageBox "You got it!"
	)
	
	on bt_myButton2 pressed do
	(
		destroyDialog myFirstRollout
	)
	
)

createDialog myFirstRollout 300 200 100 100

맨위에 버튼을 만들어주는 부분에 pos 와 width, height 를 설정해 줌으로써 사용자가 원하는 위치와 크기로 버튼을 배치할 수 있습니다.

실행해보면 아래와 같이 변경이 됩니다.

두개의 버튼이 가로로 배치되었다

 

네.. 그럴싸 해 졌습니다.

롤아웃 플로터 (rollout floater) 만들기

이번에는 좀더 고급 UI 를 이용해 보겠습니다. 3DS MAX 의 우측에 보면 여러가지 옵션이나 설정을 입력하고 기능을 선택하는 메뉴바가 나오는데요. command pannel 이라고 합니다. 해당 패널에 보면 기능이 많은 경우 기능을 접었다 폈다 할 수 있는 형태로 메뉴가 만들어져 있습니다. 

유사한 기능끼리 그룹을 만들어 묶어주고 사용하지 않는 기능 그룹은 묶어 둘 수 있어 상당히 편리한 UI 라고 할 수 있겠습니다. 스크립트  UI 로도 거의 동일한 기능을 아주 쉽게 구현할 수 있습니다.

그런 기능들의 묶음이 위에서 보여주었던 하나의 롤아웃이 되며 롤아웃들을 하나의 창에 보여줄 수 있는 개체가 롤아웃 플로터 입니다.

간단한 예를 보여드리겠습니다.

rollout myRollout_ani "my Animation"
(
	button bt_myButton1 "run" pos:[10,10] width:130 height:30
	button bt_myButton2 "stop" pos:[150,10] width:130 height:30
	
	on bt_myButton1 pressed do
	(
		messageBox "No function now"
	)
	
	on bt_myButton2 pressed do
	(
		messageBox "No function now"
	)	
)

rollout myRollout_geom "my Geometry"
(
	button bt_myButton1 "convertTo mesh" pos:[10,10] width:130 height:30
	button bt_myButton2 "remove Iso Vertex" pos:[150,10] width:130 height:30
	
	on bt_myButton1 pressed do
	(
		messageBox "No function now"
	)
	
	on bt_myButton2 pressed do
	(
		messageBox "No function now"
	)	
)

myTool = newrolloutFloater "my custon tool" 300 300 100 100

addRollout myRollout_ani myTool 
addRollout myRollout_geom myTool 

myTool.open

두개의 롤아웃을 먼저 만들고 myTool 이라는 변수를 새로운 롤아웃 플로터로 설정한뒤 myTool 에 두개의 롤아웃을 추가합니다. 끝으로 만들어진 롤아웃 플로터를 보여주는 것이죠.

간단하지 않습니까?

실행하면 아래와 같이 됩니다.

두개의 롤아웃이 있는 메뉴가 만들어 졌다.

 

이렇게 만들어진 롤아웃 플로터내의 롤아웃 들은 아래와 같이 접는것도 가능합니다.

롤아웃은 말그대로 접어 올릴수 있는 메뉴다.

이제 제법 툴같이 만들어 졌습니다.

 

자 3DS MAX 에서 롤아웃을 만드는 것을 알아보았습니다.

이제 다음 강좌에서는 롤아웃 내의 버튼에 기능을 할당해 보도록 하겠습니다. 인터넷에서 긁어 모은 각종 스크립트 들을 저렇게 만든 롤아웃 플로터 하나에 모아두면 필요할때 빠르게 기능 동작이 가능할 것 입니다.

 

그럼 이만~

 

2020/04/29 - [분류 전체보기] - 3DS MAX 스크립트로 개체 선택하기

 

3DS MAX 스크립트로 개체 선택하기

Max Script 를 이용하여 무엇인가를 하려면 필수 요소라고 할 수 있는 것 중의 하나가 개체를 선택하거나 지정하는 방법입니다. 여기서 개체는 3D Scene 안의 오브젝트, 스플라인, 헬퍼 등과 같은 사��

diy-dev-design.tistory.com

 

2019/12/16 - [DEV/MAX SCRIPT] - 3DS MAX SCRIPT 강좌?? 무작정 시작하기 - 디자이너 추천

 

3DS MAX SCRIPT 강좌?? 무작정 시작하기 - 디자이너 추천

개발이라 하면 두드러기가 나는 디자이너 들이 있습니다. 아니 대부분 그렇죠. 이글을 읽고 계신 본인도 그러하다면, 그런데 스크립트로 무언가를 하고 싶다면 어쩌겠습니까.. 배워야지요. 그래

diy-dev-design.tistory.com

 

반응형

+ Recent posts