〇、安装
1.将Neo Workflow复制到PixyzStudioInstallationDirectory/plugins/路径下
2.pipinyin库安装
将pypinyin库复制到PixyzStudioInstallationDirectory/pythonDlls/路径下
3.配置文件修改
在Neo Workflow/plugin.xml中添加/修改presets块配置文件
4.使用
PiXYZ Studio->Plugins->NeoWorkflow->Engine Performance Workflow->
填入[ModelImportPath]字段->Execute
5.功能
a.将ModelImportPath路径下所有指定格式的文件按TessellationSettings、MeshCountList和ExportFileTypeList中的参数组合,并导出
b.ModelSettings参数
DeleteAdditionalMesh:
删除同一虚拟节点下多余生成的额外mesh,判断依据为mesh顶点个数,为安全起见,仅处理含两个子mesh的虚拟节点
ChineseToPinYin:
节点名称中文转拼音,需安装pypinyin库
SetMeshWithParentName:
默认转换后mesh节点名称可能为Burp_x,可通过开启此选项,将mesh节点名称设置为其父节点的名称,并会压缩树
CorrectNodeName:
纠正节点名称,除以下字符和中文,皆视为非法字符,将以””替换,并会将连续””替换为单个”_”
合法字符
“_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789”
CompressTree:
清理一层空节点,同PiXYZ Studio->Scene->Compress Tree
RakeTree:
删除所有虚拟节点,将mesh展平在同一层,同PiXYZ Studio->Scene->Rake Tree
一、说明
1.目前按mesh个数会和指定的mesh个数存在偏差(待优化)
2.贴图需在相应引擎中进行设置,贴图将对渲染和加载时间有严重影响
3.将在相应导入文件的目录中生成输出文件,生成的文件个数为:
导入文件个数×TessellationSettings参数个数×MeshCountList参数个数×ExportFileTypeList
4.文件后缀名说明
LA9G3QA_nnz7956101_asm_0.2_27_146100.fbx
0.2 :转换精度,数值越大,模型精度越低
27:mesh个数,与meshCount值略有偏差
146100:顶点数
LA9G3QA_nnz7956101_asm_0.2_keepAll_438288.fbx
keepAll:保持原模型所有mesh节点
LA9G3QA_nnz7956101_asm_0.2_one_146094.fbx
one:合并原模型所有mesh节点
二、ModelConvertor
ModelConvertor.py
from pxz import *
from os import listdir
from os.path import isfile, isdir, join, normpath, splitext
import fnmatch
import os
import re
#拼音库
import pypinyin
#合法字符
validChar="_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
#获取目录中指定格式的文件
def getFilesInDirectory(directory,extensions,recursive,joinPath):
files = []
for f in listdir(directory):
fullPath = join(directory,f)
if isfile(fullPath):
extOk = False
if extensions:
for ext in extensions:
if fnmatch.fnmatch(f.upper(),"*." + ext.upper()):
extOk = True
break
if not extOk:
continue
if extOk:
if joinPath:
files.append(normpath(fullPath))
else:
files.append(f)
elif recursive and isdir(fullPath):
dirFiles = getFilesInDirectory(fullPath,extensions,recursive,joinPath)
if not joinPath:
tmp = dirFiles
dirFiles = []
for file in tmp:
dirFiles.append(normpath(join(f,file)))
files.extend(dirFiles)
return files
#替换文件后缀
def replaceFileExtension(file,info,newExt):
(root,ext) = splitext(file)
return root + info+"." + newExt
#将mesh名称设置为父节点名称(默认情况下,mesh名称可能为BRep_x)
def setMeshNameWithParentName():
parts = scene.getPartOccurrences(1)
for part in parts:
parent=scene.getParent(part)
parentName=core.getProperty(parent,"Name")
core.setProperty(part,"Name",parentName)
#设置正确的节点名称
def setCorrectNodeName(node):
name=core.getProperty(node, "Name")
newName=""
for char in name:
if validChar.find(char)<0 and isChinese(char)==False:
newName+=('_')
else: newName+=char
newName=re.sub("_+","_",newName)
core.setProperty(node,"Name",newName)
#字符是否为中文
def isChinese(input):
return '\u4e00' <= input <= '\u9fff'
def toPinyin(input):
output=pypinyin.slug(input,separator=" ").title().replace(" ","")
return output
def setNodeNameToPinyin(node):
name=core.getProperty(node, "Name")
newName=toPinyin(name)
core.setProperty(node,"Name",newName)
#设置中文转拼音
def setChineseToPinyin(parent):
for child in scene.getChildren(parent):
setNodeNameToPinyin(child)
setChineseToPinyin(child)
#纠正节点名称
def correctNodesName(parent):
for child in scene.getChildren(parent):
setCorrectNodeName(child)
correctNodesName(child)
#调用示例
#correctNodesName(scene.getRoot())
#输出配置参数
def debugParameters(modelImportPath,importFileTypeList,modelSettings,tessellationSettings,meshCountList,exportFileTypeList):
print(modelImportPath)
print(modelSettings)
print(tessellationSettings)
print(exportFileTypeList)
print(meshCountList)
print(exportFileTypeList)
#获取所有的mesh父节点
def getMeshParentNodes():
allParentNodes=[]
parts = scene.getPartOccurrences(1)
for part in parts:
parent=scene.getParent(part)
if parent not in allParentNodes:
allParentNodes.append(parent)
return allParentNodes
#删除额外的mesh通过顶点数
def deleteAdditonalMeshByPolygonCount(parent):
children=scene.getChildren(parent)
if len(children)>2:
return
max=-1
additionalNodes=[]
childName=core.getProperty(children[0],"Name")
for child in children:
polygonCount=scene.getPolygonCount([child])
if polygonCount>max:
max=polygonCount
for child in children:
if scene.getPolygonCount([child])!=max:
additionalNodes.append(child)
if len(additionalNodes)>1:
return
# for delete in deleteNode:
# if core.getProperty(delete,"Name")!=childName:
# deleteNode=[]
# break
scene.deleteOccurrences(additionalNodes)
#删除同一虚拟节点下多余生成的额外mesh,判断依据为mesh顶点个数,为安全起见,仅处理含两个子mesh的虚拟节点
def deleteAdditonalMesh():
allParentNodes=getMeshParentNodes()
for parent in allParentNodes:
deleteAdditonalMeshByPolygonCount(parent)
#处理模型
def handleModel(modelSettings):
if modelSettings.DeleteAddtionalMesh:
deleteAdditonalMesh()
if modelSettings.ChineseToPinYin:
setChineseToPinyin(scene.getRoot())
if modelSettings.SetMeshWithParentName:
setMeshNameWithParentName()
if modelSettings.CorrectNodeName:
correctNodesName(scene.getRoot())
if modelSettings.CompressTree:
scene.compress(scene.getRoot())
if modelSettings.RakeTree:
scene.rake(scene.getRoot(), False)
scene.resetTransform(1, True, False, False)
# 将轴心设置为对象的中心
scene.movePivotPointToOccurrenceCenter([1], True)
#转换模型
def convertingModel(model,modelSettings,tessellationSettings,meshCountList,fileName,exportFileType,exportFolder):
maxSagList=[]
for setting in tessellationSettings:
maxSagList.append(setting.MaxSag)
meshCountArgs=[]
for setting in meshCountList:
meshCountArgs.append(setting.Count)
for maxSag in maxSagList:
for meshCount in meshCountArgs:
io.importScene(model)
scene.movePivotPointToOccurrenceCenter([1], True)
allOccurrences=scene.getPartOccurrences()
algo.tessellate(allOccurrences,maxSag,-1, -1, createFreeEdges = True)
handleModel(modelSettings)
exportInfo="_"+str(maxSag)+mergeMesh(meshCount)+getVertexesCount()
exportPath=(exportFolder+"\\"+fileName+exportInfo+"."+exportFileType)
io.exportScene(exportPath)
deleteCurrentModelFile()
#获取顶点个数
def getVertexesCount():
vertexes=0
allOccurrences=scene.getPartOccurrences()
for occurrence in allOccurrences:
vertexes+=scene.getVertexCount([occurrence], False, False, False)
return "_"+str(vertexes)
#按总的mesh个数合并节点
def mergePartsByCount(allOccurrences,meshCount):
if len(allOccurrences)<=meshCount:
return "_"+str(len(allOccurrences))
groupNumber=math.ceil(len(allOccurrences)/meshCount)
scene.deleteEmptyOccurrences()
allOccurrences=scene.getPartOccurrences()
#字典键值计数器
index=0
#合并字典
mergeOccurrences={}
#合并计数器
number=0
for occurrence in allOccurrences:
#setdefault
mergeOccurrences.setdefault(index,[]).append(occurrence)
number=number+1
if number==groupNumber:
index=index+1
number=0
sorted(mergeOccurrences)
# len(dict)键的总数
if(len(mergeOccurrences)==0):
return;
for value in mergeOccurrences.values():
if len(value)!=0:
scene.mergeParts(value,1)
meshes=scene.getPartOccurrences()
return "_"+str(len(meshes))
#合并mesh
def mergeMesh(meshCount):
allOccurrences=scene.getPartOccurrences()
if meshCount==-1:
return "_keepAll"
elif meshCount==1:
scene.mergeParts(allOccurrences,1)
return "_one"
else:
return mergePartsByCount(allOccurrences,meshCount)
#删除场景中的模型文件
def deleteCurrentModelFile():
scene.deleteOccurrences(scene.getPartOccurrences())
scene.deleteEmptyOccurrences()
#模型转换器
def modelConvertor(modelImportPath,importFileTypeList,modelSettings,tessellationSettings,meshCountList,exportFileTypeList):
debugParameters(modelImportPath,importFileTypeList,modelSettings,tessellationSettings,meshCountList,exportFileTypeList)
core.removeConsoleVerbose(2)
if modelImportPath:
importFileExtensions=[]
for importFileType in importFileTypeList:
importFileExtensions.append(importFileType.Extension)
modelFiles=getFilesInDirectory(modelImportPath,importFileExtensions,True,True)
exportFileExtensions=[]
for exportFileType in exportFileTypeList:
exportFileExtensions.append(exportFileType.Extension)
for model in modelFiles:
# print(model)
fileName = os.path.split(model)[1].split(".")[0]
for exportFileType in exportFileExtensions:
parentFolder=os.path.dirname(str(model))
exportFolder=parentFolder+"\\"+fileName+"\\"+exportFileType
isExists=os.path.exists(exportFolder)
if not isExists:
os.makedirs(exportFolder)
convertingModel(model,modelSettings,tessellationSettings,meshCountList,fileName,exportFileType,exportFolder)
<?xml version="1.0" encoding="utf-8" ?>
<module name="NeoWorkflow">
<include module="IO"/>
<decltype name="FileType">
<struct>
<field name="Extension"
type="String"
value="None"/>
</struct>
</decltype>
<decltype name="ModelSettings">
<struct>
<field name="DeleteAddtionalMesh"
type="Bool"
default="False"></field>
<field name="ChineseToPinYin"
type="Bool"
default="False"></field>
<field name="SetMeshWithParentName"
type="Bool"
default="False"></field>
<field name="CorrectNodeName"
type="Bool"
default="False"></field>
<field name="CompressTree"
type="Bool"
default="False"></field>
<field name="RakeTree"
type="Bool"
default="False"></field>
</struct>
</decltype>
<decltype name="TessellationQuality">
<struct>
<field name="MaxSag"
type="Distance"
default="0.2"></field>
</struct>
</decltype>
<decltype name="TessellationQualityList">
<list type ="TessellationQuality"/>
</decltype>
<decltype name="ImportFileTypeList">
<list type="FileType"></list>
</decltype>
<decltype name="ExportFileTypeList">
<list type="FileType"></list>
</decltype>
<decltype name="MeshCount">
<struct>
<field name="Count"
type="Int"
default="-1"/>
</struct>
</decltype>
<decltype name="MeshCountList">
<list type="MeshCount"></list>
</decltype>
<function name="modelConvertor"
scriptFile="ModelConvertor.py">
<parameters group="Parameters">
<parameter name="ModelImportPath"
type="String"></parameter>
<parameter name="ImportFileTypeList"
type="ImportFileTypeList"
description=""></parameter>
<parameter name="ModelSettings"
type="ModelSettings"></parameter>
<parameter name="TessellationSettings"
type="TessellationQualityList"></parameter>
<parameter name="MeshCountList"
type="MeshCountList"></parameter>
<parameter name="ExportFileTypeList"
type="ExportFileTypeList"
description=""></parameter>
</parameters>
<presets default="PerformanceTest">
<preset name="PerformanceTest">
<Parameter name="ImportFileTypeList">
[pxz.neoworkflow.FileType("step"), pxz.neoworkflow.FileType("stp"),pxz.neoworkflow.FileType("x_t")]
</Parameter>
<Parameter name="ModelSettings">
pxz.neoworkflow.ModelSettings(True, True, True, True,True,True)
</Parameter>
<Parameter name="TessellationSettings">
[pxz.neoworkflow.TessellationQuality(0.2), pxz.neoworkflow.TessellationQuality(0.5),pxz.neoworkflow.TessellationQuality(1.0)]
</Parameter>
<Parameter name="MeshCountList">
[pxz.neoworkflow.MeshCount(-1), pxz.neoworkflow.MeshCount(1), pxz.neoworkflow.MeshCount(1000), pxz.neoworkflow.MeshCount(2000), pxz.neoworkflow.MeshCount(3000)]
</Parameter>
<Parameter name="ExportFileTypeList">
[pxz.neoworkflow.FileType("fbx"), pxz.neoworkflow.FileType("glb")]
</Parameter>
</preset>
<preset name="NeoSettings">
<Parameter name="ImportFileTypeList">
[pxz.neoworkflow.FileType("step"), pxz.neoworkflow.FileType("stp"),pxz.neoworkflow.FileType("x_t")]
</Parameter>
<Parameter name="ModelSettings">
pxz.neoworkflow.ModelSettings(True, True, True, True,True,False)
</Parameter>
<Parameter name="TessellationSettings">
[pxz.neoworkflow.TessellationQuality(0.2)]
</Parameter>
<Parameter name="MeshCountList">
[pxz.neoworkflow.MeshCount(-1)]
</Parameter>
<Parameter name="ExportFileTypeList">
[pxz.neoworkflow.FileType("fbx")]
</Parameter>
</preset>
</presets>
</function>
</module>
二、Python
1.获取制定路径下所有文件
2.字符串拼接
3.中文字符判定