Getting started with Pixyz Studio and Pixyz Scenario Processor

introduce

Pixyz product official website
Download, installation and technical documentation

Official website introduction: Pixyz supports more than 45 industrial file formats, including CATIA, JT, STEP, IFC, PVZ, NWD, USD and glTF. Includes CAD, tessellation/mesh models, point clouds, and more. Assets in Unity will be linked to the original data in real time, automatically updating as file modifications occur. Create LODs and UVs, merge and disassemble meshes, change pivots, and more in Unity to build ready-to-use assets. Optimize any data in the Unity Editor without re-importing it.

There are 4 product lines:

  1. pixyz studio: standalone software tool
    • Import/convert CAD, mesh and point cloud data
    • Optimize/export data
    • Automate tasks with the Python API
  2. pixyz plugin: A plug-in installed in unity, specially customized for unity. The process of exporting CAD models and importing them into unity is better than exporting from studio and then importing into unity.
    • Import CAD/3D/BIM data into Unity
    • Preserve hierarchy and metadata
    • Automatically run scripts with rules engine
  3. Pixyz Review
    • Drag and drop CAD, mesh or point cloud data
    • Enter virtual reality with one click
    • Research and interact with models
  4. Pixyz Scenario Processor
    • Batch conversion and model optimization
    • Run on AWS Cloud or standalone machine
    • Provide expansion possibilities

Version comparison

VersionComparison of different versions, you can see the content and status of each update

Supported file formats

Supported file formats for import and export are available in the documentation:Import/Export file formats

Please add image descriptionPlease add image description

Getting started

Only some simple contents of pixyz studio and pixyz Scenario Processor are introduced.

Pixyz Studio

It is an independent software with a visual interface. After installation and activation (free trial available), model processing can be performed.
Please add image description

Contains the most basic import, export and other functions

import

In the upper left corner, click the small arrow to select Guided import or Raw import.
Please add image description

After selecting Guided Import, you can select the file and configure it according to the preset, select the read quality, coordinate settings, import points and lines, etc.

Please add image description

Click Execute to start importing the model

At this time, you can see that the Output in the lower left corner of the model begins to output, which is the command executed during the process of importing the model (executing command xxxxx). For example, the import is executed here:

executing command process.guidedImport
executing command scene.deleteEmptyAnimation
executing command scene.applyTransformation
executing command scene.cleanUnusedMaterials
executing command scene.removeSymmetryMatrices

Please add image description

After importing, you can see the tree structure of the model in Occurrences on the left:
Please add image description

Visually see the model in the largest Viewer window in the middle

other functions

The top navigation bar has common CAD, patch, model optimization and other functions

Please add image description

Let’s take Decimate To Target as an example:

You can choose to reduce the substructure belonging to a certain occurrence. If not selected, the default is the root directory (Root). You can choose the target strategy:

  1. Triangle Count: Reduce the number of triangles to a certain number (default 10,000)
  2. Ratio: reduced to a certain ratio (default 50%)

Please add image description

Here, set the surface reduction to 50% (Ratio), select Execute, and start reducing the surface.

Similarly, you can see the execution process and results of surface subtraction in Output:

Please add image description

Export

Click Export to export the imported model. When exporting, it will be converted according to the file type you set.

Please add image description

Therefore, as long as you first import a model in a certain format and then export it to another format, you can complete the model type conversion.

For example, import '.fbx' and export '.prefab', which can convert the fbx model into a unity prefab.

In the Output in the lower left corner, you can see that the export command is executed:

Please add image description

Execute python script

You can see that there is a Scripting window next to Output, which can be used to write and run python scripts

Please add image description

Click "+" to create a new python script. Click Sample Scripts to open the local folder. When installing Pixyz Studio, script samples are also downloaded. You can open each sample to view and learn.
Please add image description

Example,101_GenericProcess.py

# Pixyz Python API sample (2022.1)
#
# 101 Generic Process
#
# Example of a typical simple process to transform a CAD model into optimized mesh(es)

from pxz import *

