1

Add tools for launching lightly toasted Houdini

This commit is contained in:
Evan Pratten 2023-10-05 19:33:00 -04:00
parent 241f1a44d7
commit ba1a922470
14 changed files with 395 additions and 6 deletions

3
.gitignore vendored
View File

@ -1 +1,2 @@
/configs/remmina /configs/remmina
__pycache__

View File

@ -5,6 +5,10 @@
"*.md.liquid": "markdown", "*.md.liquid": "markdown",
"*.js.liquid": "liquid-javascript", "*.js.liquid": "liquid-javascript",
"*.css.liquid": "liquid-css", "*.css.liquid": "liquid-css",
"*.scss.liquid": "liquid-scss", "*.scss.liquid": "liquid-scss"
} },
// Add python_modules to the python path
"python.analysis.extraPaths": [
"./python_modules"
],
} }

View File

@ -0,0 +1 @@
preference general.desk.val "Solaris"

106
configs/scripts/houdini-tool.py Executable file
View File

@ -0,0 +1,106 @@
#! /usr/bin/env python3
# fmt:off
import sys
import os
from pathlib import Path
sys.path.append((Path(os.environ["EWCONFIG_ROOT"]) / "python_modules").as_posix())
# fmt:on
import argparse
import subprocess
import logging
from ewpipe.common.dirs import HOUDINI_PROJECTS_DIR
from ewpipe.houdini.editions import (
get_binary_name_for_edition,
get_houdini_edition_args,
HOU_EDITIONS,
)
from ewpipe.houdini.installations import get_houdini_installation_path
from ewpipe.common.logging import configure_logging
from ewpipe.common.env import diff_from_current_env
logger = logging.getLogger(__name__)
def main() -> int:
# Handle program arguments
ap = argparse.ArgumentParser(
prog="houdini-tool",
description="Evan's tool for launching and managing Houdini",
)
ap.add_argument(
"--type",
"-t",
help="Houdini type",
choices=HOU_EDITIONS,
default="apprentice",
)
ap.add_argument(
"--project",
"-p",
help="Name of the project to open or create. May also be a direct path",
type=str,
required=True,
)
ap.add_argument(
"--hou-version",
help="Houdini version to use. Defaults to latest",
type=str,
default=None,
)
ap.add_argument(
"--no-project-env", help="Disables setting $HIP and $JOB", action="store_true"
)
ap.add_argument("--verbose", "-v", help="Verbose output", action="store_true")
args = ap.parse_args()
# Set up verbose logging if requested
configure_logging(verbose=args.verbose)
# Get the houdini path
hou_path = get_houdini_installation_path(version=args.hou_version)
if not hou_path:
logger.error("Could not find Houdini installation")
return 1
logger.info(f"Selected Houdini {hou_path.name[3:]} from {hou_path}")
# Determine the project path
project_path = Path(args.project)
if not project_path.is_absolute():
# This is a project name, not a path
project_path = HOUDINI_PROJECTS_DIR / project_path
logger.info(f"Opening project from: {project_path}")
# If the directory does not exist, create
project_path.mkdir(parents=True, exist_ok=True)
# If allowed, set up env vars
environment_vars = os.environ.copy()
environment_vars["HOUDINI_SCRIPT_DEBUG"] = "1"
environment_vars["HOUDINI_SPLASH_MESSAGE"] = "Loading with custom scripts"
environment_vars["HOUDINI_CONSOLE_PYTHON_PANEL_ERROR"] = "1"
if not args.no_project_env:
# environment_vars["HIP"] = str(project_path)
environment_vars["JOB"] = str(project_path)
environment_vars["HOUDINI_HIP_DEFAULT_NAME"] = f"{project_path.name}.hip"
# Figure out what has changed in the environment and print the changes
env_changes = diff_from_current_env(environment_vars)
if env_changes:
logger.info("Environment changes:")
for key, value in env_changes.items():
logger.info(f" ${key}: {value}")
# Launch houdini
cmd = [
str(hou_path / "bin" / get_binary_name_for_edition(args.type)),
"-foreground",
] + get_houdini_edition_args(args.type)
logger.info(f"Running: {' '.join(cmd)}")
status = subprocess.run(cmd, env=environment_vars, cwd=project_path).returncode
return status
if __name__ == "__main__":
sys.exit(main())

9
configs/scripts/hython-latest Executable file
View File

@ -0,0 +1,9 @@
#! /bin/bash
set -e
# Find hython
HOUDINI_PATH=`python3 ~/.config/ewconfig/python_modules/ewpipe/houdini/installations.py`
HYTHON_PATH=$HOUDINI_PATH/bin/hython
# Execute hython, passing through all arguments
$HYTHON_PATH $@

0
configs/scripts/run-logid Normal file → Executable file
View File

47
configs/scripts/usdnc-to-usd.py Executable file
View File

