Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/site.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Deploy the static marketing site (site/) to GitHub Pages at
# render.preshow.link — same mechanism as UV Studio's uv.preshow.link.
#
# The deploy is OPT-IN — it only runs when the repo variable
# DEPLOY_PAGES == 'true'. To enable it (needs a public repo OR a plan with
# private Pages, e.g. Pro):
# repo Settings → Pages → Source: "GitHub Actions"
# gh variable set DEPLOY_PAGES --body true
# Then add a DNS CNAME record: render.preshow.link → hayhamlt.github.io
# The custom domain is baked in via the CNAME file below, so the site is
# served at root — no subpath rewrites needed.
name: Site

on:
push:
branches: [main]
paths: ['site/**', '.github/workflows/site.yml']
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: true

jobs:
deploy:
if: ${{ vars.DEPLOY_PAGES == 'true' }}
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- run: echo 'render.preshow.link' > site/CNAME
- uses: actions/upload-pages-artifact@v3
with:
path: site
- id: deploy
uses: actions/deploy-pages@v4
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Render Mapper Pro
# PrevizRender <sub>(formerly Render Mapper Pro)</sub>

A standalone desktop app that maps videos onto 3D‑scene materials and renders them headlessly — built for LED‑wall / screen‑content workflows where one scene drives many video surfaces.