def process():
	allScene = [scene.getRoot()]

	# repair CAD with 0.1mm tolerance
	algo.repairCAD(allScene, 0.1, True)	

	# tessellates with maxSag=0.2mm and maxAngle=10deg
	algo.tessellate(allScene, 0.2, -1, 10)

	# repairs with a tolerance of 0.1mm
	algo.repairMesh(allScene, 0.1)

	# removes through holes with diameter under 10mm
	algo.removeHoles(allScene, True, False, False, 10)

	# removes hidden patches
	algo.hiddenRemoval(allScene,algo.SelectionLevel.Patches, 1024, 16)

	# deletes patches to allow the decimation to remesh over the base CAD patches
	algo.deletePatches(allScene)

	# decimates with surfacic tolerance to 1mm, lineic tolerance to 0.1mm and normal distorsion tolerance to 5mm
	algo.decimate(allScene, 1, 0.1, 5)

# processes the model
process()

You can copy this line of code into the Script window and execute it

For more information, you can view the official Python API

But there is a simpler method in Pixyz Studio. During the execution of tasks in the previous interface, you can see that there is a button on each separate window:Copy python code to clipboard, such as import window and surface reduction window:

Please add image description

Please add image description

After clickCopy python code to clipboard button, you can copy the code to the Script window:

Python code to import the model:

_ret_ = process.guidedImport(["D:/furniture_knoll_for_offices.dwg"], pxz.process.CoordinateSystemOptions(["automaticOrientation",0], ["automaticScale",0], False, False), ["usePreset",2], pxz.process.ImportOptions(False, True, True), True, True, False, False, False, False)

Python code for reducing model faces:

algo.decimateTarget([1], ["ratio",50.000000000000], 0, False, 5000000)

After clicking Run, you can get the same effect as using the visualization panel to reduce the surface:
Please add image description

Scripts can be used for unified process processing, but there are still some minor problems when importing and exporting.

To batch process multiple models, you can use the Pixyz Scenario Processor

Pixyz Scenario Processor

How to run the Pixyz Scenario Processor

Refer to the official documentation: How to run Pixyz Scenario Processor

Currently, there are two execution methods for referring to windows commands:

  1. python script: executed directly as a python script
  2. plugin: compiled into .pxzext for plug-in use

It also provides Scenario'sscript sample. The following directly refers to the code in the gitlab library for magic modification.

Scenario Processor sample

There are three main folders in the warehouse. The readme gives an introduction (very abstract). I will explain it based on my personal understanding:

  1. Folder Watcher: Monitors the folder on the local hard disk. All files placed in this folder will execute a python script and perform corresponding operations< a i=2> 2a. Scenario sample: Publish a python script into a plug-in for execution 2b. Folder watcher with Scenario: Cooperate with the plug-in released by 2a, execute the script in the plug-in, and perform corresponding operations

Please add image description

All in all, the effect of 1 and 2a+2b is the same. One is python script and the other is plugin. You can choose different execution methods according to personal needs.

Essential software:

  1. Pixyz Scenario Processor
  2. Pixyz Studio: for creating and publishing plugins
  3. Remember to install and configure the path of python to ensure that you can execute the script through python xxx.py on the command line.

Preparation:

  1. Pull the entire warehouse to local
  2. Apply for a trial license of Pixyz Scenario Processor,application and activation method,very important! ! !
Python Script

Open 1_Folder watcher, its directory structure is:

│   config.json
│   run.bat
├───scripts
│       sampleScript.py
│       watcher.py
├───_input
└───_output

Run run.bat to execute sampleScript.py to process the model

The running logic of the file:

  1. run.bat is a batch script, double-click to runrun.bat, open it with an editor and you can see:
python scripts/watcher.py config.json
pause

The python command is called, the 0th parameter entered is the scriptscripts/watcher.py, run the watcher.py script in the scripts directory, the 1st parameter entered is config.json , a configuration file

