
# Punch Holes Script v1.0
# Created by John Kesig
# Compatibility Versions for Maya 2022 & 2023
# Python 3
import maya.cmds as mc
class punchHolesUI(object):
def __init__(self):
#Create Script Window
self.ph_window = mc.window(title='Punch Holes', menuBar=True)
self.ph_layout = mc.formLayout(numberOfDivisions=100)
#Create variables for User Control Settings
#Circularize
self.ph_circ_text = mc.text(label='Circularize')
self.ph_circ_offset = mc.floatSliderGrp(label='Offset', field=True, minValue=-100.0, maxValue=100.0, fieldMinValue=-1000.0, fieldMaxValue=1000.0, value=0.0)
#Bevel
self.ph_bevel_text = mc.text(label='Bevel')
self.ph_bevel_fraction = mc.floatSliderGrp(label='Fraction', field=True, precision=3, minValue=0.0, maxValue=1.0, value=0.2)
self.ph_bevel_divisions = mc.intSliderGrp(label='Divisions', field=True, minValue=1, maxValue=5, value=1)
self.ph_bevel_chamfer = mc.checkBoxGrp(label='Chamfer ', numberOfCheckBoxes=1)
#Extrude
self.ph_ext_text = mc.text(label='Extrude')
self.ph_ext_check = mc.checkBoxGrp(label='Extrude? ', numberOfCheckBoxes=1)
self.ph_ext_thickness = mc.floatSliderGrp(label='Thickness', field=True, minValue=-100.0, maxValue=100.0, fieldMinValue=-1000.0, fieldMaxValue=1000.0, value=-10.0)
self.ph_ext_divisions = mc.intSliderGrp(label='Divisions', field=True, minValue=1, maxValue=5, value=1)
self.ph_ext_offset = mc.floatSliderGrp(label='Offset', field=True, minValue=-100.0, maxValue=100.0, fieldMinValue=-1000.0, fieldMaxValue=1000.0, value=0.0)
#Create buttons for the different methods
self.pvh_button = mc.button(label='Punch Holes by Vertex', backgroundColor=[0.25,0.36,0.466], command=self.punchVertHoles)
self.pfh_button = mc.button(label='Punch Holes by Face', backgroundColor=[0.25,0.46,0.36], command=self.punchFaceHoles)
self.b_button = mc.button(label='Bevel Edges', backgroundColor=[0.67,0.29,0.29], command=self.ph_Bevel)
self.e_button = mc.button(label='Extrude', backgroundColor=[0.80,0.80,1.00], command=self.ph_extrude)
#Create Separators
self.ph_circ_sep = mc.separator(h=5)
self.ph_ext_sep = mc.separator(h=5)
self.ph_bevel_sep = mc.separator(h=5)
#Layout the window
mc.formLayout(self.ph_layout, edit=True,
attachForm = [ (self.ph_circ_text, 'top', 5),
(self.ph_circ_offset, 'left',5), (self.ph_circ_offset, 'right',5),
(self.ph_circ_sep, 'top', 5),(self.ph_circ_sep, 'left', 5), (self.ph_circ_sep, 'right', 5),
(self.ph_ext_text, 'left', 5),
(self.ph_ext_check, 'left', 5),
(self.ph_ext_thickness, 'left', 5), (self.ph_ext_thickness, 'right', 5),
(self.ph_ext_divisions, 'left', 5), (self.ph_ext_divisions, 'right', 5),
(self.ph_ext_offset, 'left', 5), (self.ph_ext_offset, 'right', 5),
(self.ph_ext_sep, 'top', 5),(self.ph_ext_sep, 'left', 5), (self.ph_ext_sep, 'right', 5),
(self.ph_bevel_text, 'left', 5),
(self.ph_bevel_fraction, 'left', 5), (self.ph_bevel_fraction, 'right', 5),
(self.ph_bevel_divisions, 'left', 5), (self.ph_bevel_divisions, 'right', 5),
(self.ph_bevel_chamfer, 'left', 5), (self.ph_bevel_chamfer, 'right', 5),
(self.ph_bevel_sep, 'left', 5), (self.ph_bevel_sep, 'right', 5),
(self.ph_bevel_sep, 'top', 5),(self.ph_bevel_sep, 'left', 5), (self.ph_bevel_sep, 'right', 5),
(self.pvh_button, 'bottom', 5), (self.pvh_button, 'left', 5), (self.pvh_button, 'right', 5),
(self.pfh_button, 'bottom', 5), (self.pfh_button, 'left', 5), (self.pfh_button, 'right', 5),
(self.e_button, 'bottom', 5), (self.e_button, 'left', 5), (self.e_button, 'right', 5),
(self.b_button, 'bottom', 5), (self.b_button, 'left', 5), (self.b_button, 'right', 5),
],
attachControl = [ (self.ph_circ_offset, 'top', 5, self.ph_circ_text),
(self.ph_circ_sep,'top', 5, self.ph_circ_offset),
(self.ph_ext_text, 'top', 5, self.ph_circ_sep),
(self.ph_ext_check, 'top', 5, self.ph_ext_text),
(self.ph_ext_thickness, 'top', 5, self.ph_ext_check),
(self.ph_ext_divisions, 'top', 5, self.ph_ext_thickness),
(self.ph_ext_offset, 'top', 5, self.ph_ext_divisions),
(self.ph_ext_sep, 'top', 5, self.ph_ext_offset),
(self.ph_bevel_text, 'top', 5, self.ph_ext_sep),
(self.ph_bevel_fraction, 'top', 5, self.ph_bevel_text),
(self.ph_bevel_divisions, 'top', 5, self.ph_bevel_fraction),
(self.ph_bevel_chamfer, 'top', 5, self.ph_bevel_divisions),
(self.ph_bevel_sep, 'top', 5, self.ph_bevel_chamfer),
(self.pvh_button, 'top', 5, self.ph_bevel_sep),
(self.pfh_button, 'top', 5, self.ph_bevel_sep),
(self.e_button, 'top', 5, self.pvh_button),
(self.b_button, 'top', 5, self.e_button),
],
attachPosition = [
(self.pvh_button, 'left', 5, 50), (self.pvh_button, 'bottom', 5, 65),
(self.pfh_button, 'right', 5, 50),
(self.e_button, 'left', 5, 50), (self.e_button, 'bottom', 5, 35),
(self.b_button, 'left', 5, 50)
]
)
mc.menu(label='Documentation')
mc.menuItem(label='Help', command=self.docs_help)
mc.menuItem(label='About', command=self.docs_about)
#Display the window
mc.showWindow(self.ph_window)
def docs_about(self, *args):
about = mc.confirmDialog(
title='About',
ma='center',
message='Punch Holes Script v1.0\n\n'
'Created By John Kesig\n\n'
'If you have any questions, would like to request a feature, might be able to make it better, or report an issue just let me know by sending me an email at: [email protected]\n\n'
'\n\n\n\nThanks for using it!',
button=['Done'],
cancelButton='Done')
return
def docs_help(self, *args):
doc_help = mc.confirmDialog(
title='Help',
message='Need a little assistance with the script? That\'s fine! I\'m here to help.\n\n'
'You have essentially two ways of creating holes: Faces or Vertices\n\n'
'All you need to do is configure your options how you want them to be used in the script, and then click either the following'
'buttons: "Punch Holes by Face" or "Punch Holes by Vertex" \n\n'
'Faces\n'
'Configure your settings and the script will run over the following steps:\n'
'\t1. Subdivide the face -> Triangulate the faces\n\t2. Chamfer the vertex at the center of the face -> Circularize vertices'
'\n\t3. Select & Extrude triangular faces'
'\n\t4. Bevel the edges'
'\n\t5. Quadrangulate the extruded faces'
'\n\n\nVertices\n'
'Configure your settings and the script will run over the following steps:\n'
'\t1. Select the edges attached the vertex'
'\n\t2. Poke the newly created face'
'\n\t3. Select the center vertex -> Chamfer it -> Circularize components'
'\n\t4. Select all faces with triangles and blast them away'
'\n\t5. Select all Quad faces and extrude the geometry'
'\n\t6. Select all border edges'
'\n\n\nYou might be asking why I chose to keep beveling out of the Vertex side of the script. I did it for a couple of reasons.\n'
'\t1. I do not have a good way of selecting the corner edges.'
'\n\t2. Allow you to deselect the outside border edges before beveling. \n\nNow you may be asking'
'"Why didn\'t you just deselect the borders of the geometry?" Well... I can\'t... Not with this method at least.'
' For now this is what I have and I am open for ideas on how to make this better! You could also think of it this way:'
'It is a quick way of getting pieces beveled if you don\'t need to use the whole script!\n\n\n'
'-John Kesig'
'\n\n\n\n\nWhat\'s your favorite video game?',
button=['Done'],
cancelButton='Done')
return
def punchFaceHoles(self, *args):
#Gather users face selection
selection = mc.ls(sl=True)
test_selection = str(selection)
if '.f' not in test_selection:
selection_check = mc.framelessDialog( title='Whoops', message='Your selection contains vertex(es)\\edge(s), or you do not have any components selected. \n\nPlease select only faces.',
button=['OK'], primary=['OK'])
else:
#We need to get the face to have a star formation of edges in the mesh. We subdivide for quads, then triangulate them for a star
mc.polySubdivideFacet(cch=True, ch=True)
mc.polyTriangulate()
#Store the faces in a variables
faces = mc.ls(sl=True)
#Select the vertice directly in the middle of the star
mc.polySelectConstraint(dis=True, mode=3, type=0x0001, order=1, orderbound=(8,20))
vert = mc.ls(sl=True)
#Chamfer the vertice and circularize the components
mc.polyExtrudeVertex(width = 0.25)
pfh_circ_offset=mc.floatSliderGrp(self.ph_circ_offset, query=True, value=True)
version = int(mc.about(mjv=True))
if version < 2023:
mc.polyCircularizeEdge(radialOffset=pfh_circ_offset)
else:
mc.polyCircularize(radialOffset=pfh_circ_offset)
#Select the inner triangular faces and store in them in a variable, then remove the constraint
mc.polySelectConstraint(dis=True, mode=3, type=0x0008, size=1)
triFaces = mc.ls(sl=True)
mc.polySelectConstraint(mode=0, size=0)
#Store the inner edges of the triangular faces, we need it as a reference for beveling later
circIE = mc.polyListComponentConversion(triFaces, te=True, internal=True)
#Select the triangular faces from before and extrude them to however deep they need to go
mc.select(triFaces)
pfh_ext_thickness = mc.floatSliderGrp(self.ph_ext_thickness, query=True, value=True)
pfh_ext_divisions = mc.intSliderGrp(self.ph_ext_divisions, query=True, value=True)
pfh_ext_offset = mc.floatSliderGrp(self.ph_ext_offset, query=True, value=True)
mc.polyExtrudeFacet(thickness=pfh_ext_thickness, divisions=pfh_ext_divisions, offset=pfh_ext_offset)
#Save the extruded border edge the script just created and bevel both the extruded edge and the inner edge
circEE = mc.polyListComponentConversion(triFaces, te=True, bo=True)
mc.select(circEE + circIE)
pfh_bevel_chamfer = mc.checkBoxGrp(self.ph_bevel_chamfer, query=True, value1=True)
pfh_bevel_fraction = mc.floatSliderGrp(self.ph_bevel_fraction, query=True, value=True)
pfh_bevel_divisions = mc.intSliderGrp(self.ph_bevel_divisions, query=True, value=True)
mc.polyBevel3(chamfer=pfh_bevel_chamfer, offsetAsFraction=True, fraction=pfh_bevel_fraction, segments=pfh_bevel_divisions)
#Select the triangular faces on the inside of the bevel and run a Quadrangulate on them
#I wish this worked a little more consistently but sadly it doesn't. You will need to go over the bad ones until I can figure out a way to make it more consistent
mc.polySelectConstraint(mode=3, type=0x0008, size=1)
mc.polySelectConstraint(mode=0)
innerTriFaces = mc.ls(sl=True)
mc.polyQuad(worldSpace=False)
#Need this command in order for the user to go about their work
mc.polySelectConstraint(mode=0)
return
def punchVertHoles(self, *args):
#Gather users vertice selection
selection = mc.ls(sl=True)
test_selection = str(selection)
if '.vtx' not in test_selection:
mc.framelessDialog( title='Whoops', message='Your selection contains edge(s)\\face(s), or you do not have any components selected. \n\nPlease select only vertices.',
button=['OK'], primary=['OK'])
else:
#Store the edge of the vertex in a variable and then delete the edges
surEdges = mc.polyListComponentConversion(selection, fv=True, te=True, bo=True)
mc.select(surEdges)
mc.polyDelEdge()
#Select the now newly created n-gon face from the deleted edges and poke the face to get the star formation
mc.polySelectConstraint(mode=3, type=0x0008, size=3)
mc.polySelectConstraint(mode=0, size=0)
mc.polyPoke()
#Select the center vertex in the star formation, chamfer the vertex, and circularize the components
mc.polySelectConstraint(mode=3, type=0x0001, order=1, orderbound=(8,20))
mc.polyExtrudeVertex(width=0.25)
#Circularize components wasn't working as intended in versions of Maya previous to 2023. This should fix that
version = int(mc.about(mjv=True))
pvh_circ_offset = mc.floatSliderGrp(self.ph_circ_offset, query=True, value=True)
if version < 2023:
mc.polyCircularizeEdge(radialOffset=pvh_circ_offset)
else:
mc.polyCircularize(radialOffset=pvh_circ_offset)
mc.polySelectConstraint(mode=0, size=0)
#Select faces that have triangles and store them in a variable
mc.polySelectConstraint(mode=3, type=0x0008, size=1)
pvh_triFaces = mc.ls(sl=True)
mc.polySelectConstraint(mode=0)
#Using the triangular faces variable store a the star shaped edges in a new variable. Select the newly created edges and delete them.
pvh_triEdges = mc.polyListComponentConversion(pvh_triFaces, ff=True, te=True, internal=True)
mc.select(pvh_triEdges)
mc.polyDelEdge()
#Now we can select the newly created n-gon and select the border edges from it. We do this because it maintains edge count later in the process. Store the n-gon face and the border edges in a variable
mc.polySelectConstraint(mode=3, type=0x0008, size=3)
pvh_blastFace = mc.ls(sl=True)
pvh_bEdge = mc.polyListComponentConversion(pvh_blastFace, ff=True, te=True, bo=True)
#Select the n-gon face and blast it away.
mc.select(pvh_blastFace)
mc.polyDelFacet()
mc.polySelectConstraint(mode=0, size=0)
return
def ph_extrude(self, *args):
#Select all the quad faces from the object and store them in a variable so that we can store the border edges in another variable
mc.polySelectConstraint(mode=3, type=0x0008, size=2)
pvh_bExtFaces = mc.ls(sl=True)
pvh_bExtEdges = mc.polyListComponentConversion(pvh_bExtFaces, ff=True, te=True, bo=True)
#Extrude the quad faces and store the extruded faces in a new variable and the new extruded border edges in a new variable
pvh_ext_check = mc.checkBoxGrp(self.ph_ext_check, query=True, value1=True)
pvh_ext_thickness = mc.floatSliderGrp(self.ph_ext_thickness, query=True, value=True)
pvh_ext_divisions = mc.intSliderGrp(self.ph_ext_divisions, query=True, value=True)
pvh_ext_offset = mc.floatSliderGrp(self.ph_ext_offset, query=True, value=True)
#Don't extrude if thickness is 0. This is to prevent issues with the mesh
if pvh_ext_thickness == 0:
return
if pvh_ext_check == 1:
mc.polyExtrudeFacet(thickness=pvh_ext_thickness, divisions=pvh_ext_divisions, offset=pvh_ext_offset)
pvh_extGeo = mc.ls(sl=True)
pvh_extEdges = mc.polyListComponentConversion(pvh_extGeo, ff=True, te=True, bo=True)
#Reverse Normals if Extrusion Thickness is below 0
if pvh_ext_thickness < 0:
mc.polySelectConstraint(mode=3, type=0x0008, size=2)
mc.polyNormal(normalMode=0)
#Select all of the border edges from the variables we have declared and bevel them
mc.select(pvh_bExtEdges + pvh_extEdges)
mc.polySelectConstraint(mode=0, w=0)
return
def ph_Bevel(self, *args):
#Gather the attributes you need from the UI, and apply them to the Bevel command
ph_b_chamfer = mc.checkBoxGrp(self.ph_bevel_chamfer, query=True, value1=True)
ph_b_fraction = mc.floatSliderGrp(self.ph_bevel_fraction, query=True, value=True)
ph_b_divisions = mc.intSliderGrp(self.ph_bevel_divisions, query=True, value=True)
mc.polyBevel3(chamfer=ph_b_chamfer, offsetAsFraction=True, fraction=ph_b_fraction, segments=ph_b_divisions)
#Need this command in order for the user to go about their work
mc.polySelectConstraint(mode=0)
return
ph = punchHolesUI()
# Utility Scripts v1.0
# Created by John Kesig
# Compatibility Versions for Maya 2022 & 2023
# Python 3
class utilityScripts(object):
def __init__(self):
us_window = mc.window(title='Utility Scripts')
us_form = mc.formLayout(numberOfDivisions=30)
us_tabs = mc.tabLayout(innerMarginWidth=5, innerMarginHeight=5)
mc.formLayout(us_form, edit=True, attachForm=[(us_tabs, 'top', 2), (us_tabs, 'left', 3), (us_tabs, 'bottom', 0),(us_tabs, 'right', 3)])
# Arnold
us_tab_arnold = mc.rowColumnLayout(numberOfColumns=1)
# aiSubdivisions
mc.separator(style='single', height=5, visible=False)
mc.text(label='Subdivide')
mc.separator(style='single', height=5, visible=True)
self.us_aiSubdiv_check = mc.checkBoxGrp(label='Turn on Subdivisions? ', numberOfCheckBoxes=1, columnAttach=[1,'left', 3])
mc.separator(style='single', height=2, visible=False)
self.us_aiSubdiv_iterations = mc.intSliderGrp(label='Arnold Subdiv Iterations', field=True, columnAttach=[1,'left', 3], minValue=1, maxValue=7, value=1)
mc.separator(style='single', height=10, visible=False)
self.us_aiSubdiv_button = mc.button(label='Subdivide', recomputeSize=True, command=self.subdivideArnold)
mc.setParent('..')
# prman
us_tab_prman = mc.rowColumnLayout(numberOfColumns=1)
mc.text(label='Not yet worked out')
mc.tabLayout( us_tabs, edit=True, tabLabel=((us_tab_arnold, 'Arnold'), (us_tab_prman, 'Renderman')) )
mc.showWindow(us_window)
def subdivideArnold(self, *args):
selection = mc.ls(sl=True)
aiSubdiv_iterations = mc.intSliderGrp(self.us_aiSubdiv_iterations, query=True, value=True)
aiSubdiv_check = mc.checkBoxGrp(self.us_aiSubdiv_check, query=True, value1=True)
if aiSubdiv_check == 1:
for sel in selection:
mc.setAttr("{0}.aiSubdivType".format(sel), 1)
mc.setAttr("{0}.aiSubdivIterations".format(sel), aiSubdiv_iterations)
else:
for sel in selection:
mc.setAttr("{0}.aiSubdivType".format(sel), 0)
return
utilityScripts()

