FBX 익스포트 (2017버전 기준)
스킨적용된 메쉬 익스포트
한 파일안의 애니메이션을 특정구간별 익스포트
마지막으로 선택한 디렉토리 및 모션 값을 저장 하거나 불러오기
버그수정
모디파이어 패널이 아닐때(skin modifier 못찾는것) selectObj클릭시 오류수정
SelectObj 클릭시 본, 헬퍼 안보이게 한것 수정.
selectDir 클릭후 폴더를 선택하지 않고 창을 닫을떄 오류수정
FrameStart 최대가 100인것 수정
모션 리스트 없거나 1나만 있을떄 List_컬러체킹용 클릭시 오류수정, 개선
- 맥스 뷰포트에서 새로운 창 띄우고 거기에서 정해진 프레임별 애니선택 및 체크 할수 있게 제작
- 배치용 익스포트
사용 예시
/*
FBX Export maxscript tool
Copyright (C) 2015-2018, JinKyu Kim
This program is distributed in the hope that it will be useful.
------------------------------------------------------------------------------
-- FBX Export split animation clips in maxfile
-- By Jinkyu Kim (http://chakanz.tistory.com/, contact : chakan0000@gmail.com)
-- Created On: 2015-04-08
-- tested using Max 2017
-------------------------------------------------------------------------------
*/
global FBX_Export
try
( makedir ( (getdir #scripts) + @"\tmp\") ) catch (print "error~~~~~~~~~~~~~")
-- 익스포트될 디렉토리 저장.
Global inifilepath = (getdir #scripts )+ @"\tmp\Export_Setting.ini"
-- 배치용 디렉토리 저장.
Global iniSourceFilepath = (getdir #scripts )+ @"\tmp\SourceFile_Setting.ini"
-- 애니메이션 리스트 저장
Global aniSettingPath = maxfilepath+getFilenameFile maxfilename+ @".ini"
/*
-- 파일이름 관련 예시.
-- file="g:\\subdir1\\subdir2\\myImage.jpg"
-- filenameFromPath file -- returns: "myImage.jpg"
-- getFilenamePath file -- returns: "g:\subdir1\subdir2\"
-- getFilenameFile file -- returns: "myImage"
-- getFilenameType file -- returns: ".jpg"
-- pathIsNetworkPath "c:\\temp\\test.txt" --returns: false
-- pathIsNetworkPath "\\\\someserver\\temp\\test.txt" --returns: true
*/
global frame = #()
-- 익스포트할때 각 확장자_타입별 클래스.
global theClasses = exporterPlugin.classes
try DestroyDialog FBX_Export catch()
rollout FBX_Export "FBX_EXPORT" width:300 height:700
(
Group "OBJ Export"
(
button SelectObj "SelectObj" pos:[5,20] width:140 height:35
label showbox "Select for Export" pos:[35,65] width:200 height:50
button btn_SelectDir "SelectDir" pos:[5,115] width:50 height:20
edittext text_SelDir "" pos:[55,115] width:200 height:20
)
button ExportMesh "ExportMesh" pos:[5,140] width:140 height:35
Group "Ani Export"
(
button bt_ExportAni "ExportAni" pos:[5,530] width:140 height:35
button bt_LoadAni "Load" pos:[160,530] width:40 height:35
button bt_SaveAni "Save" pos:[200,530] width:40 height:35
edittext ed_TakeName "TakeName " pos:[10,200] width:140 height:15
spinner sp_FrameStart "FrameStart " pos:[10,220] width:200 height:15 range:[0,9000,0] type:#integer
spinner sp_FrameEnd "FrameEnd " pos:[10,240] width:200 height:15 range:[0,9000,0] type:#integer
button bt_AniAdd "ADD" pos:[5,260] width:60 height:35
button bt_AniRemove "REMOVE" pos:[65,260] width:60 height:35
button bt_AniReset "RESET" pos:[125,260] width:60 height:35
Label ed_ItemList "0개의 모션" pos:[10,300] width: 200 height:15
button bt_listcolor "LIST_컬러체킹용" pos:[200,260] width:80 height:35
dotnetControl lv_Object "System.Windows.Forms.ListView" pos:[10,320] width:280 height:200
Label ed_empty "" pos:[10,565] width:140 height:15
)
Group "Batch Export"
(
button bt_BatchExport "BatchExport" pos:[140,630] width:140 height:35
button btn_SourceDir "SourceDir" pos:[5,610] width:50 height:20
edittext text_SourceDir "" pos:[55,610] width:200 height:20
label created "Create by Jinkyu kim" pos:[185,680] width:200 height:50
)
fn SelectSkinnedBones =
(
--Mesh선택하기 위해서
hidebyCategory.helpers = true
hidebyCategory.bones = true
max select all
--모디파이 탭 선택되고 스킨안에 포함된 본들을 선택
max modify mode
--본, 더미 다시 보이게
hidebyCategory.helpers = false
hidebyCategory.bones = false
if $ !=undefined then
(
SelectMesh = $
if $.modifiers[#Skin] != undefined do
(
Tskin = $.modifiers[#Skin]
--스킨에 적용된 본갯수
BoneNum = skinOps.GetNumberBones Tskin
--배열지정
SkinBoneList=#()
-- 본객수만큼 반복 .
for i=1 to BoneNum do
(
--본 이름을 배열로 넣음
SkinBoneList[i] = skinOps.GetBoneName Tskin i 1
)
clearselection
--본이름 배열 개수만큼반복.
for i=1 to SkinBoneList.count do
(
--배열값의 이름을 얻어온다.
nodeSkinBone = getnodebyname SkinBoneList[i]
if nodeSkinBone !=undefined do
(
selectmore nodeSkinBone
FBX_Export.showbox.text ="모델네임 : "+ SelectMesh.name + " \n" +"본 개수 : "+SkinBoneList.count as string +"_Bones"
)
)
)
)
)
fn SelectDir =
(
-- 캡션은 선택창에서 보이는 글적는것.
Global Target = getSavePath caption:"Keep Working~!!!!!!!!!!!!!!!!!!!!!!!!!! \nSelect Directory for Export" initialDir:FBX_Export.text_SelDir.text
if Target != undefined then
(
FBX_Export.text_SelDir.text = Target
)
)
fn SourceDir =
(
Global SourceTarget = getSavePath caption: "익스포트할 소스폴더를 선택 하세요.!" initialDir:(getDir #maxroot)
if Target != undefined then
(
FBX_Export.text_SourceDir.text = SourceTarget
)
)
fn ExportObj =
(
-- 겟파일네임함수로 맥스파일네임을 가지고 온다.
export_name = getFilenameFile maxFileName
--처음에 Target를 썻었는데 에러가 뜸. 그래서 직접 FBX_Export.text_SelDir.text 를 넣어줌
--익스포트 클래스 5번째 항목인 FBX로 익스포트. -- "\"이것만 쓰면 영역지정이 안됨. 앞에 @ 붙여주니깐 됨.
exportFile (FBX_Export.text_SelDir.text + @"\" + export_name) #noPrompt using:theClasses[9] selectedOnly:true
print (FBX_Export.text_SelDir.text + @"\" + export_name)
)
fn ExportFBX_Option=
(
FBXExporterSetParam "SmoothingGroups" true
FBXExporterSetParam "Animation" false
FBXExporterSetParam "Cameras" false
FBXExporterSetParam "Lights" false
FBXExporterSetParam "UpAxis" "Y"
FBXExporterSetParam "ScaleFactor" 1.0
FBXExporterSetParam "ConvertUnit" "cm"
FBXExporterSetParam "Skin" true
--FBXExporterSetParam "embed Media" false --embed Media 체크시 텍스쳐를 포함하는 FBX
FBXExporterSetParam "ASCII" false -- binaries are smaller
)
local layout_def = #("", "TakeName", "StartFrame", "EndFrame")
fn initListView lv =
(
lv.gridLines = true
lv.View = (dotNetClass "System.Windows.Forms.View").Details
lv.MultiSelect = true
lv.FullRowSelect=true
lv.backColor = (dotNetClass "System.Drawing.color").DarkKhaki
/*
-- lv.Checkboxes = true
-- lv.LabelEdit = true --리스트아이템의 첫번쨰 열(세로)만 됨.
-- lv.Items.Item[1].backColor = (dotNetClass "System.Drawing.color").Green
-- lv.Items.Item[1].SubItems.Item[3].backColor = (dotNetClass "System.Drawing.color").Yellow
*/
for i in layout_def do
--layout_def의 배열 값을 하나씩 가져와 가로69인 항목을 추가한다.
(lv.Columns.add i 69 )
)
Local Item = #()
Local ItemClass =dotNetClass "ListViewItem"
Local NumF
fn addList lv mNumF mTakenF mStartF mEndF =
(
NumF = mNumF as string
TakenF = mTakenF as string
StartF = mStartF as string
EndF = mEndF as string
data = #(NumF)
-- data의 배열인 i가 아래 4개의 값(i, TakenF, StartF, EndF) )을 모아서 item에 전달.
item = for i in data collect
(
dotNetObject ItemClass #(i, TakenF, StartF, EndF)
)
lv.items.addRange item
lv.Update()
)
fn ListRemove lv =
(
local selitem = #()
a = lv.SelectedItems
SelCount = a.Count
if SelCount !=0 then
(
-- 선택된 아이템의 갯수만큼 반복한다. 반복할때마다 선택된아이템들 아이템을 수집해서 selitem의 배열에 집어넣는다. (((i-1 은 LISTVIEW 는 0부터 시작. 맥스스크립트는 1부서 시작)))
selitem = for i=1 to SelCount collect (a.item[i-1])
--selitem안에 selecteditems을 삭제.
for i in selitem do ( lv.items.remove i)
)
else messagebox "선택 후 클릭 하십시요~~"
)
--아이템 위치 확인용
fn ListViewColorCheck lv=
(
if lv.items.count !=0 do
(
for i=1 to lv.items.count do
(
--https://developer.mozilla.org/en-US/docs/Web/CSS/color_value--컬러색보는곳
lv.items.item[i-1].UseItemStyleForSubItems = false
lv.items.item[i-1].subitems.item[1].backcolor = (dotNetClass "System.Drawing.color").LightPink
lv.items.item[i-1].subitems.item[2].backcolor = (dotNetClass "System.Drawing.color").darkslateblue
lv.items.item[i-1].subitems.item[3].backcolor = (dotNetClass "System.Drawing.color").deepskyblue
-- print lv.items.item[0].subitems.item[i].text
)
)
)
/*
-- global RangeStart = animationRange.start
-- global RangeEnd = animationRange.end
*/
fn ExportAni lv =
(
AllCount = lv.Items.count
ListArray = #()
for i=1 to AllCount do
(
AniName = lv.items.item[i-1].SubItems.item[1].text
FStart = lv.items.item[i-1].Subitems.item[2].text
FEnd = lv.items.item[i-1].Subitems.item[3].text
_AniName = AniName as string
_FStar = FStart as integer
_FEnd = FEnd as integer
-- 배열을 배열에 집어 넣음.
append ListArray #(_AniName, _FStar, _FEnd)
)
-- 배열안의 1번쨰 배열을 선택해서 세개의 값을 가져갈수 있도록
for m in ListArray do
(
animationRange = interval m[2] m[3]
FBXExporterSetParam "Animation" true
FBXExporterSetParam "BakeAnimation" true
FBXExporterSetParam "BakeFrameStart" m[2]
FBXExporterSetParam "BakeFrameEnd" m[3]
FBXExporterSetParam "BakeFrameStep" 1.0
-- FbxExporterSetParam "BakeResampleAnimation" true
-- FbxExporterSetParam "NormalsPerPoly" false
-- FBXExporterSetParam "SplitAnimationIntoTakes" m[1] m[2] m[3] -- #(_AniName, _FStar, _FEnd) --2011 버전은 없는거 같음.
FBXExporterSetParam "Cameras" false
FBXExporterSetParam "Lights" false
FBXExporterSetParam "UpAxis" "Y"
FBXExporterSetParam "ScaleFactor" 1.0
FBXExporterSetParam "ConvertUnit" "cm"
FBXExporterSetParam "Skin" true
FBXExporterSetParam "ASCII" false -- binaries are smaller
export_name = getFilenameFile maxFileName
-- FBXExporterSetParam "embed Media" false
-- 처음에 Target를 썻었는데 에러가 뜸. 그래서 직접 FBX_Export.text_SelDir.text 를 넣어줌.
exportFile (FBX_Export.text_SelDir.text + @"\" + export_name+"_" + m[1]) #noPrompt using:theClasses[9] selectedOnly:true --using:theClasses[9] 맥스 버전에 따라 다름., using:FBXEXP 써도 되나봄
-- exportFile(Target + @"\" + export_name + m[1]) #noPrompt selectedOnly:boolSelOnly using:exporterPlugin.classes[intFBXID]
-- FBXExporterSetParam "SplitAnimationIntoTakes" "-c" -- [TakeName] StartValue EndValue 값초기화.
)
)
--최근 위치값 설정 저장.
fn SaveInifile _path=
(
setINISetting _path "SelectedDir" "Export_Dir" text_SelDir.text
)
--최근 위치값 설정 불러오기..
fn LoadInifile _path=
(
text_SelDir.text = getINISetting _path "SelectedDir" "Export_Dir"
)
--최근 소스파일 위치값 설정 저장.
fn SaveIniSourcefile _path=
(
setINISetting _path "SourceDir" "_Dir" text_SourceDir.text
)
--최근 소스파일 위치값 설정 불러오기..
fn LoadIniSourcefile _path=
(
text_SourceDir.text = getINISetting _path "SourceDir" "_Dir"
)
--애니 설정값 저장.
fn SaveAni_Inifile _path=
(
--저장시 이전 기록 삭제.(기존 기록 유지시 데이타 꼬임)
delINISetting _path "Animation"
-- 단순히 애니파일 구분하기위한 용도.
null = "---------------"
for i= 1 to lv_Object.items.count do
(
setINISetting _path " Animation" (null+ i as string) "Take"
setINISetting _path " Animation" ("AniName" + i as string) lv_Object.items.item[i-1].SubItems.item[1].text
setINISetting _path " Animation" ("FStart" +i as string) lv_Object.items.item[i-1].Subitems.item[2].text
setINISetting _path " Animation" ("FEnd" +i as string ) lv_Object.items.item[i-1].Subitems.item[3].text
)
setINISetting _path " Animation" (null as string) "Total "
setINISetting _path " Animation" "Count" (lv_Object.items.count as string)
)
--애니 설정값 불러오기.
fn LoadAni_Inifile _path=
(
for i= 1 to getINISetting _path " Animation" "Count" as integer do
(
TakeName = getINISetting _path " Animation" ("AniName" + i as string )
FrameStart = getINISetting _path " Animation" ("FStart" +i as string)
FrameEnd = getINISetting _path " Animation" ("FEnd" +i as string )
NumF = "선택"
addList lv_Object NumF TakeName FrameStart FrameEnd --위 라인에서 설정한 값들을 addlist(Listview에 숫자 넣는 함수) 함수에서 실행.
)
FBX_EXPORT.ed_ItemList.Text= getINISetting _path " Animation" "Count" as string +"개의 모션 "
)
fn ListReset lv =
(
lv.items.clear()
)
--ListView 아이템 갯수
fn listNum lv =
(
lv.items.count
)
on FBX_Export open do
(
print "\--------------------------------------------------------------------"
print "open"
try(LoadInifile inifilepath)catch()
LoadIniSourcefile iniSourceFilepath
initListView lv_Object
print inifilepath
)
on FBX_Export close do
(
SaveInifile inifilepath
SaveIniSourcefile iniSourceFilepath
print "\--------------------------------------------------------------------"
print "closed"
)
on SelectObj pressed do
(
SelectSkinnedBones()
)
on btn_SelectDir pressed do
(
SelectDir ()
)
on btn_SourceDir pressed do
(
SourceDir ()
)
on bt_SaveAni pressed do
(
SaveAni_Inifile aniSettingPath
)
on bt_LoadAni pressed do
(
--로딩전에 리스트 리셋하기
ListReset lv_Object
LoadAni_Inifile aniSettingPath
)
on ExportMesh pressed do
(
ExportFBX_Option()
ExportObj()
)
on bt_ExportAni pressed do
(
ExportAni lv_Object
)
local NumCount = #()
on bt_AniAdd pressed do
(
NumF = "선택"
local TakeName = FBX_EXPORT.ed_TakeName.Text
local FrameStart = FBX_EXPORT.sp_FrameStart.value
local FrameEnd = FBX_EXPORT.sp_FrameEnd.value
if (FrameStart > FrameEnd) then
messagebox "시작 값이 끝나는값보다 작아야 됩니다."
else
(
--위 라인에서 설정한 값들을 addlist 함수에서 실행.
addList lv_Object NumF TakeName FrameStart FrameEnd
--입력값 초기화
TakeName = FBX_EXPORT.ed_TakeName.Text=""
FrameStart = FBX_EXPORT.sp_FrameStart.value=0
FrameEnd = FBX_EXPORT.sp_FrameEnd.value=0
FBX_EXPORT.ed_ItemList.Text= listNum lv_Object as string +"개의 모션 "
)
)
on bt_BatchExport pressed do
(
--파일에 @가 포함이면
bAniFile = matchpattern maxfilename pattern: @"*@*"
--파일에 Mesh가 포함이면
bMeshFile = matchpattern maxfilename pattern: @"*Mesh*"
maxfiles=#()
maxfiles = getFiles ( text_SourceDir.text + "\*.max")
for f in maxfiles do
(
-- loadMaxFile() will replace the current scene content with the file content without warning!
loadmaxfile f
-- 셀렉트 스킨메쉬.
SelectSkinnedBones()
-- export 메쉬
if(bMeshFile == true) then
(
ExportFBX_Option()
ExportObj()
)
-- export 애니
if(bAniFile == true) then
(
-- LoadAni_Inifile 각각의 애니셋팅 목록값을 불러온다.
LoadAni_Inifile aniSettingPath
ExportAni lv_Object
)
)
)
on bt_AniReset pressed do
(
ListReset lv_Object
--ADD버튼을 누릴떄마다 넘버가 늘어나는데 리셋후에도 계속 늘어나 코드 추가.
NumCount = #()
FBX_EXPORT.ed_ItemList.Text= listNum lv_Object as string +"개의 모션 "
)
on bt_AniRemove pressed do
(
ListRemove lv_Object
FBX_EXPORT.ed_ItemList.Text= listNum lv_Object as string +"개의 모션 "
)
on bt_listcolor pressed do
(
ListViewColorCheck lv_Object
)
)
createdialog FBX_Export
FBX_Export_split_animationClips_in_maxfile.ms
drive.google.com