If python is not configured as the default environment of the system, you can also enter python scripts/watcher.py config.json in the command line (cmd, powershell) that can run python to execute the code

  1. config.json: Configuration file, open it to see its configuration
{
    "input_folder":  "_input/",
    "output_folder": "_output/",
    "optimization":  "True",
    "extensions":
    [
        ".pxz",
        ".fbx",
        ".glb",
        ".pdf",
        ".usdz",
        ".obj",
        ".3dxml"
    ]
}
  • The input folder is:_input/
  • The output folder is:_output/
  • optimization is True, indicating the need for optimization
  • extensions indicates that the accepted model types are: .pxz, .fbx, .glb, .pdf, .usdz, .obj, .3dxml, if there are additional required types, you can add them here and delete them if they are not needed. Type
  1. watcher.py: Monitor the folder, and the endless loop of while(1) will be executed as long as there is a file in the _input folder and has been copied (isCopyFinished) executeScenarioProcessor
import sys
import json
import os
import subprocess
from ctypes import windll

def main(config_file):
    # 读取输入参数
    # 输入文件夹 input_folder、输出文件 output_folder、后缀 extensions、是否要优化 optimization
    input_folder, output_folder, extensions, optimization = read_config(config_file)

    waiting = False

    while(1):
	    # 从 input_folder 中读取文件文件 input_files
        input_files = [file for file in os.listdir(input_folder)\
                        if (os.path.isfile(input_folder + '/' + file)\
                        and getFileExtension(file) not in ['.xml', ''])]
		
		# 如果 input_folder 中没有文件了,就不处理,继续等待
        if len(input_files) == 0:
            if not waiting:
                print('\n')
                print('Waiting for files to process...\n')
                waiting = True
            continue
        # 如果 input_folder 有文件,但是还没有复制完,不处理,继续等待
        elif not isCopyFinished(input_folder + '/' + input_files[0]):
            continue
        # 如果 input_folder 有文件,且已经复制完了,停止等待,开始处理文件
        else:
            waiting = False

		# 目前正在处理的文件 input_file 的路径:输入文件夹路径 + 第0个文件名
        input_file = input_folder + '/' + input_files[0]
        # 开始处理文件
        executeScenarioProcessor(input_file, output_folder, extensions, optimization)

		# 处理完文件后,把文件删除,否则会一直处理这个文件,真的陷入死循环了
        os.remove(input_file)

def read_config(config_file):
	# 打开配置文件 config.json,加载内容到 inputs
    with open(config_file) as config:
        inputs = json.load(config)

    # 读取输入文件夹 "input_folder" 对应的字段
    input_folder = inputs['input_folder']
    input_folder = os.path.abspath(input_folder) 
    # 读取输出文件夹 "output_folder" 对应的字段
    output_folder = inputs['output_folder'] 
    output_folder = os.path.abspath(output_folder)
    # 读取输出后缀 "extensions" 对应的字段
    extensions = inputs['extensions']
    # 读取输出优化选项 "optimization" 对应的字段
    optimization = inputs['optimization']

    print('\n')
    print('Input folder: %s\n' % input_folder)
    print('Output folder: %s\n' % output_folder)
    print('Extensions: %s\n' % ' '.join(extensions))
    print('Optimization: %s\n' % optimization)

    return input_folder, output_folder, extensions, optimization

# 判断文件是否复制完成的函数
def isCopyFinished(inputFile):
    """
    Check if the file that was dropped in the input folder has finished being copied
    """
    GENERIC_WRITE         = 1 << 30
    FILE_SHARE_READ       = 0x00000001
    OPEN_EXISTING         = 3
    FILE_ATTRIBUTE_NORMAL = 0x80
    handle = windll.Kernel32.CreateFileW(inputFile, GENERIC_WRITE, FILE_SHARE_READ, None, OPEN_EXISTING,\
            FILE_ATTRIBUTE_NORMAL, None)
    if handle != -1:
        windll.Kernel32.CloseHandle(handle)
        return True
    return False

# 获取文件的后缀
def getFileExtension(file):
    return os.path.splitext(file)[1]

