Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: Render / Turntable #30

Closed
wants to merge 101 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
cd7fc55
first commit, adding playblast module
Sasbom Nov 27, 2023
32b90dc
added utility and expanded on renderplayblast args and processing
Sasbom Nov 27, 2023
663f958
parsing complexity argument better, fixed walrus bugs
Sasbom Nov 28, 2023
eb84c53
slight refactor
Sasbom Nov 28, 2023
d04aad3
ConverFramePlaceholderblabla does internal None checking, so removed …
Sasbom Nov 28, 2023
bbac50c
Merge branch 'BigRoy:main' into enhancement/usdview_render_turntable
Sasbom Nov 28, 2023
8b9f7e6
Removed hardcoded complexity presets, added sanity checking for rende…
Sasbom Nov 28, 2023
b6c429c
Complete framerecorder initialization setup
Sasbom Nov 28, 2023
623f105
Merge branch 'BigRoy:main' into enhancement/usdview_render_turntable
Sasbom Nov 29, 2023
13f62c4
resources_rc added
Sasbom Nov 29, 2023
6d1e89e
added menu, functionality for snapshot. Doesn't work as intended with…
Sasbom Nov 29, 2023
94716ab
snapshot fix! Works now. Still needs major cleanup.
Sasbom Nov 29, 2023
14e5e00
Merge branch 'BigRoy:main' into enhancement/usdview_render_turntable
Sasbom Nov 29, 2023
028fdd4
changed logic in case no camera is selected to match usdrecord
Sasbom Nov 29, 2023
583f35b
Merge branch 'enhancement/usdview_render_turntable' of https://github…
Sasbom Nov 29, 2023
d7f4b9d
house cleaning, suggested bugfixes
Sasbom Nov 29, 2023
09a041b
slowly but surely adding methods to deal with cameras
Sasbom Nov 30, 2023
d69188d
added distance calculation function while downstairs neighbours are d…
Sasbom Nov 30, 2023
962ab0c
finished clipping planes function.
Sasbom Nov 30, 2023
b86915c
finished calculate camera position function.
Sasbom Nov 30, 2023
6b293db
cross platform catch for non-OGL native systems (macosx)
Sasbom Nov 30, 2023
1b934c1
Completed routine to add a framing camera!
Sasbom Nov 30, 2023
6cc9386
Merge branch 'BigRoy:main' into enhancement/usdview_render_turntable
Sasbom Nov 30, 2023
e0314a1
added fit parameter to add some padding
Sasbom Nov 30, 2023
f93c85b
micro commit to change a variable name in a function to something mor…
Sasbom Nov 30, 2023
4bbdb2d
Created render_util.turntable,
Sasbom Dec 1, 2023
271917b
bugfixes + exposed turntable render as temp functionality.
Sasbom Dec 1, 2023
60801ee
bugfix: adjusted transforms when framing camera as turntable camera
Sasbom Dec 1, 2023
b00d12f
added some utility to deal with the frame argument
Sasbom Dec 4, 2023
cdf43c0
made tuples_to_frames_string.tuple_gen way more sane
Sasbom Dec 4, 2023
9800a4b
made functions way less ridiculous
Sasbom Dec 4, 2023
218e7c1
refactored some things, added dialog helpers
Sasbom Dec 4, 2023
fa016b3
notes
Sasbom Dec 4, 2023
09a3104
top level function padding
Sasbom Dec 5, 2023
02be61e
Merge branch 'BigRoy:main' into enhancement/usdview_render_turntable
Sasbom Dec 5, 2023
38c53fa
Proposal: add traverse_prim_descendants to lib.usd
Sasbom Dec 5, 2023
8a1991f
cleaned up test
Sasbom Dec 5, 2023
fc4be96
struggling to do reparenting properly
Sasbom Dec 5, 2023
5f8ad2d
am no longer confused by reparenting, we're gonna do references.
Sasbom Dec 5, 2023
766e5c4
building out new reference based logic for turntable from file functi…
Sasbom Dec 6, 2023
3230460
fleshed out turntable_from_file to working order.
Sasbom Dec 6, 2023
bc8e23d
cleanup integration pass 1
Sasbom Dec 6, 2023
13a8545
Cleanup finalized
Sasbom Dec 6, 2023
8b46c65
cleared up some comments
Sasbom Dec 6, 2023
880e376
removed deprecated functions
Sasbom Dec 6, 2023
21b9868
updated comments
Sasbom Dec 6, 2023
e6289cb
updated comments
Sasbom Dec 6, 2023
290b14e
Merge branch 'enhancement/usdview_render_turntable' of https://github…
Sasbom Dec 6, 2023
8960936
A preset turntable USD file has been added.
Sasbom Dec 6, 2023
7123fdb
Cosmetics / cleanup
BigRoy Dec 6, 2023
3603058
Merge remote-tracking branch 'sasbom/enhancement/usdview_render_turnt…
BigRoy Dec 6, 2023
5125573
Merge pull request #2 from BigRoy/enhancement/usdview_render_turntabl…
Sasbom Dec 6, 2023
fbb7ab7
Changed template file to .usda, fixed up minor things in turntable.
Sasbom Dec 7, 2023
6730c38
division per element nessecary, fixed string error in dialog
Sasbom Dec 7, 2023
7568426
fix unwanted inclusion
Sasbom Dec 7, 2023
b19cf1e
Remove prims made in Render menu
Sasbom Dec 7, 2023
69ebc5d
clear bbox before stuffing it full of other things
Sasbom Dec 7, 2023
b6e5441
update gitignore to ignore temp folder
Sasbom Dec 7, 2023
1ef9f02
fix ignore of temp folder
Sasbom Dec 7, 2023
224de08
automated cleanup at the end of turntable render
Sasbom Dec 7, 2023
52bfb19
small changes, tiny checks for turntable from file functionality.
Sasbom Dec 8, 2023
da9e1e7
Added a guide to setting up your own turntable.
Sasbom Dec 8, 2023
910ff5c
Set up dialog and editor to dislay it
Sasbom Dec 8, 2023
6b65c0c
Merge branch 'enhancement/usdview_render_turntable' of https://github…
Sasbom Dec 8, 2023
8f81456
base layout style for PlayblastDialog
Sasbom Dec 8, 2023
cb2d3af
fleshing out dialog for playblasting
Sasbom Dec 8, 2023
5699639
further refined playblast menu
Sasbom Dec 8, 2023
d36af8e
added destination selection
Sasbom Dec 8, 2023
49866d2
added pre and post hooks to later hook into with the turntable
Sasbom Dec 8, 2023
5143d37
updated playblast dialog with area to select purposes
Sasbom Dec 8, 2023
38937ef
fixed some things, added progress bar and actual playblast functional…
Sasbom Dec 8, 2023
2918587
modified PlayblastDialog to be more flexible when inheriting
Sasbom Dec 9, 2023
e2060d1
Complexity combobox fixed and moved to be above render engine, sepera…
Sasbom Dec 9, 2023
25b84a2
added support for camera path argument in turntable_from_file
Sasbom Dec 9, 2023
160a512
Added search for cameras in stage from file
Sasbom Dec 9, 2023
09e1809
Pass along RenderReportable in turntable
Sasbom Dec 9, 2023
a8c4d35
started implementing turntable dialog
Sasbom Dec 9, 2023
ec89f96
added functionality for updating camera from file when finished typing.
Sasbom Dec 10, 2023
d6ede87
make sure playblast button on turntable doesn't do anything as it's n…
Sasbom Dec 10, 2023
52400cc
logic pass TurntableDialog
Sasbom Dec 11, 2023
0a6763e
adress minor fuckup
Sasbom Dec 11, 2023
65ca6b3
exposed fit parameter to dialog for generated framing cameras
Sasbom Dec 11, 2023
0e309e6
aux functionality for rendering turntables, laid out functionality
Sasbom Dec 11, 2023
3a58851
implemented stage rotation turntable in dialog
Sasbom Dec 12, 2023
f47ab3b
implemented camera rotation in dialog
Sasbom Dec 12, 2023
006a688
added better cleanup for camera around stage turntable in dialog, fix…
Sasbom Dec 12, 2023
2bf24b8
implemented turntable from file
Sasbom Dec 12, 2023
ec8d45a
cleaned up render menu in editor
Sasbom Dec 12, 2023
b543b86
minor cleanup
Sasbom Dec 12, 2023
f68ae5f
small fixes
Sasbom Dec 12, 2023
2feabcd
First pass of cleanup.
Sasbom Dec 13, 2023
4d0e808
added context managers for managing open scenes and deleting files
Sasbom Dec 13, 2023
5e85507
ExitStack shenanigans
Sasbom Dec 13, 2023
c104d5d
ExitStack shenanigans part 2: the big bugfix
Sasbom Dec 13, 2023
5fbaccb
fixed potential -1 index on camera box in playblast dialog
Sasbom Dec 13, 2023
daf480f
removed unneeded import to weakref
Sasbom Dec 13, 2023
30463db
exposed select functions in `__all__` and added missing stage context…
Sasbom Dec 14, 2023
184cc32
fixed spacing from top
Sasbom Dec 14, 2023
7f0720c
removed unnessecary import
Sasbom Dec 14, 2023
c51e2af
modified size, disallowed resizing of playblast/turntable dialogs
Sasbom Dec 14, 2023
57828f6
bugfix, making sure that right parameters get passed around and that …
Sasbom Dec 14, 2023
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
28 changes: 27 additions & 1 deletion usd_qtpy/editor.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import logging
from functools import partial

