튜토리얼 3: VRED에서 스크립트를 사용해야 하는 경우와 일반 활용 사례 동영상에서는 다음을 살펴봅니다.
-prepython 및 -postpython동영상 캡션: 안녕하세요. VRED Pro를 위한 Python 스크립팅 튜토리얼에 오신 것을 환영합니다. 저는 이 튜토리얼의 진행자인 크리스토퍼라고 하며, 오늘은 VRED의 다양한 Python 인터페이스와 이를 효율적으로 사용하는 방법에 대해 살펴보겠습니다.
VRED는 Python을 사용한 다양한 스크립팅 방법을 제공합니다. 일반적으로 장면 또는 VRED의 동작에 영향을 주는 Python 코드를 추가할 수 있는 인터페이스가 여러 개 있습니다. 하지만 모든 인터페이스가 동일한 작업에 적합한 것은 아닙니다. 문제에 따라 보다 적합한 지점이 있습니다. 여기서는 처음 시작할 때 사용할 수 있는 몇 가지 예제를 통해 간단한 것에서 복잡한 것까지 Python 스크립트를 추가할 수 있는 여러 지점을 보여드리겠습니다.
먼저, VRED 터미널에서 가장 간단한 형식의 Python 인터페이스부터 살펴보겠습니다. VRED 터미널은 Python 명령에 대한 입력 역할뿐 아니라 VRED, 다른 스크립트 또는 사용자 자체 코드에서 생성된 출력 메시지에 대한 출력 역할도 합니다. VRED를 시작한 후 터미널을 열면 여러 메시지가 표시되는데, 이를 통해 VRED의 초기화 메시지로 어떤 리소스가 로드되었는지, 오류가 발생했는지 여부를 알 수 있습니다. 사용자 또는 기타 스크립트가 생성하는 출력 메시지는 모두 터미널에 표시되므로, 터미널이 스크립트를 실행하면서 오류가 발생했는지 바로 확인하거나 로그 메시지를 볼 수 있는 유일한 위치입니다.
터미널은 인터랙티브 모드의 Python 해석기처럼 Python 명령에 대한 기본 입력기 역할도 합니다. 명령을 입력한 후 실행이 성공한 경우 녹색 피드백이 표시되고, 오류가 발생하는 경우 빨간색 피드백이 표시됩니다. 터미널은 한 줄로 구성된 입력이기 때문에 주로 Python 함수를 테스트하는 데 사용됩니다. 또한 터미널에 입력한 모든 항목은 VRED를 다시 시작하면 손실되며 파일과 함께 저장되지 않습니다. 터미널에서 스크립트 편집기, 기본 설정 또는 변형 세트에서 전역적으로 정의된 변수 및 함수에 액세스할 수 있지만 터미널에서 정의하는 모든 변수 및 함수는 다른 스크립트에서 액세스할 수 없습니다.
보다 중요한 Python 스크립팅 인터페이스에 대해 알아보기 전에 스크립트 기본 설정을 살펴보겠습니다. 스크립트 기본 설정은 사용자가 연 파일과 관계없이 VRED 응용프로그램 기본 설정에 저장되어 있고 항상 사용할 수 있는 Python 스크립트입니다. 스크립트 기본 설정은 사용자 키 매핑을 정의하는 데 유용합니다. 스크립트 기본 설정을 처음 열면 렌더 설정 또는 기타 사항을 변경하는 키 매핑이 F 키에 이미 정의되어 있습니다.
스크립트 기본 설정은 항상 필요한 간단한 Python 스크립트 또는 변수를 지정하기 위한 위치이며, 현재 열려 있는 파일과는 별개입니다. 예를 들어, 터미널 메시지에 타임스탬프를 자동으로 추가하는 몇 가지 사용자 로그 함수를 정의할 수 있습니다. 또한 스크린샷을 생성하거나 렌더링을 미리 보고 하드 드라이브에 저장하는 함수를 정의할 수도 있습니다. 그러나 대부분의 경우 추가 함수를 사용하여 키 매핑을 사용자화할 것입니다. 렌더 품질 간에 전환하는 경우를 예로 들 수 있습니다. 스크립트 기본 설정에서 정의한 모든 함수는 스크립트 편집기, 변형 세트 및 터미널에서 전역적으로 사용할 수 있습니다. 즉, 변형 세트에서 직접 스크립트 기본 설정에 정의된 함수를 호출할 수 있습니다.
변형 세트는 다음 주제이기도 합니다. 변형 세트에 직접 Python 스크립트를 작성할 수 있습니다. 변형 세트가 트리거될 때 실행되는 Python 코드를 포함할 수 있는 스크립트 탭이 있습니다. 변수와 함수는 물론, 클래스까지도 정의할 수 있지만, 주로 특정 작업을 수행하는 스크립트를 작성할 것입니다.
이 예에서는 변형 세트를 활성화하여 트리거되는 데이터 준비 도구를 구현했습니다. 여기서 설정된 변형 세트는 Python 도구 역할을 하며 두 번 클릭하여 트리거될 수 있습니다. 스크립트 자체는 다른 CAD 소프트웨어에서 가져올 때 손실된 재질과 재질 스위치를 매칭합니다. 특정 이름의 재질을 검색하고 재질 스위치를 생성합니다. 그런 다음 손상된 재질을 새 재질 스위치로 대체합니다.
여기서 스크립트가 너무 길지 않고 정확히 하나의 문제를 해결하는 것을 볼 수 있습니다. 따라서 유지 관리하기도 상당히 쉽습니다. 코드를 더욱 쉽게 읽고 이해할 수 있게 해 주는 출력 메시지와 코드 내 주석이 있으며, 이는 사용자가 코드가 실제로 의도된 작업을 수행했는지 아니면 오류가 발생했는지 확인하는 데에도 도움이 됩니다.
변형 세트에서 작성하는 Python 스크립트는 로컬로 범위가 지정됩니다. 즉, 변수, 함수 및 클래스는 이 특정 변형 세트에서만 사용할 수 있습니다. 변형 세트 B의 스크립트에서 변형 세트 A에 정의된 함수를 호출할 수는 없습니다. 따라서 둘 이상의 변형 세트에서 스크립트를 사용하려는 경우 전역적으로 사용할 수 있는 스크립트 편집기에 스크립트를 추가한 후 해당 변형 세트에서 스크립트를 호출해야 합니다. 이렇게 하면 코드 중복도 방지됩니다.
마지막으로, 변형 세트에 얼마나 많은 양의 코드를 추가하는지도 알아야 합니다. 위의 예는 약 40줄로, 이 정도는 괜찮습니다. 하지만 스크립트에 수백 줄의 코드가 포함되는 경우 유지 관리가 불가능하고 이해하기도 어려운 문제가 발생합니다. 이러한 코드를 확장하거나 오류 및 버그를 없애려면 리소스가 많이 필요합니다. 이 경우 플러그인을 사용하거나 전역적으로 사용 가능한 스크립트 편집기에서 함수를 정의하는 것이 더 좋으며, 다음으로 스크립트 편집기에 대해 살펴보겠습니다.
스크립트 편집기는 편집 메뉴의 스크립트 편집기를 클릭하여 액세스할 수 있습니다. 스크립트 편집기는 VRED 장면에 직접 연결된 Python 파일과 유사합니다. 여기에서 전역적으로 사용 가능한 변수, 함수, 클래스를 정의할 수 있습니다. 스크립트 편집기에 작성한 모든 Python 코드는 변형 세트, 터미널, 웹 엔진 등에서도 사용할 수 있습니다. 다시 말해, 전역적으로 사용할 수 있습니다.
편집기 맨 아래에 있는 실행(Run) 버튼을 클릭하면 스크립트 편집기에 작성한 모든 코드가 실행됩니다. 그러나 FileIO의 기본 설정에 있는 설정도 살펴봐야 합니다. 파일이 로드될 때 편집기의 스크립트가 자동으로 실행되는지 여부를 지정할 수 있는 VRED 파일입니다. 이 설정은 기본적으로 켜져 있으므로, 스크립트를 자동으로 실행하지 않으려면 이 옵션을 꺼야 합니다.
스크립트 편집기는 함수 및 변수를 전역적으로 정의하는 데 유용합니다. 예를 들어, 현재 카메라 위치에서 뷰포인트를 생성하는 함수를 정의할 수 있습니다. 이 함수는 모든 변형 세트에서 호출하거나 원격으로 트리거할 수도 있습니다. 그런 다음 모든 뷰포인트를 자동으로 배치 렌더링하고 정의 가능한 디렉토리에 저장하는 함수를 정의할 수 있습니다. 물론, 스크립트 편집기에서 복잡한 스크립트를 작성할 수도 있습니다.
Python에서는 여러 모듈, 클래스 및 함수를 하나의 파일에 저장할 수 있습니다. 이론적으로는 전역 함수, 다중 도구, 사용자 인터페이스도 스크립트 편집기 하나에서 구현할 수 있습니다. 하지만 제 생각에는 그러면 너무 지저분해지고 복잡하여 유지 관리가 쉽지 않을 것입니다. 스크립트가 몇백 줄의 코드보다 긴 경우에는 스크립트 플러그인 또는 모듈로 기능을 분리해야 할 수 있습니다.
추가 도구를 구현해 보는 VRED의 VR 메뉴 확장에 대한 튜토리얼 동영상이 있으니 참고하시기 바랍니다. 이 작업은 스크립트 편집기가 적합합니다.
Python 스크립트 리포지토리가 있는 경우 가져오기 기능을 사용하여 스크립트를 직접 가져오는 것이 유용할 수 있습니다. 이 작업은 파일을 열기만 하면 되는 것처럼 쉬우며, 가져온 스크립트는 스크립트 편집기의 컨텐츠에 간단히 추가되어 실행됩니다. 하지만 주의가 필요합니다. 어떤 방식으로든 장면을 조작하는 스크립트가 편집기에 이미 있는 경우 다른 스크립트를 가져올 때마다 이 스크립트가 다시 실행되기 때문입니다.
가져오기 기능을 사용하면 사용자 인터페이스 또는 파일 형식으로 있는 다른 Python 도구를 빠르게 추가할 수 있습니다. 'VR 메뉴 확장' 튜토리얼에서는 이 방법을 사용하여 장면에서 사용자 VR 도구를 가져옵니다.
VRED에서는 완전히 자동화된 렌더 파이프라인을 빌드할 수도 있습니다. 장면을 로드하고 이미지 또는 동영상을 자동으로 렌더링하려면 다음 두 명령행 옵션(-prepython 및 -postpython)을 사용하면 편리합니다. -prepython을 사용하여 장면을 로드하기 전에 스크립트를 실행하고, -postpython을 사용하여 장면을 로드한 후에 스크립트를 실행할 수 있습니다. 즉, -prepython을 사용하면 모듈 또는 스크립트 기본 설정과 마찬가지로 장면과 별개인 스크립트를 실행할 수 있습니다. -postpython을 사용하면 스크립트 편집기에 정의된 스크립트를 실행하거나 사전 설정으로 저장된 렌더링 작업을 시작할 수 있습니다. VRED 터미널에 입력할 수도 있는 모든 Python 명령을 세미콜론으로 구분하여 입력할 수 있습니다.
일반적인 활용 사례는 장면을 로드하고 다른 CAD 모델을 가져온 다음 모든 뷰포인트 또는 동영상을 렌더링하는 것입니다. 그런 다음 VRED를 종료하고 다른 파일로 시작할 수 있습니다. 자동화된 렌더링을 위해서는 몇 가지 렌더링 사전 설정을 지정하는 것이 좋습니다. 그러면 명령행에 올바른 렌더 설정을 선택할 수 있습니다.
자주 사용되는 Python 도구 및 함수를 추가하는 또 다른 방법은 사용자 Python 모듈을 사용하는 것입니다. 이러한 모듈은 lib 디렉토리 및 python 아래의 VRED 응용프로그램 디렉토리에 추가됩니다. 이 모듈은 다른 모듈과 동일하게 구조화되며, 클래스와 함수, 그리고 Python 모듈이 보유할 수 있는 모든 항목을 포함할 수 있습니다. 주의해야 할 점은 스크립트 편집기에서 스크립팅할 때처럼 모든 VRED 모듈을 자동으로 가져오는 것은 아니라는 점입니다. 즉, API v1 모듈을 사용하려면 명시적으로 가져와야 합니다. 그러나 API v2 모듈은 VRED에서 주입하므로 가져올 필요가 없습니다.
하지만 모듈은 여러 도구, Python 함수 및 클래스를 한 번에 모두 제공하는 유용한 방법입니다. VRED 설계자 또는 개발자용으로 이러한 모듈을 설치할 수 있으며 이러한 모듈은 모두 동일한 Python 스크립트 집합을 공유합니다. 이는 이러한 스크립트를 리포지토리에서 관리할 수 있고 양질의 코드를 유지하기가 훨씬 쉽다는 점에서 유용합니다. VRED 파일 또는 변형 세트에 저장된 Python 스크립트의 경우 모든 스크립트가 여러 파일에 걸쳐 분산되어 있으므로 유지하기가 쉽지 않습니다.
스크립트 플러그인은 아마도 기능을 VRED 인스턴스에 추가할 수 있는 가장 수준 높은 방법일 것입니다. 스크립트 플러그인은 사용자 인터페이스와 함께 제공되는 VRED 도구이며, VRED 메뉴 막대에서 사용할 수 있습니다. 플러그인은 하나의 간단한 작업을 수행하는 몇 줄의 코드일 수 있지만, 수백 줄의 코드가 될 수도 있고 VRED와 상호 작용하고 작업하는 방법에 대한 모든 측면을 변경할 수도 있습니다. 스크립트 편집기에서도 동일한 기능을 구현할 수 있지만, 플러그인은 VRED 파일과 별개이며, 일반적으로 타사 코드 편집기를 사용하여 작성됩니다.
자체 플러그인을 개발하는 이점은 플러그인이 VRED 메뉴 막대에 통합되어 있어 다른 설계자 및 개발자와 쉽게 공유할 수 있으며 장면에 있는 다른 Python 코드와는 별개라는 점입니다. 따라서 플러그인은 사용 중인 다른 Python 스크립트의 상태를 실수로 변경할 수 없습니다.
이를 전체적으로 다루는 튜토리얼이 있으므로 여기서는 스크립트 플러그인에 대해 너무 많은 내용을 설명하지 않겠습니다. 이 튜토리얼에서는 사용자 인터페이스와 함께 자체 스크립트 플러그인을 구현할 수 있는 방법을 소개해드릴 것이므로 꼭 확인해 보십시오!
여기서 볼 수 있듯이 다소 간단하거나 더 복잡한 Python 스크립트를 사용하여 장면을 변경하거나 VRED로 작업하는 방법은 여러 가지가 있습니다. 이제 VRED에서 Python 스크립팅을 사용하는 것과 관련하여 모든 가능한 방법을 알게 되셨을 것입니다. 문제마다 여러 가지 솔루션이 있을 수 있지만, 목표에 더 적합한 Python 인터페이스가 있습니다.
본 동영상을 통해 이러한 문제에 어떻게 접근할 수 있는지, 몇 가지 가이드라인을 알 수 있으셨기를 바랍니다. 오늘은 이것으로 마치겠습니다. 다음 튜토리얼에서도 만나볼 수 있기를 바랍니다. 참여해 주셔서 감사하며 다음에 뵙겠습니다!
튜토리얼 3: VRED에서 스크립트를 사용해야 하는 경우와 일반 활용 사례 동영상에는 세 가지 예제 Python 코드가 있습니다. 이러한 파일을 압축하여 다운로드하려면 여기를 클릭하십시오.
REM Example 1
REM -prepython "print('Executed before file has loaded')" -postpython "print('Executed after file has loaded')"
REM Example 2
start "D:\Programme\Autodesk\VREDPro-13.3\bin\WIN64\VREDPro.exe" "C:\ProgramData\Autodesk\VREDPro-13.3\examples\Automotive_Genesis.vpb" -prepython "print('---');print('Executed before file has loaded')" -postpython "print('---');print('Executed after file has loaded')"
# Example 1.0
# Global function library
import time
from datetime import datetime
def logInfo(message):
now = datetime.now().time().strftime("%H:%M:%S.%f")
print(now, '[INFO]', message)
def logWarn(message):
now = datetime.now().time().strftime("%H:%M:%S.%f")
print(now, '[WARN]', message)
def createViewpointFromCamera():
now = datetime.now().time().strftime("%H:%M:%S.%f")
vrCameraService.createViewpoint("vp_{}".format(now))
def renderViewpoints():
viewpoints = vrCameraService.getAllViewpoints()
renderDirectory = vrFileDialog.getExistingDirectory(
"Select a render directory:",
vrFileIO.getFileIOBaseDir()
)
if not renderDirectory:
logWarn("No directory where to save the renderings!")
return
for viewpoint in viewpoints:
name = viewpoint.getName()
if name.startswith('vp_'):
vrRenderSettings.setRenderFilename("{}.jpg".format(name))
vrRenderSettings.startRenderToFile(False)
# Example 1
# Recreating material switches
print('--- Create Switch Materials ---')
allMaterials = getAllMaterials()
# get all materials which name starts with "switch"
switchMaterials = list(filter(lambda matPtr: matPtr.getName().startswith('switch'), allMaterials))
# iterate all switch materials
for switchMaterial in switchMaterials:
switchPtr = switchMaterial
switchName = switchPtr.getName()
print('Found switch: ' + str(switchName))
# extract material prefix
materialPrefix = '_'.join(switchName.split('_')[1:])
# Get all materials which name starts with the material prefix
childMaterials = list(filter(lambda matPtr: matPtr.getName().startswith(materialPrefix), allMaterials))
childMaterialNames = [x.getName() for x in childMaterials]
print('Found child materials: ' + ', '.join(childMaterialNames))
# Create new material switch
print('Create new material switch...')
newMaterialSwitch = createMaterial("SwitchMaterial")
newMaterialSwitch.setName(switchName)
# Add child materials to switch
for child in childMaterials:
newMaterialSwitch.addMaterial(child)
# swap old switch material with new switch material
print('Assign new material switch...')
nodes = switchPtr.getNodes()
for node in nodes:
node.setMaterial(newMaterialSwitch)
print('')
print('Finished!')
print('---')
# Example 2
# Calling global functions in the script editor
logInfo('This is an info message from a variant set.')
logWarn('This is a warning message from a variant set.')
createViewpointFromCamera()
renderViewpoints()