posted by cimple 2012. 2. 14. 15:49

사실 이 포스트는 Maya Python Script 에 해당한다기보다는 Python String 처리라고 할 수 있다. 그러나 Maya Python Script 에도 유용하게 사용되었으므로 포스팅해 본다.


polyinfo 정보를 슬라이싱

Python script 에서 mesh 가 어떤 구조로 연결되었나를 로 알기는 사실 생각보다 까다롭다. (API 를 사용한다면 쉽겠지만)

Python script 에서 mesh 의 연결 구조를 알려 주는 script 는 polyinfo 이다.

만약, Mesh 의 이름이 pCube 라면

import maya.cmds as mc
mc.polyInfo('pCube1.vtx[0]', ve=True)


라고 입력하면, pCube 의 0번째 vertex 에 연결되어 있는 edge 가 무엇인지 알려준다. 그런데 이게 골치아프게도 return 값이 list 같은 형태가 아니라 string 이다.

# Result: [u'VERTEX 0: 10 2 0 \n'] # 


위와 같은 결과를 얻게 된다. 

즉 이것을 사용하고 싶으면 Python string 처리를 해서 잘라 써야 하는데, 익숙하지 않으면 상당히 귀찮은 일이다.

귀찮지만, 위의 경우에는 찬찬히 생각해 보면 다음의 과정을 거치면 된다.

1. 콜론(:) 의 위치를 찾는다.
2. 콜론 앞으로는 다 잘라내고, 콜론 뒤에만 쓴다.
3. 공백은 삭제하고, 각각의 숫자들만 따로 받는다.

이것이 다음 4줄의 코드로 구현된다.

tmp = mc.polyInfo('pCube1.vtx[0]', ve=True)

idx = tmp[0].find(':')

sparse = tmp[0][idx+1:]

result = sparse.split()



즉, 먼저 string 의 .find() 함수는 해당 문자열의 위치를 반환한다.

다음, 해당 문자열 바로 다음부터 쓰기 위해 슬라이싱을 할 수 있다. 문자열[숫자:] 로 하면, 문자열에서 해당 숫자 다음부터 잘라내서 쓰겠다는 말.

그리고, 그 문자열에 .split() 함수는 공백 단위로 문자열을 분리해준다.

이렇게 하면 다음과 같은 훌륭한 결과를 얻을 수 있다.

# Result: [u'10', u'2', u'0'] # 


이제 리스트 형태로 각각을 사용하기만 하면 끝.


검색, 치환하기

오브젝트 이름에서 공통된 특정 부분을 잘라내거나, 다른 것으로 바꾸고 싶을 때 replace 명령을 사용하면 된다.

jntName = 'Bizzy_L_Eye_03_jnt'

skinJntName = jntName.replace("_jnt","_skin")

skinGrpName = jntName.replace("_jnt","_grp")


posted by cimple 2012. 1. 17. 04:56

C++ 언어로 제작된 Maya Plug-in 이 .mll 파일로 배포되는 것과 같이, Python 으로 제작된 Plug-in 도 .pyc 의 컴파일 된 형태로 제작되어 배포할 수 있습니다.

실제로 마야가 설치되어 있는 폴더 안에 파이썬 라이브러리를 보면 .pyc 형태로 배포되고 있는 것을 볼 수 있습니다.

ex) C:\Program Files\Autodesk\Maya2010\Python\lib\site-packages\maya


이러한 .pyc 파일을 Compiled Bytecode 라 부르며, 소스코드를 숨길 수 있을 뿐만 아니라 파이썬 모듈을 Import 할 때 속도 향상 또한 가져다주는 장점을 가지고 있습니다.

Maya Python 파일은 Python API 뿐만 아니라 Python Script 파일, 심지어 MEL 파일도 Python 으로 wrapping 해서 .pyc 파일로 배포가 가능합니다. 따라서 소스코드 뿐만 아니라 UI 등 어떤 형태의 스크립트 파일도 배포할 수 있습니다.

이번 포스팅에서는 먼저 Python Maya Script 파일을  .pyc 파일로 제작하고, import 하는 과정을 설명하도록 하겠습니다.




1. Python Script 파일을 .pyc 파일로 제작하기

아래 파일은 Mesh 를 선택하고 버튼을 누르면 해당 Mesh 의 이름을 반환하는 간단한 기능을 가지고 있는 UI 를 Maya Python Script 로 구현한 파일입니다.


