Skip to main content
# 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

Coming Soon

 

# 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"