Skip to content

Commit

Permalink
Init MVP
Browse files Browse the repository at this point in the history
  • Loading branch information
p-hennessy committed Mar 25, 2024
0 parents commit 916545f
Show file tree
Hide file tree
Showing 10 changed files with 372 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.pyenv
build/
__pycache__/
*.pyc
*.pyo
14 changes: 14 additions & 0 deletions .sdpackageignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
##################################################
# List of files ignored when packaging the plugin
##################################################

# Ignore the packaging script.
makepackage.py

# Ignore the build directory.
build/

# Ignore compiled python modules.
*.pyc
__pycache__

7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# VTF Exporter for Substance 3D Designer

Todo

# Installing

Todo
143 changes: 143 additions & 0 deletions makepackage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
##########################################################################
# ADOBE CONFIDENTIAL
# ___________________
# Copyright 2010-2024 Adobe
# All Rights Reserved.
# * NOTICE: Adobe permits you to use, modify, and distribute this file in
# accordance with the terms of the Adobe license agreement accompanying it.
# If you have received this file from a source other than Adobe,
# then your use, modification, or distribution of it requires the prior
# written permission of Adobe.
##########################################################################

import os
import sys
import fnmatch
import json
from zipfile import ZipFile


class IgnoreFileFilter(object):
def __init__(self, filename):
self.__globs = []
self.__dirs_to_ignore = []

if os.path.exists(filename):
with open(filename, 'rt') as f:
lines = f.readlines()

for line in lines:
line = line.strip()

if line == '' or line.startswith('#'):
continue

if line.endswith('/'):
self.__dirs_to_ignore.append(line[:-1])
else:
self.__globs.append(line)

def filter(self, filepath):
# Ignore filter config files.
if filepath.endswith('.sdpackageignore'):
return False

# Check that the file is not included in any glob.
if self.__globs:
filename = os.path.basename(filepath)

for pattern in self.__globs:
if fnmatch.fnmatch(filename, pattern):
return False

# Check that the file is not inside any ignored directory.
if self.__dirs_to_ignore:
dirname = os.path.normpath(os.path.dirname(filepath))
dirs = dirname.split(os.sep)

for pattern in self.__dirs_to_ignore:
for d in dirs:
if pattern == d:
return False

return True


def read_metadata():
if not os.path.exists('pluginInfo.json'):
print("Missing metadata file")
return None

try:
f = open('pluginInfo.json', 'rt')
return json.load(f)
except Exception as e:
print('Error while checking metadata: %s' % e)
f.close()
return None

def check_metadata(metadata):
if not 'name' in metadata:
print('"name" metadata entry is missing')
return False

return True

def walk(directory):
for dirpath, dirnames, filenames in os.walk(directory, topdown=True):
for filename in filenames:
yield os.path.join(dirpath, filename)

def add_file_to_package(zfile, plugin_name, filepath):
print("Adding file %s to package" % filepath)
archive_filepath = os.path.join(plugin_name, filepath)
zfile.write(filepath, arcname=archive_filepath)

def main():
this_dir = os.path.abspath(os.path.dirname(__file__))

build_dir = os.path.join(this_dir, "build")

if not os.path.exists(build_dir):
try:
os.makedirs(build_dir)
except Exception as e:
print('Could not create build directory')
sys.exit(1)

package_parent_dir = os.path.abspath(os.path.join(this_dir, '..'))
package_dir_name = os.path.basename(this_dir)

# Save the current dir and switch to the package dir.
saved_dir = os.getcwd()
os.chdir(this_dir)

metadata = read_metadata()
if not metadata:
sys.exit(1)

if not check_metadata(metadata):
sys.exit(1)

plugin_name = metadata['name']
package_filepath = os.path.join(build_dir, plugin_name) + '.sdplugin'

try:
file_filter = IgnoreFileFilter('.sdpackageignore')

with ZipFile(package_filepath, 'w') as zfile:
for filepath in walk('.'):
if os.path.abspath(filepath) == os.path.join(this_dir, 'pluginInfo.json'):
add_file_to_package(zfile, plugin_name, filepath)
elif file_filter.filter(filepath):
add_file_to_package(zfile, plugin_name, filepath)

except Exception as e:
print("Error while packaging plugin: %s" % e)
sys.exit(1)
finally:
# Restore the saved directory.
os.chdir(saved_dir)

if __name__ == '__main__':
main()
9 changes: 9 additions & 0 deletions pluginInfo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"metadata_format_version": "1",
"name": "vtf_exporter",
"version": "0.1.1",
"author": "Zeus",
"email": "https://tf2maps.net/members/zeus.28272/",
"min_designer_version": "2019.2",
"platform": "any"
}
26 changes: 26 additions & 0 deletions vtf_exporter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
##########################################################################
# ADOBE CONFIDENTIAL
# ___________________
# Copyright 2010-2024 Adobe
# All Rights Reserved.
# * NOTICE: Adobe permits you to use, modify, and distribute this file in
# accordance with the terms of the Adobe license agreement accompanying it.
# If you have received this file from a source other than Adobe,
# then your use, modification, or distribution of it requires the prior
# written permission of Adobe.
##########################################################################

import sd

from vtf_exporter import menu