※주의!
(위 파일을 보통 배포되는 형태처럼, Maya Script Editor 에서 전체 복사이후 실행 (Ctrl+A -> Ctrl+C -> Ctrl+V -> Ctrl+Enter) 시키면, UI 는 뜨지만 버튼들이 제대로 동작하지 않을 것입니다. 이것은 내부 모듈 실행 이름이 다르기 때문인데, 관련한 내용은 잠시 뒤에 설명할 것입니다.)


위 파일을 테스트해보고 싶으면, Maya 에서 Python 모듈을 Import 하는 형태로 사용해야 합니다. Python 모듈을 Import 하고 싶으면 해당 Python 파일을 기존 Python Path 에 넣거나, 해당 파일이 있는 Path 를 Python Path 에 추가해주면 됩니다. 해당 소스코드는 아래를 참조하세요.


#Python Path 확인하기

import sys

for i in sys.path :
    print i 


#Python Path 추가하기

import sys
srcPath = "C:\.... (Custom Path)"
if srcPath not in sys.path :
    sys.path.append(srcPath) 



위와 같이 Module 을 Import 할 Path 를 잡아주고 나면, 해당 Python Script 를 Import 할 수 있습니다. 다음 명령어로 Import 해서 UI 를 실행시켜 봅시다.


#Python Module Import

import testUI



그러면, 아래 그림과 같은 UI 가 뜨는 것을 확인할 수 있습니다. 각 버튼을 눌러서 제대로 작동하는지 확인해 봅니다.

간단한 UI...지만 최대한 Maya 표준에 맞게 제작한 UI



버튼이 제대로 동작하는 지 확인하고 나면, 해당 Python Script (testUI.py) 를 넣어 두었던 폴더에 가 봅니다. 그러면 Python Script 의 Compiled Bytecode Version 인 .pyc 파일이 생성되어 있는 것을 확인할 수 있습니다.

.pyc 가 갑툭튀


이렇게 해당 Python 파일을 Module 로 Import 하면 .pyc 파일이 자동으로 생성됩니다. 이것이 .pyc 파일을 만드는 가장 간단한 방법입니다. 



Python 파일을 .pyc 파일로 만드는 또 다른 방법은 직접 컴파일 하는 방법이 있습니다. 다음 소스코드로 직접 컴파일해 봅시다.

import py_compile
py_compile.compile('C:/..(Custom Path) .../testUI.py')


※ 주의!
직접 컴파일할 때, 경로명은 슬래시 (/) 혹은 백슬래시 2개 (\\) 를 사용해야 합니다. 백슬래시 1개 (\) 는 작동하지 않습니다.

역시 .pyc 파일이 생성되어 있는 것을 확인할 수 있습니다.





2. .pyc 파일을 import 하기

.pyc 파일을 import 하는 방법은 매우 간단합니다. .py 파일을 import 하는 것과 똑같이 그냥 .pyc 파일을 import 하면 됩니다.

.pyc 파일이 잘 import 되는가를 확인하기 위해, 메모리를 청소하기 위해서 Maya 를 한번 껐다 켜고, .pyc 파일만 다른 폴더에 복사해둡니다.

그런 다음 다시 Maya 를 켜서, 먼저 다음 명령어로 testUI 를 Import 할 수 없음을 확인합니다. 당연히 아래 그림과 같은 Import Error 메시지가 떠야 합니다. (만약 testUI.py 파일을 기존 Python Path 에 넣어두었다면 import 가 되어버릴 것입니다. 이러면 .pyc 파일만 확인할 수가 없으므로 testUI.py 파일을 지워주어야겠죠?)

import testUI

 

우리를 겁나게 하는 시뻘건 에러 하지만 이번엔 이게 정상



그리고 testUI.pyc 파일이 담겨 있는 path 를 다음과 같이 등록해준 다음, import 해 봅시다.

srcPath = "C:\...(Custom Path)"

import sys

if srcPath not in sys.path:

    sys.path.append( srcPath )


import testUI

 
아까와 같이 UI 가 제대로 작동하면 .pyc 파일을 제대로 Import 한 것입니다.

.pyc 파일을 열어 보아도, 소스코드 내용을 확인할 수가 없습니다.

확인할수가 업ㅂ어

 

