370 lines
10 KiB
Python
370 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Mz Translator - Maya File Translator Plugin
|
|
|
|
Maya Python API implementation for .mz file format translator
|
|
.mz format is ZIP compressed, contains data.ma file
|
|
|
|
Usage:
|
|
Load plugin in Maya:
|
|
cmds.loadPlugin('mz_translator.py')
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import maya.OpenMaya as OpenMaya
|
|
import maya.OpenMayaMPx as OpenMayaMPx
|
|
import maya.OpenMayaUI as OpenMayaUI
|
|
import maya.cmds as cmds
|
|
from importlib import reload
|
|
|
|
# Import core modules
|
|
from mz_core import reader, writer
|
|
reload(reader)
|
|
reload(writer)
|
|
|
|
|
|
# Plugin name
|
|
kPluginName = "MzTranslator"
|
|
kPluginVersion = "1.0.0"
|
|
|
|
class MzFileTranslator(OpenMayaMPx.MPxFileTranslator):
|
|
"""
|
|
Maya .mz file format translator
|
|
|
|
Supports reading and writing .mz files (ZIP compressed, contains data.ma)
|
|
"""
|
|
_translator_name = "MayaZip"
|
|
_extension = "mz"
|
|
# Class variable
|
|
_temp_file_path = None
|
|
|
|
def __init__(self):
|
|
"""Initialize translator"""
|
|
super(MzFileTranslator, self).__init__()
|
|
self._options = {}
|
|
|
|
@staticmethod
|
|
def creator():
|
|
"""Factory method to create translator instance"""
|
|
return MzFileTranslator()
|
|
|
|
# =========================================================================
|
|
# MPxFileTranslator virtual functions
|
|
# =========================================================================
|
|
def canBeOpened(self):
|
|
"""
|
|
Return whether translator can be opened
|
|
|
|
Returns:
|
|
bool: Always returns True (can be opened)
|
|
"""
|
|
return True
|
|
|
|
def translatorName(self):
|
|
"""
|
|
Return translator name
|
|
|
|
Returns:
|
|
str: Translator name
|
|
"""
|
|
return self._translator_name
|
|
|
|
def haveWriteMethod(self):
|
|
"""
|
|
Return whether write is supported
|
|
|
|
Returns:
|
|
bool: Always returns True
|
|
"""
|
|
return True
|
|
|
|
def haveReferenceMethod(self):
|
|
"""
|
|
Return whether referencing is supported
|
|
|
|
Returns:
|
|
bool: Always returns False (not supported)
|
|
"""
|
|
return True
|
|
|
|
def haveReadMethod(self):
|
|
"""
|
|
Return whether read is supported
|
|
|
|
Returns:
|
|
bool: Always returns True
|
|
"""
|
|
return True
|
|
|
|
def fileType(self):
|
|
"""
|
|
Return file type name
|
|
|
|
Returns:
|
|
str: File type name
|
|
"""
|
|
return self._translator_name
|
|
|
|
def defaultExtension(self):
|
|
"""
|
|
Return default file extension
|
|
|
|
Returns:
|
|
str: Extension without dot
|
|
"""
|
|
return self._extension
|
|
|
|
def identifyFile(self, fileObject, buffer, size):
|
|
"""
|
|
Identify if file is .mz format
|
|
|
|
Args:
|
|
fileObject: MFileObject
|
|
buffer: File buffer
|
|
buffer_size: Buffer size
|
|
|
|
Returns:
|
|
int: kIsMyFileType if identified, kUnknown if failed
|
|
"""
|
|
import zipfile
|
|
|
|
file_name = fileObject.fullName()
|
|
|
|
# Check extension
|
|
if not file_name.lower().endswith('.mz'):
|
|
return OpenMayaMPx.MPxFileTranslator.kNotMyFileType
|
|
|
|
# Try to identify as ZIP file
|
|
try:
|
|
# Check if file is valid ZIP format
|
|
with open(file_name, 'rb') as f:
|
|
# Check ZIP file magic (PK\x03\x04)
|
|
magic = f.read(4)
|
|
if magic == b'PK\x03\x04' or magic == b'PK\x05\x06':
|
|
# Further check if contains data.ma
|
|
with zipfile.ZipFile(file_name, 'r') as zf:
|
|
if 'data.ma' in zf.namelist():
|
|
return OpenMayaMPx.MPxFileTranslator.kIsMyFileType
|
|
except Exception:
|
|
pass
|
|
|
|
return OpenMayaMPx.MPxFileTranslator.kNotMyFileType
|
|
|
|
def writer(self, fileObject, options, mode):
|
|
"""
|
|
Write Maya scene to .mz file
|
|
|
|
Args:
|
|
fileObject: MFileObject
|
|
options: Options dictionary
|
|
mode: File mode
|
|
|
|
Returns:
|
|
bool: True if successful
|
|
"""
|
|
try:
|
|
file_path = fileObject.fullName()
|
|
|
|
# Ensure extension is correct
|
|
if not file_path.lower().endswith('.mz'):
|
|
file_path += '.mz'
|
|
fileObject.setRawFullName(file_path)
|
|
|
|
# Parse options
|
|
writer_options = self._parse_options(options)
|
|
|
|
# Write .mz file
|
|
writer.write_scene_to_mz(file_path, writer_options)
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
raise RuntimeError(f"Failed to write .mz file: {str(e)}")
|
|
|
|
def reader(self, fileObject, options, mode):
|
|
"""
|
|
Read Maya scene from .mz file
|
|
|
|
Args:
|
|
fileObject: MFileObject
|
|
options: Options dictionary
|
|
mode: File mode
|
|
|
|
Returns:
|
|
bool: True if successful
|
|
"""
|
|
try:
|
|
file_path = fileObject.fullName()
|
|
|
|
# Read .mz file to temp file
|
|
temp_ma_path = reader.read_to_temp_file(file_path)
|
|
|
|
# Save temp file path for cleanup
|
|
MzFileTranslator._temp_file_path = temp_ma_path
|
|
print(temp_ma_path)
|
|
print(options)
|
|
if mode == OpenMayaMPx.MPxFileTranslator.kOpenAccessMode:
|
|
OpenMaya.MFileIO.open(temp_ma_path, None, True)
|
|
|
|
if mode == OpenMayaMPx.MPxFileTranslator.kImportAccessMode:
|
|
OpenMaya.MFileIO.importFile(temp_ma_path, None, True)
|
|
# Use Maya's file command to read
|
|
# cmds.file(temp_ma_path, force=True, open=True, type='mayaAscii')
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
raise RuntimeError(f"Failed to read .mz file: {str(e)}")
|
|
|
|
def optionsScript(self):
|
|
"""
|
|
Return Export Options UI script
|
|
|
|
Returns:
|
|
str: Python script string
|
|
"""
|
|
script = """
|
|
import maya.cmds as cmds
|
|
|
|
def MzTranslatorOptions():
|
|
# Create options dialog
|
|
if cmds.window('MzTranslatorOptions', exists=True):
|
|
cmds.deleteUI('MzTranslatorOptions')
|
|
|
|
cmds.window('MzTranslatorOptions', title='Mz Translator Options')
|
|
cmds.columnLayout(adjustableColumn=True)
|
|
|
|
# Compression level
|
|
cmds.text(label='Compression Level (0-9):')
|
|
cmds.intSliderGrp('compressionLevel',
|
|
label='Compression',
|
|
field=True,
|
|
minValue=0,
|
|
maxValue=9,
|
|
value=9,
|
|
step=1)
|
|
|
|
# Version info
|
|
cmds.text(label='Version: 1.0.0')
|
|
|
|
cmds.button(label='OK', command='cmds.layoutDialog(dismiss=\"ok\")')
|
|
cmds.button(label='Cancel', command='cmds.layoutDialog(dismiss=\"cancel\")')
|
|
|
|
cmds.showWindow()
|
|
|
|
return 'ok'
|
|
|
|
# Call options dialog
|
|
result = cmds.layoutDialog(ui=MzTranslatorOptions)
|
|
"""
|
|
return script
|
|
|
|
# =========================================================================
|
|
# Helper methods
|
|
# =========================================================================
|
|
|
|
def _parse_options(self, options):
|
|
"""
|
|
Parse options string to dictionary
|
|
|
|
Args:
|
|
options: Options string (format: key=value;key=value)
|
|
|
|
Returns:
|
|
dict: Options dictionary
|
|
"""
|
|
result = {}
|
|
|
|
if not options:
|
|
return result
|
|
|
|
# Parse options string
|
|
for pair in options.split(';'):
|
|
if '=' in pair:
|
|
key, value = pair.split('=', 1)
|
|
result[key.strip()] = value.strip()
|
|
|
|
return result
|
|
|
|
|
|
# =========================================================================
|
|
# Maya plugin registration functions
|
|
# =========================================================================
|
|
# creator
|
|
def translatorCreator():
|
|
return OpenMayaMPx.asMPxPtr( MzFileTranslator() )
|
|
|
|
def initializePlugin(mobject):
|
|
"""
|
|
Initialize plugin
|
|
|
|
Args:
|
|
mobject: MObject
|
|
"""
|
|
# Create plugin
|
|
pluginFn = OpenMayaMPx.MFnPlugin(mobject, "MzTranslator", "1.0.0", "Any")
|
|
|
|
# Register file translator
|
|
try:
|
|
pluginFn.registerFileTranslator(
|
|
MzFileTranslator._translator_name, # Translator name
|
|
"", # Options script (optional)
|
|
translatorCreator, # Factory function
|
|
"", # Default options
|
|
MzFileTranslator._extension # File extension
|
|
)
|
|
print(f"[{kPluginName}] Plugin initialized successfully (v{kPluginVersion})")
|
|
|
|
except Exception as e:
|
|
sys.stderr.write(f"Failed to register file translator: {str(e)}\n")
|
|
raise
|
|
|
|
|
|
def uninitializePlugin(mobject):
|
|
"""
|
|
Uninitialize plugin
|
|
|
|
Args:
|
|
mobject: MObject
|
|
"""
|
|
# Get plugin function
|
|
pluginFn = OpenMayaMPx.MFnPlugin(mobject)
|
|
|
|
# Deregister file translator
|
|
try:
|
|
pluginFn.deregisterFileTranslator(MzFileTranslator._translator_name)
|
|
print(f"[{kPluginName}] Plugin uninitialized successfully")
|
|
|
|
except Exception as e:
|
|
sys.stderr.write(f"Failed to deregister file translator: {str(e)}\n")
|
|
|
|
|
|
# =========================================================================
|
|
# Convenience functions
|
|
# =========================================================================
|
|
|
|
def cleanup_temp_file():
|
|
"""Cleanup temp file"""
|
|
if MzFileTranslator._temp_file_path:
|
|
import os
|
|
import shutil
|
|
try:
|
|
temp_dir = os.path.dirname(MzFileTranslator._temp_file_path)
|
|
if os.path.exists(temp_dir):
|
|
shutil.rmtree(temp_dir)
|
|
except Exception:
|
|
pass
|
|
MzFileTranslator._temp_file_path = None
|
|
|
|
|
|
# =========================================================================
|
|
# Auto cleanup when plugin loads
|
|
# =========================================================================
|
|
|
|
# Register cleanup callback when Maya scene closes
|
|
try:
|
|
# This will cleanup temp files when Maya scene closes
|
|
cmds.scriptJob(event=['SceneClosed', cleanup_temp_file], permanent=True)
|
|
except Exception:
|
|
pass |