# This was developed for a specific filename convention. Can be changed according to your needs
import hou, os, glob, shutil
# Get the caching node, name of the hip file, and full path to the hip file
node = hou.node('..')
hip_name = hou.hipFile.basename()
scene_path = hou.hipFile.path()
# Gather and Retreieve the Output paramter path, then check to see if it exists
dir_path = node.evalParm('sopoutput')
path = os.path.exists(dir_path)
# This function is designed to save the current scene but save a backup in the versioned folder
def save_scene(scene_path, dir_path):
# We need to get the last folder so we can save the current versioned scene into it
splitPath_toSimulation = (dir_path.split('/'))[:-1]
fullPath_toSimulation = '/'.join(splitPath_toSimulation)
# Store backup folder and scene location and check to see if it already exists
backup_folder = fullPath_toSimulation + '/backup'
backup_scene = backup_folder + '/' + hip_name
folder_check = os.path.exists(backup_folder)
# If the backup folder exists, save the scene
if folder_check:
hou.hipFile.save()
shutil.copy2(scene_path,backup_scene)
# If the backup folder does not exist, create it then save a new version
if folder_check == False:
os.makedirs(backup_folder)
hou.hipFile.save()
shutil.copy2(scene_path,backup_scene)
# If the path returns True, it will prompt the user to decide whether or not they want to Version Up or Overwrite the simulation version
if path:
buttons = ('Version Up', 'Overwrite', 'Cancel')
details = '''If you choose to version up, it will go to the next available version rather than \nthe next number in sequential order. \n\nIf you choose to Overwrite this version, you will need to confirm your choice.'''
version_option = hou.ui.displayMessage('This version already exists, do you want to Overwrite it or Version up?', buttons=buttons, details=details, default_choice=0, close_choice=-1)
# If they choose to Version Up, it will version up the code based on the next available number rather than the next number in sequential order
if version_option == 0:
# Gather the version parameter of the node, and retreive the current value
p_version = node.parm('version')
v_version = node.evalParm('version')
# Split the path based on the characters '/v' for versions
path_toVersions = dir_path.split('/v')[0]
# Concatenate the versions and the basename
list_versions = glob.glob(os.path.join(path_toVersions, 'v*'))
# Find the max version in the list of versioned folders and increase it by 1
max_version = max([int(os.path.splitext(os.path.basename(version))[0][1:]) for version in list_versions])
new_version = f'v{max_version + 1}'
new_dir_path = path_toVersions + '/' + new_version
os.makedirs(new_dir_path)
# Store the new version number and set it to the parameter
n_version = int(new_version.split('v')[1])
p_version.set(n_version)
print('Versioning up to ' + str(n_version))
backup_folder = new_dir_path + '/backup'
backup_scene = backup_folder + '/' + hip_name
os.makedirs(backup_folder)
hou.hipFile.save()
shutil.copy2(scene_path, backup_scene)
# If the user chooses to overwrite the simulation version, it will prompt them to confirm they want to do this before moving on
if version_option == 1:
d_buttons = ('Yes', 'WAIT NO')
double_option = hou.ui.displayMessage('Are you sure you want to Overwrite this version?', buttons=d_buttons, default_choice=1, close_choice=1)
# Let the user know that you will be overwriting the simulation and then move on to saving the scene
if double_option == 0:
print('Overwriting Current Simulation Version')
save_scene(scene_path, dir_path)
if version_option == 2:
print('Canceled')
# If path doesn't exist, save the scene, store backup and simulate
if path == False:
save_scene(scene_path, dir_path)
# This was developed for a specific filename convention. Can be modified to meet your needs
import hou, os, glob, re
# We need to grab hip file path, the path to the scenes, and scene name
hip_name = hou.hipFile.path()
scene_name = hip_name.split('/')[-1].split('_')[0]
path_to_scenes = os.path.dirname(hip_name)
# Get all the scene files associated with .hip, store them in a list with their names
get_scenes = glob.glob(os.path.join(path_to_scenes, scene_name + '*_v*.hip'))
scenes = []
for version in get_scenes:
scenes.append(os.path.basename(version))
# Split the extension off from the names, and store it
split_ext = scenes[-1].split('.')
ext = os.path.splitext(scenes[0])[1]
split = re.split(r'(_v)', split_ext[0])
user = hip_name.split('_')[-1].split('.')[0]
# Store the standard name in a variable
common = split[0] + split[1]
# Store the version number in a variable and increment it by 1, including some padding
versions = [int(file.split('_v')[1].split('.')[0].split('_')[0]) for file in scenes]
max_version = f'{max(versions) + 1:03}'
# Rebuild the name of the file
new_save_name = common + max_version + '_' + user + ext
# Save the file
hou.hipFile.save(file_name=path_to_scenes + '/' + new_save_name)
#------------------------------------------------------------------
import hou
import os
# Set the root context
root = hou.node('/obj/')
# Create the dictionary that will store the key and value pairs for the dark interface
dark_nw_boxes = {
'ASSETS': (0.281, 0.281, 0.278),
'GEO': (0.529, 0.596, 0.415),
'LGT': (0.980, 0.898, 0.533),
'FX': (0.455, 0.580, 0.917),
'RENDER': (0.709, 0.611, 0.882)
}
light_nw_boxes = {
'ASSETS': (1, 1, 1),
'GEO': (0.486, 0.772, 0.462),
'LGT': (1, 0.960, 0.407),
'FX': (0.266, 0.549, 0.796),
'RENDER': (0.521, 0.376, 0.658)
}
# Set the order by which the network boxes will be generated
nw_order = ['ASSETS', 'GEO', 'LGT', 'FX', 'RENDER']
# The network boxes will start at this position in the network
pos = 16
# Get the Houdini UI Theme (Generally Light or Dark)
def get_houdini_color_scheme(file_path):
color_scheme = {}
with open(file_path, 'r') as file:
current_section = None
for line in file:
line = line.strip()
# Skip empty lines and comments
if not line or line.startswith('#'):
continue
# Identify section
if line.endswith('";'):
current_section = line[0:-1]
if current_section.startswith('colors.scheme'):
color_scheme = current_section
key, value = color_scheme.split(':=')
color_scheme = value.strip()
return color_scheme
# Get the Houdini home directory and ui file stored in it
hip = hou.homeHoudiniDirectory()
pref = 'ui.pref'
file = os.path.join(hip, pref)
dark = '"Houdini Dark"'
# Store the Houdini UI Theme (Normally Dark or Light)
houdini_color_scheme = get_houdini_color_scheme(file)
# Create Horizontal Network Boxes, set size, comment, positions, and colors
for key in nw_order:
if (houdini_color_scheme == dark):
value = dark_nw_boxes[key]
else:
value = light_nw_boxes[key]
nb = root.createNetworkBox(name=key)
nb.setComment(key)
nb.setSize([8.0, 4.0])
nb.setColor(hou.Color(value))
nb.setPosition([0, pos])
pos -= 5
import hou
import os
# Set the root context
root = hou.node('/obj/')
# Create the dictionary that will store the key and value pairs for the dark interface
dark_nw_boxes = {
'ASSETS': (0.281, 0.281, 0.278),
'GEO': (0.529, 0.596, 0.415),
'LGT': (0.980, 0.898, 0.533),
'FX': (0.455, 0.580, 0.917),
'RENDER': (0.709, 0.611, 0.882)
}
# Create the dictionary that will store the key and value pairs for the light interface
light_nw_boxes = {
'ASSETS': (1, 1, 1),
'GEO': (0.486, 0.772, 0.462),
'LGT': (1, 0.960, 0.407),
'FX': (0.266, 0.549, 0.796),
'RENDER': (0.521, 0.376, 0.658)
}
# Set the order by which the network boxes will be generated
nw_order = ['ASSETS', 'GEO', 'LGT', 'FX', 'RENDER']
# The network boxes will start at this position in the network
pos = -16
# Get the Houdini UI Theme (Generally Light or Dark)
def get_houdini_color_scheme(file_path):
color_scheme = {}
with open(file_path, 'r') as file:
current_section = None
for line in file:
line = line.strip()
# Skip empty lines and comments
if not line or line.startswith('#'):
continue
# Identify section
if line.endswith('";'):
current_section = line[0:-1]
if current_section.startswith('colors.scheme'):
color_scheme = current_section
key, value = color_scheme.split(':=')
color_scheme = value.strip()
return color_scheme
# Get the Houdini home directory and ui file stored in it
hip = hou.homeHoudiniDirectory()
pref = 'ui.pref'
file = os.path.join(hip, pref)
dark = '"Houdini Dark"'
# Store the Houdini UI Theme (Normally Dark or Light)
houdini_color_scheme = get_houdini_color_scheme(file)
# Create Vertical Network Boxes, set size, comment, positions, and colors
for key in nw_order:
if (houdini_color_scheme == dark):
value = dark_nw_boxes[key]
else:
value = light_nw_boxes[key]
nb = root.createNetworkBox(name=key)
nb.setComment(key)
nb.setSize([4.0, 8.0])
nb.setColor(hou.Color(value))
nb.setPosition([pos, 0])
pos += 5