# 用 PiXYZScenarioProcessor 执行命令,核心步骤
def executeScenarioProcessor(input_file, output_folder, extensions, optimization):
	# 注意:把 PiXYZScenarioProcessor.exe 的路径换成自己本地的路径
    args = ['C:\Program Files\PiXYZScenarioProcessor\PiXYZScenarioProcessor.exe', 'scripts/sampleScript.py', input_file, output_folder, str(extensions), str(optimization)]
    print(sys.argv[0])
    print(args)
    # 启动子进程运行指令
    p = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
    while p.poll() is None:
        l = str(p.stdout.readline().rstrip()) # This blocks until it receives a newline.
        print(l)
    print(p.stdout.read())

if __name__ == "__main__":
    main(sys.argv[1])

If you do not need to monitor the input folder in real time and delete the models in the input folder, you just need to convert all the models in the input into the output folder. The above code can be simplified as:

import sys
import json
import os
import subprocess

def main(config_file):
    input_folder, output_folder = read_config(config_file)

    input_files = [file for file in os.listdir(input_folder)\
                    if (os.path.isfile(input_folder + '/' + file)\
                    and getFileExtension(file) not in ['.xml', ''])]

    print(input_files)

    if len(input_files)>0:
        for i in range(len(input_files)):
            print("-"*30, i, input_files[i], "-"*30)
            input_file = input_folder + '/' + input_files[i]
            executeScenarioProcessor(input_file, output_folder)

def read_config(config_file):

    with open(config_file) as config:
        inputs = json.load(config)

    input_folder = inputs['input_folder']
    input_folder = os.path.abspath(input_folder) 

    output_folder = inputs['output_folder'] 
    output_folder = os.path.abspath(output_folder)

    print('\n')
    print('Input folder: %s\n' % input_folder)
    print('Output folder: %s\n' % output_folder)

    return input_folder, output_folder

def getFileExtension(file):
    return os.path.splitext(file)[1]

def try_decode_bytes(b):
    for encoding in ['utf-8', 'gbk']:
        try:
            return b.decode(encoding, errors="ignore")
        except Exception as e:
            pass
            
    return '{}'.format(b)

def executeScenarioProcessor(input_file, output_folder, extensions, optimization):
    args = ['C:\Program Files\PiXYZScenarioProcessor\PiXYZScenarioProcessor.exe', 'scripts/sampleScript.py', input_file, output_folder, str(extensions), str(optimization)]
    print(sys.argv[0])
    print(args)
    p = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
    while p.poll() is None:
        l = str(p.stdout.readline().rstrip()) # This blocks until it receives a newline.
        print(l)
    print(p.stdout.read())


if __name__ == "__main__":
    main(sys.argv[1])
  1. sampleScript.py
import os
import time
import sys

def convertFile(inputFile, outputFolder, extensions, optimization):
	fileName = getFileNameWithoutExtension(inputFile)
	
	# Set log file in output folder for debug purposes (otherwise it will be overwritten by next 3d file processing)
	core.setLogFile(outputFolder + '/' + fileName + '.log')

	# Imports and prepares the input_files using the automatic "Guided import" process (see documentation)
	roots = process.guidedImport([inputFile], pxz.process.CoordinateSystemOptions(["automaticOrientation",0], ["automaticScale",0], False, True), ["usePreset",2], pxz.process.ImportOptions(False, True, True), False, False, False, False, False, False)

	if optimization:# Comment or delete the lines not necessary to your workflow, adjust parameters' values if necessary
		
		t0, n_triangles, n_vertices, n_parts = getStats(roots[0])

		# Automatically selects and deletes parts in the scene, whose size is lower than a maximum size defined in by the parameter Size (in millimeters)
		SIZE = 20
		scene.selectByMaximumSize(roots, SIZE, -1, False)
		scene.deleteSelection()
		
		# Removes through holes from CAD models whose diameter is below the defined diameter
		DIAMETER = 10
		algo.removeHoles(roots, True, False, False, DIAMETER, 0)
		
		# Deletes patches borders (black lines delimiting CAD models' faces) to allow decimation (run in the next step) to be more efficient. Otherwise, patches borders are considered as important information to preserve.
		algo.deletePatches(roots, True)
		
		# Reduces meshes density (triangles count) by decimating them, using the "Decimate To Quality" function (see documentation). The values used here are meant to decimate meshes just enough to lower the triangles count without affecting the visual quality too much, especially on CAD models.
		algo.decimate(roots, 1, 0.1, 3, -1, False)
		
		# Removes triangles not visible from a set of cameras automatically placed around the model.
		algo.hiddenRemoval(roots, 2, 1024, 16, 90, False, 1)

		t1, _n_triangles, _n_vertices, _n_parts = getStats(roots[0])

	# Export files
	for extension in extensions:
		io.exportScene(outputFolder + '/' + fileName + extension)
	
	if optimization:
		printStats(fileName, t1 - t0, n_triangles, _n_triangles, n_vertices, _n_vertices, n_parts, _n_parts)