def initializeSDPlugin():
menu.create_menu()


def uninitializeSDPlugin():
menu.destroy_menu()



69 changes: 69 additions & 0 deletions vtf_exporter/bulk_export.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import sd
from sd.api.sdproperty import *
from sd.api.sdvaluetexture import SDValueTexture

from pathlib import Path
import subprocess
import os
import tempfile

from vtf_exporter import config


def bulk_export():
ctx = sd.getContext()
app = ctx.getSDApplication()
uiMgr = app.getQtForPythonUIMgr()

graph = uiMgr.getCurrentGraph()

# Get current graph inputs

for preset in graph.getPresets():
set_preset(graph, preset)
save_outputs_as_vtf(graph, preset.getLabel())

# Reset graph inputs


def set_preset(graph, preset):
for preset_input in preset.getInputs():
value = preset_input.getValue()
id = preset_input.getIdentifier()
graph.setInputPropertyValueFromId(id, value)

graph.compute()


def save_outputs_as_vtf(graph, preset_name):
plugin_config = config.get_vtex_config_file()
vtex_exe = plugin_config.get("vtex_path")
# TODO Error handling if vtex path is not configured

for node in graph.getOutputNodes():
props = node.getProperties(SDPropertyCategory.Output)
value = node.getPropertyValue(props[0])
texture = SDValueTexture.get(value)

output_name = props[0].getId()

file_name = f"{graph.getIdentifier()}_{preset_name}_{output_name}"

save_temp_dir = tempfile.mkdtemp()
save_temp_file = os.path.join(str(save_temp_dir), f"{file_name}.tga")
save_vtex_config_file = os.path.join(str(save_temp_dir), f"{file_name}.txt")

# TODO Take a filepath in config?
save_dir = Path(f'C:/Users/phenn/Desktop')

# TODO Take additional args based on alpha channel
with open(save_vtex_config_file, "w") as file:
file.write("stripalphachannel 1\n")

texture.save(save_temp_file)

command = [str(vtex_exe), "-nopause", "-outdir", str(save_dir), save_vtex_config_file]
output = subprocess.run(command, shell=True, capture_output=True)

os.remove(save_temp_file)
os.remove(save_vtex_config_file)
21 changes: 21 additions & 0 deletions vtf_exporter/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os
import json
from vtf_exporter.constants import *


def get_vtex_config_file():
if os.path.exists(CONFIG_DIR):
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE) as file:
return json.load(file)

return {}


def save_vtex_config_file(config_data):
if not os.path.exists(CONFIG_DIR):
os.mkdir(CONFIG_DIR)

with open(CONFIG_FILE, "w") as file:
file.write(json.dumps(config_data, indent=4))

8 changes: 8 additions & 0 deletions vtf_exporter/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import os

SUBSTANCE_DIR = os.path.join(os.getenv('APPDATA') , "Allegorithmic", "Substance Designer")
CONFIG_DIR = os.path.join(SUBSTANCE_DIR, "vtf_exporter")
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.json")

PLUGIN_VERSION = "0.1.0"
MAIN_MENU_NAME = "vtf_exporter_main"
70 changes: 70 additions & 0 deletions vtf_exporter/menu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import sd
from PySide2 import QtWidgets

from vtf_exporter import config
from vtf_exporter.bulk_export import bulk_export


def create_menu():
app = sd.getContext().getSDApplication()
uiMgr = app.getQtForPythonUIMgr()

menu = uiMgr.newMenu(
menuTitle="VTF Exporter",
objectName="vtf_exporter_main"
)

act_bulk_export = QtWidgets.QAction("Bulk Export Graph Presets", menu)
act_bulk_export.triggered.connect(bulk_export)
menu.addAction(act_bulk_export)

menu.addSeparator()

configure = QtWidgets.QAction("Configure", menu)
configure.triggered.connect(create_config_dialog)
menu.addAction(configure)



def destroy_menu():
app = sd.getContext().getSDApplication()
uiMgr = app.getQtForPythonUIMgr()
uiMgr.deleteMenu("vtf_exporter_main")


def create_config_dialog():
app = sd.getContext().getSDApplication()
uiMgr = app.getQtForPythonUIMgr()

mainWindow = uiMgr.getMainWindow()
dialog = QtWidgets.QDialog(parent=mainWindow)

layout = QtWidgets.QVBoxLayout()
layout2 = QtWidgets.QHBoxLayout()

label_vtex_location = QtWidgets.QLabel("vtex.exe")

vtex_path = config.get_vtex_config_file().get("vtex_path")
if vtex_path:
txt_vtex_location = QtWidgets.QLineEdit(vtex_path)
else:
txt_vtex_location = QtWidgets.QLineEdit()

layout2.addWidget(label_vtex_location)
layout2.addWidget(txt_vtex_location)

layout.addLayout(layout2)

btn_save = QtWidgets.QPushButton("Save")
def save_config():
config_data = {"vtex_path": txt_vtex_location.text()}
save_vtex_config_file(config_data)
dialog.accept()

btn_save.clicked.connect(save_config)

layout.addWidget(btn_save)

dialog.setLayout(layout)

dialog.show()

0 comments on commit 916545f

Please sign in to comment.