@ -0,0 +1,47 @@
#! /usr/bin/env -S hython-latest -I
import argparse
import sys
from pxr import Usd
from pathlib import Path
def main() -> int:
# Handle program arguments
ap = argparse.ArgumentParser(
prog="usdnc-to-usd", description="Convert USDNC files to USD"
)
ap.add_argument("input", help="Input file", type=Path)
ap.add_argument(
"--output",
"-o",
help="Output file. Defaults to the input file with a new extension.",
type=Path,
default=None,
)
ap.add_argument(
"--format",
"-f",
help="Output format. Defaults to usda.",
type=str,
default="usda",
choices=["usda", "usdc"],
)
args = ap.parse_args()
# Read the input file
print(f"Opening stage from: {args.input}")
stage = Usd.Stage.Open(args.input.as_posix())
# Determine the output file
if not args.output:
args.output = args.input.with_suffix(f".{args.format}")
# Write the output file
print(f"Writing stage to: {args.output}")
stage.Export(args.output.as_posix())
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -74,6 +74,8 @@ Host sdf.org *.sdf.org
# Guru # Guru
Host *.gurustudio.com Host *.gurustudio.com
User "guru-domain\\epratten" User "guru-domain\\epratten"
Host td-prod td-prod2 td-prod3 td-prod4
User guru
# Personal Infra # Personal Infra
Host oci-arm Host oci-arm

View File