from qtpy import QtWidgets, QtCore

from . import (
prim_hierarchy,
layer_editor,
prim_spec_editor
prim_spec_editor,
render_util
)


Expand All @@ -29,6 +31,8 @@ def __init__(self, stage, parent=None):
title = f"{title}: {name}"
self.setWindowTitle(title)

self._stage = stage

self.setWindowFlags(
self.windowFlags() |
QtCore.Qt.Dialog
Expand All @@ -49,8 +53,10 @@ def __init__(self, stage, parent=None):
splitter.addWidget(hierarchy_widget)

viewer_widget = None
self._stageview = None
if HAS_VIEWER:
viewer_widget = viewer.Widget(stage=stage)
self._stageview = viewer_widget.view
splitter.addWidget(viewer_widget)

prim_spec_editor_widget = prim_spec_editor.SpecEditorWindow(stage=stage)
Expand All @@ -75,6 +81,8 @@ def build_menubar(self):

menubar = QtWidgets.QMenuBar()

# Panels menu

panels_menu = menubar.addMenu("Panels")
for label, widget in self._panels.items():
action = panels_menu.addAction(label)
Expand All @@ -94,5 +102,23 @@ def update_panel_checkstate():

panels_menu.aboutToShow.connect(update_panel_checkstate)

# Render menu

render_menu = menubar.addMenu("Render")
render_labels = "Playblast" , "Snapshot"
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8 - check your linter. :)

Suggested change
render_labels = "Playblast" , "Snapshot"
render_labels = "Playblast", "Snapshot"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8 - check your linter. :)

I really should ; _ ;

On the topic of PEP8 on a different note, I know that snake_case is preferable for functions,
I just look at Qt and Usd and can't help but copy their style to maintain consistency.
That's my reasoning for naming functions the way I do. I will rename them all to be snake case, I will never more be tempted by the demons hiding in libraries.

render_actions = {label : render_menu.addAction(label) for label in render_labels}

# Testing
print("\n".join(render_util.getAllRenderEngineNames()))
goal_file = R"C:\dump\playblastview##.##.png"

def render_snapshot(stage,stageview,path):
cam = render_util.playblast.cameraFromView(stage,stageview)
BigRoy marked this conversation as resolved.
Show resolved Hide resolved
render_util.playblast.renderPlayblast(stage,path,"1",1920,cam,renderer="GL")
BigRoy marked this conversation as resolved.
Show resolved Hide resolved

render_snap = partial(render_snapshot,self._stage,self._stageview,goal_file)
BigRoy marked this conversation as resolved.
Show resolved Hide resolved

render_actions["Snapshot"].triggered.connect(render_snap)

layout = self.layout()
layout.setMenuBar(menubar)
2 changes: 2 additions & 0 deletions usd_qtpy/render_util/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# module named render_util to not collide in names with houdini's render.py
BigRoy marked this conversation as resolved.
Show resolved Hide resolved
from .playblast import *
166 changes: 166 additions & 0 deletions usd_qtpy/render_util/playblast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Playblast framework
# Inspired by: Prism, usdrecord

# NOTES:
# pxr.UsdViewq.ExportFreeCameraToStage will export the camera from the view (a FreeCamera/ pxr.Gf.Camera, purely OpenGL)

from pxr import Usd, UsdGeom
from pxr import UsdAppUtils
from pxr import Tf, Sdf

from pxr.Usdviewq.stageView import StageView
BigRoy marked this conversation as resolved.
Show resolved Hide resolved
from viewer import CustomStageView

from qtpy import QtCore

from typing import Union
import logging
BigRoy marked this conversation as resolved.
Show resolved Hide resolved

def _setupOGLWidget(width : int, height : int, samples : int = 4):
BigRoy marked this conversation as resolved.
Show resolved Hide resolved
"""
Utility function to produce a Qt openGL widget capable of catching
the output of a render
"""

from qtpy import QtOpenGL

# format object contains information about the Qt OpenGL buffer.
QGLformat = QtOpenGL.QGLFormat()
QGLformat.setSampleBuffers(True) # Enable multisample buffer
QGLformat.setSamples(samples) # default samples is 4 / px

GLWidget = QtOpenGL.QGLWidget(QGLformat)
GLWidget.setFixedSize(QtCore.QSize(width,height))

GLWidget.makeCurrent() # bind widget buffer as target for OpenGL operations.

return GLWidget

def findCameras(stage : Usd.Stage, TraverseAll = True) -> list[UsdGeom.Camera]:
"""
Return all camera primitives.
TraverseAll is on by default. This means that inactive cameras will also be shown.
"""

if TraverseAll:
gen = stage.TraverseAll()
else:
gen = stage.Traverse()

cams = [c for c in gen if UsdGeom.Camera(c)]
return cams
BigRoy marked this conversation as resolved.
Show resolved Hide resolved

def cameraFromView(stage : Usd.Stage, stageview : Union[StageView, CustomStageView], name : str = "playblastCam") -> UsdGeom.Camera:
""" Catches a stage view whether it'd be from the custom viewer or from the baseclass and calls the export to stage function."""
stageview.ExportFreeCameraToStage(stage,name)
return UsdGeom.Camera.Get(stage,Sdf.Path(f"/{name}"))

# Source: UsdAppUtils.colorArgs.py
def getColorArgs():
return ("disabled","sRGB","openColorIO")
BigRoy marked this conversation as resolved.
Show resolved Hide resolved

def getComplexityLevels():
"""
Returns a generator that iterates through all registered complexity presets in UsdAppUtils.complexityArgs
"""
from pxr.UsdAppUtils.complexityArgs import RefinementComplexities as Complex
return (item.name for item in Complex._ordered)

def getAllRenderEngineNames():
"""
Returns a generator that will iterate through all names of Render Engine Plugin / Hydra Delegates
"""
from pxr.UsdImagingGL import Engine as En
return (En.GetRendererDisplayName(pluginId) for pluginId in En.GetRendererPlugins())

def getRenderPlugin(enginestr : str):
from pxr.UsdImagingGL import Engine as En
for plug in En.GetRendererPlugins():
if enginestr == En.GetRendererDisplayName(plug):
return plug
return None

def checkRenderEngineName(enginestr : str) -> Union[str, None]:
plugnames = getAllRenderEngineNames()
if enginestr in plugnames:
return enginestr
return None

def renderPlayblast(stage : Usd.Stage, outputpath : str, frames : str, width : int,
camera : UsdGeom.Camera = None, complexity : Union[str,int] = "High",
renderer : str = "GL", colormode : str = "sRGB"):
from pxr.UsdAppUtils.framesArgs import FrameSpecIterator, ConvertFramePlaceholderToFloatSpec
from pxr.UsdAppUtils.complexityArgs import RefinementComplexities as Complex
from pxr import UsdUtils

# rectify pathname for use in .format with path.format(frame = timeCode.getValue())gi
if not (outputpath := ConvertFramePlaceholderToFloatSpec(outputpath)):
raise ValueError("Invalid/Empty filepath for rendering")

# ensure right complexity object is picked.
# the internal _RefinementComplexity.value is used to set rendering quality
if isinstance(complexity,str):
# ensure key correctness
complexity = complexity.lower() # set all to lowercase
complexity = complexity.title() # Uppercase Each Word (In Case Of "Very High")
preset_names = getComplexityLevels()
if complexity not in preset_names:
raise ValueError(f"Value: {complexity} entered for complexity is not valid")

complex_level = Complex.fromName(complexity)
elif isinstance(complexity,int):
complexity = min(max(complexity,0),3) # clamp to range of 0-3, 4 elements
complex_level = Complex._ordered[complexity]

complex_level = complex_level.value

# validate render engine
if not checkRenderEngineName(renderer):
raise ValueError(f"Render engine arguement invalid")
renderer = getRenderPlugin(renderer)

# TEMP: pick first found camera
if not camera:
camera = next(findCameras(stage), None)
if not camera:
# Same procedure as default for pxr.UsdAppUtils.cameraArgs.py
path = Sdf.Path(UsdUtils.GetPrimaryCameraName())
camera = UsdAppUtils.GetCameraAtPath(stage, path)

if colormode not in getColorArgs():
raise ValueError("Color correction mode specifier is invalid.")

# Set up OpenGL FBO to write to within Widget
# Actual size doesn't matter
# it does need to be stored in a variable though, otherwise it'll be collected
ogl_widget = _setupOGLWidget(width,width)

# Create FrameRecorder
frameRecorder = UsdAppUtils.FrameRecorder()
frameRecorder.SetRendererPlugin(renderer)
frameRecorder.SetImageWidth(width) # Only width is needed, heigh will be computer from camera properties.
frameRecorder.SetComplexity(complex_level)
frameRecorder.SetColorCorrectionMode(colormode)
#frameRecorder.SetIncludedPurposes(["default","render","proxy","guide"]) # set to all purposes for now.

# Use Usds own frame specification parser
# The following are examples of valid FrameSpecs:
# 123 - 101:105 - 105:101 - 101:109x2 - 101:110x2 - 101:104x0.5
frame_iterator = FrameSpecIterator(frames)

if not frame_iterator:
frame_iterator = [Usd.TimeCode.EarliestTime()]

for timeCode in frame_iterator:
currentframe = outputpath.format(frame = timeCode.GetValue())
print(currentframe)
try:
print("attempting render")
frameRecorder.Record(stage, camera, timeCode, currentframe)
print("Done!")
except Tf.ErrorException as e:
logging.error("Recording aborted due to the following failure at time code {0}: {1}".format(timeCode, str(e)))
BigRoy marked this conversation as resolved.
Show resolved Hide resolved
break

# Set reference to None so that it can be collected before Qt context.
frameRecorder = None
BigRoy marked this conversation as resolved.
Show resolved Hide resolved
Loading