.pyc 파일의 import 를 정리하면 다음과 같습니다.

  • Module 로 Import 됩니다.
    - 기존 Python Module 을 사용하는 것처럼, 모듈명.함수() 형태로 모듈 안의 함수들을 사용할 수 있습니다.
    - 위에 제시된 예제 파일처럼 모듈 안에 들어있는 것이 Class 라면, Class 의 객체를 만들어 사용하거나, 직접 Class 의 멤버함수를 사용할 수 있습니다.
    - ex) import testUI
            myUI = testUI.TestUI()
            myUI.showMyWindow()

            또는

            import testUI
            testUI.testUI().showMyWindow()
            
  • Import 되는 순간 실행됩니다.
    - Import 되는 순간 .pyc 의 내용들이 메모리에 올라가게 됩니다. 
    - 예제 파일에서는 import maya.cmds as mcTestUI().showMyWindow() 가 전역적으로 실행되고, 클래스가 메모리에 올라갑니다. 
     



Maya UI 파일을 .pyc 파일로 배포할 때 주의사항

UI 파일을 .pyc 파일로 배포할 때에 주의사항이 있습니다. 각종 UI 에는 다양한 형태의 '버튼' (이라고 통칭합시다) 이 있는데, 이런 버튼은 마야에서 command 를 실행하게 됩니다. 예를 들면

mc.button(label="Select mesh and press", align="center", command="testUI.TestUI().importMesh()")


예제에 있는 버튼의 한 예입니다. "Select mesh and press" 라는 버튼을 누르면 "testUI.TestUI().importMesh()" 라는 커맨드를 실행하게 되는 버튼입니다.

이와 같이, UI 의 각종 버튼들은 command 인자로 받는 " " 쌍따옴표 안에 들어있는 커맨드를 그대로 실행합니다. 때문에, module 명을 임의로 import 하였을 경우 command 를 실행시키지 못합니다.

예를 들어, 다음과 같이 myUI 라는 임의의 이름으로 module 을 import 한다면

import testUI as myUI


다른 Python module 들은 myUI.함수명() 과 같은 형태로 실행시킬 수 있지만, Maya UI 의 경우 command 를 임의의 모듈명을 받아서 실행시킬 방법이 없습니다. 

따라서 UI 파일이 함수의 나열 형태로 구성되어 있다면 UI 내의 command 부분을

command = "모듈명.함수명()"


으로 정해주어야 하거나, 예제와 같이 클래스 형태로 구성되어 있다면 

command = "모듈명.클래스명().함수명()"


으로 정해 주어야 하고, 모듈을 import 할 때에는

import 모듈명


으로만 import 해야 UI 를 .pyc 파일로 배포하고, 정상적으로 사용할 수 있습니다.





3. Mel 파일을 .pyc 파일로 제작하기


Mel 파일을 .pyc 파일로 제작하는 방법은 간단합니다. Mel Script 를 Python Script 에서 실행시킬 수 있도록 바꾸어 주면, 이후 과정은 당연히 Python Script 를 .pyc 로 만드는 과정과 동일합니다. 예제를 보는 것이 이해가 빠를 것입니다.

import maya.mel as mel

mel.eval('polySphere -r 3;')

mel.eval('polyCube -w 2 -h 6;')

mel.eval('polyCylinder -h 7 -sx 10;')


위와 같은 python 파일을 만들어서 컴파일하면 MEL 파일도 .pyc 형태로 배포할 수 있습니다.





4. Maya Python Plug-in 파일을 .pyc 파일로 제작하기

아래 소스코드는 'Hello, World' 를 찍는 간단한 Maya Python Plug-in 파일입니다.

import sys

import maya.OpenMaya as OpenMaya

import maya.OpenMayaMPx as OpenMayaMPx


kPluginCmdName = "spHelloWorld"


# command

class scriptedCommand(OpenMayaMPx.MPxCommand):

        def __init__(self):

                OpenMayaMPx.MPxCommand.__init__(self)

        def doIt(self,argList):

                print "Hello World!"


# Creator

def cmdCreator():

        return OpenMayaMPx.asMPxPtr( scriptedCommand() )

        

# Initialize the script plug-in

