Integrating up through commit 90f050496
This commit is contained in:
+27
@@ -0,0 +1,27 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# from maya_mapping import MayaMapping
|
||||
# from max_mapping import MaxMapping
|
||||
# from blender_mapping import BlenderMapping
|
||||
|
||||
# def get_dcc_mapping(app):
|
||||
# app = app.lower()
|
||||
# if app == 'maya':
|
||||
# return MayaMapping()
|
||||
# elif app == '3dsmax':
|
||||
# return MaxMapping()
|
||||
# elif app == 'blender':
|
||||
# return BlenderMapping()
|
||||
# else:
|
||||
# return NullMapping(app)
|
||||
|
||||
+628
@@ -0,0 +1,628 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
Usage
|
||||
=====
|
||||
Put usage instructions here.
|
||||
|
||||
Output
|
||||
======
|
||||
Put output information here.
|
||||
|
||||
Relevant Links:
|
||||
https://pythonhosted.org/an_example_pypi_project/sphinx.html
|
||||
https://github.com/ideasman42/pyfbx_i42
|
||||
https://pypi.org/project/py-fbx/
|
||||
https://www.quora.com/How-do-I-execute-Maya-script-without-lauching-Maya
|
||||
https://stackoverflow.com/questions/27437733/use-external-python-script-to-open-maya-and-run-another-script-inside-maya
|
||||
|
||||
Notes:
|
||||
-- Materials information can be extracted from ASCII fbx pretty easily, binary is possible but more difficult
|
||||
-- FBX files could be exported as ASCII files and I could use regex there to extract material information
|
||||
-- I couldn't get pyfbx_i42 to work, but purportedly it can extract information from binary files. You may just have
|
||||
to use the specified python versions
|
||||
|
||||
-- Jonny wants me to use pathlib wherever possible for OO pathing, as well as python-box (aka Box) for dict access
|
||||
|
||||
# Things to do:
|
||||
--> Create the cube demo for Jonny with 4 attached materials
|
||||
--> Create mapping widget for stacked layout
|
||||
--> Allow export of JSON file for demo
|
||||
--> Allow custom field entries for description, etc.
|
||||
--> Need to figure out how to clear pointers for stored data properly
|
||||
"""
|
||||
|
||||
from PySide2 import QtWidgets, QtCore, QtGui
|
||||
from PySide2.QtCore import Signal, Slot, QThread, QAbstractItemModel, QModelIndex, QObject
|
||||
from maya import OpenMayaUI as omui
|
||||
from maya.standalone import initialize
|
||||
from shiboken2 import wrapInstance
|
||||
import pymel.core as pm
|
||||
# import sphinx
|
||||
# import azpy
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
mayaMainWindowPtr = omui.MQtUtil.mainWindow()
|
||||
mayaMainWindow = wrapInstance(long(mayaMainWindowPtr), QtWidgets.QWidget)
|
||||
|
||||
|
||||
class MayaToLumberyard(QtWidgets.QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(MayaToLumberyard, self).__init__(parent)
|
||||
|
||||
self.app = QtWidgets.QApplication.instance()
|
||||
self.setParent(mayaMainWindow)
|
||||
self.setWindowFlags(QtCore.Qt.Window)
|
||||
self.setGeometry(50, 50, 600, 500)
|
||||
self.setObjectName('MaterialsToLumberyard')
|
||||
self.setWindowTitle('Maya To Lumberyard')
|
||||
self.isTopLevel()
|
||||
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowMinMaxButtonsHint)
|
||||
|
||||
self.desktop_location = os.path.join(os.path.expanduser('~'), 'Desktop')
|
||||
self.bold_font_large = QtGui.QFont('Plastique', 7, QtGui.QFont.Bold)
|
||||
self.medium_font = QtGui.QFont('Plastique', 7, QtGui.QFont.Normal)
|
||||
self.target_file_list = []
|
||||
self.materials_dict = {}
|
||||
self.material_definitions = {}
|
||||
self.processed_materials = []
|
||||
self.current_scene = pm.sceneName()
|
||||
self.model = None
|
||||
self.total_transfer_materials = 1
|
||||
|
||||
self.main_container = QtWidgets.QVBoxLayout(self)
|
||||
self.main_container.setAlignment(QtCore.Qt.AlignTop)
|
||||
self.setLayout(self.main_container)
|
||||
|
||||
# Header Bar ------>
|
||||
self.header_bar_layout = QtWidgets.QHBoxLayout()
|
||||
self.select_files_button = QtWidgets.QPushButton('Select Files')
|
||||
self.select_files_button.clicked.connect(self.choose_files_clicked)
|
||||
self.select_files_button.setFixedSize(80, 35)
|
||||
self.header_bar_layout.addWidget(self.select_files_button)
|
||||
self.header_bar_layout.addSpacing(15)
|
||||
self.use_current_file_checkbox = QtWidgets.QCheckBox('Use Current File')
|
||||
self.use_current_file_checkbox.setFont(self.bold_font_large)
|
||||
self.use_current_file_checkbox.clicked.connect(self.use_current_file_clicked)
|
||||
self.header_bar_layout.addWidget(self.use_current_file_checkbox)
|
||||
self.header_bar_layout.addSpacing(100)
|
||||
self.switch_layout_combobox = QtWidgets.QComboBox()
|
||||
self.switch_layout_combobox.setEnabled(False)
|
||||
self.switch_layout_combobox.setFixedSize(250, 30)
|
||||
self.combobox_items = ['Target Files', 'Extracted Values', 'Material Tree']
|
||||
self.switch_layout_combobox.setStyleSheet('QComboBox {padding-left:6px;}')
|
||||
self.switch_layout_combobox.addItems(self.combobox_items)
|
||||
self.header_bar_layout.addWidget(self.switch_layout_combobox)
|
||||
self.header_bar_layout.addSpacing(4)
|
||||
self.main_container.addSpacing(5)
|
||||
self.main_container.addLayout(self.header_bar_layout)
|
||||
|
||||
# Separation Line ------>
|
||||
self.separatorLayout1 = QtWidgets.QHBoxLayout()
|
||||
self.line1 = QtWidgets.QLabel()
|
||||
self.line1.setFrameStyle(QtWidgets.QFrame.HLine | QtWidgets.QFrame.Sunken)
|
||||
self.line1.setLineWidth(1)
|
||||
self.line1.setFixedHeight(10)
|
||||
self.separatorLayout1.addWidget(self.line1)
|
||||
self.main_container.addLayout(self.separatorLayout1)
|
||||
|
||||
# ++++++++++++++++++++++++++++++++++++++++++++++++#
|
||||
# File Source Table / Attributes (Stacked Layout) #
|
||||
# ++++++++++++++++++++++++++++++++++++++++++++++++#
|
||||
self.content_stacked_layout = QtWidgets.QStackedLayout()
|
||||
self.main_container.addLayout(self.content_stacked_layout)
|
||||
self.switch_layout_combobox.currentIndexChanged.connect(self.layout_combobox_changed)
|
||||
|
||||
# --- Files Table
|
||||
self.target_files_table = QtWidgets.QTableWidget()
|
||||
self.target_files_table.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
self.target_files_table.setColumnCount(2)
|
||||
self.target_files_table.setAlternatingRowColors(True)
|
||||
self.target_files_table.setHorizontalHeaderLabels(['File List', ''])
|
||||
self.target_files_table.horizontalHeader().setStyleSheet('QHeaderView::section {padding-top:9px; padding-left:10px;}')
|
||||
self.target_files_table.verticalHeader().hide()
|
||||
files_header = self.target_files_table.horizontalHeader()
|
||||
files_header.setFixedHeight(30)
|
||||
files_header.setDefaultAlignment(QtCore.Qt.AlignLeft)
|
||||
files_header.setContentsMargins(10, 10, 0, 0)
|
||||
files_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
|
||||
files_header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
|
||||
self.target_files_table.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||
self.content_stacked_layout.addWidget(self.target_files_table)
|
||||
|
||||
# --- Scene Information Table
|
||||
self.material_tree_view = QtWidgets.QTreeView()
|
||||
self.headers = ['Key', 'Value']
|
||||
self.material_tree_view.setStyleSheet('QTreeView::item {height:25px;} QHeaderView::section {height:30px; padding-left:10px}')
|
||||
|
||||
self.material_tree_view.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
self.material_tree_view.setAlternatingRowColors(True)
|
||||
self.material_tree_view.setUniformRowHeights(True)
|
||||
self.content_stacked_layout.addWidget(self.material_tree_view)
|
||||
|
||||
# --- LY Material Definitions
|
||||
self.material_definitions_widget = QtWidgets.QWidget()
|
||||
self.material_definitions_layout = QtWidgets.QHBoxLayout(self.material_definitions_widget)
|
||||
self.material_definitions_layout.setSpacing(0)
|
||||
self.material_definitions_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.material_definitions_frame = QtWidgets.QFrame(self.material_definitions_widget)
|
||||
self.material_definitions_frame.setGeometry(0, 0, 5000, 5000)
|
||||
self.material_definitions_frame.setStyleSheet('background-color:rgb(150,150,150);')
|
||||
|
||||
self.title_bar_widget = QtWidgets.QWidget()
|
||||
self.title_bar_layout = QtWidgets.QHBoxLayout(self.title_bar_widget)
|
||||
self.title_bar_layout.setContentsMargins(17, 17, 17, 0)
|
||||
self.title_bar_layout.setAlignment(QtCore.Qt.AlignTop)
|
||||
self.title_bar_frame = QtWidgets.QFrame(self.title_bar_widget)
|
||||
self.title_bar_frame.setGeometry(0, 0, 5000, 60)
|
||||
self.title_bar_frame.setStyleSheet('background-color:rgb(102,69,153);')
|
||||
self.material_definitions_layout.addWidget(self.title_bar_widget)
|
||||
|
||||
self.material_name = QtWidgets.QCheckBox('StingrayPBS2')
|
||||
self.material_name.setStyleSheet('spacing:10px; color:white')
|
||||
self.material_name.setFont(self.bold_font_large)
|
||||
self.material_name.setChecked(True)
|
||||
self.title_bar_layout.addWidget(self.material_name)
|
||||
|
||||
# Forward/Back Buttons -------------------------->>
|
||||
self.previous_next_button_layout = QtWidgets.QHBoxLayout()
|
||||
self.item_count = QtWidgets.QLabel('1 of 10')
|
||||
self.previous_next_button_layout.addWidget(self.item_count)
|
||||
self.previous_next_button_layout.addSpacing(10)
|
||||
self.previous_next_button_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.previous_next_button_layout.setAlignment(QtCore.Qt.AlignRight)
|
||||
self.previous_button = QtWidgets.QToolButton()
|
||||
self.previous_button.setArrowType(QtCore.Qt.LeftArrow)
|
||||
self.previous_button.setStyleSheet('background-color:rgb(110,110,110);')
|
||||
self.previous_button.clicked.connect(self.previous_button_clicked)
|
||||
self.previous_next_button_layout.addWidget(self.previous_button)
|
||||
self.next_button = QtWidgets.QToolButton()
|
||||
self.next_button.setArrowType(QtCore.Qt.RightArrow)
|
||||
self.next_button.setStyleSheet('background-color:rgb(110,110,110);')
|
||||
self.next_button.clicked.connect(self.next_button_clicked)
|
||||
self.previous_next_button_layout.addWidget(self.next_button)
|
||||
self.title_bar_layout.addLayout(self.previous_next_button_layout)
|
||||
self.content_stacked_layout.addWidget(self.material_definitions_widget)
|
||||
|
||||
# File processing buttons ------>
|
||||
self.process_files_layout = QtWidgets.QHBoxLayout()
|
||||
self.main_container.addLayout(self.process_files_layout)
|
||||
self.process_files_button = QtWidgets.QPushButton('Process Listed Files')
|
||||
self.process_files_button.setFixedHeight(50)
|
||||
self.process_files_button.clicked.connect(self.process_files_clicked)
|
||||
self.process_files_layout.addWidget(self.process_files_button)
|
||||
self.reset_button = QtWidgets.QPushButton('Reset')
|
||||
self.reset_button.setFixedSize(50, 50)
|
||||
self.reset_button.clicked.connect(self.reset_clicked)
|
||||
self.reset_button.setEnabled(False)
|
||||
self.process_files_layout.addWidget(self.reset_button)
|
||||
self.initialize_window()
|
||||
|
||||
def initialize_window(self):
|
||||
if self.current_scene:
|
||||
self.target_file_list = [self.current_scene]
|
||||
self.use_current_file_checkbox.setChecked(True)
|
||||
self.populate_files_table()
|
||||
|
||||
def populate_files_table(self):
|
||||
self.target_files_table.setRowCount(0)
|
||||
for index, entry in enumerate(self.target_file_list):
|
||||
entry = entry[1] if type(entry) == list else entry
|
||||
self.target_files_table.insertRow(index)
|
||||
item = QtWidgets.QTableWidgetItem(' {}'.format(entry))
|
||||
self.target_files_table.setRowHeight(index, 45)
|
||||
remove_button = QtWidgets.QPushButton(' Remove ')
|
||||
remove_button.setStyleSheet('border-width:0px; background-color:rgb(100,100,100);')
|
||||
remove_button.clicked.connect(self.remove_file_clicked)
|
||||
self.target_files_table.setItem(index, 0, item)
|
||||
self.target_files_table.setCellWidget(index, 1, remove_button)
|
||||
|
||||
def process_file_list(self):
|
||||
file_processing_errors = []
|
||||
for maya_file_location in self.target_file_list:
|
||||
try:
|
||||
if maya_file_location != self.current_scene:
|
||||
pm.openFile(maya_file_location, force=True)
|
||||
self.current_scene = maya_file_location
|
||||
self.get_scene_materials_description()
|
||||
except Exception as e:
|
||||
file_processing_errors.append([maya_file_location, e])
|
||||
|
||||
# Create Model with extracted values from file list
|
||||
self.set_material_model()
|
||||
|
||||
# Setup Lumberyard Material File Values
|
||||
self.map_materials()
|
||||
print ('MaterialDefinitions:'.format(self.material_definitions))
|
||||
print json.dumps(self.material_definitions, sort_keys=True, indent=4)
|
||||
|
||||
# Update UI Layout
|
||||
self.set_material_view()
|
||||
self.switch_layout_combobox.setCurrentIndex(2)
|
||||
self.set_ui_buttons()
|
||||
|
||||
def map_materials(self):
|
||||
root = self.model.rootItem
|
||||
for row in range(self.model.rowCount()):
|
||||
name = self.model.get_attribute_value('MaterialName', root.child(row))
|
||||
material_type = self.model.get_attribute_value('MaterialType', root.child(row))
|
||||
file_connections = {}
|
||||
shader_attributes = {}
|
||||
|
||||
for childIndex in range(root.child(row).childCount()):
|
||||
child_item = root.child(row).child(childIndex)
|
||||
child_value = child_item.itemData
|
||||
if child_item.childCount():
|
||||
target_dict = file_connections if child_value[0] == 'FileConnections' else shader_attributes
|
||||
for subChildIndex in range(child_item.childCount()):
|
||||
sub_child_data = child_item.child(subChildIndex).itemData
|
||||
target_dict[sub_child_data[0]] = sub_child_data[1]
|
||||
self.set_pbr_material_description(name, material_type, file_connections)
|
||||
|
||||
def reset_all_values(self):
|
||||
pass
|
||||
# Need to figure out how to clear pointers for stored data properly
|
||||
# self.target_files_table.setRowCount(0)
|
||||
# self.model.beginResetModel()
|
||||
# self.model.qDeleteAll(mResults)
|
||||
# self.model.endResetModel()
|
||||
# self.initialize_window()
|
||||
|
||||
############################
|
||||
# Getters/Setters ##########
|
||||
############################
|
||||
|
||||
@staticmethod
|
||||
def get_materials(target_mesh):
|
||||
shading_group = pm.listConnections(pm.PyNode(target_mesh), type='shadingEngine')
|
||||
materials = pm.ls(pm.listConnections(shading_group), materials=1)
|
||||
return list(set(materials))
|
||||
|
||||
@staticmethod
|
||||
def get_shader(material_name):
|
||||
connections = pm.listConnections(material_name, type='shadingEngine')[0]
|
||||
shader_name = '{}.surfaceShader'.format(connections)
|
||||
shader = pm.listConnections(shader_name)[0]
|
||||
return shader
|
||||
|
||||
@staticmethod
|
||||
def get_shader_information(shader):
|
||||
shader_file_connections = {}
|
||||
for node in pm.listConnections(shader, type='file', c=True):
|
||||
shader_file_connections[str(node[0])] = str(pm.getAttr(node[1].fileTextureName))
|
||||
shader_attributes = {}
|
||||
for shader_attribute in pm.listAttr(shader, s=True, iu=True):
|
||||
try:
|
||||
shader_attributes[str(shader_attribute)] = pm.getAttr('{}.{}'.format(shader, shader_attribute))
|
||||
except pm.MayaAttributeError as e:
|
||||
print ('MayaAttributeError: {}'.format(e))
|
||||
|
||||
return shader_file_connections, shader_attributes
|
||||
|
||||
@staticmethod
|
||||
def get_shader_properties(name, material_type, file_connections):
|
||||
""" This system will probably need rethinking if DCCs and compatible materials grow """
|
||||
attr_list = {}
|
||||
if material_type == 'StingrayPBS':
|
||||
naming_exceptions = {'color': 'baseColor', 'ao': 'ambientOcclusion'}
|
||||
maps = 'color, metallic, roughness, normal, emissive, ao, opacity'.split(', ')
|
||||
for m in maps:
|
||||
texture_attribute = 'TEX_{}_map'.format(m)
|
||||
for tex in file_connections.keys():
|
||||
if tex.find(texture_attribute) != -1:
|
||||
key = m if m not in naming_exceptions else naming_exceptions.get(m)
|
||||
attr_list[key] = {'useTexture': 'true',
|
||||
'textureMap': file_connections.get('{}.{}'.format(name, texture_attribute))}
|
||||
return attr_list
|
||||
|
||||
@staticmethod
|
||||
def get_increment(name):
|
||||
last_number = re.compile(r'(?:[^\d]*(\d+)[^\d]*)+')
|
||||
number_found = last_number.search(name)
|
||||
if number_found:
|
||||
next_number = str(int(number_found.group(1)) + 1)
|
||||
start, end = number_found.span(1)
|
||||
name = name[:max(end - len(next_number), start)] + next_number + name[end:]
|
||||
return name
|
||||
|
||||
@staticmethod
|
||||
def get_material_template(shader_type):
|
||||
definitions = os.path.join(os.path.dirname(os.path.abspath(__file__)), '{}.material'.format(shader_type))
|
||||
if os.path.exists(definitions):
|
||||
with open(definitions) as f:
|
||||
return json.load(f)
|
||||
|
||||
def get_scene_materials_description(self):
|
||||
scene_geo = pm.ls(v=True, geometry=True)
|
||||
for target_mesh in scene_geo:
|
||||
material_list = self.get_materials(target_mesh)
|
||||
for material_name in material_list:
|
||||
material_type = pm.nodeType(material_name, api=True)
|
||||
material_listed = [x for x in self.materials_dict if self.materials_dict[x]['MaterialName'] == material_name]
|
||||
|
||||
if not material_listed:
|
||||
self.set_material_dict(str(material_name), str(material_type), target_mesh)
|
||||
else:
|
||||
mesh_list = self.materials_dict[material_name].get('AppliedMesh')
|
||||
if not isinstance(mesh_list, list):
|
||||
self.materials_dict[material_name]['AppliedMesh'] = [mesh_list, target_mesh]
|
||||
else:
|
||||
mesh_list.append(target_mesh)
|
||||
|
||||
def set_material_dict(self, material_name, material_type, material_mesh):
|
||||
shader = self.get_shader(material_name)
|
||||
shader_file_connections, shader_attributes = self.get_shader_information(shader)
|
||||
material_dict = {'MaterialName': material_name, 'MaterialType': material_type, 'AppliedMesh': material_mesh,
|
||||
'FileConnections': shader_file_connections, 'SceneName': str(self.current_scene),
|
||||
'MaterialAttributes': shader_attributes}
|
||||
material_name = 'Material_{}'.format(self.total_transfer_materials)
|
||||
self.materials_dict[material_name] = material_dict
|
||||
self.total_transfer_materials += 1
|
||||
|
||||
def set_material_model(self):
|
||||
self.model = MaterialsModel(self.headers, self.materials_dict)
|
||||
|
||||
def set_material_view(self):
|
||||
self.material_tree_view.setModel(self.model)
|
||||
self.material_tree_view.expandAll()
|
||||
self.material_tree_view.resizeColumnToContents(0)
|
||||
|
||||
def set_pbr_material_description(self, name, material_type, file_connections):
|
||||
# Build dictionary for material description based on extracted values
|
||||
default_settings = self.get_material_template('pbr')
|
||||
material = {'description': name,
|
||||
'materialType': default_settings.get('materialType'),
|
||||
'parentMaterial': default_settings.get('parentMaterial'),
|
||||
'propertyLayoutVersion': default_settings.get('propertyLayoutVersion'),
|
||||
'properties': self.get_shader_properties(name, material_type, file_connections)}
|
||||
self.material_definitions[name if name not in self.material_definitions.keys() else self.get_increment(name)] = material
|
||||
|
||||
def set_ui_buttons(self):
|
||||
display_index = self.content_stacked_layout.currentIndex()
|
||||
self.switch_layout_combobox.setEnabled(True)
|
||||
# Target Files
|
||||
if display_index == 0:
|
||||
self.use_current_file_checkbox.setEnabled(True)
|
||||
self.select_files_button.setEnabled(True)
|
||||
self.reset_button.setEnabled(True)
|
||||
self.process_files_button.setText('Process Listed Files')
|
||||
|
||||
# Extracted Values
|
||||
elif display_index == 1:
|
||||
self.reset_button.setEnabled(True)
|
||||
self.process_files_button.setEnabled(False)
|
||||
self.use_current_file_checkbox.setEnabled(False)
|
||||
self.select_files_button.setEnabled(False)
|
||||
|
||||
# Material Tree
|
||||
else:
|
||||
self.use_current_file_checkbox.setEnabled(False)
|
||||
self.select_files_button.setEnabled(False)
|
||||
self.process_files_button.setText('Export Selected Materials')
|
||||
if self.material_definitions:
|
||||
self.process_files_button.setEnabled(True)
|
||||
|
||||
############################
|
||||
# Button Actions ###########
|
||||
############################
|
||||
|
||||
def use_current_file_clicked(self):
|
||||
self.current_scene = pm.sceneName()
|
||||
if self.use_current_file_checkbox.isChecked():
|
||||
self.target_file_list.insert(0, self.current_scene)
|
||||
self.target_file_list = list(set(self.target_file_list))
|
||||
else:
|
||||
if self.current_scene in self.target_file_list:
|
||||
del self.target_file_list[self.target_file_list.index(self.current_scene)]
|
||||
self.populate_files_table()
|
||||
|
||||
def remove_file_clicked(self):
|
||||
file_index = self.target_files_table.indexAt(self.sender().pos())
|
||||
target_file = self.target_file_list[file_index.row()]
|
||||
if target_file == pm.sceneName():
|
||||
self.use_current_file_checkbox.setChecked(False)
|
||||
del self.target_file_list[file_index.row()]
|
||||
self.populate_files_table()
|
||||
|
||||
def process_files_clicked(self):
|
||||
self.process_file_list()
|
||||
|
||||
def choose_files_clicked(self):
|
||||
dialog = QtWidgets.QFileDialog(self, 'Shift-Select Target Files', self.desktop_location)
|
||||
dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile)
|
||||
dialog.setNameFilter('Maya Files (*.ma *.mb *.fbx)')
|
||||
dialog.setOption(QtWidgets.QFileDialog.DontUseNativeDialog, True)
|
||||
file_view = dialog.findChild(QtWidgets.QListView, 'listView')
|
||||
|
||||
# Workaround for selecting multiple files with File Dialog
|
||||
if file_view:
|
||||
file_view.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
|
||||
f_tree_view = dialog.findChild(QtWidgets.QTreeView)
|
||||
if f_tree_view:
|
||||
f_tree_view.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
|
||||
|
||||
if dialog.exec_() == QtWidgets.QDialog.Accepted:
|
||||
self.target_file_list = dialog.selectedFiles()
|
||||
if self.target_file_list:
|
||||
self.populate_files_table()
|
||||
self.process_files_button.setEnabled(True)
|
||||
|
||||
def layout_combobox_changed(self):
|
||||
self.content_stacked_layout.setCurrentIndex(self.switch_layout_combobox.currentIndex())
|
||||
self.set_ui_buttons()
|
||||
|
||||
def reset_clicked(self):
|
||||
self.reset_all_values()
|
||||
|
||||
def previous_button_clicked(self):
|
||||
print ('Previous button clicked')
|
||||
|
||||
def next_button_clicked(self):
|
||||
print ('Next button clicked')
|
||||
|
||||
|
||||
class MaterialsModel(QAbstractItemModel):
|
||||
def __init__(self, headers, data, parent=None):
|
||||
super(MaterialsModel, self).__init__(parent)
|
||||
|
||||
self.rootItem = TreeNode(headers)
|
||||
self.parents = [self.rootItem]
|
||||
self.indentations = [0]
|
||||
self.create_data(data)
|
||||
|
||||
def create_data(self, data, indent=-1):
|
||||
if type(data) == dict:
|
||||
indent += 1
|
||||
position = 4 * indent
|
||||
for key, value in data.iteritems():
|
||||
if position > self.indentations[-1]:
|
||||
if self.parents[-1].childCount() > 0:
|
||||
self.parents.append(self.parents[-1].child(self.parents[-1].childCount() - 1))
|
||||
self.indentations.append(position)
|
||||
else:
|
||||
while position < self.indentations[-1] and len(self.parents) > 0:
|
||||
self.parents.pop()
|
||||
self.indentations.pop()
|
||||
parent = self.parents[-1]
|
||||
parent.insertChildren(parent.childCount(), 1, parent.columnCount())
|
||||
parent.child(parent.childCount() - 1).setData(0, key)
|
||||
value_string = str(value) if type(value) != dict else str('')
|
||||
parent.child(parent.childCount() - 1).setData(1, value_string)
|
||||
try:
|
||||
self.create_data(value, indent)
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_attribute_value(search_string, search_column):
|
||||
for childIndex in range(search_column.childCount()):
|
||||
child_item = search_column.child(childIndex)
|
||||
child_value = child_item.itemData
|
||||
if child_value[0] == search_string:
|
||||
return child_value[1]
|
||||
return None
|
||||
|
||||
def index(self, row, column, index=QModelIndex()):
|
||||
""" Returns the index of the item in the model specified by the given row, column and parent index """
|
||||
if not self.hasIndex(row, column, index):
|
||||
return QModelIndex()
|
||||
if not index.isValid():
|
||||
item = self.rootItem
|
||||
else:
|
||||
item = index.internalPointer()
|
||||
|
||||
child = item.child(row)
|
||||
if child:
|
||||
return self.createIndex(row, column, child)
|
||||
return QModelIndex()
|
||||
|
||||
def parent(self, index):
|
||||
"""
|
||||
Returns the parent of the model item with the given index If the item has no parent,
|
||||
an invalid QModelIndex is returned
|
||||
"""
|
||||
if not index.isValid():
|
||||
return QModelIndex()
|
||||
item = index.internalPointer()
|
||||
if not item:
|
||||
return QModelIndex()
|
||||
|
||||
parent = item.parentItem
|
||||
if parent == self.rootItem:
|
||||
return QModelIndex()
|
||||
else:
|
||||
return self.createIndex(parent.childNumber(), 0, parent)
|
||||
|
||||
def rowCount(self, index=QModelIndex()):
|
||||
"""
|
||||
Returns the number of rows under the given parent. When the parent is valid it means that
|
||||
rowCount is returning the number of children of parent
|
||||
"""
|
||||
if index.isValid():
|
||||
parent = index.internalPointer()
|
||||
else:
|
||||
parent = self.rootItem
|
||||
return parent.childCount()
|
||||
|
||||
def columnCount(self, index=QModelIndex()):
|
||||
""" Returns the number of columns for the children of the given parent """
|
||||
return self.rootItem.columnCount()
|
||||
|
||||
def data(self, index, role=QtCore.Qt.DisplayRole):
|
||||
""" Returns the data stored under the given role for the item referred to by the index """
|
||||
if index.isValid() and role == QtCore.Qt.DisplayRole:
|
||||
return index.internalPointer().data(index.column())
|
||||
elif not index.isValid():
|
||||
return self.rootItem.data(index.column())
|
||||
|
||||
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
|
||||
""" Returns the data for the given role and section in the header with the specified orientation """
|
||||
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
|
||||
return self.rootItem.data(section)
|
||||
|
||||
|
||||
class TreeNode(object):
|
||||
def __init__(self, data, parent=None):
|
||||
self.parentItem = parent
|
||||
self.itemData = data
|
||||
self.children = []
|
||||
|
||||
def child(self, row):
|
||||
return self.children[row]
|
||||
|
||||
def childCount(self):
|
||||
return len(self.children)
|
||||
|
||||
def childNumber(self):
|
||||
if self.parentItem is not None:
|
||||
return self.parentItem.children.index(self)
|
||||
|
||||
def columnCount(self):
|
||||
return len(self.itemData)
|
||||
|
||||
def data(self, column):
|
||||
return self.itemData[column]
|
||||
|
||||
def insertChildren(self, position, count, columns):
|
||||
if position < 0 or position > len(self.children):
|
||||
return False
|
||||
for row in range(count):
|
||||
data = [v for v in range(columns)]
|
||||
item = TreeNode(data, self)
|
||||
self.children.insert(position, item)
|
||||
|
||||
def parent(self):
|
||||
return self.parentItem
|
||||
|
||||
def setData(self, column, value):
|
||||
if column < 0 or column >= len(self.itemData):
|
||||
return False
|
||||
self.itemData[column] = value
|
||||
|
||||
|
||||
def delete_instances():
|
||||
for obj in mayaMainWindow.children():
|
||||
if str(type(obj)) == "<class 'DCC_Materials.maya_materials_export.MayaToLumberyard'>":
|
||||
if obj.__class__.__name__ == "MayaToLumberyard":
|
||||
obj.setParent(None)
|
||||
obj.deleteLater()
|
||||
|
||||
|
||||
def show_ui():
|
||||
delete_instances()
|
||||
ui = MayaToLumberyard(mayaMainWindow)
|
||||
ui.show()
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "Materials/Types/StandardPBR.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
"ambientOcclusion": {
|
||||
"factor": 1.0,
|
||||
"useTexture": true,
|
||||
"textureMap": "EngineAssets/TextureMsg/DefaultNoUVs.tif"
|
||||
},
|
||||
"baseColor": {
|
||||
"color": [ 1.0, 1.0, 1.0 ],
|
||||
"factor": 1.0,
|
||||
"useTexture": true,
|
||||
"textureMap": "EngineAssets/TextureMsg/DefaultNoUVs.tif"
|
||||
},
|
||||
"emissive": {
|
||||
"color": [ 1.0, 1.0, 1.0 ],
|
||||
"intensity": 1.0,
|
||||
"useTexture": false,
|
||||
"textureMap": "EngineAssets/TextureMsg/DefaultNoUVs.tif"
|
||||
},
|
||||
"metallic": {
|
||||
"factor": 0.0,
|
||||
"useTexture": false,
|
||||
"textureMap": ""
|
||||
},
|
||||
"roughness": {
|
||||
"factor": 1.0,
|
||||
"useTexture": true,
|
||||
"textureMap": "EngineAssets/TextureMsg/DefaultNoUVs_spec.tif"
|
||||
},
|
||||
"specularF0": {
|
||||
"factor": 0.5,
|
||||
"useTexture": false,
|
||||
"textureMap": ""
|
||||
},
|
||||
"normal": {
|
||||
"factor": 1.0,
|
||||
"useTexture": true,
|
||||
"textureMap": "EngineAssets/TextureMsg/DefaultNoUVs_ddn.tif"
|
||||
},
|
||||
"opacity": {
|
||||
"doubleSided": false,
|
||||
"factor": 1.0,
|
||||
"cutoutAlpha": false,
|
||||
"cutoutThreshold": 0.5,
|
||||
"useBaseColorTextureAlpha": false,
|
||||
"useTexture": false,
|
||||
"textureMap": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
import pymel.core as pmc
|
||||
import sys
|
||||
import types
|
||||
|
||||
|
||||
def syspath():
|
||||
print 'sys.path:'
|
||||
for p in sys.path:
|
||||
print ' ' + p
|
||||
|
||||
|
||||
def info(obj):
|
||||
"""Prints information about the object."""
|
||||
|
||||
lines = ['Info for %s' % obj.name(),
|
||||
'Attributes:']
|
||||
# Get the name of all attributes
|
||||
for a in obj.listAttr():
|
||||
lines.append(' ' + a.name())
|
||||
lines.append('MEL type: %s' % obj.type())
|
||||
lines.append('MRO:')
|
||||
lines.extend([' ' + t.__name__ for t in type(obj).__mro__])
|
||||
result = '\n'.join(lines)
|
||||
print result
|
||||
|
||||
|
||||
def _is_pymel(obj):
|
||||
try: # (1)
|
||||
module = obj.__module__ # (2)
|
||||
except AttributeError: # (3)
|
||||
try:
|
||||
module = obj.__name__ # (4)
|
||||
except AttributeError:
|
||||
return None # (5)
|
||||
return module.startswith('pymel') # (6)
|
||||
|
||||
|
||||
def _py_to_helpstr(obj):
|
||||
if isinstance(obj, basestring):
|
||||
return 'search.html?q=%s' % (obj.replace(' ', '+'))
|
||||
if not _is_pymel(obj):
|
||||
return None
|
||||
if isinstance(obj, types.ModuleType):
|
||||
return ('generated/%(module)s.html#module-%(module)s' %
|
||||
dict(module=obj.__name__))
|
||||
if isinstance(obj, types.MethodType):
|
||||
return ('generated/classes/%(module)s/'
|
||||
'%(module)s.%(typename)s.html'
|
||||
'#%(module)s.%(typename)s.%(methname)s' % dict(
|
||||
module=obj.__module__,
|
||||
typename=obj.im_class.__name__,
|
||||
methname=obj.__name__))
|
||||
if isinstance(obj, types.FunctionType):
|
||||
return ('generated/functions/%(module)s/'
|
||||
'%(module)s.%(funcname)s.html'
|
||||
'#%(module)s.%(funcname)s' % dict(
|
||||
module=obj.__module__,
|
||||
funcname=obj.__name__))
|
||||
if not isinstance(obj, type):
|
||||
obj = type(obj)
|
||||
return ('generated/classes/%(module)s/'
|
||||
'%(module)s.%(typename)s.html'
|
||||
'#%(module)s.%(typename)s' % dict(
|
||||
module=obj.__module__,
|
||||
typename=obj.__name__))
|
||||
|
||||
|
||||
def test_py_to_helpstr():
|
||||
def dotest(obj, ideal):
|
||||
result = _py_to_helpstr(obj)
|
||||
assert result == ideal, '%s != %s' % (result, ideal)
|
||||
dotest('maya rocks', 'search.html?q=maya+rocks')
|
||||
dotest(pmc.nodetypes,
|
||||
'generated/pymel.core.nodetypes.html'
|
||||
'#module-pymel.core.nodetypes')
|
||||
dotest(pmc.nodetypes.Joint,
|
||||
'generated/classes/pymel.core.nodetypes/'
|
||||
'pymel.core.nodetypes.Joint.html'
|
||||
'#pymel.core.nodetypes.Joint')
|
||||
dotest(pmc.nodetypes.Joint(),
|
||||
'generated/classes/pymel.core.nodetypes/'
|
||||
'pymel.core.nodetypes.Joint.html'
|
||||
'#pymel.core.nodetypes.Joint')
|
||||
dotest(pmc.nodetypes.Joint().getTranslation,
|
||||
'generated/classes/pymel.core.nodetypes/'
|
||||
'pymel.core.nodetypes.Joint.html'
|
||||
'#pymel.core.nodetypes.Joint.getTranslation')
|
||||
dotest(pmc.joint,
|
||||
'generated/functions/pymel.core.animation/'
|
||||
'pymel.core.animation.joint.html'
|
||||
'#pymel.core.animation.joint')
|
||||
dotest(object(), None)
|
||||
dotest(10, None)
|
||||
dotest([], None)
|
||||
dotest(sys, None)
|
||||
|
||||
|
||||
def test_py_to_helpstrFAIL():
|
||||
assert 1 == 2, '1 != 2'
|
||||
|
||||
|
||||
import webbrowser # (1)
|
||||
HELP_ROOT_URL = ('http://help.autodesk.com/cloudhelp/2018/ENU/Maya-Tech-Docs/PyMel/')# (2)
|
||||
|
||||
|
||||
def pmhelp(obj): # (3)
|
||||
"""Gives help for a pymel or python object.
|
||||
|
||||
If obj is not a PyMEL object, use Python's built-in
|
||||
`help` function.
|
||||
If obj is a string, open a web browser to a search in the
|
||||
PyMEL help for the string.
|
||||
Otherwise, open a web browser to the page for the object.
|
||||
"""
|
||||
tail = _py_to_helpstr(obj)
|
||||
if tail is None:
|
||||
help(obj) # (4)
|
||||
else:
|
||||
webbrowser.open(HELP_ROOT_URL + tail) # (5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_py_to_helpstr()
|
||||
print 'Tests ran successfully.'
|
||||
Reference in New Issue
Block a user