# Synchronize Directories Script v1.2
# Created by John Kesig
# This script is designed to synchronize one directory to another. Then it will generate a robocopy script that will get run and saved in the Windows Task Scheduler
# This was designed to be run while you are in the folder you want to synchronize. For that, I created a command in the right-click context menu to do this. You
# absolutely could change this to choose both source and destination directories. I also wanted this to be as native to the OS as possible so I didn't go with Python for this
Add-Type -AssemblyName System.Windows.Forms
# Gets and stores the starting folder
Function Source($sourceDirectory='')
{
$rootDirectory = Get-Item .
return $rootDirectory
}
# Prompts the user to select the destination directory they want to synchronize to
Function Destination($destinationDirectory='')
{
$folder = New-Object System.Windows.Forms.FolderBrowserDialog
$folder.Description = 'Choose Destination Folder to Synchronize'
if($folder.ShowDialog() -eq "Ok")
{
$folderpath += $folder.SelectedPath
}
return $folderpath
}
# Creates the Synchronization script to be used by the Task Scheduler
Function CreateSyncScript($syncScriptPath='')
{
# We have to do a bit of error checking here, because the spaces in file paths will create problems so I implemented an escape character
$sourceLocation = $source
$sourceLocation = $sourceLocation -replace ' ', '` '
$destinationLocation = $destination
$destinationLocation = $destinationLocation -replace ' ', '` '
# Create Log Name
$logName = '{0} Copy Log' -f $shortname
$logName = $logName -replace ' ', '` '
# This is where the powershell script will be stored. Feel free to change this if you want
$scriptDirectory = 'C:\SyncScripts\'
$logDirectory = 'C:\SyncScripts\Logs\'
# Test if the Script Directory exists. If it doesn't, it will create it.
if(!(Test-Path -PathType Container $scriptDirectory))
{
New-Item -ItemType Directory -Path $scriptDirectory
}
if(!(Test-Path -PathType Container $logDirectory))
{
New-Item -ItemType Directory -Path $logDirectory
}
# This is the script that will be run by Powershell to synchronize the two locations
$script = 'robocopy {0} {1} *.* -e /v /tee /log+:{2}{3}.txt' -f $sourceLocation,$destinationLocation,$logDirectory,$logName
# Test to see if the sync script file already exists. If it doesn't, it will create the file
$testPath = Test-Path -PathType Leaf -Path $scriptDirectory$sourceShortName.ps1
if($testPath -ne $true)
{
$scriptPath = New-Item -Path $scriptDirectory$sourceShortName.ps1
Set-Content -Path $scriptPath -Value $script
}
return $scriptPath
}
# Creates the Task for the Task Scheduler
Function CreateTask()
{
# Create everything associated with the Task
# The Name is the folder you are doing a robocopy from and to
$name = 'Synchronize {0} to {1}' -f $sourceShortName,$destinationShortName
# The action is going to be launched by Powershell and it will run the script located in the Script Path above
$action = New-ScheduledTaskAction -Execute 'PowerShell.exe' -Argument $scriptPath
# You can designate a time and frequency here
$trigger = New-ScheduledTaskTrigger -Daily -At '7:00 PM'
$description = 'Synchronizes {0} to {1}' -f $sourceShortName,$destinationShortName
# This script used to run when you go into idle, but sometimes people don't have Idle enabled so I removed it
$settings = New-ScheduledTaskSettingsSet -IdleDuration 00:15:00 -DontStopOnIdleEnd
$task = New-ScheduledTask -Description $description -Action $action -Trigger $trigger -Settings $settings
# Register the task to the Task Manager
Register-ScheduledTask -TaskName $name -InputObject $task
# Enable and Start the Task
Enable-ScheduledTask -TaskName $name
Start-ScheduledTask -TaskName $name
}
# Gather the source folder this script will pull from (It will take from the one you initiated the script in)
$source = Source
# Allow the user to choose where they want the files to be synchronized to
$destination = Destination
# Only get the names for the directories
$sourceShortName = $source|Split-Path -Leaf
$destinationShortName = $destination|Split-Path -Leaf
# Create the sync script that will be run for Task Scheduler
$syncScript = CreateSyncScript($source,$destination,$sourceShortName)
$scriptPath = $syncScript[1]
# Check to see if the $syncScript variable doesn't come back empty. IF it does then it prompts the user that a script has already been created
# If the script comes back with a path, it will create the task and prompt the user that everything has been completed
if($scriptPath -eq $null)
{
[System.Windows.Forms.MessageBox]::Show('Your Sync Script has already been created. Check your SyncScripts folder')
} else {
$task = CreateTask($sourceShortName,$destinationShortName,$scriptPath)
[System.Windows.Forms.MessageBox]::Show('Your Sync Task has been generated. Please go to Task Scheduler to see your sync job. A log will be generated the next time the task is run')
}

