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. 1. 11. 03:42



Solid Skeleton 이 하루만에 버전 2.0 이 나왔습니다. 사용자 입장에서 조금 큰 폭으로 바뀌었기에 아예 버전을 2.0 으로 한 단계 높였습니다. 강화된 기능은 다음과 같습니다.


1. UI 제공
2. 조인트의 크기와 모양을 실시간으로 변환 가능


다음은 시연 동영상입니다.






쉐이더까지 자동으로 입힐 수 있으나, 렌더링은 각자 용도에 맞게 하시면 되리라 생각되어 구태여 넣지 않았습니다.

그럼 유용하게 사용하세요!





Version 2.1 로 업그레이드 되었습니다.

수정 사항은

- 캐릭터가 하나의 조인트 구조가 아닌 여러 개로 따로 떨어진 조인트 구조를 가졌을 경우, 각각의 조인트의 root 를 선택하고 generate 버튼을 계속 누르는 방식으로 추가할 수 있습니다.

- 물론 처음부터 root 에 해당하는 모든 조인트를 선택하고 generate 버튼을 눌러서 생성할 수도 있습니다.

아래는 시연 동영상입니다.




ThEnd.

posted by cimple 2010. 11. 15. 23:31

Maya 에서 스크립트를 작성하다 보면 무한 루프에 빠지는 경우가 있다.

for 문이나 while 문 (특히 while 이놈!) 을 잘못 쓰면 무한 루프에 빠져 버리는 경우가 있는데,

ctrl+break 가 통하는 것도 아니고 esc 를 아무리 연타해도 안 되고, 마야가 먹통이 되어 버려서 결국에는 작업관리자를 켜야만 하는 처지에 놓이게 된다.

이 때, 반복문 내에 무한루프를 방지할 수 있는 간단한 장치를 마련할 수 있는데, 방법이 조금 독특하다.

'외부 파일을 이용' 하는 방법이다. 


사용 방법은 다음과 같다.




1. 위에 첨부된 파일(BREAKMEL.bat) 를 받아서, 소중한 곳에 잘 간직해둔다.
2. 마야에서 MEL scripting 을 하면서, 무한루프가 의심되는 반복문 안에 다음 구문을 넣어준다.

import os
if os.path.exists("c:/breakMel") : break
3. 만약 스크립팅을 하다가 무한 루프에 빠지면, 첨부된 파일을 실행하기만 하면 반복문이 종료될 것이다.



간단한 예제는 다음과 같다.



위와 같은 구문을 스크립트 에디터에서 실행하면, 아마 지구가 멸망할 때까지 1을 찍어댈 것이다.
이 때, 첨부된 BREAKMEL.bat 를 실행시키기만 하면 무한루프를 중지시킨다.
이 때 임시로 생성되는 파일은 자동으로 삭제되니 신경쓰지 않아도 된다.




이 방식의 원리는 다음과 같다.

1. 반복문이 계속 돌면서, 컴퓨터 내부에 특정 파일이 존재하는지 계속 검사를 한다.
2. 만약 무한루프에 빠지면, 그 특정 파일을 생성시킴으로써 반복문을 종료시킬 수 있다.


여기에서 '컴퓨터 내부를 계속 검사한다' 라는 부분이 상당히 찜찜하게 느껴질 것이다.
하지만, 이 방식을 처음 제시한 저자에 따르면 거의 퍼포먼스의 저하가 없다고 한다.

첨부파일인 BREAKMEL.bat 는 뭐 복잡한 구조를 가지고 있는 것이 아니라, 다음이 전부이다.


즉, c:\ 에 breakMel 이라는 가상의 파일을 하나 만들고, 아무 키나 누르면 그걸 지운다는 거다.


재밌는 방식이고, 꽤나 괜찮은 아이디어이다.
하지만 퍼포먼스의 저하가 '전무' 하다는 것은 사실 믿을 수 없다;; 약간 궁여지책처럼 느껴지기도 하고, 의심이 많이 가는 반복문에서만 사용하는 것이 좋을 것 같다.



아이디어는 다음 블로그 포스팅에서 참고하였다.

http://www.naughtynathan.co.uk/?p=59



ThEnd.

posted by cimple 2010. 8. 17. 14:29

Python Script 로 처음 구현해 본 Fibonacci 수열이다.

List 를 사용해서 손쉽게 구현할 수 있었다.



fibonacci = [1, 1]
max_range = 7;

for i in range(0, max_range-2, 1):
   a1 = fibonacci[i];
   a2 = fibonacci[i+1];
   fibonacci.append(a1+a2);

print fibonacci



그리고 이 Fibonacci 수열을 이용해서 구를 extrude 시키는 기능을 구현해 보았다.


코드는 다음과 같다.





numFace = 0;

for i in range (0, len(fibonacci), 1): 
   mc.select(cl = True); 
   for j in range(0, fibonacci[i], 1):
      mc.select("pSphere1.f[%d]" % numFace, add=True)
      numFace = numFace+1   
   mc.polyExtrudeFacet(ltz = 0.1*fibonacci[i]);


그런데 이 때, 2번째 for 문의 range 를 1로 시작하는 실수를 했더니 계속해서 문제가 발생했다.
즉, 이런 형태의 for 문이 만들어지면

for i in range (1, 1, 1)

for 문은 아무런 기능을 하지 못하고, 만약 그 안에서 특정 기능을 수행해야 할 경우 error 를 발생시키게 된다.

좌우지간 위의 코드를 실행하면 다음과 같은 결과를 얻는다.








그리고 다음은 피보나치 수열을 11번 항까지, 그리고 extrude 에 keepFacesTogether = 0 을 주고 실행한 모습.




별 거 아니지만 재밌다.


ThEnd.