def getFileNameWithoutExtension(file):
	return os.path.splitext(os.path.basename(file))[0]

def getStats(root):
	core.configureInterfaceLogger(False, False, False) # hide next lines from logs
	
	t = time.time()
	n_triangles = scene.getPolygonCount([root], True, False, False)
	n_vertices = scene.getVertexCount([root], False, False, False)
	n_parts = len(scene.getPartOccurrences(root))

	core.configureInterfaceLogger(True, True, True) # reenable logs

	return t, n_triangles, n_vertices, n_parts

def printStats(fileName, t, n_triangles, _n_triangles, n_vertices, _n_vertices, n_parts, _n_parts):
	print('\n')
	print('{:<20s}{:<3s}\n'.format('file ', fileName))
	print('{:<20s}{:<8.3f}{:<3s}\n'.format('optimization ', t, ' s'))
	print('{:<20s}{:<3s}\n'.format('triangles ', str(n_triangles) + ' -> ' + str(_n_triangles)))
	print('{:<20s}{:<3s}\n'.format('vertices ', str(n_vertices) + ' -> ' + str(_n_vertices)))
	print('{:<20s}{:<3s}\n'.format('parts ', str(n_parts) + ' -> ' + str(_n_parts)))

# Get arguments passed in command line and call main function
if __name__ == "__main__":
	convertFile(sys.argv[1], sys.argv[2], eval(sys.argv[3]), eval(sys.argv[4]))

Interpretation:

In the function of watcher.py, the input parameters are encapsulated: executeScenarioProcessor

args = ['C:\Program Files\PiXYZScenarioProcessor\PiXYZScenarioProcessor.exe', 'scripts/sampleScript.py', input_file, output_folder, str(extensions), str(optimization)]

corresponds to argv in sampleScript.py respectively:

if __name__ == "__main__":
	convertFile(sys.argv[1], sys.argv[2], eval(sys.argv[3]), eval(sys.argv[4]))

Right now:

  • sys.argv[1] = input_file
  • sys.argv[2] = output_folder
  • eval(sys.argv[3]) = extensions
  • eval(sys.argv[4])) = optimization

Use guidedImport to import inputFile and return it to roots.

	# Imports and prepares the input_files using the automatic "Guided import" process (see documentation)
	roots = process.guidedImport([inputFile], pxz.process.CoordinateSystemOptions(["automaticOrientation",0], ["automaticScale",0], False, True), ["usePreset",2], pxz.process.ImportOptions(False, True, True), False, False, False, False, False, False)

After that, there is a series of operations on the roots, such as counting the number of triangular patches, the number of vertices, and the number of parts under the roots (maybe parts can be translated into components?)

t0, n_triangles, n_vertices, n_parts = getStats(roots[0])

def getStats(root):
	core.configureInterfaceLogger(False, False, False) # hide next lines from logs
	
	t = time.time()
	n_triangles = scene.getPolygonCount([root], True, False, False)
	n_vertices = scene.getVertexCount([root], False, False, False)
	n_parts = len(scene.getPartOccurrences(root))

	core.configureInterfaceLogger(True, True, True) # reenable logs

	return t, n_triangles, n_vertices, n_parts

