- Implemented `image_plane.py` to build the Image Plane Geometry Nodes group with customizable parameters for geometry, subdivide, and thickness. - Created `image_plane_validation.py` to validate the functionality of the image plane, ensuring correct geometry generation and material assignment. - Developed `material_layers.py` to construct shader node groups for image materials, including depth and normal handling. - Authored `plane_layouts.py` to define node arrangements for image geometry assets. - Introduced `shadeless.py` to create a shadeless shader node group that displays color without scene lighting while preserving shadows and reflections.
92 lines
2.5 KiB
Python
92 lines
2.5 KiB
Python
import argparse
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
BLENDER_SCRIPTS = (
|
|
"asset_library.py",
|
|
"asset_validation.py",
|
|
"image_plane_validation.py",
|
|
"cutout_validation.py",
|
|
)
|
|
|
|
|
|
def project_env_value(name, path=None):
|
|
env_path = path or PROJECT_ROOT / ".env"
|
|
if not env_path.is_file():
|
|
return None
|
|
for raw_line in env_path.read_text(encoding="utf-8").splitlines():
|
|
line = raw_line.strip()
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
if line.startswith("export "):
|
|
line = line.removeprefix("export ").lstrip()
|
|
key, separator, value = line.partition("=")
|
|
if separator and key.strip() == name:
|
|
value = value.strip()
|
|
if len(value) >= 2 and value[0] == value[-1] and value[0] in "\"'":
|
|
value = value[1:-1]
|
|
return value or None
|
|
return None
|
|
|
|
|
|
def blender_path(explicit=None):
|
|
candidate = (
|
|
explicit
|
|
or os.environ.get("BLENDER_BIN")
|
|
or project_env_value("BLENDER_BIN")
|
|
or shutil.which("blender")
|
|
)
|
|
if not candidate:
|
|
raise RuntimeError(
|
|
"Blender not found: pass --blender, set BLENDER_BIN, add it to .env, "
|
|
"or add blender to PATH"
|
|
)
|
|
path = Path(candidate).expanduser()
|
|
if not path.is_file():
|
|
raise RuntimeError(f"Blender executable does not exist: {path}")
|
|
return path
|
|
|
|
|
|
def run(command):
|
|
print("+", subprocess.list2cmdline([str(part) for part in command]), flush=True)
|
|
subprocess.run(command, cwd=PROJECT_ROOT, check=True)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Build and validate O_AnyImage.blend.",
|
|
)
|
|
parser.add_argument(
|
|
"--blender",
|
|
help="Blender executable; overrides BLENDER_BIN, .env, and PATH.",
|
|
)
|
|
parser.add_argument(
|
|
"--skip-tests",
|
|
action="store_true",
|
|
help="Skip the Python test suite.",
|
|
)
|
|
options = parser.parse_args()
|
|
executable = blender_path(options.blender)
|
|
|
|
for script_name in BLENDER_SCRIPTS:
|
|
run(
|
|
(
|
|
executable,
|
|
"--factory-startup",
|
|
"--background",
|
|
"--python",
|
|
PROJECT_ROOT / "tools" / "node_assets" / script_name,
|
|
)
|
|
)
|
|
if not options.skip_tests:
|
|
run((sys.executable, "-m", "pytest"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|