Python Tools - Session 13B

Scene Archiver Complete and LOD Start

2020.04.09 - Session 13B

Session Navigation

Overview

This session completes the Scene Archiver tool and introduces LOD (Level of Detail) generation. The archiver now saves a copy of the scene, copies all referenced textures to a relative path, creates a zip archive, and reverts back to the original scene. We also begin a simple LOD tool that duplicates meshes and applies polyReduce at increasing levels.

Key Concepts

  • File operations with shutil - Copy files with shutil.copy2 and create zip archives with shutil.make_archive
  • Texture path retargeting - Find all file nodes, copy textures, and update paths to relative locations
  • Scene save/restore - Save to a new location, then revert to the original scene
  • Maya project management - Set and restore the Maya project directory with mel.setProject
  • LOD generation - Duplicate meshes and reduce polygon count with pm.polyReduce
  • LOD groups - Use Maya's LevelOfDetailGroup to organize LOD meshes

Source Code - archiver.py (Complete Version)

"""
#############################################################################
filename    archiver.py
author      [author]
course      [course]
Brief Description:
    Scene Archive Tool (Part 2)
    Project to cover the use of path manipulation and scene trawling
    Session 13B - Apr 9 2020
#############################################################################
"""
import pymel.core as pm
import os
import shutil

def archive_scene():
    """Small utility to archive the current scene
    This will:
        - Save a copy of the current scene
        - Copy all textures to a new location relative to the scene
        - Zip up the result
        - Revert back so it looks like nothing happened
    """

    # Get the current project directory
    current_wd = pm.workspace.getcwd()

    # Get full file path
    scene_path = pm.sceneName()
    print 'Scene Path = ', scene_path

    # Error check - bail if the scene isn't saved
    if scene_path == None or scene_path == '':
        print 'WARNING - Scene is not saved'
        return

    # Save the scene
    pm.saveFile()

    # Get the scene file name
    scene_name_with_ext = os.path.basename(scene_path)
    print 'Scene File = ', scene_name_with_ext

    # Get the name without extension
    scene_name = os.path.splitext(scene_name_with_ext)[0]
    print 'Scene Name = ', scene_name

    # Get scene directory
    scene_dir = os.path.dirname(scene_path)
    print 'Scene Dir = ', scene_dir

    # Create the target archive directory
    target_dir = os.path.join(scene_dir, scene_name)
    print 'Target Dir = ', target_dir

    # Exit if the target files exist
    if os.path.exists(target_dir):
        print 'WARNING - target exists, aborting'
        return

    # Create the target Maya scene path
    new_file_name = os.path.join(target_dir, scene_name_with_ext)
    print 'Target File = ', new_file_name

    # Create the target texture directory
    texture_dir = os.path.join(target_dir, 'textures')
    print 'Texture Dir = ', texture_dir

    # Ensure the directories exist
    if not os.path.exists(texture_dir):
        os.makedirs(texture_dir)

    # Set the current project
    pm.mel.setProject(target_dir)

    # Save the file to the new location
    pm.saveAs(new_file_name)

    for file_node in pm.ls(type='file'):
        # Get the texture path from the current file node
        tex_path = file_node.fileTextureName.get()
        # Check if the file exists on disk
        if os.path.exists(tex_path):
            # Assemble the path for the target texture
            tex_file_name = os.path.basename(tex_path)
            new_tex_path = os.path.join(texture_dir, tex_file_name)

            # Check if the target file already exists
            if not os.path.exists(new_tex_path):
                print 'Copying %s to %s' %(tex_path, new_tex_path)
                shutil.copy2(tex_path, new_tex_path)

                # Set the new path to be relative
                new_tex_rel_path = os.path.join('textures', tex_file_name)
                file_node.fileTextureName.set(new_tex_path)

    # Save the scene now we have the file path changes
    pm.saveFile()

    # Open the original file
    pm.openFile(scene_path, force=True)

    # Set the project back to what it was when we started
    pm.mel.setProject(current_wd)

    # Zip the new directory up to a zip file with the same name
    shutil.make_archive(target_dir, 'zip', target_dir)

Source Code - lod_start.py

import pymel.core as pm

def create_lod_level(mesh_transform, reduction):
    dupe = pm.duplicate(mesh_transform)[0]
    print 'Reducing %s by %i percent' %(dupe, reduction)
    pm.polyReduce(dupe, percentage=reduction)
    return dupe

def make_lods(mesh_transform, lod_count, reduction):
    all_meshes = [mesh_transform]
    for i in range(1, lod_count):
        new_lod = create_lod_level(mesh_transform, reduction)
        all_meshes.append(new_lod)
        mesh_transform = new_lod

    pm.select(all_meshes)
    pm.mel.LevelOfDetailGroup()
    return all_meshes
← Prev Next ->