Integrating up through commit 90f050496

This commit is contained in:
alexpete
2021-04-07 14:03:29 -07:00
parent 8f2ed080a9
commit c2cbd430fe
2694 changed files with 285622 additions and 176874 deletions
@@ -0,0 +1,53 @@
# coding:utf-8
#!/usr/bin/python
#
# 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.
#
# -- This line is 75 characters -------------------------------------------
# The __init__.py files help guide import statements without automatically
# importing all of the modules
"""DCCsi.sdk.substance.builder.__init__"""
from azpy.env_bool import env_bool
from azpy.constants import ENVAR_DCCSI_DEV_MODE
# global space
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
_PACKAGENAME = __name__
if _PACKAGENAME is '__main__':
_PACKAGENAME = 'DCCsi.SDK.Substance.builder'
import azpy
_LOGGER = azpy.initialize_logger(_PACKAGENAME)
_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME}))
# -------------------------------------------------------------------------
#
__all__ = ['bootstrap',
'atom_material',
'sb_gui_main',
'sbs_to_sbsar',
'sbsar_info',
'sbsar_render',
'sbsar_utils',
'substance_tools',
'watchdog',
'ui']
#
# -------------------------------------------------------------------------
if _DCCSI_DEV_MODE:
# If in dev mode this will test imports of __all__
from azpy import test_imports
_LOGGER.debug('Testing Imports from {0}'.format(_PACKAGENAME))
test_imports(__all__,
_pkg=_PACKAGENAME,
_logger=_LOGGER)
# -------------------------------------------------------------------------
del _LOGGER
@@ -0,0 +1,199 @@
# coding:utf-8
#!/usr/bin/python
#
# 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.
# -- This line is 75 characters -------------------------------------------
"""Empty Doc String""" # To Do: add documentation
# -------------------------------------------------------------------------
# built-ins
import os
import sys
#import simplejson as json
import json
# Lumberyard extensions
from azpy.env_bool import env_bool
from azpy.constants import ENVAR_DCCSI_GDEBUG
from azpy.constants import ENVAR_DCCSI_DEV_MODE
from azpy.constants import *
# 3rdparty (we provide)
from box import Box
from pathlib import Path
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# set up global space, logging etc.
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
_PACKAGENAME = __name__
if _PACKAGENAME is '__main__':
_PACKAGENAME = 'DCCsi.SDK.substance.builder.atom_material'
import azpy
_LOGGER = azpy.initialize_logger(_PACKAGENAME)
_LOGGER.debug('Starting up: {0}.'.format({_PACKAGENAME}))
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# early attach WingIDE debugger (can refactor to include other IDEs later)
if _DCCSI_DEV_MODE:
from azpy.test.entry_test import connect_wing
foo = connect_wing()
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# previous
class AtomPBR:
def __init__(self, material_file):
# loading .material File
self.material_file = material_file
self.input_data = open(self.material_file, "r")
self.material = json.load(self.input_data)
self.mat_box = Box(self.material)
self.input_data.close()
# List of texture slots
self.tex = ['baseColor', 'metallic', 'roughness', 'normalMap', 'opacity']
# Construct texture maps
self.basecolor_tex = ""
self.metallic_tex = ""
self.roughness_tex = ""
self.normalmap_tex = ""
self.opacity_tex = ""
def load(self, material_file):
input_data = open(material_file, "r")
self.material = json.load(input_data)
self.mat_box = Box(self.material)
input_data.close()
def get_map(self, tex_slot):
return self.mat_box.properties[tex_slot].parameters.textureMap
def set_map(self, tex_slot, tex_map):
self.mat_box.properties[tex_slot].parameters.textureMap = tex_map
def write(self, material_out):
output_data = open(material_out, "w+")
output_data.write(json.dumps(self.mat_box, indent=4))
output_data.close()
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# new?
class AtomMaterial:
def __init__(self, material_file):
# loading .material File
self.material_file = material_file
self.input_data = open(self.material_file, "r")
self.material = json.load(self.input_data)
self.mat_box = Box(self.material)
self.input_data.close()
# List of texture slots
# old tex maps
# self.tex = ['DiffuseMap', 'NormalMap', 'SpecularMap', 'EnvironmentMap']
self.tex = ['baseColor', 'metallic', 'roughness', 'specularF0', 'normal', 'opacity']
self.texture_map = {'baseColor': 'baseColor',
'metallic': 'metallic',
'roughness': 'roughness',
'specularF0': 'specular',
'normal': 'normal',
'opacity': 'opacity'
}
def load(self, material_file):
input_data = open(material_file, "r")
self.material = json.load(input_data)
self.mat_box = Box(self.material)
input_data.close()
def get_material_type(self):
return self.mat_box.materialType
# old getMap function
# def getMap(self, tex_slot):
# return self.mat_box.properties.general[tex_slot]
def get_map(self, tex_slot):
return self.mat_box.properties[tex_slot].textureMap
def set_map(self, tex_slot, tex_map):
self.mat_box.properties[tex_slot].textureMap = tex_map
self.mat_box.properties[tex_slot].useTexture = True
self.mat_box.properties[tex_slot].factor = 1.0
def write(self, material_out):
if not material_out.parent.exists():
try:
material_out.parent.mkdir(mode=0o777, parents=True, exist_ok=True)
_LOGGER.info('mkdir: {}'.format(material_out.parent))
except Exception as e:
_LOGGER.error(e)
raise(e)
else:
_LOGGER.info('exists: {}'.format(material_out.parent))
material_out.touch()
output_data = open(str(material_out), "w+")
output_data.write(json.dumps(self.mat_box, indent=4))
output_data.close()
return material_out
# -------------------------------------------------------------------------
###########################################################################
# Main Code Block, runs this script as main (testing)
# -------------------------------------------------------------------------
if __name__ == "__main__":
"""Run this file as main"""
_LOGGER.info("Test Run:: {0}.".format({_PACKAGENAME}))
_LOGGER.info("{0} :: if __name__ == '__main__':".format(_PACKAGENAME))
material_path = Path(Path(__file__).parent.parent, 'resources', 'atom')
# material_01 = AtomPBR("atom_pbr.material", "awesome.material")
# material_01 = AtomPBR("atom_pbr.material")
material_01 = AtomMaterial(Path(material_path, "StandardPBR_AllProperties.material"))
# material_01.load("atom_pbr.material")
# material_01.map(material_01.tex[2]).textureMap = "materials/substance/amazing_xzzzx.tif"
# # print(material_01.metallic)
# material_01.write("awesome.material")
# print(material_01.tex[2])
# print(material_01.getMap(material_01.tex[3]))
# material_01.baesColor_tex = "materials/substance/amazing_bc.tif"
# material_01.setMap(material_01.tex[0], material_01.baesColor_tex)
# material_01.write("awesome.material")
# Test material parser for the new format.
material_01.baseColor_tex = "Textures/Streaming/streaming99.dds"
material_01.metallic_tex = "Textures/Streaming/streaming99.dds"
material_01.set_map(material_01.tex[0], material_01.baseColor_tex)
material_01.set_map(material_01.tex[1], material_01.metallic_tex)
material_out = material_01.write(Path(material_path, "atom_variant00.material"))
_LOGGER.info('materialType is:: {}'.format(material_01.get_material_type()))
if material_out.exists():
_LOGGER.info('Wrote material file: {}'.format(material_out))
# remove the logger
del _LOGGER
# ---- END ---------------------------------------------------------------
@@ -0,0 +1,958 @@
# coding:utf-8
#!/usr/bin/python
#
# 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 __future__ import unicode_literals
# from builtins import str
# -- This line is 75 characters -------------------------------------------
# built in's
import os
import sys
import site
import logging as _logging
from collections import OrderedDict
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# normal scripts we would do the following 'from dynaconf import settings'
# for GUI apps that need PySide2 we init in the following manner instead
import azpy.config_utils
_config = azpy.config_utils.get_dccsi_config()
settings = _config.get_config_settings(setup_ly_pyside=True)
from pathlib import Path
from pathlib import PurePath
# Lumberyard extensions
#import azpy
from azpy.env_bool import env_bool
from azpy.constants import ENVAR_DCCSI_GDEBUG
from azpy.constants import ENVAR_DCCSI_DEV_MODE
# set up global space, logging etc.
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, settings.DCCSI_GDEBUG)
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, settings.DCCSI_GDEBUG)
for handler in _logging.root.handlers[:]:
_logging.root.removeHandler(handler)
_MODULENAME = 'DCCsi.SDK.substance.builder.sb_gui_main'
_log_level = _logging.INFO
if _G_DEBUG:
_log_level = _logging.DEBUG
_LOGGER = azpy.initialize_logger(name=_MODULENAME,
log_to_file=True,
default_log_level=_log_level)
_LOGGER.debug('Starting up: {0}.'.format({_MODULENAME}))
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# early attach WingIDE debugger (can refactor to include other IDEs later)
if _DCCSI_DEV_MODE:
from azpy.test.entry_test import connect_wing
foo = connect_wing()
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# now we should be cooking with gas (and can use the dnynamic settings)
# this block is really for standalone
from dynaconf import settings
import config
_LOGGER.debug('config.py is: {}'.format(config))
# initialize the Lumberyard Qt / PySide2
config.init_ly_pyside(settings.LY_DEV) # for standalone
settings.setenv() # for standalone
# log debug info about Qt/PySide2
_LOGGER.debug('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH))
_LOGGER.debug('LY_BIN_PATH: {}'.format(settings.LY_BIN_PATH))
_LOGGER.debug('QT_PLUGIN_PATH: {}'.format(settings.QT_PLUGIN_PATH))
_LOGGER.debug('QT_QPA_PLATFORM_PLUGIN_PATH: {}'.format(settings.QT_QPA_PLATFORM_PLUGIN_PATH))
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# Qt and Pyside2 imports block
import PySide2
_LOGGER.debug('PySide2 is:'.format(PySide2.__file__))
import PySide2.QtCore
from PySide2 import QtCore, QtWidgets
# from PySide2.QtCore import QTimer
from PySide2.QtCore import QProcess, Signal, Slot, QTextCodec
from PySide2.QtGui import QTextCursor, QColor
from PySide2.QtWidgets import QApplication, QPlainTextEdit
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# substance automation toolkit (aka pysbs)
# To Do: move this into some kind of substance specific configuration
# SDK\substance should probably have it's own .env and settings.py (dynaconf)
# Note: pysbs has it's own installer and it wants to install itself directly
# into a python distribution (assumes vanilla external)
# we don't want to modify lumberyards core distribution
# and we can't ship pusbs anyway, so I used pip install --target
# and installed it into a folder within the substance local installation
# so a data driven configuration needs to allow this to be easily set
# outside of the DCCsi
from azpy.constants import PATH_SAT_INSTALL_PATH
_PYSBS_DIR_PATH = Path(PATH_SAT_INSTALL_PATH).resolve()
site.addsitedir(str(_PYSBS_DIR_PATH)) # 'install' is the folder I created
import pysbs
import pysbs.batchtools as pysbs_batch
import pysbs.context as pysbs_context
# local modules
import sbsar_utils
from atom_material import AtomMaterial
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# To Do: still should manage via dynaconf (dynamic config and settings)
from azpy.constants import ENVAR_LY_DEV
_LY_DEV = Path(os.getenv(ENVAR_LY_DEV, None)).resolve()
from azpy.constants import ENVAR_LY_PROJECT
_LY_PROJECT = os.getenv(ENVAR_LY_PROJECT, None)
from azpy.constants import ENVAR_LY_PROJECT_PATH
_LY_PROJECT_PATH = Path(os.getenv(ENVAR_LY_PROJECT_PATH, None)).resolve()
from azpy.constants import ENVAR_DCCSI_SDK_PATH
_DCCSI_SDK_PATH = Path(os.getenv(ENVAR_DCCSI_SDK_PATH, None)).resolve()
# build some reuseable path parts
_PROJECT_ASSET_PATH = Path(_LY_PROJECT_PATH).resolve()
_PROJECT_ASSETS_PATH = Path(_LY_PROJECT_PATH, 'Materials').resolve()
# To Do: figure out a proper way to deal with Lumberyard game projects
_GEM_MATPLAY_PATH = Path(_LY_DEV, 'Gems', 'AtomContent', 'AtomMaterialPlayground').resolve()
_GEM_ROYALTYFREE = Path(_LY_DEV, 'Gems', 'AtomContent', 'RoyaltyFreeAssets').resolve()
_GEM_SUBSOURCELIBRARY = Path(_LY_DEV, 'Gems', 'AtomContent', 'SubstanceSourceLibrary').resolve()
_SUB_LIBRARY_PATH = Path(_GEM_SUBSOURCELIBRARY, 'Assets', 'SubstanceSource', 'Library').resolve()
# ^ This hard codes a bunch of known asset gems, again bad
# To Do: figure out a proper way to scrap the gem registry from project
# path to watcher script
_WATCHER_SCRIPT_PATH = Path(_DCCSI_SDK_PATH, 'substance', 'builder', 'watchdog', '__init__.py').resolve()
_TEX_RNDR_PATH = Path(_LY_PROJECT_PATH, 'Materials', 'Substance').resolve()
_MAT_OUTPUT_PATH = Path(_LY_PROJECT_PATH, 'Materials', 'Substance').resolve()
_SBSAR_COOK_PATH = Path(_LY_PROJECT_PATH, 'Materials', 'Substance').resolve()
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# sys.path.insert(0, os.path.abspath('../ui/'))
# import main_win
# sys.path.insert(0, os.path.abspath('..'))
# from watchdog import *
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
class Window(QtWidgets.QDialog):
def __init__(self, parent=None, project_path=None, default_material_path=None):
super(Window, self).__init__(parent)
# we should really init non-Qt stuff and set things up as properties
if project_path is None:
self.project_path = str(_LY_PROJECT_PATH)
else:
self.project_path = Path(project_path)
if default_material_path is None:
self._default_material_path = Path(_DCCSIG_PATH,
'sdk',
'substance',
'resources',
'atom',
'StandardPBR_AllProperties.material')
else:
self._default_material_path = default_material_path
# the start building up qt specific ui, etc
# but really, much of this should be moved to a self.setup_ui() method
self.root_directory = str(_PROJECT_ASSET_PATH)
# os.chdir(self.root_directory) # change working dir path
self.browseButton = self.create_button("&Browse...", self.browse)
self.browseButton1 = self.create_button("&Browse...", self.browse)
self.findButton = self.create_button("&Find", self.find)
self.removeButton = self.create_button("&Remove", self.remove)
self.startWatch = self.create_button("&Start Watch", self.watch)
self.killButton = self.create_button("&Stop Watch", self.kill)
self.clearlogButton = self.create_button("&Clear Log", self.clear_log)
self.atomMaterial = self.create_button("&Atom Material", self.atom_material)
self.substance_ext_filters = ["*.sbs", "*.sbsar"]
self.textComboBox = self.createComboBox()
# ^^ this is never added to a layout ... it's a phatom text entry that is empty
# it is never updated to add text
# but it's contents are retreived for use in self.find()
self.atomMatNameComboBox = self.createComboBox("Setup Material name here(default:sbsar name)")
# _texturePath = Path(self.project_path, 'Assets', 'SubstanceLibrary', 'textures').resolve()
self.texRenderPathComboBox = self.createComboBox(str(_TEX_RNDR_PATH))
self.matOutputPathComboBox = self.createComboBox(str(_MAT_OUTPUT_PATH))
# self.directoryComboBox = self.createComboBox(QtCore.QDir.currentPath())
# I changed this to scan the _LY_PROJECT
# self.sbsarDirectory = self.return_1st_sbsar(Path(self.project_path, 'Assets')).resolve().parent
self.sbsarDirectory = QtCore.QDir()
self.sbsarDirectory.setCurrent(str(_PROJECT_ASSET_PATH))
self.sbsarDirectory.setNameFilters(self.substance_ext_filters)
self.directoryComboBox = self.createComboBox(self.sbsarDirectory.canonicalPath())
self.builderDirComboBox = self.createComboBox(self.sbsarDirectory.canonicalPath())
# I DO NOT like or want all of this ui creation here in the __init__,
# it is literally no better then a .ui file
# Rob G is wrong, except he is right if we are building with custom widgets
# but almost nothing here is custom so it's waste of lines of code
# extremely difficult to iterate on ... I feel this contributes to
# the length of time spent to stand this tool up
# textLabel = QtWidgets.QLabel("Containing text:")
self.directoryLabel = QtWidgets.QLabel("Watch directory:")
self.builderdirLabel = QtWidgets.QLabel("Watch directory:")
self.filesFoundLabel = QtWidgets.QLabel()
self.watcherLogLabel = QtWidgets.QLabel("Watcher Log")
self.fileListLabel = QtWidgets.QLabel("File List")
self.sbsarPresetLabel = QtWidgets.QLabel("Sbsar Preset:")
self.OutputResLabel = QtWidgets.QLabel("Output Size:")
self.OutputsLabel = QtWidgets.QLabel("Outputs:")
self.atomMatLabel = QtWidgets.QLabel("Material Name: ")
self.atomMatDirLabel = QtWidgets.QLabel("Material Path : ")
self.textOutputPathLabel = QtWidgets.QLabel("Texture Output:")
self.randomSeedLabel = QtWidgets.QLabel("Random Seed:")
self.watcher_log = QPlainTextEdit()
self.watcher_log.setReadOnly(True)
# self.watcher_log.setFixedHeight(500)
self.watcher_log.setMaximumBlockCount(10000) # limit console to 10000 lines
self.watcher_log._cursor_output = self.watcher_log.textCursor()
self.create_files_table()
buttonsLayout = QtWidgets.QHBoxLayout()
buttonsLayout.addStretch()
# buttonsLayout.addWidget(self.atomMaterial)
# buttonsLayout.addSpacing(50)
buttonsLayout.addWidget(self.findButton)
# buttonsLayout.addWidget(self.removeButton)
watcherBtnLayout = QtWidgets.QHBoxLayout()
watcherBtnLayout.addStretch()
watcherBtnLayout.addWidget(self.startWatch)
watcherBtnLayout.addWidget(self.killButton)
watcherBtnLayout.addWidget(self.clearlogButton)
self.setObjectName("Dialog")
self.gridLayout = QtWidgets.QGridLayout(self)
self.gridLayout.setObjectName("gridLayout1_0")
self.tabLayout = QtWidgets.QTabWidget(self)
self.tabLayout.setObjectName("tabLayout")
self.tabBuilder = QtWidgets.QTabWidget()
self.tabBuilder.setObjectName("tabBuilder")
self.gridLayout1_1 = QtWidgets.QGridLayout(self.tabBuilder)
self.gridLayout1_1.setObjectName("gridLayout1_1")
self.builderLayout = QtWidgets.QGridLayout()
self.builderLayout.setObjectName("builderLayout")
# self.extensionLabel = QtWidgets.QLabel("Watch extensions:")
# self.builderLayout.addWidget(self.extensionLabel, 0, 0, 1, 1)
# self.extFilterComboBox = self.createComboBox(str(self.substance_ext_filters))
# self.builderLayout.addWidget(self.extFilterComboBox, 0, 1, 1, 2)
# filename for search
self.filenameSearchLabel = QtWidgets.QLabel("Filename search:")
self.builderLayout.addWidget(self.filenameSearchLabel, 0, 0, 1, 1)
self.filenameSearchComboBox = self.createComboBox(None)
self.builderLayout.addWidget(self.filenameSearchComboBox, 0, 1, 1, 2)
self.builderLayout.addWidget(self.builderdirLabel, 1, 0)
self.builderLayout.addWidget(self.builderDirComboBox, 1, 1, 1, 1)
self.builderLayout.addWidget(self.browseButton1, 1, 2, 1, 1)
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.addWidget(self.filesTable)
self.verticalLayout.addWidget(self.filesFoundLabel)
self.builderLayout.addLayout(self.verticalLayout, 2, 0, 1, 3)
self.builderLayout.addLayout(buttonsLayout, 4, 0, 1, 3)
self.inputGroupBox = QtWidgets.QGroupBox("Input Parameters")
self.inputGridLayout = QtWidgets.QGridLayout(self.inputGroupBox)
self.inputGridLayout.addWidget(self.randomSeedLabel, 2, 0, 1, 1)
self.randomSeedComboBox = self.createComboBox("0000000000")
self.inputGridLayout.addWidget(self.randomSeedComboBox, 2, 1, 1, 2)
self.randomSeedBtn = self.create_button("&Generate", self.randomseed)
self.inputGridLayout.addWidget(self.randomSeedBtn, 2, 3, 1, 1)
self.randomSeedComboBox.setDisabled(True)
self.gridLayout1_1.addLayout(self.builderLayout, 0, 0, 1, 1)
self.tabLayout.addTab(self.tabBuilder, "Builder")
self.tabWatcher = QtWidgets.QTabWidget()
self.tabWatcher.setObjectName("tabWatcher")
self.gridLayout2_1 = QtWidgets.QGridLayout(self.tabWatcher)
self.gridLayout2_1.setObjectName("gridLayout2_0")
self.watcherLayout = QtWidgets.QGridLayout()
self.watcherLayout.setObjectName("watcherLayout")
self.watcherLayout.addWidget(self.directoryLabel, 0, 0)
self.watcherLayout.addWidget(self.directoryComboBox, 0, 1)
self.watcherLayout.addWidget(self.browseButton, 0, 2)
self.watcherHBoxLayout = QtWidgets.QHBoxLayout()
self.watcherHBoxLayout.addWidget(self.watcher_log)
self.watcherLayout.addLayout(self.watcherHBoxLayout, 1, 0, 1, 3)
self.watcherLayout.addLayout(watcherBtnLayout, 2, 0, 1, 3)
self.gridLayout2_1.addLayout(self.watcherLayout, 0, 0, 1, 1)
self.tabLayout.addTab(self.tabWatcher, "Watcher")
self.gridLayout.addWidget(self.tabLayout)
# self.setLayout(self.watcherLayout)
self.setWindowTitle("Substance Builder")
self.resize(800, 600)
# self.path = self.directoryComboBox.currentText()
self.path = Path(self.project_path)
self.groupboxOutputs = QtWidgets.QGroupBox("Outputs")
self.groupboxMat = QtWidgets.QGroupBox("Atom Material && Textures")
self.watcher_script = _WATCHER_SCRIPT_PATH
self.outputRenderPath = _TEX_RNDR_PATH
self.preset = ''
self.presetItem = ''
self.texRenderpath = self.texRenderPathComboBox.currentText()
self.res = 11
self.output_name = '{inputGraphUrl}_{outputNodeName}'
self.randomstr = 1
self.preset_index = -1
self.outputCookPath = _SBSAR_COOK_PATH
self.selected_tex = []
self.material_path = _MAT_OUTPUT_PATH
self._reader = self.start_reader()
def start_reader(self):
# set up
self._reader = ProcessOutputReader()
self._reader.produce_output.connect(self.append_output)
self._reader.start('python', ['-u', self.watcher_script, self.sbsarDirectory])
return self._reader
@Slot(str)
def append_output(self, text):
self.watcher_log._cursor_output.insertText(text)
self.scroll_to_last_line()
def scroll_to_last_line(self):
cursor = self.watcher_log.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.movePosition(QTextCursor.Up if cursor.atBlockStart() else
QTextCursor.StartOfLine)
self.watcher_log.setTextCursor(cursor)
def output_text(self, text):
self.watcher_log._cursor_output.insertText(text)
self.watcher_log.scroll_to_last_line()
def browse(self):
self.root_directory = QtWidgets.QFileDialog.getExistingDirectory(self,
"Find watcher folder",
str(_SUB_LIBRARY_PATH))
if self.root_directory:
if self.directoryComboBox.findText(self.root_directory) or self.builderDirComboBox.findText(
self.root_directory) == -1:
self.directoryComboBox.addItem(self.root_directory)
self.builderDirComboBox.addItem(self.root_directory)
self.directoryComboBox.setCurrentIndex(self.directoryComboBox.findText(self.root_directory))
self.builderDirComboBox.setCurrentIndex(self.builderDirComboBox.findText(self.root_directory))
self.path = self.root_directory
self.find()
return self.path
def browseRenderPath(self):
self.texRenderpath = QtWidgets.QFileDialog.getExistingDirectory(self,
"Setup textures output path",
str(_TEX_RNDR_PATH))
if self.texRenderpath:
if self.texRenderPathComboBox.findText(self.texRenderpath) == -1:
self.texRenderPathComboBox.addItem(self.texRenderpath)
self.texRenderPathComboBox.setCurrentIndex(self.texRenderPathComboBox.findText(self.texRenderpath))
self.texRenderPathComboBox.setCurrentText(self.texRenderpath)
return self.texRenderpath
def browseMaterialPath(self):
self.material_path = QtWidgets.QFileDialog.getExistingDirectory(self,
"Setup Atom Material output path",
str(_MAT_OUTPUT_PATH))
if self.material_path:
if self.matOutputPathComboBox.findText(self.material_path) == -1:
self.matOutputPathComboBox.addItem(self.material_path)
self.matOutputPathComboBox.setCurrentIndex(self.matOutputPathComboBox.findText(self.material_path))
self.matOutputPathComboBox.setCurrentText(self.material_path)
return self.material_path
@staticmethod
def updateComboBox(comboBox):
if comboBox.findText(comboBox.currentText()) == -1:
comboBox.addItem(comboBox.currentText())
def find(self, rootpath=None, filename=None, FILE_TEMPLATES=['*.sbs', '*.sbsar']):
self.filesTable.setRowCount(0)
if not filename:
fileName = self.filenameSearchComboBox.currentText()
# ^ this looks like it is meant to either be a file name pattern for search
# or ext patterns? Or both?
text = self.textComboBox.currentText()
# ^ what is this even for???
# it never gets filled out but we continue to pass it on?
# I assume it's really meant to be a file name and file ext pattern search?
if rootpath:
self.path = rootpath
else:
self.path = self.builderDirComboBox.currentText()
self.directoryComboBox.setCurrentText(self.path)
# self.path = "C:/Users/chunghao/Documents/Allegorithmic/Substance Designer/sbsar"
self.updateComboBox(self.filenameSearchComboBox)
self.updateComboBox(self.textComboBox)
self.updateComboBox(self.directoryComboBox)
self.currentDir = QtCore.QDir(str(Path(self.path).resolve()))
dirModel = QtWidgets.QFileSystemModel()
dirModel.setRootPath(self.currentDir.canonicalPath())
dirModel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Dirs)
if fileName:
FILE_TEMPLATES = [str(fileName)]
dirModel.setNameFilters(FILE_TEMPLATES)
# right now this filters to *.sbs and *.sbsar
# we use the project\assets dir as the root and query the filesystem
# instead of only a single directory
iterator = QtCore.QDirIterator(self.currentDir.canonicalPath(),
FILE_TEMPLATES,
QtCore.QDir.Files,
QtCore.QDirIterator.Subdirectories)
# now we can iterate over that filtered filesystem
files = []
while iterator.hasNext():
files.append(iterator.next())
files.sort()
# if not fileName or filename == '':
# fileName = "*"
# files = self.currentDir.entryList([fileName],
# QtCore.QDir.Files | QtCore.QDir.NoSymLinks)
files = self.find_files(files, text)
self.show_files(files)
return self.path
def find_files(self, files, text):
progressDialog = QtWidgets.QProgressDialog(self)
progressDialog.setCancelButtonText("&Cancel")
progressDialog.setRange(0, len(files))
progressDialog.setWindowTitle("Find Files")
foundFiles = []
for i in range(len(files)):
progressDialog.setValue(i)
progressDialog.setLabelText("Searching file number %d of %d..." % (i, len(files)))
QtCore.qApp.processEvents()
if progressDialog.wasCanceled():
break
inFile = QtCore.QFile(self.currentDir.absoluteFilePath(files[i]))
if inFile.open(QtCore.QIODevice.ReadOnly):
stream = QtCore.QTextStream(inFile)
while not stream.atEnd():
if progressDialog.wasCanceled():
break
line = stream.readLine()
if text in line:
foundFiles.append(files[i])
break
progressDialog.close()
return foundFiles
def show_files(self, files):
for fn in files:
file = QtCore.QFile(self.currentDir.absoluteFilePath(fn))
size = QtCore.QFileInfo(file).size()
fileNameItem = QtWidgets.QTableWidgetItem(fn)
fileNameItem.setFlags(fileNameItem.flags() ^ QtCore.Qt.ItemIsEditable)
sizeItem = QtWidgets.QTableWidgetItem("%d KB" % (int((size + 1023) / 1024)))
sizeItem.setTextAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
sizeItem.setFlags(sizeItem.flags() ^ QtCore.Qt.ItemIsEditable)
row = self.filesTable.rowCount()
self.filesTable.insertRow(row)
self.filesTable.setItem(row, 0, fileNameItem)
self.filesTable.setItem(row, 1, sizeItem)
self.filesTable.setAlternatingRowColors(True)
self.filesTable.setStyleSheet("alternate-background-color: #444444; background-color: #4d4d4d;")
self.filesFoundLabel.setText(
"%d file(s) found (Double click on a SBSAR to cook, SBS file to load.)" % len(files))
def create_button(self, text, member):
button = QtWidgets.QPushButton(text)
button.clicked.connect(member)
return button
def createComboBox(self, text=""):
comboBox = QtWidgets.QComboBox()
comboBox.setEditable(True)
comboBox.addItem(text)
comboBox.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Preferred)
return comboBox
def create_files_table(self):
self.filesTable = QtWidgets.QTableWidget(0, 2)
# self.filesTable.setMinimumHeight(300)
# self.filesTable.setFixedHeight(300)
self.filesTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.filesTable.setHorizontalHeaderLabels(("File Name", "Size"))
self.filesTable.horizontalHeaderItem(0).setTextAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft)
self.filesTable.horizontalHeaderItem(1).setTextAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
# self.filesTable.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignRight)
self.filesTable.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
self.filesTable.horizontalHeader().setDefaultSectionSize(5)
self.filesTable.verticalHeader().hide()
# self.filesTable.verticalHeader().show()
self.filesTable.setColumnWidth(1, 80)
self.filesTable.setShowGrid(False)
self.filesTable.verticalHeader().setDefaultSectionSize(5)
# style = "::section {""background-color: (241, 255, 175); }"
# self.filesTable.setStyleSheet(style)
# self.filesTable.setFixedHeight(200)
self.filesTable.cellActivated.connect(self.open_file_of_item)
def create_sbsar_tex(self):
_LOGGER.debug('Running: .create_sbsar_tex')
# self.builderLayout.addWidget(self.OutputsLabel, 10, 0)
self.sbsarName = self.item.text().replace(".sbsar", "")
self.groupboxOutputs = QtWidgets.QGroupBox("Output Texture Maps")
self.horizontalLayoutOuputs = QtWidgets.QHBoxLayout(self.groupboxOutputs)
self.horizontalLayoutOuputs.setObjectName(("horizontalLayoutOuputs"))
# self.vertical_layout_outputs = QtWidgets.QVBoxLayout(self.groupboxOutputs)
# self.vertical_layout_outputs.setObjectName("vertical_layout_outputs")
self.sbsar_tex_outputs = sbsar_utils.output_info(self.path, self.sbsarName, 'tex_maps')
for index, tex in enumerate(self.sbsar_tex_outputs):
_LOGGER.info(tex)
#text_item_checkbox = QtWidgets.QCheckBox(self.groupboxOutputs)
#text_item_checkbox.setObjectName(self.sbsarName + "_" + tex)
# text_item_checkbox.setEnabled(True)
# text_item_checkbox.setChecked(True)
# text_item_checkbox.setText(tex)
# self.horizontalLayoutOuputs.addWidget(text_item_checkbox)
self.sbsar_tex_outputs[index] = QtWidgets.QCheckBox(self.groupboxOutputs)
self.sbsar_tex_outputs[index].setObjectName(self.sbsarName + "_" + tex)
self.sbsar_tex_outputs[index].setEnabled(True)
self.sbsar_tex_outputs[index].setChecked(True)
self.sbsar_tex_outputs[index].setText(tex)
self.horizontalLayoutOuputs.addWidget(self.sbsar_tex_outputs[index])
self.builderLayout.addWidget(self.groupboxOutputs, 7, 0, 1, 3)
def create_sbsar_presets(self):
_LOGGER.debug('Running: .create_sbsar_presets')
self.inputGridLayout.addWidget(self.sbsarPresetLabel, 0, 0, 1, 1)
self.sbsar_presets = sbsar_utils.output_info(self.path, self.item.text().replace(".sbsar", ""), 'presets')
self.comboBox_SbsarPreset = QtWidgets.QComboBox(self)
self.comboBox_SbsarPreset.addItem("Default preset")
for self.presetItem in self.sbsar_presets:
_LOGGER.info(self.presetItem)
# preset_index = 0
self.comboBox_SbsarPreset.addItem(self.presetItem, self.presetItem)
# self.comboBox_SbsarPreset.setItemText(preset_index, self.presetItem)
# preset_index += 1
self.inputGridLayout.addWidget(self.comboBox_SbsarPreset, 0, 1, 1, 3)
self.comboBox_SbsarPreset.activated.connect(self.preset_selected)
def preset_selected(self, index):
self.preset = self.comboBox_SbsarPreset.itemData(index)
if self.preset in self.sbsar_presets:
self.atomMatNameComboBox.setCurrentText(self.sbsarName + "_" + self.preset.replace(" ", ""))
_LOGGER.info('Preset(variant): ' + self.atomMatNameComboBox.currentText())
self.preset_index = index - 1
_LOGGER.info('The Preset index: ' + str(self.preset_index))
return self.preset_index
# return self.atomMatNameComboBox.currentText()
else:
self.atomMatNameComboBox.setCurrentText(self.sbsarName)
_LOGGER.info('Default preset selected.')
self.preset_index = index - 1
_LOGGER.info(self.preset_index)
return self.preset_index
def create_tex_res(self):
self.inputGridLayout.addWidget(self.OutputResLabel, 1, 0, 1, 1)
self.resSelector = QtWidgets.QComboBox(self)
# self.resSelector.addItem("512x512", "$outputsize@9,9")
# self.resSelector.addItem("1024x1024", "$outputsize@10,10")
# self.resSelector.addItem("2048x2048", "$outputsize@11,11")
self.resSelector.addItem("512x512", 9)
self.resSelector.addItem("1024x1024", 10)
self.resSelector.addItem("2048x2048", 11)
self.resSelector.addItem("4096x4096", 12)
self.resSelector.addItem("8096x8096", 13)
self.inputGridLayout.addWidget(self.resSelector, 1, 1, 1, 3)
self.resSelector.activated.connect(self.res_selected)
self.resSelector.setCurrentIndex(2)
def res_selected(self, index):
self.res = self.resSelector.itemData(index)
_LOGGER.info(self.res)
return self.res
def remove(self):
self.builderLayout.removeWidget(self.groupboxOutputs)
self.groupboxOutputs.close()
def kill(self):
self._reader.kill()
def clear_log(self):
self.watcher_log.clear()
def atom_material(self, preset=None,
material_type=None,
tex_ext='.tif',
mat_ext='.material'):
"""To Do"""
if preset is None:
preset = self.preset
preset = str(preset).replace(" ", "-")
# setup material template
if material_type is None:
material = AtomMaterial(self._default_material_path)
mat_file_tag = str(self.atomMatNameComboBox.currentText()).replace(" ", "_")
# file_path = Path(self.atomMatNameComboBox.currentText())
# file_name = file_path.stem
# ^^ object oriented paths do all these things you want without destrpying the path
# write less code, use your autocomplete, reed the docs
# p.name, Final component only (name.ext)
# p.stem, Final component without extension.
# p.ext, Extension only. .suffix in pathlib
# if you want relative paths here is a better way
# first of all, assume we know the project we are in
#_LY_PROJECT_PATH
texture_output_path = Path(self.texRenderPathComboBox.currentText()).resolve()
rel_tex_path = None
for p in texture_output_path.parts:
if _LY_PROJECT == p:
index = texture_output_path.parts.index(_LY_PROJECT)
rel_tuple = texture_output_path.parts[index + 1:]
rel_tex_path = Path(*list(rel_tuple))
# if 'ambientocclusion' in self.preset:
# num_index = 5
# else:
num_index = 5
# ^^ ok what is all this magic mumbo jumbo?
if preset is None or preset == '':
preset_slug = preset # gets rid of _ when not needed
else:
preset_slug = '_' + preset
for tex_index in range(num_index):
# tex_map_name = material.texture_map[material.tex[tex_index]] + tex_ext
# ^ this is not so great, AtomMaterial is very rigid and only very specifically
# supports a hand coded implmentation of StandardPBR
# we should have a more generic material class that can consume any
# then we can somehow check for the type we are compatible with
# after that you might want a wrapper class above the atom material data that knows how
# to map from substance to the atom material, and builds a specific data structure above
# the generic one
material_texture_slot = material.texture_map[material.tex[tex_index]]
material_slot_texture = material_texture_slot + tex_ext
# text_map_path = Path(rel_tex_path + "\\" + tex_file_name + "_" + tex_map_name).resolve()
texture_slug = mat_file_tag + preset_slug + '_' + material_slot_texture
rel_texture_map_path = Path(rel_tex_path, texture_slug) # don't .resolve()
# posiz / slash
material.set_map(material.tex[tex_index], str(rel_texture_map_path.as_posix()))
# mat_path = self.material_path + "/" + self.atomMatNameComboBox.currentText().replace(" ", "") + ".material"
material_path = Path(self.material_path, mat_file_tag + preset_slug + mat_ext)
material.write(material_path)
self.preset = ''
_LOGGER.info('{}'.format(material_path))
return material_path
def create_single(self):
if self.preset is None:
self.atom_material(self.preset)
else:
self.atom_material(self.preset)
def create_all_material(self):
# always make the make
# self.atom_material('base')
self.atom_material(self.preset)
# then make any others
if len(self.sbsar_presets) > 0:
for i in self.sbsar_presets:
_LOGGER.info('Preset(variant): {}'.format(i.replace(" ", "")))
self.atom_material(i)
def print_something(self, words):
self.words = words
_LOGGER.info(self.words)
def watch(self):
self._reader.start('python', ['-u', self.watcher_script, self.sbsarDirectory])
def create_atom_mat(self, item_path=None):
self.atom_matPath_button = self.create_button("&Browse", self.browseMaterialPath)
self.atom_material_button = self.create_button("&Create Single", self.create_single)
self.atom_material_output = self.create_button("&Create All", self.create_all_material)
self.renderPathbutton = self.create_button("&Output Path", self.browseRenderPath)
self.rendeTexbutton = self.create_button("&Render Textures", self.render_tex)
if item_path is None:
item_path = Path(self.item.text()).resolve()
# self.sbsarName = self.item.text().replace(".sbsar", "")
self.sbsarName = item_path.stem
self.groupboxMat = QtWidgets.QGroupBox("Atom Material")
self.atomMatNameComboBox.setCurrentText(self.sbsarName)
# self.atomMatNameComboBox.setDisabled(True)
# self.texRenderPathComboBox.setCurrentText(self.path)
self.gridLayoutMat = QtWidgets.QGridLayout(self.groupboxMat)
self.MatHBox0 = QtWidgets.QHBoxLayout()
self.MatHBox0.addWidget(self.atomMatDirLabel)
self.MatHBox0.addWidget(self.matOutputPathComboBox)
self.MatHBox0.addWidget(self.atom_matPath_button)
# self.MatHBox0.addWidget(self.atom_matPath_button2)
self.gridLayoutMat.addLayout(self.MatHBox0, 0, 0, 1, 4)
self.MatHBox = QtWidgets.QHBoxLayout()
self.MatHBox.addWidget(self.atomMatLabel)
self.MatHBox.addWidget(self.atomMatNameComboBox)
self.MatHBox.addWidget(self.atom_material_output)
self.MatHBox.addWidget(self.atom_material_button)
self.gridLayoutMat.addLayout(self.MatHBox, 1, 0, 1, 4)
self.MatHBox1 = QtWidgets.QHBoxLayout()
self.MatHBox1.addWidget(self.textOutputPathLabel)
self.MatHBox1.addWidget(self.texRenderPathComboBox)
self.MatHBox1.addWidget(self.renderPathbutton)
self.MatHBox1.addWidget(self.rendeTexbutton)
self.gridLayoutMat.addLayout(self.MatHBox1, 2, 0, 1, 4)
self.builderLayout.addWidget(self.groupboxMat, 8, 0, 1, 3)
# _LOG.info("\n")
def randomseed(self):
import random
random_num = random.randrange(1, 10 ** 10)
self.randomstr = str(random_num)
self.randomSeedComboBox.setCurrentText(str(random_num))
_LOGGER.info('Random Seed: ' + self.randomstr)
return self.randomstr
def render_tex(self):
"""To Do"""
texture_output_path = Path(self.texRenderPathComboBox.currentText()).resolve()
sbsar_item_path = Path(self.item.text()).resolve()
if not texture_output_path.exists():
try:
texture_output_path.mkdir(mode=0o777)
_LOGGER.info('mkdir: {}'.format(texture_output_path))
except Exception as e:
_LOGGER.ERROR(e)
raise(e)
else:
_LOGGER.info('exists: {}'.format(texture_output_path))
_LOGGER.info('Render texture from: ' + self.path + "/" + self.item.text())
# for index, tex in enumerate(self.groupboxOutputs.children()):
# if self.groupboxOutputs.children()
for index, tex in enumerate(self.sbsar_tex_outputs):
if self.sbsar_tex_outputs[index].isChecked() is True:
self.selected_tex.append(tex.text())
print(self.selected_tex)
self.outputCookPath = sbsar_item_path.parent
sbsar_utils.render_sbsar(self.outputCookPath,
self.selected_tex,
sbsar_item_path.stem,
texture_output_path,
self.preset_index,
self.res,
self.randomstr)
self.selected_tex = []
def find_1st_sbsar(self, seedpath=None):
i = None # previously broke if found none
if seedpath is None:
seedpath = Path(self.project_path, 'Assets')
filelist = []
for r, d, f in os.walk(seedpath):
for file in f:
filelist.append(file)
for i in range(len(filelist)):
if filelist[i].split(".")[-1] == "sbsar":
return i
else:
i += 1
return i
def return_1st_sbsar(self, seedpath=None):
if seedpath is None:
seedpath = Path(self.project_path, 'Assets')
filelist = []
for r, d, f in os.walk(seedpath):
for file in f:
if Path(file).ext == ".sbsar":
filelist.append(Path(r, file).absolute())
return filelist[0]
def open_file_of_item(self, row, column):
self.item = self.filesTable.item(row, 0)
item_path = Path(self.item.text()).resolve()
_LOGGER.debug('Item path: {}'.format(item_path))
# Load sbsar parameters.
if PurePath(item_path).suffix == ".sbsar":
self.create_sbsar_presets()
self.create_tex_res()
self.builderLayout.removeWidget(self.groupboxOutputs)
self.groupboxOutputs.close()
self.create_sbsar_tex()
self.builderLayout.removeWidget(self.groupboxMat)
self.groupboxMat.close()
self.builderLayout.addWidget(self.inputGroupBox, 5, 0, 1, 3)
self.randomSeedComboBox.setCurrentText("0000000000")
self.create_atom_mat(item_path)
# Cook sbsar file and trigger watcher to generate textures.
elif self.item.text().split(".")[-1] == "sbs":
sbsar_utils.cook_sbsar(self.path + "/" + self.item.text(), sbsar_utils._PYSBS_CONTEXT, self.path,
self.item.text().split(".")[0])
sbsar_utils.output_info()
class ProcessOutputReader(QProcess):
produce_output = Signal(str)
def __init__(self, parent=None):
super().__init__(parent=parent)
# merge stderr channel into stdout channel
self.setProcessChannelMode(QProcess.MergedChannels)
# prepare decoding process' output to Unicode
self._codec = QTextCodec.codecForLocale()
self._decoder_stdout = self._codec.makeDecoder()
# only necessary when stderr channel isn't merged into stdout:
# self._decoder_stderr = codec.makeDecoder()
self.readyReadStandardOutput.connect(self._ready_read_standard_output)
# only necessary when stderr channel isn't merged into stdout:
# self.readyReadStandardError.connect(self._ready_read_standard_error)
@Slot()
def _ready_read_standard_output(self):
raw_bytes = self.readAllStandardOutput()
text = self._decoder_stdout.toUnicode(raw_bytes)
self.produce_output.emit(text)
# only necessary when stderr channel isn't merged into stdout:
# @Slot()
# def _ready_read_standard_error(self):
# raw_bytes = self.readAllStandardError()
# text = self._decoder_stderr.toUnicode(raw_bytes)
# self.produce_output.emit(text)
def substance_builder_launcher():
app = QApplication(sys.argv)
window = Window()
_LOGGER.info('cwd: {}'.format(os.getcwd())) # project
_LOGGER.info('file: {}'.format(__file__)) # *might* come back relative
# we should ensure we know the abs path
qss_filepath = Path(_DCCSIG_PATH).resolve().absolute()
qss_filepath = Path(qss_filepath, 'SDK', 'substance', 'builder',
'ui', 'stylesheets', 'LYstyle.qss').resolve().absolute()
window.setStyleSheet(qss_filepath.read_text())
# window.find() # uhm ... this is VERY odd, shouldn't you do this in __init__?
window.find()
first_item = window.find_1st_sbsar()
window.show()
# window.open_file_of_item(first_item, 0)
sys.exit(app.exec_())
###########################################################################
# Main Code Block, runs this script as main (testing)
# -------------------------------------------------------------------------
if __name__ == '__main__':
"""Run this file as main"""
_LOGGER.info("Test Run:: {0}.".format({_MODULENAME}))
_LOGGER.info("{0} :: if __name__ == '__main__':".format(_MODULENAME))
_LOGGER.info('file: {}'.format(os.getcwd())) # project
_LOGGER.info('file: {}'.format(__file__)) # *might* come back relative
substance_builder_launcher()
@@ -0,0 +1,133 @@
# coding:utf-8
#!/usr/bin/python
#
# 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.
# -- This line is 75 characters -------------------------------------------
"""Empty Doc String""" # To Do: add documentation
# -------------------------------------------------------------------------
# built-ins
import os
import sys
import site
import subprocess
# Lumberyard extensions
from azpy.env_bool import env_bool
from azpy.constants import ENVAR_DCCSI_GDEBUG
from azpy.constants import ENVAR_DCCSI_DEV_MODE
from azpy.constants import *
# 3rdparty
from unipath import Path
from unipath import FILES
import click
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# substance automation toolkit (aka pysbs)
# To Do: manage with dynaconf environment
_PYSBS_DIR_PATH = Path(PATH_PROGRAMFILES_X64,
'Allegorithmic',
'Substance Automation Toolkit',
'Python API',
'install').resolve()
site.addsitedir(str(_PYSBS_DIR_PATH)) # 'install' is the folder I created
# Susbstance
import pysbs.batchtools as pysbs_batch
import pysbs.context as pysbs_context
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# set up global space, logging etc.
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
_PACKAGENAME = __name__
if _PACKAGENAME is '__main__':
_PACKAGENAME = 'DCCsi.SDK.substance.builder.sbs_to_sbsar'
import azpy
_LOGGER = azpy.initialize_logger(_PACKAGENAME)
_LOGGER.debug('Starting up: {0}.'.format({_PACKAGENAME}))
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# Defining CONSTANTS
# To Do: shouldn't need this _BASE_ENVVAR_DICT (replace with dynaconf config)
from collections import OrderedDict
_SYNTH_ENV_DICT = OrderedDict()
_SYNTH_ENV_DICT = azpy.synthetic_env.stash_env(_SYNTH_ENV_DICT)
# grab a specific path from the base_env
_PATH_DCCSI = _SYNTH_ENV_DICT[ENVAR_DCCSIG_PATH]
_LY_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_LY_PROJECT_PATH]
# build some reuseable path parts
_PATH_MOCK_ASSETS = Path(_LY_PROJECT_PATH, 'Assets').norm()
_PATH_MOCK_SUBLIB = Path(_PATH_MOCK_ASSETS, 'SubstanceSource').norm()
_PATH_MOCK_SBS = Path(_PATH_MOCK_SUBLIB, 'sbs').norm()
_PATH_MOCK_SBSAR = Path(_PATH_MOCK_SUBLIB, 'sbsar').norm()
_PATH_MOCK_MAT = Path(_PATH_MOCK_ASSETS, 'Textures').norm()
_PATH_MOCK_MAT_SUB = Path(_PATH_MOCK_MAT, 'Substance').norm()
# this will combine two parts into a single path (object)
# It also returnd the fixed-up version (norm)
_PATH_INPUT_SBS = Path(_PATH_MOCK_SBS, 'alien_rock_coral_formation', 'alien_rock_coral_formation.sbs').norm()
_PATH_COOK_OUTPUT = _PATH_MOCK_SBSAR.norm()
_PATH_RENDER_OUTPUT = _PATH_MOCK_MAT_SUB
_SBS_NAME = _PATH_INPUT_SBS.split('.sbs')[0].split('\\')[-1]
# quick test variables, will be removed
_PYSBS_CONTEXT = pysbs_context.Context()
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
@click.command()
@click.option('--sbs_path', default=_PATH_INPUT_SBS, help='Sbs path.')
@click.option('--sbsar_path', default=_PATH_COOK_OUTPUT, help='Sbsar output path.')
@click.option('--sbsar_name', default=_SBS_NAME, help='Sbsar output name.')
def sbs_to_sbsar(sbs_path, sbsar_path, sbsar_name):
""" Cook SBSAR from SBS """
sbs_path = Path(sbs_path).norm()
click.echo(sbs_path)
sbsar_path = Path(sbsar_path).norm()
click.echo(_SBS_NAME)
pysbs_batch.sbscooker(quiet=True,
inputs=sbs_path,
includes=_PYSBS_CONTEXT.getDefaultPackagePath(),
alias=_PYSBS_CONTEXT.getUrlAliasMgr().getAllAliases(),
output_path=sbsar_path,
output_name=sbsar_name,
compression_mode=2).wait()
# --------------------------------------------------------------------------
###########################################################################
# Main Code Block, runs this script as main (testing)
# -------------------------------------------------------------------------
if __name__ == '__main__':
"""Run this file as main"""
_LOGGER.debug("{0} :: if __name__ == '__main__':".format(_PACKAGENAME))
_LOGGER.debug(sbs_to_sbsar())
# remove the logger
del _LOGGER
# ---- END ---------------------------------------------------------------
@@ -0,0 +1,177 @@
# coding:utf-8
#!/usr/bin/python
#
# 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.
# -- This line is 75 characters -------------------------------------------
"""Empty Doc String""" # To Do: add documentation
# -------------------------------------------------------------------------
# built-ins
import os
import sys
import site
import subprocess
import logging
# Lumberyard extensions
from azpy.env_bool import env_bool
from azpy.constants import ENVAR_DCCSI_GDEBUG
from azpy.constants import ENVAR_DCCSI_DEV_MODE
from azpy.constants import *
# 3rdparty
from unipath import Path
import click
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# substance automation toolkit (aka pysbs)
# To Do: manage with dynaconf environment
_PYSBS_DIR_PATH = Path(PATH_PROGRAMFILES_X64,
'Allegorithmic',
'Substance Automation Toolkit',
'Python API',
'install').resolve()
site.addsitedir(str(_PYSBS_DIR_PATH)) # 'install' is the folder I created
# Susbstance
import pysbs.batchtools as pysbs_batch
import pysbs.context as pysbs_context
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# set up global space, logging etc.
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
_PACKAGENAME = __name__
if _PACKAGENAME is '__main__':
_PACKAGENAME = 'DCCsi.SDK.substance.builder.sbsar_info'
import azpy
_LOGGER = azpy.initialize_logger(_PACKAGENAME)
_LOGGER.debug('Starting up: {0}.'.format({_PACKAGENAME}))
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# global space debug flag
_G_DEBUG = os.getenv(ENVAR_DCCSI_GDEBUG, False)
# global space debug flag
_DCCSI_DEV_MODE = os.getenv(ENVAR_DCCSI_DEV_MODE, False)
_MODULE_PATH = Path(__file__)
_ORG_TAG = 'Amazon_Lumberyard'
_APP_TAG = 'DCCsi'
_TOOL_TAG = 'sdk.substance.builder.sbsar_info'
_TYPE_TAG = 'module'
_MODULENAME = __name__
if _MODULENAME is '__main__':
_MODULENAME = _TOOL_TAG
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# Defining CONSTANTS
# To Do: shouldn't need this _BASE_ENVVAR_DICT (replace with dynaconf config)
from collections import OrderedDict
_SYNTH_ENV_DICT = OrderedDict()
_SYNTH_ENV_DICT = azpy.synthetic_env.stash_env(_SYNTH_ENV_DICT)
# grab a specific path from the base_env
_PATH_DCCSI = _SYNTH_ENV_DICT[ENVAR_DCCSIG_PATH]
_LY_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_LY_PROJECT_PATH]
# build some reuseable path parts
_PATH_MOCK_ASSETS = Path(_LY_PROJECT_PATH, 'Assets').norm()
_PATH_MOCK_SUBLIB = Path(_PATH_MOCK_ASSETS, 'SubstanceSource').norm()
_PATH_MOCK_SBS = Path(_PATH_MOCK_SUBLIB, 'sbs').norm()
_PATH_MOCK_SBSAR = Path(_PATH_MOCK_SUBLIB, 'sbsar').norm()
_PATH_MOCK_MAT = Path(_PATH_MOCK_ASSETS, 'Textures').norm()
_PATH_MOCK_MAT_SUB = Path(_PATH_MOCK_MAT, 'Substance').norm()
# this will combine two parts into a single path (object)
# It also returnd the fixed-up version (norm)
_PATH_INPUT_SBS = Path(_PATH_MOCK_SBS, 'bronze_yellow.sbs').norm()
_PATH_COOK_OUTPUT = _PATH_MOCK_SBSAR.norm()
_PATH_RENDER_OUTPUT = _PATH_MOCK_MAT_SUB
# quick test variables, will be removed
_PYSBS_CONTEXT = pysbs_context.Context()
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
def output_info(_outputCookPath, _outputName):
""" SBSAR information"""
info_lists = []
input_n_output = pysbs_batch.sbsrender_info(input=os.path.join(_outputCookPath, _outputName + '.sbsar'),
stdout=subprocess.PIPE)
for info_list in input_n_output.stdout.read().splitlines():
info_lists.append(info_list.decode('utf-8'))
_outputs, _params, _presets, _inputs, _input, _input_type = [], [], [], [], [], []
for info_list in info_lists:
if 'OUTPUT' in info_list:
_outputs.append(info_list.split(' ')[3])
elif 'INPUT $' in info_list:
_params.append(info_list.split(' ')[3:])
elif 'PRESET' in info_list:
_presets.append(info_list.split('PRESET ')[1])
elif 'INPUT' in info_list and (not '$'in info_list):
_inputs.append(info_list.split(' ')[3:])
_input.append(info_list.split(' ')[3:][0])
_input_type.append(info_list.split(' ')[3:][1])
info_list = {'texmaps': _outputs,
'params': _params,
'presets': _presets,
'inputs': _inputs,
'input': _input,
'input_type': _input_type}
return info_list
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
@click.command()
@click.option('--sbsar_path', default=_PATH_COOK_OUTPUT, help='Sbsar directory.')
@click.option('--sbsar_name', default=_PATH_INPUT_SBS.stem, help='Sbsar name.')
@click.option('--output_type', default='inputs', help='Output Type: \n'
'texmaps | presets | inputs | input | input_type | params')
def sbsar_info(sbsar_path, sbsar_name, output_type):
click.echo(output_info(sbsar_path, sbsar_name)[output_type])
###########################################################################
# Main Code Block, runs this script as main (testing)
# -------------------------------------------------------------------------
if __name__ == '__main__':
"""Run this file as main"""
_LOGGER.debug("{0} :: if __name__ == '__main__':".format(_PACKAGENAME))
_LOGGER.debug("Test Run:: {0}.".format({_MODULENAME}))
_LOGGER.debug("{0} :: if __name__ == '__main__':".format(_TOOL_TAG))
_LOGGER.debug(sbsar_info())
# remove the logger
del _LOGGER
# ---- END ---------------------------------------------------------------
@@ -0,0 +1,136 @@
# coding:utf-8
#!/usr/bin/python
#
# 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.
# -- This line is 75 characters -------------------------------------------
"""Empty Doc String""" # To Do: add documentation
# -------------------------------------------------------------------------
# built-ins
import os
import sys
import site
# Lumberyard extensions
from azpy.env_bool import env_bool
from azpy.constants import ENVAR_DCCSI_GDEBUG
from azpy.constants import ENVAR_DCCSI_DEV_MODE
from azpy.constants import *
# 3rdparty (we provide)
from unipath import Path
import click
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# substance automation toolkit (aka pysbs)
# To Do: manage with dynaconf environment
_PYSBS_DIR_PATH = Path(PATH_PROGRAMFILES_X64,
'Allegorithmic',
'Substance Automation Toolkit',
'Python API',
'install').resolve()
site.addsitedir(str(_PYSBS_DIR_PATH)) # 'install' is the folder I created
# Susbstance
import pysbs.batchtools as pysbs_batch
import pysbs.context as pysbs_context
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# set up global space, logging etc.
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
_PACKAGENAME = __name__
if _PACKAGENAME is '__main__':
_PACKAGENAME = 'DCCsi.SDK.substance.builder.sbsar_render'
import azpy
_LOGGER = azpy.initialize_logger(_PACKAGENAME)
_LOGGER.debug('Starting up: {0}.'.format({_PACKAGENAME}))
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# Defining CONSTANTS
# To Do: shouldn't need this _BASE_ENVVAR_DICT (replace with dynaconf config)
from collections import OrderedDict
_SYNTH_ENV_DICT = OrderedDict()
_SYNTH_ENV_DICT = azpy.synthetic_env.stash_env(_SYNTH_ENV_DICT)
# grab a specific path from the base_env
_PATH_DCCSI = _SYNTH_ENV_DICT[ENVAR_DCCSIG_PATH]
_LY_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_LY_PROJECT_PATH]
# build some reuseable path parts
_PATH_MOCK_ASSETS = Path(_LY_PROJECT_PATH, 'Assets').norm()
_PATH_MOCK_SUBLIB = Path(_PATH_MOCK_ASSETS, 'SubstanceSource').norm()
_PATH_MOCK_SBS = Path(_PATH_MOCK_SUBLIB, 'sbs').norm()
_PATH_MOCK_SBSAR = Path(_PATH_MOCK_SUBLIB, 'sbsar').norm()
_PATH_MOCK_MAT = Path(_PATH_MOCK_ASSETS, 'Textures').norm()
_PATH_MOCK_MAT_SUB = Path(_PATH_MOCK_MAT, 'Substance').norm()
# this will combine two parts into a single path (object)
# It also returnd the fixed-up version (norm)
_PATH_INPUT_SBS = Path(_PATH_MOCK_SBS, 'alien_rock_coral_formation','alien_rock_coral_formation.sbs').norm()
_PATH_COOK_OUTPUT = _PATH_MOCK_SBSAR.norm()
_PATH_RENDER_OUTPUT = _PATH_MOCK_MAT_SUB
# quick test variables, will be removed
_OUTPUT_SIZE = 9
_user_preset = ['Red Coral', 'Red Sand', 'Grey Stone']
integer1 = 0
integer2 = [0, 0]
float1 = 0.0
float2 = [0.0, 0.0]
_PYSBS_CONTEXT = pysbs_context.Context()
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
@click.command()
@click.option('--sbsar_path', default=_PATH_COOK_OUTPUT, help='Sbsar directory.')
@click.option('--sbsar_name', default=_PATH_INPUT_SBS.stem, help='Sbsar name.')
@click.option('--tex_path', default=_PATH_RENDER_OUTPUT, help='Texture output path.')
@click.option('--output_size', default=9, help='512x512, 9 | 1024x1024, 10 | 2048x2048, 11')
def sbsar_render(sbsar_path, sbsar_name, tex_path, output_size):
""" Doc String"""
pysbs_batch.sbsrender_render(inputs=os.path.join(sbsar_path, sbsar_name + '.sbsar'),
# input_graph=_inputGraphPath,
output_path=tex_path,
output_name='{inputGraphUrl}_{outputNodeName}',
output_format='tif',
set_value=['$outputsize@%s,%s' % (output_size, output_size), '$randomseed@1'],
# use_preset = _user_preset[0]
no_report=True,
verbose=True
).wait()
# --------------------------------------------------------------------------
###########################################################################
# Main Code Block, runs this script as main (testing)
# -------------------------------------------------------------------------
if __name__ == '__main__':
"""Run this file as main"""
_LOGGER.debug("{0} :: if __name__ == '__main__':".format(_PACKAGENAME))
_LOGGER.debug(sbsar_render())
# remove the logger
del _LOGGER
# ---- END ---------------------------------------------------------------
@@ -0,0 +1,248 @@
# coding:utf-8
#!/usr/bin/python
#
# 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.
# -- This line is 75 characters -------------------------------------------
"""Empty Doc String""" # To Do: add documentation
# -------------------------------------------------------------------------
# built-ins
import os
import sys
import site
import subprocess
import logging as _logging
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# set up global space, logging etc.
from azpy.env_bool import env_bool
from azpy.constants import ENVAR_DCCSI_GDEBUG
from azpy.constants import ENVAR_DCCSI_DEV_MODE
# we boostrap access to some lib site-packages
from dynaconf import settings
from pathlib import Path
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, settings.DCCSI_GDEBUG)
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, settings.DCCSI_DEV_MODE)
_MODULENAME = 'DCCsi.SDK.substance.builder.sbsar_utils'
_LOGGER = _logging.getLogger(_MODULENAME)
_LOGGER.debug('Starting up: {0}.'.format({_MODULENAME}))
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# substance automation toolkit (aka pysbs)
# To Do: manage with dynaconf environment
from azpy.constants import PATH_SAT_INSTALL_PATH
_SAT_INSTALL_PATH = Path(PATH_SAT_INSTALL_PATH).resolve()
site.addsitedir(str(_SAT_INSTALL_PATH)) # 'install' is the folder I created
# Susbstance
import pysbs.batchtools as pysbs_batch
import pysbs.context as _pysbs_context
_PYSBS_CONTEXT = _pysbs_context.Context()
# -------------------------------------------------------------------------
# --------------------------------------------------------------------------
def cook_sbsar(input_sbs, cook_output_path):
""" Doc String"""
input_sbs = Path(input_sbs).resolve()
cook_output_path = Path(cook_output_path).resolve()
if not cook_output_path.exists():
try:
cook_output_path.mkdir()
except:
_LOGGER.warning('Could not mkdir: {}'.format(cook_output_path))
output_name = input_sbs.stem
pysbs_batch.sbscooker(quiet=True,
inputs=str(input_sbs),
includes=_PYSBS_CONTEXT.getDefaultPackagePath(),
alias=_PYSBS_CONTEXT.getUrlAliasMgr().getAllAliases(),
output_path=str(cook_output_path),
output_name=output_name,
compression_mode=2).wait()
new_file = Path(cook_output_path, output_name + '.sbsar').resolve()
if new_file.exists():
return new_file
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
def info_sbsar(input_path):
""" Doc String"""
sbsar_info = []
in_file = Path(input_path).resolve()
input_n_output = pysbs_batch.sbsrender_info(input=str(in_file),
stdout=subprocess.PIPE)
for info in input_n_output.stdout.read().splitlines():
sbsar_info.append(info.decode('utf-8'))
return sbsar_info
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
def output_info(cook_output_path, output_name, output_type):
""" Doc String"""
info_lists = []
input_file = Path(cook_output_path, output_name + '.sbsar').resolve()
input_n_output = pysbs_batch.sbsrender_info(input=str(input_file),
stdout=subprocess.PIPE)
for info_list in input_n_output.stdout.read().splitlines():
info_lists.append(info_list.decode('utf-8'))
tex_maps, params, output_size, presets, inputs = [], [], [], [], []
for info_list in info_lists:
if 'OUTPUT' in info_list:
tex_maps.append(info_list.split(' ')[3])
elif 'INPUT $' in info_list:
params.append(info_list.split(' ')[3:])
elif 'PRESET' in info_list:
presets.append(info_list.split('PRESET ')[1])
elif 'INPUT' in info_list and not '$' in info_list:
inputs.append(info_list.split(' ')[3:])
output_info = {'tex_maps': tex_maps,
'params': params,
'output_size': output_size,
'presets': presets,
'inputs': inputs}
return output_info[output_type]
# To Do: Make preset a optional argument.
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
def render_sbsar(cook_output_path, output_texture_type, sbsar_name,
render_to_output_path, use_preset, output_size, random_seed):
""" Render textures maps from SBSAR"""
# _tex_output = ['diffuse', 'basecolor', 'normal']
# print(_tex_output)
cook_output_path = Path(cook_output_path).resolve()
texture_path = Path(render_to_output_path).resolve()
sbsar_file = Path(cook_output_path, sbsar_name + '.sbsar').resolve()
if output_info(cook_output_path, sbsar_name, 'presets'):
if use_preset == -1:
preset_base = '_'
pysbs_batch.sbsrender_render(inputs=str(sbsar_file),
# input_graph=_inputGraphPath,
input_graph_output=str(output_texture_type),
output_path=str(texture_path),
output_name=sbsar_name + preset_base + '{outputNodeName}',
output_format='tif',
set_value=['$outputsize@{},{}'.format(output_size, output_size),
'$randomseed@{}'.format(random_seed)],
no_report=True,
verbose=True
).wait()
else:
preset_name = output_info(cook_output_path, sbsar_name, 'presets')[use_preset]
preset_base = '_'
pysbs_batch.sbsrender_render(inputs=str(sbsar_file),
# input_graph=_inputGraphPath,
input_graph_output=str(output_texture_type),
output_path=str(texture_path),
output_name=sbsar_name + preset_base + preset_name.replace(' ', '') + '_{outputNodeName}',
output_format='tif',
set_value=['$outputsize@{},{}'.format(output_size, output_size),
'$randomseed@{}'.format(random_seed)],
use_preset=preset_name,
no_report=True,
verbose=True
).wait()
else:
pysbs_batch.sbsrender_render(inputs=str(sbsar_file),
# input_graph=_inputGraphPath,
input_graph_output=str(output_texture_type),
output_path=str(texture_path),
output_name=sbsar_name + '_{outputNodeName}',
output_format='tif',
set_value=['$outputsize@{},{}'.format(output_size, output_size),
'$randomseed@{}'.format(random_seed)],
no_report=True,
verbose=True
).wait()
# --------------------------------------------------------------------------
###########################################################################
# Main Code Block, runs this script as main (testing)
# -------------------------------------------------------------------------
if __name__ == "__main__":
"""Run this file as main"""
# ---------------------------------------------------------------------
# Defining CONSTANTS
# To Do: shouldn't need this _SYNTH_ENV_DICT (replace with dynaconf config)
from azpy import synthetic_env
_SYNTH_ENV_DICT = synthetic_env.stash_env()
from azpy.constants import ENVAR_DCCSIG_PATH
from azpy.constants import ENVAR_LY_PROJECT_PATH
# grab a specific path from the base_env
_PATH_DCCSI = _SYNTH_ENV_DICT[ENVAR_DCCSIG_PATH]
# use DCCsi as the project path for this test
_LY_PROJECT_PATH = _PATH_DCCSI
_PROJECT_ASSETS_PATH = Path(_LY_PROJECT_PATH, 'Assets').resolve()
_PROJECT_MATERIALS_PATH = Path(_PROJECT_ASSETS_PATH, 'Materials').resolve()
# this will combine two parts into a single path (object)
# It also returnd the fixed-up version (norm)
_PATH_OUTPUT = Path(_PROJECT_MATERIALS_PATH, 'Fabric')
_PATH_INPUT_SBS = Path(_PROJECT_MATERIALS_PATH, 'Fabric', 'fabric.sbsar')
# ---------------------------------------------------------------------
_LOGGER.debug("{0} :: if __name__ == '__main__':".format(_MODULENAME))
_LOGGER.debug('_SYNTH_ENV_DICT: {}'.format(_SYNTH_ENV_DICT))
_LOGGER.info('presets: {}'.format(output_info(_PATH_OUTPUT,
_PATH_INPUT_SBS.stem,
'presets')))
_LOGGER.info('params: {}'.format(output_info(_PATH_OUTPUT,
_PATH_INPUT_SBS.stem,
'params')))
_LOGGER.info('inputs: {}'.format(output_info(_PATH_OUTPUT,
_PATH_INPUT_SBS.stem,
'inputs')))
_LOGGER.info('tex_maps: {}'.format(output_info(_PATH_OUTPUT,
_PATH_INPUT_SBS.stem,
'tex_maps')))
_LOGGER.info(info_sbsar(Path(_PATH_INPUT_SBS)))
new_file = cook_sbsar(_PATH_INPUT_SBS, Path(_PATH_OUTPUT, '.tests'))
if new_file:
_LOGGER.info('Cooked out: {}'.format(new_file))
render_sbsar(cook_output_path=Path(_PATH_OUTPUT, '.tests'),
output_texture_type='basecolor',
sbsar_name=_PATH_INPUT_SBS.stem,
render_to_output_path=Path(_PATH_OUTPUT, '.tests'),
use_preset=-1,
output_size=256,
random_seed=1001)
# remove the logger
del _LOGGER
# ---- END ---------------------------------------------------------------
@@ -0,0 +1,149 @@
# coding:utf-8
#!/usr/bin/python
#
# 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.
# -- This line is 75 characters -------------------------------------------
"""Empty Doc String""" # To Do: add documentation
# -------------------------------------------------------------------------
# built-ins
import os
import sys
import site
# Lumberyard extensions
from azpy.env_bool import env_bool
from azpy.constants import ENVAR_DCCSI_GDEBUG
from azpy.constants import ENVAR_DCCSI_DEV_MODE
from azpy.constants import *
import sbsar_utils
# 3rdparty
from unipath import Path
import click
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# substance automation toolkit (aka pysbs)
# To Do: manage with dynaconf environment
_PYSBS_DIR_PATH = Path(PATH_PROGRAMFILES_X64,
'Allegorithmic',
'Substance Automation Toolkit',
'Python API',
'install').resolve()
site.addsitedir(str(_PYSBS_DIR_PATH)) # 'install' is the folder I created
# Susbstance
import pysbs.batchtools as pysbs_batch
import pysbs.context as pysbs_context
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# set up global space, logging etc.
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
_PACKAGENAME = __name__
if _PACKAGENAME is '__main__':
_PACKAGENAME = 'DCCsi.SDK.substance.builder.substance_tools'
import azpy
_LOGGER = azpy.initialize_logger(_PACKAGENAME)
_LOGGER.debug('Starting up: {0}.'.format({_PACKAGENAME}))
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# Defining CONSTANTS
# To Do: shouldn't need this _BASE_ENVVAR_DICT (replace with dynaconf config)
from collections import OrderedDict
_SYNTH_ENV_DICT = OrderedDict()
_SYNTH_ENV_DICT = azpy.synthetic_env.stash_env(_SYNTH_ENV_DICT)
# grab a specific path from the base_env
_PATH_DCCSI = _SYNTH_ENV_DICT[ENVAR_DCCSIG_PATH]
_LY_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_LY_PROJECT_PATH]
# build some reuseable path parts
_PATH_MOCK_ASSETS = Path(_LY_PROJECT_PATH, 'Assets').norm()
_PATH_MOCK_SUBLIB = Path(_PATH_MOCK_ASSETS, 'SubstanceSource').norm()
_PATH_MOCK_SBS = Path(_PATH_MOCK_SUBLIB, 'sbs').norm()
_PATH_MOCK_SBSAR = Path(_PATH_MOCK_SUBLIB, 'sbsar').norm()
_PATH_MOCK_MAT = Path(_PATH_MOCK_ASSETS, 'Textures').norm()
_PATH_MOCK_MAT_SUB = Path(_PATH_MOCK_MAT, 'Substance').norm()
_PATH_INPUT_SBS = Path(_PATH_MOCK_SBS, 'bronze_yellow.sbs').norm()
_PATH_COOK_OUTPUT = _PATH_MOCK_SBSAR.norm()
_PATH_RENDER_OUTPUT = _PATH_MOCK_MAT_SUB
_SBS_NAME = _PATH_INPUT_SBS.split('.sbs')[0].split('\\')[-1]
_PYSBS_CONTEXT = pysbs_context.Context()
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
@click.group()
def builder_tools():
pass
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
@builder_tools.command()
@click.option('--sbsar_path', default=_PATH_COOK_OUTPUT, help='Sbsar directory.')
@click.option('--sbsar_name', default=_PATH_INPUT_SBS.stem, help='Sbsar name.')
@click.option('--output_type', default='inputs', help='Output Type: \n'
'tex_maps | presets | inputs | input | input_type | params')
def info(sbsar_path, sbsar_name, output_type):
"""SBSAR information"""
click.echo(sbsar_utils.output_info(sbsar_path, sbsar_name, output_type))
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
@builder_tools.command()
@click.option('--sbs_path', default=_PATH_INPUT_SBS, help='Sbs path.')
@click.option('--sbsar_path', default=_PATH_COOK_OUTPUT, help='Sbsar output path.')
def sbs2sbsar(sbs_path, sbsar_path):
""" Cook SBSAR from SBS"""
sbsar_utils.cook_sbsar(sbs_path, sbsar_path)
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
@builder_tools.command()
@click.option('--sbsar_path', default=_PATH_COOK_OUTPUT, help='Sbsar directory.')
@click.option('--sbsar_name', default=_PATH_INPUT_SBS.stem, help='Sbsar name.')
@click.option('--tex_path', default=_PATH_RENDER_OUTPUT, help='Texture output path.')
@click.option('--preset', required=False, help='Preset')
@click.option('--output_size', default=9, help='512x512, 9 | 1024x1024, 10 | 2048x2048, 11')
def render(sbsar_path, sbsar_name, tex_path, preset, output_size):
""" Render textures maps from SBSAR"""
sbsar_utils.render_sbsar(sbsar_path, sbsar_name, tex_path, preset, output_size)
# -------------------------------------------------------------------------
###########################################################################
# Main Code Block, runs this script as main (testing)
# -------------------------------------------------------------------------
if __name__ == '__main__':
"""Run this file as main"""
_LOGGER.debug("{0} :: if __name__ == '__main__':".format(_PACKAGENAME))
_LOGGER.debug(builder_tools())
# remove the logger
del _LOGGER
# ---- END ---------------------------------------------------------------
@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
# !/usr/bin/python
#
# 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.
#
# -------------------------------------------------------------------------
# https://gist.github.com/blubberdiblub/007bb92991d01ad29877931f75260b39
import sys
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QProcess, QTextCodec
from PyQt5.QtGui import QTextCursor
from PyQt5.QtWidgets import QApplication, QPlainTextEdit
class ProcessOutputReader(QProcess):
produce_output = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent=parent)
# merge stderr channel into stdout channel
self.setProcessChannelMode(QProcess.MergedChannels)
# prepare decoding process' output to Unicode
codec = QTextCodec.codecForLocale()
self._decoder_stdout = codec.makeDecoder()
# only necessary when stderr channel isn't merged into stdout:
# self._decoder_stderr = codec.makeDecoder()
self.readyReadStandardOutput.connect(self._ready_read_standard_output)
# only necessary when stderr channel isn't merged into stdout:
# self.readyReadStandardError.connect(self._ready_read_standard_error)
@pyqtSlot()
def _ready_read_standard_output(self):
raw_bytes = self.readAllStandardOutput()
text = self._decoder_stdout.toUnicode(raw_bytes)
self.produce_output.emit(text)
# only necessary when stderr channel isn't merged into stdout:
# @pyqtSlot()
# def _ready_read_standard_error(self):
# raw_bytes = self.readAllStandardError()
# text = self._decoder_stderr.toUnicode(raw_bytes)
# self.produce_output.emit(text)
class MyConsole(QPlainTextEdit):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setReadOnly(True)
self.setMaximumBlockCount(10000) # limit console to 10000 lines
self._cursor_output = self.textCursor()
@pyqtSlot(str)
def append_output(self, text):
self._cursor_output.insertText(text)
self.scroll_to_last_line()
def scroll_to_last_line(self):
cursor = self.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.movePosition(QTextCursor.Up if cursor.atBlockStart() else
QTextCursor.StartOfLine)
self.setTextCursor(cursor)
# create the application instance
app = QApplication(sys.argv)
# create a process output reader
reader = ProcessOutputReader()
# create a console and connect the process output reader to it
console = MyConsole()
reader.produce_output.connect(console.append_output)
reader.start('python', ['-u', 'C:\\dccapi\\dev\\Gems\\DccScriptingInterface\\LyPy\\si_substance\\builder\\watchdog'
'\\__init__.py', 'C:\\Users\\chunghao\\Documents\\Allegorithmic\\Substance Designer'
'\\sbsar']) # start the process
console.show() # make the console visible
app.exec_() # run the PyQt main loop
@@ -0,0 +1,99 @@
# -*- coding: utf-8 -*-
# !/usr/bin/python
#
# 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 sys
from PySide2.QtCore import QProcess, Signal, Slot, QTextCodec
from PySide2.QtGui import QTextCursor
from PySide2.QtWidgets import QApplication, QPlainTextEdit
from PySide2.QtCore import QTimer
class ProcessOutputReader(QProcess):
produce_output = Signal(str)
def __init__(self, parent=None):
super().__init__(parent=parent)
# merge stderr channel into stdout channel
self.setProcessChannelMode(QProcess.MergedChannels)
# prepare decoding process' output to Unicode
self._codec = QTextCodec.codecForLocale()
self._decoder_stdout = self._codec.makeDecoder()
# only necessary when stderr channel isn't merged into stdout:
# self._decoder_stderr = codec.makeDecoder()
self.readyReadStandardOutput.connect(self._ready_read_standard_output)
# only necessary when stderr channel isn't merged into stdout:
# self.readyReadStandardError.connect(self._ready_read_standard_error)
@Slot()
def _ready_read_standard_output(self):
raw_bytes = self.readAllStandardOutput()
text = self._decoder_stdout.toUnicode(raw_bytes)
self.produce_output.emit(text)
# only necessary when stderr channel isn't merged into stdout:
# @Slot()
# def _ready_read_standard_error(self):
# raw_bytes = self.readAllStandardError()
# text = self._decoder_stderr.toUnicode(raw_bytes)
# self.produce_output.emit(text)
class MyConsole(QPlainTextEdit):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setReadOnly(True)
self.setMaximumBlockCount(10000) # limit console to 10000 lines
self._cursor_output = self.textCursor()
@Slot(str)
def append_output(self, text):
self._cursor_output.insertText(text)
self.scroll_to_last_line()
def scroll_to_last_line(self):
cursor = self.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.movePosition(QTextCursor.Up if cursor.atBlockStart() else
QTextCursor.StartOfLine)
self.setTextCursor(cursor)
def output_text(self, text):
self._cursor_output.insertText(text)
self.scroll_to_last_line()
# create the application instance
app = QApplication(sys.argv)
# create a process output reader
reader = ProcessOutputReader()
# create a console and connect the process output reader to it
console = MyConsole()
reader.produce_output.connect(console.append_output)
reader.start('python', ['-u', 'C:\\dccapi\\dev\\Gems\\DccScriptingInterface\\LyPy\\si_substance\\builder\\watchdog'
'\\__init__.py', 'C:\\Users\\chunghao\\Documents\\Allegorithmic\\Substance Designer'
'\\sbsar']) # start the process
console.show() # make the console visible
# app.exec_() # run the PyQt main loop
timer = QTimer()
timer.timeout.connect(lambda: None)
timer.start(100)
sys.exit(app.exec_())
@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
# !/usr/bin/python
#
# 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 os
import sys
from PySide2.QtCore import QFile, QSize
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QSizePolicy
from PySide2.QtWidgets import QMainWindow, QWidget, QVBoxLayout, QHBoxLayout
_MODULE_DIR_PATH = os.path.dirname(os.path.abspath(__file__))
_UI_FILEPATH = "{0}\\\\sbs_builder_widget.ui".format(_MODULE_DIR_PATH)
_PROGRAM_NAME_VERSION = 'Substance Builder'
class UiLoader(QUiLoader):
def __init__(self, baseInstance):
super(UiLoader, self).__init__(baseInstance)
self._baseInstance = baseInstance
def createWidget(self, classname, parent=None, name=""):
widget = super(UiLoader, self).createWidget(
classname, parent, name)
if parent is None:
return self._baseInstance
else:
setattr(self._baseInstance, name, widget)
return widget
class MainWindow(QMainWindow):
def __init__(self, ui_file=_UI_FILEPATH, parent=None):
super().__init__(parent)
# Setup central widget and layout
self.central_widget = QWidget()
self.central_layout = QVBoxLayout(self.central_widget)
self.central_layout.setContentsMargins(8, 8, 8, 8)
self.setCentralWidget(self.central_widget)
self.setup_layout(ui_file)
self.setMinimumSize(QSize(675, 825))
def setup_layout(self, ui_file):
self.layout = QHBoxLayout()
self.widget = MyWidget(ui_file, self)
self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.layout.addWidget(self.widget)
self.central_layout.addLayout(self.layout)
class MyWidget(QWidget):
def __init__(self, ui_file=_UI_FILEPATH, parent=None):
super().__init__(parent)
loader = UiLoader(parent)
file = QFile(ui_file)
file.open(QFile.ReadOnly)
loader.load(file, self)
file.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWindow = MainWindow(_UI_FILEPATH)
mainWindow.setWindowTitle(_PROGRAM_NAME_VERSION)
mainWindow.show()
sys.exit(app.exec_())
@@ -0,0 +1,565 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>560</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>560</height>
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="minimumSize">
<size>
<width>0</width>
<height>240</height>
</size>
</property>
<property name="title">
<string>Watcher</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<widget class="QLineEdit" name="lineEdit">
<property name="geometry">
<rect>
<x>122</x>
<y>44</y>
<width>211</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>Watch folder</string>
</property>
</widget>
<widget class="QToolButton" name="toolButton">
<property name="geometry">
<rect>
<x>340</x>
<y>44</y>
<width>25</width>
<height>19</height>
</rect>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>24</x>
<y>70</y>
<width>92</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Wacher Extensions</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>24</x>
<y>44</y>
<width>64</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Wacher Foler</string>
</property>
</widget>
<widget class="QRadioButton" name="radioButton">
<property name="geometry">
<rect>
<x>24</x>
<y>21</y>
<width>81</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string> On / Off</string>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit_2">
<property name="geometry">
<rect>
<x>122</x>
<y>70</y>
<width>211</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>*.sbsar, *.sbs</string>
</property>
</widget>
<widget class="QTextEdit" name="textEdit">
<property name="geometry">
<rect>
<x>30</x>
<y>100</y>
<width>331</width>
<height>131</height>
</rect>
</property>
</widget>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="minimumSize">
<size>
<width>0</width>
<height>280</height>
</size>
</property>
<property name="title">
<string>SBSAR</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<widget class="QLabel" name="label_9">
<property name="geometry">
<rect>
<x>20</x>
<y>150</y>
<width>70</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>RandomSeed</string>
</property>
</widget>
<widget class="Line" name="line">
<property name="geometry">
<rect>
<x>120</x>
<y>50</y>
<width>20</width>
<height>161</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>240</x>
<y>100</y>
<width>121</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Create .material</string>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_8">
<property name="geometry">
<rect>
<x>140</x>
<y>150</y>
<width>70</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Glossness</string>
</property>
</widget>
<widget class="QLabel" name="label_8">
<property name="geometry">
<rect>
<x>20</x>
<y>100</y>
<width>21</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Size</string>
</property>
</widget>
<widget class="QLabel" name="label_10">
<property name="geometry">
<rect>
<x>240</x>
<y>50</y>
<width>47</width>
<height>13</height>
</rect>
</property>
<property name="text">
<string>Name</string>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>20</x>
<y>30</y>
<width>41</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Preset</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton_2">
<property name="geometry">
<rect>
<x>240</x>
<y>190</y>
<width>121</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Render textures</string>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit_4">
<property name="geometry">
<rect>
<x>20</x>
<y>240</y>
<width>181</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_6">
<property name="geometry">
<rect>
<x>240</x>
<y>30</y>
<width>71</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>Atom Material</string>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_4">
<property name="geometry">
<rect>
<x>140</x>
<y>190</y>
<width>70</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Opacity</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_5">
<property name="geometry">
<rect>
<x>140</x>
<y>170</y>
<width>81</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Roughness</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QComboBox" name="comboBox_2">
<property name="geometry">
<rect>
<x>20</x>
<y>120</y>
<width>101</width>
<height>22</height>
</rect>
</property>
<item>
<property name="text">
<string> 512 * 512</string>
</property>
</item>
<item>
<property name="text">
<string>1024 * 1024</string>
</property>
</item>
<item>
<property name="text">
<string>2048 * 2048</string>
</property>
</item>
</widget>
<widget class="QLineEdit" name="lineEdit_3">
<property name="geometry">
<rect>
<x>240</x>
<y>70</y>
<width>121</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>140</x>
<y>30</y>
<width>41</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Output</string>
</property>
</widget>
<widget class="QComboBox" name="comboBox">
<property name="geometry">
<rect>
<x>20</x>
<y>50</y>
<width>101</width>
<height>22</height>
</rect>
</property>
<item>
<property name="text">
<string>Grey Stone</string>
</property>
</item>
<item>
<property name="text">
<string>Red Coral</string>
</property>
</item>
<item>
<property name="text">
<string>Red Stone</string>
</property>
</item>
</widget>
<widget class="QToolButton" name="toolButton_2">
<property name="geometry">
<rect>
<x>210</x>
<y>240</y>
<width>25</width>
<height>19</height>
</rect>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
<widget class="Line" name="line_4">
<property name="geometry">
<rect>
<x>10</x>
<y>210</y>
<width>361</width>
<height>20</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_7">
<property name="geometry">
<rect>
<x>140</x>
<y>90</y>
<width>70</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Specular</string>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_2">
<property name="geometry">
<rect>
<x>140</x>
<y>110</y>
<width>70</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Metallic</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QCheckBox" name="checkBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>140</x>
<y>50</y>
<width>71</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Base Color</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QPushButton" name="pushButton_3">
<property name="geometry">
<rect>
<x>240</x>
<y>240</y>
<width>121</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Bake SBSAR from SBS</string>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_3">
<property name="geometry">
<rect>
<x>140</x>
<y>130</y>
<width>81</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Normal map</string>
</property>
</widget>
<widget class="Line" name="line_2">
<property name="geometry">
<rect>
<x>220</x>
<y>50</y>
<width>20</width>
<height>161</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
<widget class="QLabel" name="label_7">
<property name="geometry">
<rect>
<x>20</x>
<y>80</y>
<width>91</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Parameters</string>
</property>
</widget>
<widget class="QSpinBox" name="spinBox">
<property name="geometry">
<rect>
<x>20</x>
<y>170</y>
<width>101</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_6">
<property name="geometry">
<rect>
<x>140</x>
<y>70</y>
<width>70</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Albedo</string>
</property>
</widget>
</widget>
</item>
<item row="1" column="0">
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>toolButton</sender>
<signal>pressed()</signal>
<receiver>lineEdit</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>362</x>
<y>63</y>
</hint>
<hint type="destinationlabel">
<x>237</x>
<y>63</y>
</hint>
</hints>
</connection>
</connections>
</ui>
@@ -0,0 +1,494 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>651</width>
<height>807</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Watcher</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QRadioButton" name="radioButton_2">
<property name="text">
<string> On / Off</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Wacher Foler</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_6">
<property name="text">
<string>Watch folder</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="toolButton_3">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Wacher Extensions</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEdit_7">
<property name="text">
<string>*.sbsar, *.sbs</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="textEdit_2"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Sbsar</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTableWidget" name="tableWidget_fileslist">
<property name="columnCount">
<number>1</number>
</property>
<attribute name="horizontalHeaderVisible">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>100</number>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>20</number>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<row>
<property name="text">
<string>Item #01</string>
</property>
<property name="font">
<font>
<underline>false</underline>
<strikeout>false</strikeout>
<stylestrategy>PreferDefault</stylestrategy>
</font>
</property>
</row>
<row>
<property name="text">
<string>Item #02</string>
</property>
</row>
<row>
<property name="text">
<string>Item #03</string>
</property>
</row>
<row>
<property name="text">
<string>Item #04</string>
</property>
</row>
<row>
<property name="text">
<string>Item #05</string>
</property>
</row>
<row>
<property name="text">
<string>Item #06</string>
</property>
</row>
<row>
<property name="text">
<string>Item #07</string>
</property>
</row>
<row>
<property name="text">
<string>Item #08</string>
</property>
</row>
<column>
<property name="text">
<string>File Path</string>
</property>
</column>
<item row="0" column="0">
<property name="text">
<string>C:\Users\chunghao\Documents\Allegorithmic\Substance Designer\sbsar\item01.sbsar</string>
</property>
</item>
<item row="1" column="0">
<property name="text">
<string>C:\Users\chunghao\Documents\Allegorithmic\Substance Designer\sbsar\item02.sbsar</string>
</property>
</item>
<item row="2" column="0">
<property name="text">
<string>C:\Users\chunghao\Documents\Allegorithmic\Substance Designer\sbsar\item03.sbsar</string>
</property>
</item>
<item row="3" column="0">
<property name="text">
<string>C:\Users\chunghao\Documents\Allegorithmic\Substance Designer\sbsar\item04.sbsar</string>
</property>
</item>
<item row="4" column="0">
<property name="text">
<string>C:\Users\chunghao\Documents\Allegorithmic\Substance Designer\sbsar\item05.sbsar</string>
</property>
</item>
<item row="5" column="0">
<property name="text">
<string>C:\Users\chunghao\Documents\Allegorithmic\Substance Designer\sbsar\item06.sbsar</string>
</property>
</item>
<item row="6" column="0">
<property name="text">
<string>C:\Users\chunghao\Documents\Allegorithmic\Substance Designer\sbsar\item07.sbsar</string>
</property>
</item>
<item row="7" column="0">
<property name="text">
<string>C:\Users\chunghao\Documents\Allegorithmic\Substance Designer\sbsar\item08.sbsar</string>
</property>
</item>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox_parameters">
<property name="title">
<string>Parameters</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QLabel" name="label_14">
<property name="text">
<string>Preset</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_15">
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_16">
<property name="text">
<string>RandomSeed</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QComboBox" name="comboBox_3">
<item>
<property name="text">
<string>Grey Stone</string>
</property>
</item>
<item>
<property name="text">
<string>Red Coral</string>
</property>
</item>
<item>
<property name="text">
<string>Red Stone</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox_4">
<item>
<property name="text">
<string> 512 * 512</string>
</property>
</item>
<item>
<property name="text">
<string>1024 * 1024</string>
</property>
</item>
<item>
<property name="text">
<string>2048 * 2048</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_2"/>
</item>
</layout>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Output">
<property name="title">
<string>Output</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QCheckBox" name="checkBox_9">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Base Color</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_10">
<property name="text">
<string>Albedo</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_11">
<property name="text">
<string>Specular</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_12">
<property name="text">
<string>Metallic</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_13">
<property name="text">
<string>Normal map</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_14">
<property name="text">
<string>Glossness</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_15">
<property name="text">
<string>Roughness</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_16">
<property name="text">
<string>Opacity</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Material">
<property name="title">
<string>Atom Material</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_4"/>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>Create .material</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_4">
<property name="text">
<string>Render textures</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="lineEdit_5"/>
</item>
<item>
<widget class="QToolButton" name="toolButton_2">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_5">
<property name="text">
<string>Bake SBSAR from SBS</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,241 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>461</width>
<height>502</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Watcher</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QRadioButton" name="radioButton_2">
<property name="text">
<string> On / Off</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Wacher Foler</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_6">
<property name="text">
<string>Watch folder</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="toolButton_3">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Wacher Extensions</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEdit_7">
<property name="text">
<string>*.sbsar, *.sbs</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="textEdit_2"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Sbsar</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox_parameters">
<property name="title">
<string>Parameters</string>
</property>
<layout class="QGridLayout" name="gridLayout_2"/>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Output">
<property name="title">
<string>Output</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QCheckBox" name="checkBox_9">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Base Color</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_10">
<property name="text">
<string>Albedo</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_11">
<property name="text">
<string>Specular</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_12">
<property name="text">
<string>Metallic</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_13">
<property name="text">
<string>Normal map</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_14">
<property name="text">
<string>Glossness</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_15">
<property name="text">
<string>Roughness</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_16">
<property name="text">
<string>Opacity</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Material">
<property name="title">
<string>Atom Material</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="lineEdit_5"/>
</item>
<item>
<widget class="QToolButton" name="toolButton_2">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_5">
<property name="text">
<string>Bake SBSAR from SBS</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,341 @@
# -*- coding: utf-8 -*-
# !/usr/bin/python
#
# 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 sys, os
from PySide2 import QtCore, QtWidgets
from PySide2.QtCore import QTimer
from PySide2.QtCore import QProcess, Signal, Slot, QTextCodec
from PySide2.QtGui import QTextCursor
from PySide2.QtWidgets import QApplication, QPlainTextEdit
sys.path.insert(0, os.path.abspath('../..'))
import builder
# sys.path.insert(0, os.path.abspath('../ui/'))
# import main_win
sys.path.insert(0, os.path.abspath('../watchdog/'))
import watchdog
class Window(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.browseButton = self.create_button("&Browse...", self.browse)
self.findButton = self.create_button("&Find", self.find)
self.removeButton = self.create_button("&Remove", self.remove)
self.startWatch = self.create_button("&Start Watch", self.watch)
self.killButton = self.create_button("&Stop Watch", self.kill)
self.fileComboBox = self.createComboBox("*")
self.textComboBox = self.createComboBox()
# self.directoryComboBox = self.createComboBox(QtCore.QDir.currentPath())
self.sbsarDirectory = "C:/Users/chunghao/Documents/Allegorithmic/Substance Designer/sbsar"
self.directoryComboBox = self.createComboBox(self.sbsarDirectory)
self.extensionLabel = QtWidgets.QLabel("Watch extensions:")
# textLabel = QtWidgets.QLabel("Containing text:")
self.directoryLabel = QtWidgets.QLabel("Watch directory:")
self.filesFoundLabel = QtWidgets.QLabel()
self.wacherLogLabel = QtWidgets.QLabel("Watcher Log")
self.fileListLabel = QtWidgets.QLabel("File List")
self.sbsarPresetLabel = QtWidgets.QLabel("Sbsar Preset")
self.OutputResLabel = QtWidgets.QLabel("Output Size")
self.OutputsLabel = QtWidgets.QLabel("Outputs")
self.watcher_log = QPlainTextEdit()
self.watcher_log.setReadOnly(True)
self.watcher_log.setMaximumBlockCount(10000) # limit console to 10000 lines
self.watcher_log._cursor_output = self.watcher_log.textCursor()
self.create_files_table()
buttonsLayout = QtWidgets.QHBoxLayout()
buttonsLayout.addStretch()
buttonsLayout.addWidget(self.findButton)
# buttonsLayout.addWidget(self.removeButton)
buttonsLayout.addWidget(self.startWatch)
buttonsLayout.addWidget(self.killButton)
self.mainLayout = QtWidgets.QGridLayout()
self.mainLayout.addWidget(self.extensionLabel, 0, 0)
self.mainLayout.addWidget(self.fileComboBox, 0, 1, 1, 2)
# self.mainLayout.addWidget(textLabel, 1, 0)
# self.mainLayout.addWidget(self.textComboBox, 1, 1, 1, 2)
self.mainLayout.addWidget(self.directoryLabel, 2, 0)
self.mainLayout.addWidget(self.wacherLogLabel, 3, 0)
self.mainLayout.addWidget(self.fileListLabel, 4, 0)
self.mainLayout.addWidget(self.directoryComboBox, 2, 1)
self.mainLayout.addWidget(self.browseButton, 2, 2)
self.mainLayout.addWidget(self.watcher_log, 3, 1, 1, 3)
self.mainLayout.addWidget(self.filesTable, 4, 1, 1, 3)
self.mainLayout.addWidget(self.filesFoundLabel, 5, 1)
self.mainLayout.addLayout(buttonsLayout, 6, 0, 1, 3)
self.setLayout(self.mainLayout)
self.setWindowTitle("Substance Builder")
self.resize(800, 300)
self.path = self.directoryComboBox.currentText()
self.groupbox_outputs = QtWidgets.QGroupBox("Outputs")
self.watcher_script = "C:/dccapi/dev/Gems/DccScriptingInterface/LyPy/si_substance/builder/watchdog/__init__.py"
@Slot(str)
def append_output(self, text):
self.watcher_log._cursor_output.insertText(text)
self.scroll_to_last_line()
def scroll_to_last_line(self):
cursor = self.watcher_log.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.movePosition(QTextCursor.Up if cursor.atBlockStart() else
QTextCursor.StartOfLine)
self.watcher_log.setTextCursor(cursor)
def output_text(self, text):
self.watcher_log._cursor_output.insertText(text)
self.watcher_log.scroll_to_last_line()
def browse(self):
# self.directory = QtWidgets.QFileDialog.getExistingDirectory(self, "Find Files",
# QtCore.QDir.currentPath())
self.directory = QtWidgets.QFileDialog.getExistingDirectory(self, "Find watcher folder",
"C:/Users/chunghao/Documents/Allegorithmic/Substance Designer/sbsar")
if self.directory:
if self.directoryComboBox.findText(self.directory) == -1:
self.directoryComboBox.addItem(self.directory)
self.directoryComboBox.setCurrentIndex(self.directoryComboBox.findText(self.directory))
self.path = self.directory
self.find()
return self.path
@staticmethod
def updateComboBox(comboBox):
if comboBox.findText(comboBox.currentText()) == -1:
comboBox.addItem(comboBox.currentText())
def find(self):
self.filesTable.setRowCount(0)
fileName = self.fileComboBox.currentText()
text = self.textComboBox.currentText()
self.path = self.directoryComboBox.currentText()
# self.path = "C:/Users/chunghao/Documents/Allegorithmic/Substance Designer/sbsar"
self.updateComboBox(self.fileComboBox)
self.updateComboBox(self.textComboBox)
self.updateComboBox(self.directoryComboBox)
self.currentDir = QtCore.QDir(self.path)
if not fileName:
fileName = "*"
files = self.currentDir.entryList([fileName],
QtCore.QDir.Files | QtCore.QDir.NoSymLinks)
if text:
files = self.find_files(files, text)
self.show_files(files)
return self.path
def find_files(self, files, text):
progressDialog = QtWidgets.QProgressDialog(self)
progressDialog.setCancelButtonText("&Cancel")
progressDialog.setRange(0, len(files))
progressDialog.setWindowTitle("Find Files")
foundFiles = []
for i in range(len(files)):
progressDialog.setValue(i)
progressDialog.setLabelText("Searching file number %d of %d..." % (i, len(files)))
QtCore.qApp.processEvents()
if progressDialog.wasCanceled():
break
inFile = QtCore.QFile(self.currentDir.absoluteFilePath(files[i]))
if inFile.open(QtCore.QIODevice.ReadOnly):
stream = QtCore.QTextStream(inFile)
while not stream.atEnd():
if progressDialog.wasCanceled():
break
line = stream.readLine()
if text in line:
foundFiles.append(files[i])
break
progressDialog.close()
return foundFiles
def show_files(self, files):
for fn in files:
# file = QtCore.QFile(self.currentDir.absoluteFilePath(fn))
# size = QtCore.QFileInfo(file).size()
fileNameItem = QtWidgets.QTableWidgetItem(fn)
fileNameItem.setFlags(fileNameItem.flags() ^ QtCore.Qt.ItemIsEditable)
# sizeItem = QtWidgets.QTableWidgetItem("%d KB" % (int((size + 1023) / 1024)))
# sizeItem.setTextAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
# sizeItem.setFlags(sizeItem.flags() ^ QtCore.Qt.ItemIsEditable)
row = self.filesTable.rowCount()
self.filesTable.insertRow(row)
self.filesTable.setItem(row, 0, fileNameItem)
# self.filesTable.setItem(row, 1, sizeItem)
self.filesFoundLabel.setText("%d file(s) found (Double click on a file to open it)" % len(files))
def create_button(self, text, member):
button = QtWidgets.QPushButton(text)
button.clicked.connect(member)
return button
def createComboBox(self, text=""):
comboBox = QtWidgets.QComboBox()
comboBox.setEditable(True)
comboBox.addItem(text)
comboBox.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Preferred)
return comboBox
def create_files_table(self):
self.filesTable = QtWidgets.QTableWidget(0, 1)
self.filesTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.filesTable.setHorizontalHeaderLabels(("File Name", ""))
self.filesTable.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
# self.filesTable.verticalHeader().hide()
self.filesTable.verticalHeader().show()
self.filesTable.setShowGrid(True)
self.filesTable.cellActivated.connect(self.open_fileOfitem)
def create_sbsar_params(self):
self.mainLayout.addWidget(self.OutputsLabel, 9, 0)
self.sbsarName = self.item.text().replace(".sbsar", "")
self.groupbox_outputs = QtWidgets.QGroupBox("Outputs")
self.horizontalLayoutOuputs = QtWidgets.QHBoxLayout(self.groupbox_outputs)
self.horizontalLayoutOuputs.setObjectName(("horizontalLayoutOuputs"))
# self.vertical_layout_outputs = QtWidgets.QVBoxLayout(self.groupbox_outputs)
# self.vertical_layout_outputs.setObjectName("vertical_layout_outputs")
self.sbsar_tex_outputs = builder.output_info(self.path, self.sbsarName)['_outputs']
for index, tex in enumerate(self.sbsar_tex_outputs):
print(tex, end=" ")
self.sbsar_tex_outputs[index] = QtWidgets.QCheckBox(self.groupbox_outputs)
self.sbsar_tex_outputs[index].setEnabled(True)
self.sbsar_tex_outputs[index].setChecked(True)
self.sbsar_tex_outputs[index].setObjectName(self.sbsarName+"_"+tex)
self.sbsar_tex_outputs[index].setText(tex)
self.horizontalLayoutOuputs.addWidget(self.sbsar_tex_outputs[index])
self.mainLayout.addWidget(self.groupbox_outputs, 9, 1, 1, 3)
print("\n")
def create_sbsar_presets(self):
self.mainLayout.addWidget(self.sbsarPresetLabel, 7, 0)
self.sbsar_presets = builder.output_info(self.path, self.item.text().replace(".sbsar", ""))['_presets']
self.comboBox_SbsarPreset = QtWidgets.QComboBox(self)
for self.presetItem in self.sbsar_presets:
print(self.presetItem, end=", ")
preset_index = 0
self.comboBox_SbsarPreset.addItem(self.presetItem)
self.comboBox_SbsarPreset.setItemText(preset_index, self.presetItem)
preset_index += 1
self.mainLayout.addWidget(self.comboBox_SbsarPreset, 7, 1, 1, 3)
print("\n")
self.comboBox_SbsarPreset.activated[str].connect(self.preset_selected)
def preset_selected(self, text):
self.selected_text = text
print(self.selected_text)
def create_tex_res(self):
self.mainLayout.addWidget(self.OutputResLabel, 8, 0)
self.resSelector = QtWidgets.QComboBox(self)
self.res = ["512x512", "1024x1024", "2048x2048"]
self.resSelector.addItems(self.res)
self.mainLayout.addWidget(self.resSelector, 8, 1, 1, 3)
self.resSelector.activated[str].connect(self.res_selected)
def res_selected(self):
if self.resSelector.currentIndex() == 0: print("$outputsize@9,9")
elif self.resSelector.currentIndex() == 1: print("$outputsize@10,10")
elif self.resSelector.currentIndex() == 2: print("$outputsize@11,11")
def remove(self):
self.mainLayout.removeWidget(self.groupbox_outputs)
self.groupbox_outputs.close()
def kill(self):
reader.kill()
def watch(self):
reader.start('python', ['-u', window.watcher_script, window.sbsarDirectory])
def open_fileOfitem(self, row, column):
self.item = self.filesTable.item(row, 0)
print(self.path+"/"+self.item.text())
if self.item.text().split(".")[-1] == "sbsar":
self.create_sbsar_presets()
self.create_tex_res()
self.mainLayout.removeWidget(self.groupbox_outputs)
self.groupbox_outputs.close()
self.create_sbsar_params()
elif self.item.text().split(".")[-1] == "sbs":
print("This is a Substance File")
builder.cook_sbsar(self.path+"/"+self.item.text(), builder._context, self.path, self.item.text().split(".")[0])
# builder.output_info()
class ProcessOutputReader(QProcess):
produce_output = Signal(str)
def __init__(self, parent=None):
super().__init__(parent=parent)
# merge stderr channel into stdout channel
self.setProcessChannelMode(QProcess.MergedChannels)
# prepare decoding process' output to Unicode
self._codec = QTextCodec.codecForLocale()
self._decoder_stdout = self._codec.makeDecoder()
# only necessary when stderr channel isn't merged into stdout:
# self._decoder_stderr = codec.makeDecoder()
self.readyReadStandardOutput.connect(self._ready_read_standard_output)
# only necessary when stderr channel isn't merged into stdout:
# self.readyReadStandardError.connect(self._ready_read_standard_error)
@Slot()
def _ready_read_standard_output(self):
raw_bytes = self.readAllStandardOutput()
text = self._decoder_stdout.toUnicode(raw_bytes)
self.produce_output.emit(text)
# only necessary when stderr channel isn't merged into stdout:
# @Slot()
# def _ready_read_standard_error(self):
# raw_bytes = self.readAllStandardError()
# text = self._decoder_stderr.toUnicode(raw_bytes)
# self.produce_output.emit(text)
if __name__ == '__main__':
app = QApplication(sys.argv)
reader = ProcessOutputReader()
window = Window()
reader.produce_output.connect(window.append_output)
reader.start('python', ['-u', window.watcher_script, window.sbsarDirectory])
window.show()
timer = QTimer()
timer.timeout.connect(lambda: None)
timer.start(10)
# sys.exit(reader.kill())
sys.exit(app.exec_())
@@ -0,0 +1,67 @@
/*
* 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.
*
*/
/* ============================================================= */
/* Global styles */
/* ============================================================= */
*
{
background-color: #444444;
color: white;
font-family: "Amazon Ember";
font-weight: 400;
}
QMainWindow,
QDialog,
QDockWidget
{
color: #cccccc;
background-color: #393a3c;
}
QTextEdit,
QPlainText,
QLineEdit,
QSpinBox,
QDoubleSpinBox,
QComboBox
{
background-color: #e9e9e9;
color: black;
font-family: "Amazon Ember";
border-radius: 2px;
border-width: 0px;
font-size: 12px;
line-height: 16px;
}
@import "Text.qss";
@import "PushButton.qss";
@import "Menu.qss";
@import "CheckBox.qss";
@import "RadioButton.qss";
@import "ToolTip.qss";
@import "ProgressBar.qss";
@import "ColorPicker.qss";
@import "Slider.qss";
@import "Card.qss";
@import "BrowseEdit.qss";
@import "BreadCrumbs.qss";
@import "LineEdit.qss";
@import "ComboBox.qss";
@import "SegmentControl.qss";
@import "SpinBox.qss";
@import "ScrollBar.qss";
@@ -0,0 +1,22 @@
/*
* 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.
*
*/
/*
BreadCrumbs are QWidgets with a QHBoxLayout with 0 content margins and one QLabel child widget.
*/
AzQtComponents--BreadCrumbs
{
}
@@ -0,0 +1,61 @@
/*
* 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.
*
*/
AzQtComponents--BrowseEdit
{
border-color: transparent;
border-width: 2px;
border-style: solid;
border-radius: 3px;
}
AzQtComponents--BrowseEdit QLineEdit
{
height: 16px;
border-width: 0px;
border-style: solid;
border-color: #444444;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
}
AzQtComponents--BrowseEdit QLineEdit:focus
{
background-color: #FFFFFF;
border-color: #FFFFFF;
}
AzQtComponents--BrowseEdit QLineEdit:disabled
{
background-color: #777777;
border-color: #777777;
color: #999999;
}
AzQtComponents--BrowseEdit QToolButton
{
max-width: 15px;
min-width: 15px;
max-height: 14px;
min-height: 14px;
background-color: #444444;
border-color: #000000;
border-width: 1px;
border-style: solid;
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
}
@@ -0,0 +1,154 @@
/*
* 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.
*
*/
AzQtComponents--Card
{
margin: 0px;
padding: 0px;
border: 1px solid rgb(33, 34, 35);
border-radius: 2px;
}
AzQtComponents--CardNotification
{
background-color: #1AFDC32D;
margin: 2px;
border: 1px solid #FDC32D;
border-radius: 2px;
}
AzQtComponents--CardNotification #HeaderFrame,
AzQtComponents--CardNotification #Icon,
AzQtComponents--CardNotification #Title
{
background-color: transparent;
border: none;
}
/**
* CardHeader is a QFrame with a QHBoxLayout that contains the following children:
* #Expander: a QCheckBox used as a "tree expander".
* #Icon: a QLabel displaying an icon. The icon is set from c++.
* #Title: a QLabel displaying text.
* #ContextMenu: a QPushButton used to launch a context menu.
*/
AzQtComponents--CardHeader
{
margin: 0px;
padding: 0px;
border: none;
}
AzQtComponents--CardHeader #Background
{
margin: 2px;
padding: 0px;
border: none;
background-image: none;
background-color: transparent;
}
.primaryCardHeader
{
background-color: #222222;
}
.secondaryCardHeader
{
background-color: transparent;
}
AzQtComponents--CardHeader #Background[readOnly=true]
{
background-repeat: repeat-xy;
}
AzQtComponents--CardHeader #Background[readOnly=false]
{
background-image: none;
}
AzQtComponents--CardHeader #Title
{
font-weight: bold;
background-color: transparent;
}
AzQtComponents--CardHeader #Expander
{
width: 16px;
height: 16px;
qproperty-flat: true;
border: none;
color: transparent;
background-color: transparent;
selection-color: transparent;
selection-background-color: transparent;
image: none;
}
AzQtComponents--CardHeader #Expander:enabled:checked
{
image: url(:/Cards/img/UI20/Cards/group_open.png);
}
AzQtComponents--CardHeader #Expander:enabled:!checked
{
image: url(:/Cards/img/UI20/Cards/group_closed.png);
}
AzQtComponents--CardHeader #Icon,
AzQtComponents--CardHeader #WarningIcon
{
width: 16px;
height: 16px;
border: none;
background-color: transparent;
}
AzQtComponents--CardHeader #ContextMenu
{
qproperty-flat: true;
border: none;
color: transparent;
background-color: transparent;
selection-color: transparent;
selection-background-color: transparent;
image: url(:/Cards/img/UI20/Cards/menu_ico.png);
}
AzQtComponents--CardHeader #Help
{
qproperty-flat: true;
border: none;
color: transparent;
background-color: transparent;
selection-color: transparent;
selection-background-color: transparent;
image: url(:/Cards/img/UI20/Cards/help.png);
}
.separator
{
color: #999999;
}
AzQtComponents--Card #SeparatorContainer
{
margin-left: 2px;
margin-right: 2px;
border: none;
}
@@ -0,0 +1,106 @@
/*
* 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.
*
*/
QCheckBox
{
font-size: 12px;
line-height: 18px;
color: rgb(204, 204, 204);
outline: 0;
}
QCheckBox::indicator:!focus {
width: 14px;
height: 14px;
border: 1px solid white;
border-radius: 2px;
margin: 2px;
}
QCheckBox::indicator:focus {
width: 16px;
height: 16px;
border: 2px solid rgb(201, 170, 254);
border-radius: 4px;
margin: 0px;
}
QCheckBox::indicator:disabled {
border-color: rgb(128, 128, 128);
}
QCheckBox::indicator:indeterminate:!focus {
image: url(../builder/ui/stylesheets/img/UI20/combo-tristate.png);
}
QCheckBox::indicator:indeterminate:focus {
image: url(../builder/ui/stylesheets/img/UI20/combo-tristate-focus.png);
}
QCheckBox::indicator:checked:!focus {
image: url(../builder/ui/stylesheets/img/UI20/combo-checked.png);
}
QCheckBox::indicator:checked:focus {
image: url(../builder/ui/stylesheets/img/UI20/combo-checked-focus.png);
}
QCheckBox::indicator:checked:disabled {
image: url(../builder/ui/stylesheets/img/UI20/combo-checked-disabled.png);
}
QCheckBox[class="ToggleSwitch"]::indicator
{
width: 32px;
height: 16px;
border-width: 0px;
margin: 2px;
}
QCheckBox[class="ToggleSwitch"]::indicator:Unchecked
{
image: url(../builder/ui/stylesheets/img/UI20/toggle-unchecked.png);
}
QCheckBox[class="ToggleSwitch"]::indicator:disabled
{
image: url(../builder/ui/stylesheets/img/UI20/toggle-unchecked-disabled.png);
}
QCheckBox[class="ToggleSwitch"]::indicator:Unchecked:focus
{
width: 36px;
height: 20px;
border-width: 0px;
margin: 0px;
image: url(../builder/ui/stylesheets/img/UI20/toggle-unchecked-focus.png);
}
QCheckBox[class="ToggleSwitch"]::indicator:Checked
{
image: url(../builder/ui/stylesheets/img/UI20/toggle-checked.png);
}
QCheckBox[class="ToggleSwitch"]::indicator:Checked:disabled
{
image: url(../builder/ui/stylesheets/img/UI20/toggle-checked-disabled.png);
}
QCheckBox[class="ToggleSwitch"]::indicator:Checked:focus
{
width: 36px;
height: 20px;
border-width: 0px;
margin: 0px;
image: url(../builder/ui/stylesheets/img/UI20/toggle-checked-focus.png);
}
@@ -0,0 +1,131 @@
/*
* 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.
*
*/
AzQtComponents--ColorPicker
{
padding: 100px;
}
AzQtComponents--ColorPicker *
{
font-size: 12px;
}
AzQtComponents--ColorPicker QTextEdit,
AzQtComponents--ColorPicker QPlainText,
AzQtComponents--ColorPicker QLineEdit,
AzQtComponents--ColorPicker QSpinBox,
AzQtComponents--ColorPicker QDoubleSpinBox
{
padding-bottom: -2px;
}
AzQtComponents--ColorPicker QToolButton
{
border: none;
max-width: 16px;
min-width: 16px;
max-height: 16px;
min-height: 16px;
background-color: transparent;
}
AzQtComponents--ColorPicker QLabel,
AzQtComponents--ColorPicker QCheckBox,
AzQtComponents--PaletteView,
AzQtComponents--ColorPicker #Container
{
background-color: transparent;
}
AzQtComponents--ColorPicker QScrollArea
{
border: none;
background-color: transparent;
}
AzQtComponents--ColorPicker QLabel
{
padding: 0px;
margin: 0px;
}
AzQtComponents--PaletteCard AzQtComponents--CardHeader
{
background-color: transparent;
}
AzQtComponents--PaletteCard AzQtComponents--CardHeader #Title
{
font-weight: normal;
}
AzQtComponents--PaletteCard[modified="true"] AzQtComponents--CardHeader #Title
{
color: #ffa500;
}
AzQtComponents--PaletteCard AzQtComponents--CardHeader #Expander:enabled:checked
{
image: url(:/ColorPickerDialog/PaletteCard/open.png);
}
AzQtComponents--PaletteCard AzQtComponents--CardHeader #Expander:enabled:!checked
{
image: url(:/ColorPickerDialog/PaletteCard/closed.png);
}
AzQtComponents--ColorPicker QSpinBox,
AzQtComponents--ColorPicker QDoubleSpinBox
{
padding-left: 0px;
}
AzQtComponents--ColorPicker QSpinBox::down-button,
AzQtComponents--ColorPicker QSpinBox::up-button,
AzQtComponents--ColorPicker QDoubleSpinBox::down-button,
AzQtComponents--ColorPicker QDoubleSpinBox::up-button
{
width: 0px;
border-style: none;
}
AzQtComponents--ColorPicker .HorizontalSeparator
{
border-top: 1px solid #393a3c;
border-bottom: 1px solid #393a3c;
border-left: none;
border-right: none;
background: #222222;
}
AzQtComponents--ColorPicker AzQtComponents--PaletteView
{
border: none;
}
AzQtComponents--ColorGrid,
AzQtComponents--ColorPreview,
AzQtComponents--Swatch
{
/* Note: Swatches will ignore any 'background*' property set here */
border: 1px solid #333333;
border-radius: 2px;
}
AzQtComponents--Swatch:selected
{
/* Note: Swatches will ignore any 'background*' property set here */
border: 2px solid #C8ABFF;
}
@@ -0,0 +1,111 @@
/*
* 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.
*
*/
/* NOTE: a number of QComboBox properties are defined in BaseStyleSheet.qss already, along with other text entry controls */
/* ComboBox */
QComboBox
{
height: 16px;
padding-left: 8px;
margin: 4px;
font-size: 12px;
}
QComboBox:focus
{
background-color: #FFFFFF;
margin: 2px;
border-width: 2px;
border-style: solid;
border-radius: 4px;
border-color: #C09EFF;
}
QComboBox:disabled
{
background-color: #777777;
color: #999999;
}
QComboBox::drop-down
{
border-width: 0;
}
QComboBox::down-arrow
{
image: url("../builder/ui/stylesheets/img/triangle3.png");
}
QComboBox::down-arrow:disabled
{
image: url("../builder/ui/stylesheets/img/triangle1.png");
}
/* Popup */
QComboBox QAbstractItemView
{
background-color: #222222;
padding: 4px 0px 4px 0px;
border-radius: 2px;
outline: none; /* Disable focus rect */
show-decoration-selected: 1;
}
QComboBox QAbstractItemView::item
{
color: #FFFFFF;
padding-top: 6px;
padding-bottom: 6px;
padding-right: 24px;
padding-left: 2px; /* There is already 22px from the check mark */
/* Without that the padding don't apply */
border: 0px solid transparent;
}
/*
This is needed as a result of using padding / border in ::item as
selection-background-color and selection-color are no more honored...
*/
QComboBox QAbstractItemView::item:selected
{
background-color: #444444;
color: #FFFFFF;
}
QComboBox QAbstractItemView::item:disabled
{
color: #555555;
}
QComboBox QAbstractItemView::separator
{
height: 1px;
background: #444444;
}
QComboBox QAbstractItemView::indicator
{
/* Keep in sync with the check mark image size */
width: 12px;
height: 9px;
image: none;
}
QComboBox QAbstractItemView::indicator:checked
{
image: url(../builder/ui/stylesheets/img/menu-check.png);
}
@@ -0,0 +1,36 @@
/*
* 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.
*
*/
/* NOTE: a number of QLineEdit properties are defined in BaseStyleSheet.qss already, along with other text entry controls */
QLineEdit
{
height: 16px;
padding-left: 8px;
border-width: 2px;
border-style: solid;
}
QLineEdit[HasSearchAction=true] {
padding-left: 4px;
}
QLineEdit:focus
{
background-color: #FFFFFF;
}
QLineEdit:disabled
{
background-color: #777777;
color: #999999;
}
@@ -0,0 +1,53 @@
/*
* 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.
*
*/
QMenuBar::item:selected
{
background: #222222;
}
QMenu
{
background-color: #222222;
color: #FFFFFF;
padding: 4px 0px 4px 0px;
/* Note: with QMenu, only 'margin' works, not 'margin-bottom' or 'margin-top'. */
}
QMenu::item
{
color: #FFFFFF;
font-size: 12px;
padding-top: 6px;
padding-bottom: 6px;
padding-right: 24px;
padding-left: 24px;
}
QMenu::item:selected
{
background-color: #444444;
}
QMenu::item:disabled
{
color: #555555;
}
QMenu::separator
{
height: 1px;
background: #444444;
}
@@ -0,0 +1,28 @@
/*
* 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.
*
*/
QProgressBar
{
border: 0px transparent #000000;
border-radius: 0px;
background: #888888;
height: 7px;
font-size: 1px; /* hack to get around the fact that QProgressBar's minimumSizeHint
is computed from font size, even when text is invisible */
qproperty-textVisible: False;
}
QProgressBar::chunk
{
background: #B48BFF;
}
@@ -0,0 +1,80 @@
/*
* 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.
*
*/
/* =============================================================
Push Buttons
Painting of the background and the frame of PushButtons
are done in code, in PushButton.cpp.
It's done there because the default Qt rendering, or at least
the Fusion style, which we use as our base style, doesn't
properly anti-alias and position the button bounding box
when we customize the background colors and styles here. Also,
there's literally no way to get the colors and styling info out
of the stylesheet.
Below is the configuration of the text colors for the
PushButtons and the font size, which can be done here in the
stylesheet.
Everything else can be configured via PushButton.ini
============================================================= */
QPushButton
{
font-size: 12px;
color: white;
font-family: "Amazon Ember";
}
QPushButton:hover,
QPushButton:pressed
{
color: white;
}
QPushButton:disabled
{
color: #C8C8C8;
}
QPushButton.Primary,
QPushButton:default,
QToolButton::menu-indicator,
QToolButton.SmallIcon::menu-indicator
{
color: white;
}
QPushButton.Primary:disabled,
QPushButton:default:disabled,
QToolButton.SmallIcon:disabled::menu-indicator
{
color: #C8C8C8;
}
QPushButton::menu-indicator
{
subcontrol-position: right center;
subcontrol-origin: padding;
left: -8px;
image: url("../builder/ui/stylesheets/img/triangle1.png")
}
QPushButton.SmallIcon
{
margin: 0px;
}
@@ -0,0 +1,67 @@
/*
* 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.
*
*/
QRadioButton
{
font-size: 12px;
line-height: 18px;
color: rgb(204, 204, 204);
outline: 0;
}
QRadioButton::indicator:!focus
{
width: 16px;
height: 16px;
margin: 2px;
}
QRadioButton::indicator:focus
{
width: 20px;
height: 20px;
margin: 0px;
}
QRadioButton::indicator:checked
{
image: url(../builder/ui/stylesheets/img/UI20/radio-checked.png);
}
QRadioButton::indicator:checked:disabled
{
image: url(../builder/ui/stylesheets/img/UI20/radio-checked-disabled.png);
}
QRadioButton::indicator:checked:focus
{
image: url(../builder/ui/stylesheets/img/UI20/radio-checked-focus.png);
}
QRadioButton::indicator:unchecked
{
image: url(../builder/ui/stylesheets/img/UI20/radio-unchecked.png);
}
QRadioButton::indicator:unchecked:disabled
{
image: url(../builder/ui/stylesheets/img/UI20/radio-unchecked-disabled.png);
}
QRadioButton::indicator:unchecked:focus
{
image: url(../builder/ui/stylesheets/img/UI20/radio-unchecked-disabled.png);
}
@@ -0,0 +1,43 @@
/*
* 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.
*
*/
QScrollBar:vertical
{
border: 4px solid rgba(85, 85, 85, 65%);
width: 16px;
background-color: rgba(85, 85, 85, 65%);
margin: 0px;
}
QScrollBar:horizontal
{
border: 4px solid rgba(85, 85, 85, 65%);
height: 16px;
background-color: rgba(85, 85, 85, 65%);
margin: 0px;
}
QScrollBar::handle
{
border: 0px solid #000000;
border-radius: 4px;
background: #000000;
}
QScrollBar::add-line,
QScrollBar::sub-line
{
border: none;
background: none;
color: none;
}
@@ -0,0 +1,61 @@
/*
* 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.
*
*/
QTabWidget
{
}
/* An empty ::pane rule removes the default pane style. */
QTabWidget::pane
{
}
QTabBar
{
qproperty-drawBase: 0;
alignment: center;
}
QTabBar::tab
{
background-color: #333333;
border-color: #000000;
border-style: solid;
border-width: 1px;
font-size: 12px;
height: 28px;
line-height: 24px;
margin-left: 0px;
min-width: 100px
}
QTabBar::tab::middle,
QTabBar::tab::last
{
margin-left: -1px;
}
QTabBar::tab:selected
{
background-color: #222222;
}
QTabBar::tab:hover
{
background-color: #444444;
}
@@ -0,0 +1,13 @@
/*
* 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.
*
*/
@@ -0,0 +1,71 @@
/*
* 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.
*
*/
QSpinBox,
QDoubleSpinBox
{
height: 16px;
margin: 2px;
padding-left: 8px;
}
QSpinBox:focus,
QDoubleSpinBox:focus
{
margin: 0px;
border-width: 2px;
border-style: solid;
border-radius: 4px;
border-color: #C19CFE;
}
QSpinBox:disabled,
QDoubleSpinBox:disabled
{
background-color: #777777;
color: #999999;
}
QSpinBox::up-button,
QDoubleSpinBox::up-button,
QSpinBox::down-button,
QDoubleSpinBox::down-button
{
width: 14px;
height: 6px;
border-width: 1px;
border-color: #222222;
border-style: solid;
background-color: #333333;
}
QSpinBox::up-button,
QDoubleSpinBox::up-button
{
border-top-right-radius: 2px;
image: url(../builder/ui/stylesheets/img/UI20/spinbox-up-arrow.png);
}
QSpinBox::down-button,
QDoubleSpinBox::down-button
{
border-bottom-right-radius: 2px;
image: url(../builder/ui/stylesheets/img/UI20/spinbox-down-arrow.png);
}
QSpinBox::up-button:pressed,
QDoubleSpinBox::up-button:pressed,
QSpinBox::down-button:pressed,
QDoubleSpinBox::down-button:pressed
{
background-color: #111111;
}
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2c45dda65605edfffe83701f467acbc28230fefdfedcddd1879d12e534f1c903
size 1228
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b959d561237d63b859eb9a0bb5ef1fa614a5788b3604a4515232b904803e2e2b
size 59380
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b327b9b1ab34d6facc574b4602eb678afa97825bf1aa721fa7505ef5c43ab051
size 59380
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:febc3883ac09ea579877deb49a41441f618a1558f63a838ca3e216c65c638b1c
size 448
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7e89ed1a88e4b7488a34f9d5ecc3cf231d1c3b2b225fe956e009431ae0cd02d3
size 438
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b7226b603a86d0f49f1fcc0159389555c02814af5382ba7b52374cac7e38f3b8
size 446
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:63ed841be1103f4892ee5a6e9d7a8b0cdb0985a8267d445299aafc62b645c2fd
size 546
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:122e36afcffdb645c3e31d1209c1faa3f4902499b52d012a4b6d48cffd2f20fe
size 1160
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:127d144107a9fbde39166ed0376c4426f5cc27a2158414157757763b5191f41f
size 1154
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6e1d4d0b66d84814957526e97187064789cc292dd6ddad761adde100b62712eb
size 295
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:41085a9d05c4d360ad5e01e1d59dd431938b091b043b607f1dbe5d97979138a7
size 372
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d40eecfe6550998ce610a9315db836e0f5ff4b5481c14de80b1ef58fbee60c53
size 271
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d7b7a54c2b0f629e3db70aa148dfd296b1a373398cf0a25e352d6a7f52a6cb6d
size 381
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e7788561044ecb229d2c1175891d5ebe04c5a68c6b9a55faf2ecb22139ad84c9
size 1042
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:01a9e46b7ae80b68288b8d3a81e26b01c00fd8b72bcedd7ce7cebcfac1529153
size 1043
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6c90511863eea1443491f48892e6a686325d098d459dfcacbb8b560f46bf29a9
size 294
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1dbebd9235853b1b5e5b75669dbb411c1f798681e78ea6bdfcf392bdf14280fb
size 169
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3d623ad9cecbb6c3fee912eb39689fe5442f805cd41d32fd6d5a49f5c7b049f7
size 372
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:41307cc4f2c5ac0c77cbe2eb15b488a5d52f6c05d850f8dd8d0540ad66e3a925
size 1189
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:23bad55f0bf5efcbbe9017dd6b82e5f925349d5c6a2d120f2924aa7e6cd1f3dd
size 1122
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:73080538eae03ac319851d4a4c0928547c457eee730b67458794f0ac50a1f174
size 528
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6686a111a1d0f0e02d544d24135b9b94599fb332cd43912675c6840eb318bc00
size 3293
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d49c822d8f43f0f05dc8e2fa1ccee86ef3731cca074a586eb5b943caddaa6e52
size 3306
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7c8bf7b3f70d5f2683a8e495ddf80ecea1a437d63d259730becfd546286a8be2
size 3191
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1b4c23e60d84cfa16a310638ffc30d14c7821dbb852e96c5cbfb814aa0af3b1d
size 3244
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:beb00c64d91c8e9035414fba270f2b9477a1d15debb2d4acad0481256948b58d
size 298
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9b15e390624dc61ad6ae48faedb2f901f016615548747b6ed66296a8dd6ce5d7
size 296
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b85878ce2422dba963334dfc04648d893b7f17f9f4e4a395d6fea92c3dc5998e
size 198
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:47df1e68051bd26a470e5d9d8af45c26e33c56d06cc379f861d8300963b606de
size 188
@@ -0,0 +1,92 @@
/*
* 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.
*
*/
.secondaryText
{
color: #BBBBBB;
}
.primaryText
{
color: white;
}
.highlightedText
{
color: #C9AAFE;
}
.blackText
{
color: black;
}
/*
Qt supports the line-height field, but only for QTextEdit controls, not for QLabel.
Even then, the support isn't exactly what we want.
So instead, we apply margin-top and bottom appropriately to get the required spec.
To get the right line-height, we need to do:
margin-top: ((line-height - font-size) / 2) px;
margin-bottom: ((line-height - font-size) / 2) px;
*/
QLabel
{
font-size: 12px;
margin-top: 2px;
margin-bottom: 2px;
font-family: "Amazon Ember";
}
.Headline
{
font-family: "Amazon Ember Light";
font-size: 24px;
margin-top: 4px;
margin-bottom: 4px;
}
.Title
{
font-size: 18px;
margin-top: 7px;
margin-bottom: 7px;
}
.Subtitle
{
font-family: "Amazon Ember Light";
font-size: 16px;
margin-top: 8px;
margin-bottom: 8px;
}
.Paragraph
{
font-size: 12px;
/*
line-height doesn't work with multi-line text fields.
With single-line text fields, we can get close enough using the margin-top and margin-bottom. That doesn't
work for multi-line fields, because there's no inbetween line margins.
*/
margin-top: 4px;
margin-bottom: 4px;
}
@@ -0,0 +1,23 @@
/*
* 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.
*
*/
QToolTip
{
color: #ffffff;
background-color: black;
border: 1px transparent black;
margin-left: 2px;
font-size: 12px;
margin-top: 0px;
margin-bottom: 0px;
}
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c9f7e274bfcd9e1fd31084758b9ca782a1c8ba5e2760dee5d27e8f84ec9e90f5
size 596
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:13a6a1da6e5e21fbe94b510c68671dfcdc38a5f9fe526699c2d9cccb5387876e
size 595
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f470a07169b00dc104bfb365bc7b9e126ca57e133bc26545e4c539322c965fd4
size 202
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2834eb4101a3b5ee1f59a8e12d55dc68de0825e24a06c7468a7d9eb4afb3e21f
size 218
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2ebc0472590293c3b5e53fabef409bf1db056d3c02582066cf8e6655e5386999
size 221
@@ -0,0 +1,371 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>461</width>
<height>502</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Watcher</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QRadioButton" name="radioButton_2">
<property name="text">
<string> On / Off</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Wacher Foler</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_6">
<property name="text">
<string>Watch folder</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="toolButton_3">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Wacher Extensions</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEdit_7">
<property name="text">
<string>*.sbsar, *.sbs</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="textEdit_2"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Sbsar</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox_parameters">
<property name="title">
<string>Parameters</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QLabel" name="label_14">
<property name="text">
<string>Preset</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_15">
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_16">
<property name="text">
<string>RandomSeed</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QComboBox" name="comboBox_3">
<item>
<property name="text">
<string>Grey Stone</string>
</property>
</item>
<item>
<property name="text">
<string>Red Coral</string>
</property>
</item>
<item>
<property name="text">
<string>Red Stone</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox_4">
<item>
<property name="text">
<string> 512 * 512</string>
</property>
</item>
<item>
<property name="text">
<string>1024 * 1024</string>
</property>
</item>
<item>
<property name="text">
<string>2048 * 2048</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_2"/>
</item>
</layout>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Output">
<property name="title">
<string>Output</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QCheckBox" name="checkBox_9">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Base Color</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_10">
<property name="text">
<string>Albedo</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_11">
<property name="text">
<string>Specular</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_12">
<property name="text">
<string>Metallic</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_13">
<property name="text">
<string>Normal map</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_14">
<property name="text">
<string>Glossness</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_15">
<property name="text">
<string>Roughness</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_16">
<property name="text">
<string>Opacity</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Material">
<property name="title">
<string>Atom Material</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_4"/>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>Create .material</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_4">
<property name="text">
<string>Render textures</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="lineEdit_5"/>
</item>
<item>
<widget class="QToolButton" name="toolButton_2">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_5">
<property name="text">
<string>Bake SBSAR from SBS</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,166 @@
# coding:utf-8
#!/usr/bin/python
#
# 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.
# -- This line is 75 characters -------------------------------------------
# The __init__.py files help guide import statements without automatically
# built-ins
import os
import sys
import site
import time
# -------------------------------------------------------------------------
# we don't have access yet to the DCCsi Lib\site-packages
# (1) this will give us import access to dccsi and azpy import
_DCCSIG_PATH = os.getenv('DCCSIG_PATH', os.getcwd()) # always?, doubtful
# ^^ this assume that the \DccScriptingInterface is the cwd!!! (launch there)
site.addsitedir(_DCCSIG_PATH)
# Lumberyard extensions
from azpy.env_bool import env_bool
from azpy.constants import ENVAR_DCCSI_GDEBUG
from azpy.constants import ENVAR_DCCSI_DEV_MODE
from azpy.constants import *
# 3rdparty
from unipath import Path
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# substance automation toolkit (aka pysbs)
# To Do: manage with dynaconf environment
_PYSBS_DIR_PATH = Path(PATH_PROGRAMFILES_X64,
'Allegorithmic',
'Substance Automation Toolkit',
'Python API',
'install').resolve()
site.addsitedir(str(_PYSBS_DIR_PATH)) # 'install' is the folder I created
# Susbstance
import pysbs.batchtools as pysbs_batch
import pysbs.context as pysbs_context
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# set up global space, logging etc.
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
_PACKAGENAME = __name__
if _PACKAGENAME is '__main__':
_PACKAGENAME = 'DCCsi.SDK.substance.builder.watchdog'
import azpy
_LOGGER = azpy.initialize_logger(_PACKAGENAME)
_LOGGER.debug('Starting up: {0}.'.format({_PACKAGENAME}))
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# check some env var tags (fail if no, likely means no proper code access)from collections import OrderedDict
from collections import OrderedDict
_SYNTH_ENV_DICT = OrderedDict()
_SYNTH_ENV_DICT = azpy.synthetic_env.stash_env(_SYNTH_ENV_DICT)
_LY_DEV = _SYNTH_ENV_DICT[ENVAR_LY_DEV]
_LY_PROJECT_PATH = _SYNTH_ENV_DICT[ENVAR_LY_PROJECT_PATH]
# -------------------------------------------------------------------------
class MyHandler(PatternMatchingEventHandler):
patterns = ["*.sbsar", "*.txt"]
def process(self, event):
"""
event.event_type
'modified' | 'created' | 'moved' | 'deleted'
event.is_directory
True | False
event.src_path
path/to/observed/file
"""
self.outputName = event.src_path.split(".sbsar")[0].split("/")[-1]
self.outputCookPath = event.src_path.split(self.outputName)
self.outputRenderPath = Path(_LY_PROJECT_PATH, 'Assets', 'Textures', 'Substance').norm()
_LOGGER.debug(self.outputCookPath, self.outputName, self.outputRenderPath)
pysbs_batch.sbsrender_info(input=event.src_path)
pysbs_batch.sbsrender_render(inputs=event.src_path,
# inputs=os.path.join(self.outputCookPath, self.outputName + '.sbsar'),
# input_graph=_inputGraphPath,
output_path=self.outputRenderPath,
output_name='{inputGraphUrl}_{outputNodeName}',
output_format='tif',
# set_value=['$outputsize@%s,%s' % (_outputSize, _outputSize), '$randomseed@1'],
# use_preset = _user_preset[0]
no_report=True,
verbose=True
).wait()
# print(self.outputName, self.outputCookPath)
# builder.output_info(self.outputCookPath, self.outputName)
# print(builder.output_info(event.src_path.split("/")[0], event.src_path.split("/")[-1].split(".sbsar")[0])['_inputs'])
# pysbs_batch.sbsrender_render(
# input = event.src_path,
# output_path = 'C:/dccapi/dev/Gems/DccScriptingInterface/MockProject/Assets/Materials/Substance/textures',
# output_name = '{inputGraphUrl}_{outputNodeName}',
# output_format = 'tif',
# # set_value=['$outputsize@9,9', '$randomseed@3']
# set_value = '$outputsize@10,10',
# use_preset = ['Red Coral', 'Red Sand', 'Grey Stone'][0]
# ).wait()
# pysbs_batch.sbsrender_render(
# input = event.src_path,
# output_path = 'C:/dccapi/dev/Gems/DccScriptingInterface/MockProject/Assets/Materials/Substance/textures',
# output_name = '{inputGraphUrl}_{outputNodeName}',
# output_format = 'tif',
# # set_value=['$outputsize@9,9', '$randomseed@3']
# set_value = '$outputsize@10,10',
# use_preset = 'Red Sand'
# ).wait()
# print("Hello World!!!")
def on_modified(self, event):
self.process(event)
def on_created(self, event):
self.process(event)
if __name__ == '__main__':
"""Run this file as main"""
_TYPE_TAG = 'MODULE_TEST'
_TEST_APP_NAME = '{0}-{1}'.format(_TOOL_TAG, _TYPE_TAG)
_LOGGER = setup_config_logging(_TEST_APP_NAME)
_LOGGER.debug("Test Run:: {0}.".format({_PACKAGENAME}))
_LOGGER.debug("{0} :: if __name__ == '__main__':".format(_TOOL_TAG))
args = sys.argv[1:]
observer = Observer()
observer.schedule(MyHandler(), path=args[0] if args else '.')
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()