#!/bin/bash
## This script is used to bootstrap a machine and get it configured for Salt. An admin on the domain needs to input their credentials themselves and choose whether or not to install Salt Minion or Salt Master & Minion.
## We relied on Salt to install our Linux NVIDIA drivers but that is something you could do through here, you may end up with a mismatch in driver implementation later down the road though. Be careful.
# Gather user account for the domain
read -p 'Username for domain admin: ' ADUSER
while True
do
read -s -p 'Password for domain admin: ' ADPASS
echo
read -s -p 'Please re-enter domain admin password: ' ADPASS2
echo
if [[ "$ADPASS" = "$ADPASS2"]]; then
break
else
echo "Passwords did not match, please try again"
fi
done
while true;
do
read -p 'Installing Salt Master or Minion? [M/m]'
echo
read SALT
case $SALT in
[M]* ) useMaster=True; break;;
[m]* ) useMinion=false; break;;
* ) echo "Please use either 'M' to indicate Master or 'm' to indicate minion"
esac
done
PROXY= #REPLACE WITH PROXY "PATH:PORT"
# # Install Certificates
scp `# username@machinePath:/path/to/grab/certificate /destination/path/to/install/certificate`
update-ca-trust
# # Add proxy
echo "proxy=" "$PROXY" >> /etc/yum.conf
# # Install Updates
yum check-update
yum -y upgrade
# # Install EPEL Repo
yum -y install epel-release
yum check-update
# # Distable Firewall (This is your choice, for the env we ran we didn't need it)
systemctl stop firewalld
systemctl disable firewalld
# # Disable SELinux (Again this was our choice, you can change this)
sed -i '7s/.*/SELINUX=disabled/' /etc/selinux/config
setenforce 0
# # Setup Salt (We used salt instead of Ansible, you can take this out if you want)
curl --proxy $PROXY -o bootstrap-salt.sh -L https://bootstrap.saltproject.io
chmod 775 ./bootstrap-salt.sh
if [[ $SALT = true]]; then
while true;
do
./boostrap-salt.sh -M -H $PROXY stable
done
fi
if [[ $SALT = false]]; then
while false;
do
./bootstrap-salt.sh -H $PROXY stable
done
fi
sed -i '16s/.*/master: /' `# ADD SALT MASTER IP ADDRESS TO THE LEFT OF THE /` /etc/salt/minion
systemctl enable salt-minion.service
systemctl restart salt-minion.service
# Join domain
# You may not be able to log in via your AD credentials on a new machine build
# because this smb.conf file has changed, and uses a different kerberos method
# for instance. Please check it here, and match it in the salt smb_client config.
yum -y install realmd oddjob-mkhomedir oddjob samba-winbind-clients samba-winbind samba-common-tools samba-winbind-krb5-locator
mv /etc/samba/smb.conf /etc/samba/smb.conf.orig
echo "$ADPASS" | realm join --client-software=winbind --user=$ADUSER `#ADD DOMAIN PATH HERE`
sed -i '4s/.*/template homedir= \/home\/%U/' /etc/samba/smb.conf
sed -i '13s/.*/winbind use default domain = yes/' /etc/samba/smb.conf
systemctl restart winbind.service
# test that adding to the domain worked
id $ADUSER
#!/bin/bash
# This is a script based on RHEL distros. For DEB based distros, replace the yum with apt-get
ARGS=$(getopt --options 'bfhd:' --longoptions 'backend,frontend,help,destination:' -- "$@")
eval set --"$ARGS"
backend=false
frontend=false
help=false
destination=false
while true; do
case "$1" in
-b|--backend)
backend="true"
shift;;
-f|--frontend)
frontend="true"
shift;;
-h|--help)
help="true"
shift;;
-d|--destination)
destination="true";
dvalue="$2";
shift 2;;
--)
break;;
*)
echo -e "Error: Unknown option:" "$1"
exit 1;;
esac
done
if [ "$help" == true ]
then
echo "$(basename "$0") options available: [-b, --backend] [-f, --frontend] [-d, --destination <directory path>] [-h, --help]"
exit
fi
if [ "$dvalue" == "" ]
then
echo "Please input a destination directory"
echo "$dvalue"
exit
fi
# Check if sudo is being run on command
if [ "$EUID" -ne 0 ]
then
echo "Error: Please run as root"
exit
fi
# Check if Docker is installed on the machine
which docker &> /dev/null
if [ $? -ne 0 ]
then
echo "Docker is not installed. Installing Docker"
yum config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo -y
yum update -y
yum install docker-ce docker-ce-cli containerd.io -y
# Start Docker and Enable it so it runs automatically when Host is turned on
systemctl start docker
systemctl enable docker
# Add current user to the docker list of users
usermod -aG docker $USER
echo "Docker has been installed, started and enabled. Your user has been added to Docker"
fi
# Check if Git is installed on the machine
which git &> /dev/null
if [ $? -ne 0 ]
then
echo "Git is not installed. Installing Git"
yum install git -y
fi
# Check if Make is installed on the machine
which make &> /dev/null
if [ $? -ne 0 ]
then
echo "Make is not installed. Installing Make"
yum install make -y
fi
# Clone the Ayon Docker repository
repo_url="https://github.com/ynput/ayon-docker"
if [ "$destination" == "true" ]
then
mkdir "$dvalue/ayon"
git clone "$repo_url" "$dvalue/ayon"
else
git clone "$repo_url"
fi
cd "$dvalue/ayon"
docker compose up -d
make setup
# Run the make commands on the developer backend/frontend
if [ "$backend" == true ] && [ "$frontend" == true ]
then
make backend && make frontend
elif [ "$frontend" == true ]
then
make frontend
elif [ "$backend" == true ]
then
make backend
fi
# Finishing closing messages
if [ "$backend" == true ] && [ "$frontend" == true ]
then
echo -e "\nThe developer backend & frontend have also been setup\n"
elif [ "$backend" == true ]
then
echo -e "\nThe Backend has also been setup\n"
elif [ "$frontend" == true ]
then
echo -e "\nThe Frontend has also been setup\n"
fi
echo -e "Access your AYON Server here: http://localhost:5000"
echo -e "To access on another workstation, use this machines IP Address + Port number in your browser\n"