def initializePlugin(mobject):

        mplugin = OpenMayaMPx.MFnPlugin(mobject)

        try:

                mplugin.registerCommand( kPluginCmdName, cmdCreator )

        except:

                sys.stderr.write( "Failed to register command: %s\n" % kPluginCmdName )

                raise


# Uninitialize the script plug-in

def uninitializePlugin(mobject):

        mplugin = OpenMayaMPx.MFnPlugin(mobject)

        try:

                mplugin.deregisterCommand( kPluginCmdName )

        except:

                sys.stderr.write( "Failed to unregister command: %s\n" % kPluginCmdName )

                raise




이 소스코드를 파이썬 파일로 저장한 다음, Plug-in Manager 에서 불러들여봅니다. 스크립트 에디터에서 spHelloWorld 라는 커맨드를 입력하면 'Hello World!' 라는 텍스트를 출력합니다.




그렇다면 이 Python 파일을 바로 컴파일 해서, .pyc 형태로 만들면 바로 배포 가능하겠네? 라고 생각할 수 있지만 현실은 그렇지 않습니다. 세상일이 그렇게 마음대로 되던가요. 플러그인 파일 전체를 컴파일 해서 .pyc 파일로 만들면, Maya Plug-in Manager 에서 .pyc 파일이 보이긴 합니다(!). 그러나 제대로 플러그인을 읽어 들이지 못하는 것을 볼 수 있습니다.


비록 눈에는 보이지만

세상 일이 맘처럼 쉽지만은 않은 예.pyc





Python Plug-in 파일을 배포하기 위해서는 코어 부분과 이니셜라이징 부분을 분리해서, 코어 부분은 .pyc 파일로 만들고, Plug-in 이니셜라이징 부분은 해당 .pyc 파일을 Import 하는 .py 파일을 만들어 두 파일을 다 배포하면 됩니다.

먼저 위 코드에서 코어 부분을 다음과 같이 떼어 컴파일합니다.

helloWorldCore.py

import sys

import maya.OpenMaya as OpenMaya

import maya.OpenMayaMPx as OpenMayaMPx


kPluginCmdName = "spHelloWorld"


# command

class scriptedCommand(OpenMayaMPx.MPxCommand):

        def __init__(self):

                OpenMayaMPx.MPxCommand.__init__(self)

        def doIt(self,argList):

                print "Hello World!"


# Creator

def cmdCreator():

        return OpenMayaMPx.asMPxPtr( scriptedCommand() )



그렇다면 helloWorldCore.pyc 파일이 제작되겠죠? 그리고 나서 이 .pyc 파일을 import 해서 이니셜라이징 하는 파일을 제작합니다. 원본 파일과 달라진 부분을 진한 글씨로 표시하겠습니다.

helloWorld.py

import sys

import maya.OpenMaya as OpenMaya

import maya.OpenMayaMPx as OpenMayaMPx
import helloWorldCore as hwc


# Initialize the script plug-in

def initializePlugin(mobject):

        mplugin = OpenMayaMPx.MFnPlugin(mobject)

        try:

                mplugin.registerCommand( hwc.kPluginCmdName, hwc.cmdCreator )

        except:

                sys.stderr.write( "Failed to register command: %s\n" % hwc.kPluginCmdName )

                raise


# Uninitialize the script plug-in

def uninitializePlugin(mobject):

        mplugin = OpenMayaMPx.MFnPlugin(mobject)

        try:

                mplugin.deregisterCommand( hwc.kPluginCmdName )

        except:

                sys.stderr.write( "Failed to unregister command: %s\n" % kPluginCmdName )

                raise


그리고 이 helloWorld.py 를 Plug-in Manager 에서 불러들이면 정상적으로 플러그인이 동작하는 것을 확인할 수 있습니다.

정리하면

  • 코어 부분은 .pyc 파일로, 이니셜라이징 하는 부분은 .py 파일로 제작합니다. 
  • 이니셜라이징 하는 .py 파일이 코어 파일인 .pyc 파일을 import 해서 사용하므로, .pyc 파일의 위치는 기존 Python Path 에 넣어주거나, 해당 위치를 Python Path 에 추가해주어야 합니다. 
  • 이니셜라이징 하는 .py 파일 안에, .pyc 파일이 있는 커스텀 경로를 Python Path 로 등록하는 부분을 추가해 주는 것이 하나의 적합한 배포 형태가 될 것 같습니다.





5. 마치며

1) Python Version