@ -55,6 +55,9 @@
~/bin/run-logid: ~/bin/run-logid:
path: configs/scripts/run-logid path: configs/scripts/run-logid
mode: 755 mode: 755
~/bin/houdini-tool: configs/scripts/houdini-tool.py
~/bin/hython-latest: configs/scripts/hython-latest
~/bin/usdnc-to-usd: configs/scripts/usdnc-to-usd.py
# Systemd Services # Systemd Services
# ~/.config/systemd/user/logid.service: configs/systemd/user/logid.service # ~/.config/systemd/user/logid.service: configs/systemd/user/logid.service
@ -71,6 +74,7 @@
~/.config/termux/termux.properties: configs/termux/termux.properties ~/.config/termux/termux.properties: configs/termux/termux.properties
~/.config/user-tmpfiles.d/discord-rpc.conf: configs/user-tmpfiles.d/discord-rpc.conf ~/.config/user-tmpfiles.d/discord-rpc.conf: configs/user-tmpfiles.d/discord-rpc.conf
~/.config/logid/logid.cfg: configs/logid/logid.cfg ~/.config/logid/logid.cfg: configs/logid/logid.cfg
~/houdini19.5/scripts: configs/houdini19.5/scripts
- shell: - shell:
# Make sure we have our git modules # Make sure we have our git modules
@ -78,9 +82,7 @@
# Install SSH config # Install SSH config
- [sh ./helpers/install-ssh-config.sh, Installing SSH config] - [sh ./helpers/install-ssh-config.sh, Installing SSH config]
# Ensure that all downloaded scripts are executable # Ensure that all downloaded scripts are executable
- [chmod +x configs/scripts/catto, Making catto executable] - [chmod +x configs/scripts/*, Making bin scripts executable]
- [chmod +x configs/scripts/aspath, Making aspath executable]
- [chmod +x configs/scripts/fetch-steamdeck-screenshots, Making fetch-steamdeck-screenshots executable]
- [chmod +x configs/nautilus/scripts/*, Making nautilus scripts executable] - [chmod +x configs/nautilus/scripts/*, Making nautilus scripts executable]
# Configure GNOME # Configure GNOME
- [sh ./helpers/configure-gnome.sh, Configuring GNOME] - [sh ./helpers/configure-gnome.sh, Configuring GNOME]

View File

@ -0,0 +1,13 @@
from pathlib import Path
DCC_DATA_BASE_DIR = Path.home() / "Videos" / "DCC"
"""The base directory for storing data across DCCs"""
HOUDINI_BASE_DIR = DCC_DATA_BASE_DIR / "Houdini"
"""The base directory for storing Houdini data"""
HOUDINI_PROJECTS_DIR = HOUDINI_BASE_DIR / "Projects"
"""The base directory for storing Houdini projects"""
BLENDER_BASE_DIR = DCC_DATA_BASE_DIR / "Blender"
"""The base directory for storing Blender data"""

View File

@ -0,0 +1,31 @@
import os
from typing import Dict
def diff_environments(env_1: Dict[str, str], env_2: Dict[str, str]) -> Dict[str, str]:
"""Diff two environments.
Args:
env_1 (Dict[str,str]): First environment
env_2 (Dict[str,str]): Second environment
Returns:
Dict[str,str]: Difference between the two environments
"""
return {
key: value
for key, value in env_1.items()
if key not in env_2 or env_2[key] != value
}
def diff_from_current_env(new_env: Dict[str, str]) -> Dict[str, str]:
"""Diff the current environment from the given environment.
Args:
new_env (Dict[str, str]): New environment
Returns:
Dict[str, str]: Difference between the current environment and the given environment
"""
return diff_environments(os.environ, new_env) # type: ignore

View File

@ -0,0 +1,8 @@
import logging
def configure_logging(verbose: bool = False):
logging.basicConfig(
level=logging.DEBUG if verbose else logging.INFO,
format="%(levelname)s:\t%(message)s",
)

View File

@ -0,0 +1,36 @@
from typing import List
HOU_EDITIONS = ["core", "fx", "indie", "apprentice"]
"""All possible Houdini editions."""
def get_binary_name_for_edition(edition: str) -> str:
"""Get the appropriate binary name for the given Houdini edition.
Args:
edition (str): Hooudini edition
Returns:
str: Binary name
"""
if edition in ["core", "fx"]:
return f"houdini{edition}"
else:
return "houdini"
def get_houdini_edition_args(edition: str) -> List[str]:
"""Get the appropriate arguments to launch a given Houdini edition.
Args:
edition (str): Houdini edition
Returns:
List[str]: Arguments
"""
if edition in ["indie", "apprentice"]:
return [f"-{edition}"]
else:
return []

View File

@ -0,0 +1,129 @@
import logging
import platform
import argparse
import sys
from pathlib import Path
from typing import Optional
logger = logging.getLogger(__name__)
def get_default_houdini_installation_base_path() -> Path:
"""Get the default Houdini installation base path.
Returns:
Path: Default Houdini installation base path
"""
if platform.system() == "Linux":
return Path("/opt")
elif platform.system() == "Windows":
return Path("C:/Program Files/Side Effects Software")
else:
raise RuntimeError(f"Unsupported platform: {platform.system()}")
def find_latest_houdini_installation(base_path: Path) -> Optional[Path]:
"""Find the latest Houdini installation in the given base path.
Args:
base_path (Path): Base path to look for Houdini installations in.
Returns:
Optional[Path]: Houdini installation path if found
"""
logger.debug(f"Looking for the latest Houdini installation in: {base_path}")
# Look for possible houdini installations
if platform.system() == "Linux":
possible_installations = sorted(base_path.glob("hfs*"))
elif platform.system() == "Windows":
possible_installations = sorted(base_path.glob("Houdini *"))
else:
raise RuntimeError(f"Unsupported platform: {platform.system()}")
logger.debug(
f"Search found the following Houdini installations: {[str(i) for i in possible_installations]}"
)
# Remove `Houdini Server` if it exists
possible_installations = [
installation
for installation in possible_installations
if "Server" not in installation.name
]
# If there are no installations, return None
if not possible_installations:
return None
# Otherwise, return the latest installation
latest_installation = possible_installations[-1]
logger.debug(f"Latest Houdini installation: {latest_installation}")
return latest_installation
def get_houdini_installation_path(
version: Optional[str] = None,
base_path: Optional[Path] = None,
not_exists_ok: bool = False,
) -> Optional[Path]:
"""Get the path to the Houdini installation for the given version.
Args:
version (Optional[str], optional): Houdini version to target. Defaults to None.
not_exists_ok (bool, optional): If true, allows bad paths to be returned. Defaults to False.
Raises:
RuntimeError: Thrown if the platform is not supported.
Returns:
Optional[Path]: Path to the Houdini installation if found
"""
logger.debug(f"Finding Houdini installation for version: {version}")
# Get the default installation base path
if not base_path:
base_path = get_default_houdini_installation_base_path()
logger.debug(f"Searching for Houdini installations in: {base_path}")
# If we don't have a version, find the latest installation
if not version:
logger.debug("No version specified, finding latest installation")
return find_latest_houdini_installation(base_path)
# Otherwise, find the installation for the given version
if platform.system() == "Linux":
installation_path = base_path / f"hfs{version}"
elif platform.system() == "Windows":
installation_path = base_path / f"Houdini {version}"
else:
raise RuntimeError(f"Unsupported platform: {platform.system()}")
# If the installation path does not exist, return None
if (not installation_path.exists()) and not not_exists_ok:
logger.debug(f"Installation path does not exist: {installation_path}")
return None
# Otherwise, return the installation path
logger.debug(f"Found installation path: {installation_path}")
return installation_path
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("--version", "-v", help="Houdini version", type=str)
ap.add_argument("--base-path", "-b", help="Houdini base path", type=str)
ap.add_argument("--not-exists-ok", help="Allow bad paths", action="store_true")
args = ap.parse_args()
result = get_houdini_installation_path(
version=args.version,
base_path=Path(args.base_path) if args.base_path else None,
not_exists_ok=args.not_exists_ok,
)
if not result:
print("Could not find Houdini", file=sys.stderr)
sys.exit(1)
print(result)
sys.exit(0)