Select and delete certain parts

SIZE = 20
scene.selectByMaximumSize(roots, SIZE, -1, False)
scene.deleteSelection()

For others, you can refer to the official Python api and the samples that come with the studio

Plugin

The script published as a plug-in is the same script as the above, but it requires two steps:

  1. Open 2a_Scenario sample, publish the sampleScript.py plug-in, and put the plug-in in the PiXYZScenarioProcessor\plugins directory
  2. Open 2b_Folder watcher with Scenario, use run.bat to execute watcher.py, run the plug-in

The directory structure of 2a_Scenario sample is as follows:

│   publish.bat
│   sample.pxzext
└───sample
    │   plugin.xml
    └───scripts
            sampleScript.py
  1. sampleScript.pyThe biggest difference from 1_Folder watcher is that there is no execution code:
# Get arguments passed in command line and call main function
if __name__ == "__main__":
	convertFile(sys.argv[1], sys.argv[2], eval(sys.argv[3]), eval(sys.argv[4]))
  1. plugin.xmlThe function to be executed is specified in as convertFile, and the input parameters are:
<?xml version="1.0" encoding="utf-8" ?>
<module name="sample" version="2021.1.1.5">
  <include module="Core"/>
  <include module="IO"/>
  <function name="convertFile" state="Stable" scriptable="true" guiable="true" description="" logName="" scriptFile="sampleScript.py">
    <parameters>
      <parameter name="inputFile" type="ImportFilePath" state="Stable"/>
      <parameter name="outputFolder" type="OutputDirectoryPath" state="Stable"/>
      <parameter name="extensions" type="StringList" state="Stable"/>
      <parameter name="optimization" type="Bool" state="Stable"/>
    </parameters>
  </function>
</module>
  1. publish.bat specifies the use of PiXYZStudio's PiXYZStudioPublishPlugin to publish the plug-in (note: you need to use PiXYZStudio's < /span>)PiXYZStudioPublishPlugin.exe instead of PiXYZScenarioProcessor.exe

The compiled folder issample, the compiled plug-in name is sample.pxzext, and then sample.pxzext is copied to PiXYZScenarioProcessor in the directory plugins in the directory

Change the paths of PiXYZStudio and PiXYZScenarioProcessor to your own local ones. You can also execute them one by one, and then manually put the compiled sample.pxzext into PiXYZScenarioProcessor\plugins below

rem Compile plugin sources
"C:\Program Files\PiXYZStudio\PiXYZStudioPublishPlugin.exe" "%cd%\sample" "%cd%\sample.pxzext"

rem Copy scenario plugin in SP installation folder
copy "%cd%\sample.pxzext" "C:\ProgramData\PiXYZScenarioProcessor\plugins\"

pause

After the compilation is completed, place sample.pxzext under PiXYZScenarioProcessor\plugins and you can open 2b_Folder watcher with Scenario

The directory structure of 2b_Folder watcher with Scenario is:

│   config.json
│   run.bat
├───scripts
│       watcher.py
├───_input
└───_output

The difference between and 1 is under scripts sampleScript.py now becomes PiXYZScenarioProcessor\plugins\sample.pxzext

watcher.pyThe biggest difference from 1 is that the input parameters args have changed:

1_Folder watcher\scripts\watcher.py

args = ['C:\Program Files\PiXYZScenarioProcessor\PiXYZScenarioProcessor.exe', 'scripts/sampleScript.py', input_file, output_folder, str(extensions), str(optimization)]

2b_Folder watcher with Scenario\scripts\watcher.py

args = ['C:\Program Files\PiXYZScenarioProcessor\PiXYZScenarioProcessor.exe', 'sample', 'convertFile', "\"" + input_file.replace("\\", "\\\\") + "\"", "\"" + output_folder.replace("\\", "\\\\") + "\"", str(extensions), "\"" + str(optimization) + "\""]

Input sample, convertFile and and specified in plugin.xml correspond to modulefunction

Supongo que te gusta

Origin blog.csdn.net/iteapoy/article/details/134860029
Recomendado
Clasificación