.pyc 파일은 Python version specific 한 파일입니다. 다시 말해서, 다양한 version 의 python 에서 모두 동작하게 만들고 싶으면, 각각의 python 에 맞는 .pyc 파일을 제작해서 배포하여야 합니다.

우리는 Maya Python Script 파일을 배포하므로, Maya 가 사용하는 Python version 이 중요한데, 현재 Maya 2012 의 경우 Python 2.6 을 사용하고 있습니다. (이것이 우리가 Python 2.6 을 설치해야 하는 이유) 그러므로, Python 2.6 버전에서 제작 / 컴파일 하면 되겠습니다.



2) 정말 안전한가?

실컷 설명해 두었지만 안타깝게도 이것이 Python script 를 완벽하게 보호해주는 솔루션이 되지는 못합니다. 구글에서는 손쉽게 Python Script 를 Crack 해서 원본 소스코드를 볼 수 있는 툴을 구할 수 있습니다. 

그렇지만 .pyc 가 완전히 보안에 취약하지는 않습니다. 그렇다면 Maya 도 .pyc 형태로 라이브러리를 배포하지 않았겠죠; .pyc 가 일정 용량이 넘어가면 Crack 으로도 소스코드를 풀 수가 없다고 합니다. 즉 소스 코드의 분량이 어느 정도 이상이 되면 보안성이 향상된다는 이야기인데, 여기에 대해서는 사실 좀더 조사가 필요합니다; 따라서 신중한 접근이 필요할 것 같습니다.



이 포스팅은 다음과 같은 고마운 링크들의 도움을 받았습니다.

http://pyfaq.infogami.com/how-do-i-create-a-pyc-file

http://forums.cgsociety.org/archive/index.php/t-880405.html

http://forums.cgsociety.org/archive/index.php/t-605089.html

http://www.mail-archive.com/python_inside_maya@googlegroups.com/msg01024.html

http://mayastation.typepad.com/maya-station/2010/03/the-lowdown-on-the-python-pyc-files-compiled-bytecode.html


ThEnd. 

posted by cimple 2011. 6. 30. 17:11



비둘기 프로젝트를 하는데 다음과 같은 조건을 충족시킬 문제가 떨어졌다.

1. 타임라인이 움직이면 깃털들이 랜덤하게 팔락거렸으면 좋겠다.
2. 추가로 플러그인을 만들지 말고 마야에 있는 기능으로 만들었으면 좋겠다.

이런 문제에서는 Maya Expression 이 적당하다. 각각의 깃털들에 대해서 서로 다른 Expression 이 적용되어야 하므로, Python Script 를 이용해서 Expression 을 만들었다.

코드는 다음과 같이 간단하다.



먼저 날개 mesh 들을 모두 선택한 다음, 날개 메쉬 각각에 expression 을 만들어 주는 것이다.

물론 이러면 항상 일정한 값으로 (0.0~3.0 사이 랜덤하게) rotateX 값이 깃털에 들어가게 되는데

controllable 하게 만들어 줄 수도 있다.

하지만 이 정도면 python script 상에서 어떻게 expression 을 만들면 되는지 hello world 는 찍을 수 있을 것이다.


ThEnd.





p.s.

결국 추가로 최종적으로 만든 expression 은 다음과 같다.

expression 안에 다른 attribute 를 넣고 싶으면 getAttr 같은 것을 쓸 필요 없이 바로 쓰면 되고,

if 문으로 조건문 제어가 가능하다.

Python script 로 2줄 이상의 expression 을 넣고 싶으면 일단은 다음과 같이 죽 붙여 쓰는 형태로 짜야 하는 것 같은데

다른 방법이 있는지 찾아봐야겠다.








p.s.2. 3중 따옴표 (""") 의 사용

재밌는 기능을 발견했다. Maya Python Script 에만 있는 기능은 아니고, Python 자체에 있는 기능인데 3중 따옴표를 쓰면 여러 줄의 문자열을 한 string 에 입력할 수 있다.



여러 줄의 expression 을 입력하고 싶을 때 스크립트로 다른 expression 을 입력해서 기존의 expression 을 수정하는 방법은 없는 것 같다. 그러면 또 다른 expression 이 생겨버린다. expression 을 수정하기 위해서는 expression editor 를 이용하는 방법 뿐인듯.

ThEnd.