PrevizRender is one of three sibling tools built by [hamLT](https://motion.hamlt.com) and powered by [Toy Robot Media](https://toyrobotmedia.com): **[Preshow.link](https://www.preshow.link)** plans and previzes the show, **[UV Studio](https://uv.preshow.link)** maps artwork onto the venue's screens, and **PrevizRender** renders the comps locally. Product site: **[render.preshow.link](https://render.preshow.link)** (deployed from [`site/`](site/) via GitHub Pages, same as UV Studio's). (Installer and bundle artifacts keep the `RenderMapperPro` name so auto-updates and install paths stay valid.)

It supports **two render backends**, chosen automatically by scene type: **Blender** (`.blend`, `.fbx`, `.usd`, …) and **Cinema 4D + Redshift** (`.c4d`). It runs the renderer in the background, so a renderer crash can never take down the app, and ships bundled static `ffmpeg`/`ffprobe` so audio muxing and clip probing work out of the box.

![Render Mapper Pro](docs/screenshots/render-mapper-pro.png)
![PrevizRender](docs/screenshots/previzrender.png)

## Download

Expand Down
34 changes: 22 additions & 12 deletions app_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ def __init__(self) -> None:
self._c4dpy_path = _find_c4dpy() # Cinema 4D headless Python, if installed
self._deadline_repo_path = ""
self._deadline_command_path = ""
self._deadline_job_name_template = "Render Mapper Pro Job - {scene_name}"
self._deadline_job_name_template = f"{APP_NAME} Job - {{scene_name}}"
self._deadline_comment = ""
self._discovered_materials: list[str] = []
self._discovered_cameras: list[str] = []
Expand Down Expand Up @@ -728,7 +728,7 @@ def _guide_sections() -> list[tuple[str, str]]:
return [
("Getting Started", """
<h2>Welcome</h2>
<p class="lead">Render Mapper Pro maps your videos onto a 3D scene's
<p class="lead">PrevizRender maps your videos onto a 3D scene's
materials and renders them — on your machine or a render farm. Each tab
above covers one part of the app; here's the whole flow first.</p>
<p><img src="main-window.png" width="640"></p>
Expand Down Expand Up @@ -1341,13 +1341,23 @@ def centered(w):

name = centered(QLabel(APP_NAME))
name.setStyleSheet(f"color:{pal.text}; font-size:19px; font-weight:700; margin-top:8px;")
ver = centered(QLabel(f"Version {APP_VERSION}"))
ver = centered(QLabel(f"Version {APP_VERSION} · formerly Render Mapper Pro"))
ver.setStyleSheet(f"color:{pal.text_muted}; font-size:12px;")
desc = centered(QLabel("Automated video-texture mapping and headless\n"
"rendering — Blender, Cinema 4D and three.js."))
desc.setStyleSheet(f"color:{pal.text_muted}; font-size:12px; margin-top:8px;")
desc.setWordWrap(True)

family = centered(QLabel(
'Sibling apps: '
f'<a style="color:{pal.accent}; text-decoration:none;" '
'href="https://www.preshow.link">Preshow.link</a> for show previz · '
f'<a style="color:{pal.accent}; text-decoration:none;" '
'href="https://uv.preshow.link">UV Studio</a> for screen UVs'))
family.setStyleSheet(f"color:{pal.text_muted}; font-size:12px; margin-top:4px;")
family.setOpenExternalLinks(True)
family.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)

sep = QFrame()
sep.setFrameShape(QFrame.Shape.HLine)
sep.setStyleSheet(f"color:{pal.border}; margin:14px 40px;")
Expand Down Expand Up @@ -2737,7 +2747,7 @@ def _job_engine(j: RenderJob) -> str:
error(
self, "App Component Missing",
"The app's render component couldn't be found — the installation may be "
f"damaged. Reinstall Render Mapper Pro.\n\nDetails: {exc}")
f"damaged. Reinstall {APP_NAME}.\n\nDetails: {exc}")
self._is_rendering = False
return
# Bundled ffmpeg path for the workers: C4D always muxes its movie with it,
Expand Down Expand Up @@ -3456,8 +3466,8 @@ def _open_path(path: str) -> None:
# ── Project save/open ────────────────────────────────────────────────
def _save_project(self) -> None:
p, _ = QFileDialog.getSaveFileName(
self, "Save Project", str(Path.home() / f"render_mapper_project{PROJECT_EXT}"),
f"Render Mapper Project (*{PROJECT_EXT})")
self, "Save Project", str(Path.home() / f"previzrender_project{PROJECT_EXT}"),
f"PrevizRender Project (*{PROJECT_EXT})")
if not p:
return
if not p.lower().endswith(PROJECT_EXT):
Expand All @@ -3475,7 +3485,7 @@ def _save_project(self) -> None:
def _open_project(self) -> None:
p, _ = QFileDialog.getOpenFileName(
self, "Open Project", str(Path.home()),
f"Render Mapper Project (*{PROJECT_EXT})")
f"PrevizRender Project (*{PROJECT_EXT})")
if not p:
return
try:
Expand Down Expand Up @@ -3584,7 +3594,7 @@ def _send_test() -> None:
if not self._discord_webhook:
self._show_toast("Enter a webhook URL first.", "warning")
return
self._dispatch_discord("✅ Test notification from Render Mapper Pro")
self._dispatch_discord(f"✅ Test notification from {APP_NAME}")
self._show_toast("Test sent — check Discord (and Live Logs for errors).", "info")

test_btn.clicked.connect(_send_test)
Expand Down Expand Up @@ -3981,9 +3991,9 @@ def _apply_profile_data(self, d: dict) -> None:
self.deadline_panel.dl_chunk_spin.setValue(int(d.get("deadline_chunk_size", 1)))
self.deadline_panel.dl_suspended_cb.setChecked(bool(d.get("deadline_suspended", False)))
self.deadline_panel.dl_submit_scene_cb.setChecked(bool(d.get("deadline_submit_scene", True)))
self._deadline_job_name_template = str(d.get("deadline_job_name_template", "Render Mapper Pro Job - {scene_name}"))
self._deadline_job_name_template = str(d.get("deadline_job_name_template", f"{APP_NAME} Job - {{scene_name}}"))
if self._deadline_job_name_template == "BlenderRender Job - {scene_name}": # migrate old app name
self._deadline_job_name_template = "Render Mapper Pro Job - {scene_name}"
self._deadline_job_name_template = f"{APP_NAME} Job - {{scene_name}}"
self.deadline_panel.dl_name_template_edit.setText(self._deadline_job_name_template)
self.deadline_panel.dl_machine_limit_spin.setValue(int(d.get("deadline_machine_limit", 0)))
self.deadline_panel.dl_limits_edit.setText(str(d.get("deadline_limits", "")))
Expand Down Expand Up @@ -4185,7 +4195,7 @@ def note_missing(vp: str, vn: str) -> None:
deadline_chunk_size=int(jd.get("deadline_chunk_size", 1)),
deadline_suspended=bool(jd.get("deadline_suspended", False)),
deadline_submit_scene=bool(jd.get("deadline_submit_scene", True)),
deadline_job_name_template=str(jd.get("deadline_job_name_template", "Render Mapper Pro Job - {scene_name}")),
deadline_job_name_template=str(jd.get("deadline_job_name_template", f"{APP_NAME} Job - {{scene_name}}")),
deadline_machine_limit=int(jd.get("deadline_machine_limit", 0)),
deadline_limits=str(jd.get("deadline_limits", "")),
deadline_command_path=str(jd.get("deadline_command_path", "")),
Expand Down Expand Up @@ -4672,7 +4682,7 @@ def run_qt_app() -> None:
if probe.state() == QLocalSocket.LocalSocketState.ConnectedState:
probe.waitForBytesWritten(250)
probe.disconnectFromServer()
print("Render Mapper Pro is already running — focusing the existing window.", file=sys.stderr)
print(f"{APP_NAME} is already running — focusing the existing window.", file=sys.stderr)
return
# No live instance; clear any stale socket and become the server.
QLocalServer.removeServer(SINGLE_INSTANCE_KEY)
Expand Down
6 changes: 5 additions & 1 deletion app_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@
tag whose value doesn't match this string.
"""
__version__ = "1.8.34"
APP_NAME = "Render Mapper Pro" # display name; single source shared by the app + mixins
# Display name; single source shared by the app + mixins. "PrevizRender" is
# the product name (formerly "Render Mapper Pro"); installer/bundle artifact
# names in BlenderVideoMapper.spec and installer/windows.iss intentionally keep
# the old name so the auto-update chain and install paths stay valid.
APP_NAME = "PrevizRender"
3 changes: 2 additions & 1 deletion app_window/reporting_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from datetime import datetime
from pathlib import Path

from app_version import APP_NAME
from app_window.base import _WindowMembers
from core.logging_setup import get_logger
from core.metrics import estimate_energy_cost
Expand Down Expand Up @@ -105,7 +106,7 @@ def _build_html_report(self) -> str:
'.err{color:#ed4245;font-family:ui-monospace,monospace;font-size:12px;'
'white-space:pre-wrap;margin:6px 0}.brand{color:#e8833a;font-weight:700}'
'</style></head><body>'
'<h1><span class="brand">Render Mapper Pro</span> — Render Report</h1>'
f'<h1><span class="brand">{APP_NAME}</span> — Render Report</h1>'
f'<div class="sub">{stamp} · Scene: {scene}</div>'
'<table><thead><tr><th>Job</th><th>Status</th><th>Frames</th>'
'<th>Duration</th><th>Avg/frame</th><th>Est. Cost</th></tr></thead>'
Expand Down
2 changes: 1 addition & 1 deletion core/logging_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def setup_logging(log_path: Path | str | None = None, level: int = logging.INFO,

logger.propagate = False
logger.info("%s", "=" * 60)
logger.info("Render Mapper Pro %s — session start", version or "?")
logger.info("PrevizRender %s — session start", version or "?")
return logger


Expand Down
Binary file added docs/screenshots/previzrender.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/screenshots/render-mapper-pro.png
Binary file not shown.
Binary file added site/img/app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/img/toy-robot-media-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading