abstractfactory / maya-capture

Playblasting in Maya done right
MIT License
165 stars 37 forks source link

Support for Viewport 2.0 #1

Closed mottosso closed 8 years ago

mottosso commented 10 years ago

Including simply using it, but also specifying flags such as anti-alias, motion-blur etc.

mottosso commented 9 years ago

Continuing from #14

And according to Maya they would belong to ViewportOptions because they are related to the Viewport, see modelEditor.

Ah, yes that makes more sense, let's go for that.

They seem to be rather cryptic.

Hm, what do you suggest? We could do e.g. viewport = ViewportOptions.VIEWPORT2? Or version them viewport = 0 # legacy, viewport = 1 # high quality, viewport = 2 # 2.0?

BigRoy commented 9 years ago

Maybe we can provide our own enum like that indeed for clarity and still allow to pass in a string to accommodate for custom renderers.

I think it's clear if they are:

Or whatever is close to how they show in the UI.

BigRoy commented 9 years ago

Note that the options for Viewport 2.0 are set on a node called hardwareRenderingGlobals (so setAttr and getAttr should get us there.)

These are some of the default settings:

class Viewport2Options:

    # Transparency Algorithm
    # 0: simple, 1: object sorting, 2: weighted average, 3: depth peeling
    transparencyAlghorithm = 1

    # Motion Blur
    motionBlurEnable = 0
    motionBlurType = 0   # transform (currently there is no other option)
    motionBlurShutterOpenFraction = 0.2
    motionBlurSampleCount = 8

    # Anti-aliasing
    lineAAEnable = 0
    multiSampleEnable = 0
    multiSampleCount = 8

    # Screen Space Ambient Occlusion
    ssaoEnable = 1
    ssaoRadius = 16
    ssaoFilterRadius = 16
    ssaoAmount = 1.0
    ssaoSamples = 8

There are much more settings that could be of interest.

mottosso commented 9 years ago

I'm having a bit of trouble keeping up with the changes to be honest, but I think it sounds good and I trust your judgement.

mkolar commented 8 years ago

did you guys ever get around to this?

mottosso commented 8 years ago

Passing the ball to @BigRoy

BigRoy commented 8 years ago

With separate classes this actually easy to implement. I can set that up and show it off a bit.

@mkolar what specifically do you need?

mkolar commented 8 years ago

I can set that up and show it off a bit.

Awesome I though I'd look at it myself if no one tried yet.

what specifically do you need?

Practically I need to be able to capture exactly what animator sees in the viewport including dof, motion blur, AA, AO, lights, shadows.

With the range of models we're doing now, we prefer giving artist control over how they present it, because different scenarios ask for different approaches.

Ideally I'd like capture to take all the settings from current viewport, with any options passed to it acting as overrides.

mottosso commented 8 years ago

exactly what animator sees in the viewport.. Ideally I'd like capture to take all the settings from current viewport, with any options passed to it acting as overrides.

Sorry, that isn't what I had in mind for this module. It needs to be reliable and produce identical results across runs. It can't be dependent on the current state of the scene, but instead capture it exactly as specified and otherwise resort to internal defaults, defaults based on the original Maya defaults on a clean install.

What you could do though, is write a little "collector" to gather the current state, and pass that to capture.

BigRoy commented 8 years ago

Ideally I'd like capture to take all the settings from current viewport, with any options passed to it acting as overrides.

This is what I had in mind as well.

Sorry, that isn't what I had in mind for this module. It needs to be reliable and produce identical results across runs. It can't be dependent on the current state of the scene, but instead capture it exactly as specified and otherwise resort to internal defaults, defaults based on the original Maya defaults on a clean install.

It'd make much more sense if the package would provide presets for anyone to use: default, low, high. These could be used for reliability?

capture(preset="default")

Just as reliable as it is now, yet much more explicit.

mkolar commented 8 years ago

I have no problem with that, however there are not enough options in capture to do that currently, at least to my knowledge.

It needs to be reliable and produce identical results across runs.

Absolutely reasonable for certain productions. Not very desirable with multiple smaller project in the pipeline, where each requires something else. For example we have a show, where everything needs to be capture with flat lighting and constant shaders, and another, where director wants to see nicely lit viewport captures to get closer to better see the model details.

defaults based on the original Maya defaults on a clean install

I've yet to see where the defaults would produce result acceptable for production reviews.

Maybe having 2 modes would work. Default would stay as it is now, and artist mode would work based of current viewport settings?

BigRoy commented 8 years ago

By the way this is what I was testing before class based options.

They would all be triggered through a context manager that first retrieves the "current" to store the state the artist is working with and then applies the "overrides" that were passed in to the capture() command.

Similarly this could also mean that playblasting with the current settings would become something like passing along: options.current()

I was separating these out into different classes because some of the commands need to be triggered differently cmds.modelEditor vs cmds.setAttr for camera attributes, etc. And it kept the code base simpler. Of course this could be one single class that would know which command to trigger based on the "option" name.

mottosso commented 8 years ago

Maybe having 2 modes would work. Default would stay as it is now, and artist mode would work based of current viewport settings?

That's a good idea!

Maybe a flag set on the module itself, to not continue to pollute the already overpopulated argument list?

import capture
capture.defaults = capture.FromActiveView
capture.defaults = capture.MayaDefaults

I've yet to see where the defaults would produce result acceptable for production reviews.

To be honest, I'd expect production reviews to be consistent, and not at all depend on how any particular artist had his Maya set up at any particular time of the day. Production reviews is the primary goal of this module, the idea is that you wouldn't use any of the defaults, but when you do, you can count on them being consistent.

BigRoy commented 8 years ago

To be honest, I'd expect production reviews to be consistent, and not at all depend on how any particular artist had his Maya set up at any particular time of the day. Production reviews is the primary goal of this module, the idea is that you wouldn't use any of the defaults, but when you do, you can count on them being consistent.

Sure. But one production might need that "specific background color" just to have the production review look good. These same "reviews" should be consistent but also good enough to possibly be share-worthy with a client. We've had productions where we set the background color to a dark blue shade to represent night time of the scene. Even though it's an alteration it was intended as a consistency within that project decided upon by the creative artists. I'd rather not interfere too much here and having to make some new lines of code to support each new project like that.

I'd rather be able to make "presets" for a project that at some point could be managed by the creative lead instead of being pushed to a TA/TD.

mkolar commented 8 years ago

Maybe a flag set on the module itself, to not continue to pollute the already overpopulated argument list?

sure that works.

I'd expect production reviews to be consistent,

Maybe we got a misunderstanding :). Consistent within a project, mostly yes, But it can vary wildly between projects or even sequences.

edit: @BigRoy was a tiny bit faster :)

mottosso commented 8 years ago

I'd rather be able to make "presets" for a project that at some point could be managed by the creative lead.

How about something like this?

# mycapture.py
def capture():
  from capture import capture
  capture( ... ) # My settings

That's your "preset". To use it, you:

import mycapture
mycapture.capture()

That way, you get what you want, and capture remains minimal.

BigRoy commented 8 years ago

Sure. Could definitely work. I think the important bit is just that capture has a way to get the current settings so it becomes easy to build a current preset or even define settings. Especially because future releases might expand on features (like viewport 2.0 support).

I'm also fine with it defaulting to some "default" that seems to be somewhat arbitrarily chosen. (Or at least will never fit everyone's needs, so why bother trying to?). Yet it should be as easy as something like:

capture(options=capture.current_options())

Similarly I think we mentioned before revamping the options into something like a dictionary. I think that's a very neat idea, since it'd be easy to store presets (save/load even!) into JSON.

def save(path):
    data = capture.current_options()
    with open(path, "w") as f:
        json.dump(data, f)

And loading it as options:

def load(path):
    with open(path, "r") as f:
        return json.load(f)

Using it like:

capture(options=load(path))

Similarly the default settings could be just a dictionary.

mottosso commented 8 years ago

I think the important bit is just that capture has a way to get the current settings so it becomes easy to build a current preset or even define settings.

I like that idea. Like a capture.parse_active_view() command which inspects the currently active view and returns a dictionary capable of being expanded into the capture command.

import capture
settings = capture.parse_active_view()
capture.capture(**settings)

That way, you could, as you say, save it out to a JSON or YAML and edit away. making your own "preset".

import json
import capture
with open("mypreset.json") as f:
    settings = json.load(f)
capture.capture(**settings)

I think that sounds great. We will need to convert the current CameraOptions and similar option classes into pure dictionaries, which is a good direction overall as well.

Sweet!

BigRoy commented 8 years ago

We just need a mapping somewhere on how specific sets of attributes need to be handled. As stated before managing these attributes is different for some settings, some are attributes on the camera. Some are values on the modelEditor. Similarly we'll need a counterpart on how to retrieve the current value. (If we're changing the value we need to revert to the current value afterwards).

This is somewhat what I did with the class-based system for the options. For each attribute type you'd need to know what "setting names" there are and what commands are related, e.g. this is what I had before :

class DisplayOptions(Options):
    """Display options for :func:`capture`
    Use this struct for background color, anti-alias and other
    display-related options.
    """
    displayGradient = True
    background = (0.631, 0.631, 0.631)
    backgroundTop = (0.535, 0.617, 0.702)
    backgroundBottom = (0.052, 0.052, 0.052)

    _colors = ['background', 'backgroundTop', 'backgroundBottom']
    _prefs = ['displayGradient']

    @staticmethod
    def current():
        """Return the currently set options."""
        from maya import cmds

        options = DisplayOptions()

        for clr in options._colors:
            value = cmds.displayRGBColor(clr, query=True)
            setattr(options, clr, value)

        for pref in options._prefs:
            value = cmds.displayPref(query=True, **{pref: True})
            setattr(options, pref, value)

    def set(self):
        """Applies the options."""
        from maya import cmds

        for clr in self._colors:
            value = getattr(self, clr)
            cmds.displayRGBColor(clr, *value)
        for pref in self._prefs:
            value = getattr(self, pref)
            cmds.displayPref(**{pref: value})

And to set the values we need to set attributes on the hardwareRendering node

Theoretically we could just make a dictionary for all attribute settings that exists that coincide with a getter and setter.

settings = {
'background': (displayRGBColorGetter, displayRGBColorSetter),
'backgroundTop ': (displayRGBColorGetter, displayRGBColorSetter),
'backgroundBottom ': (displayRGBColorGetter, displayRGBColorSetter)
}

And so forth.

The applying of the settings in a context manager could be along the lines of:

@contextlib.contextmanager
def apply_options(options):
    original= {}
    for opt, value in options.iteritems():
        getter, setter = settings[opt]
        original[opt] = getter()
        setter(value)
    try:
        yield
    finally:
        for opt, value in original.iteritems():
            setter = settings[opt][1]
            setter(value)

Tricky bit there being that some of the commands require access to the camera, others to the modelPanel, etc, etc.

mottosso commented 8 years ago

We just need a mapping somewhere on how specific sets of attributes need to be handled.

Here's how I'm thinking.

I feel classes can get complex fast, and I'd rather not introduce more complexity unless we must.

BigRoy commented 8 years ago

Sure. I wanted to step away from classes as well. It's just that there are "options" not yet currently implemented that need a new argument in that case.

As such mentioned simplifying the interface to the user, just options and the parser/retriever of the options to manage how to go about it for a certain option/setting.

The separation for "camera_options", etc could still stay and could be the abstraction of what needs to be passed to the methods. E.g. "panel_options" will be given the modelPanel along with it. "camera_options" the camera.

Could that make sense?

And there should then still be a way to retrieve the current state of "camera_options", etc.

From an artist perspective just "options" would be the simplest one. Even though we need some handling in the parser of current settings and applying them.

mottosso commented 8 years ago

I don't know what you mean @BigRoy, but I've mocked up an example of something that works.

27

Let's continue the discussion about presets in there.

mottosso commented 8 years ago

Presets aside, was it straightforward to setup a PR for this?

BigRoy commented 8 years ago

Presets aside, was it straightforward to setup a PR for this?

It shouldn't be that hard. It might introduce another "options" type though, since this uses another command again.

That's other commands than what the camera_options, display_options, viewport_options require to operate. Would this just become viewport2_options?

For clarity sakes it would become something like:


Viewport2Options = {
    transparencyAlghorithm: 1,
    motionBlurEnable: 0,
    motionBlurType: 0 ,
    motionBlurShutterOpenFraction: 0.2,
    motionBlurSampleCount: 8,
    lineAAEnable: 0,
    multiSampleEnable: 0,
    multiSampleCount: 8,
    ssaoEnable: 1,
    ssaoRadius: 16,
    ssaoFilterRadius: 16,
    ssaoAmount: 1.0,
    ssaoSamples: 8
}

And more parameters.

mottosso commented 8 years ago

Ah, yeah, that looks intuitive enough. Good thinking!

BigRoy commented 8 years ago

So had a quick look at the available attributes on the hardwareRenderingGlobals and here's a quick capture from it:

{
 "bloomAmount": 1.0,
 "bloomEnable": False,
 "bloomFilterAux": 0.0,
 "bloomFilterRadius": 0.0,
 "bloomThreshold": 0.0,
 "bumpBakeResolution": 64,
 "colorBakeResolution": 64,
 "compressSharedVertexData": True,
 "consolidateWorld": True,
 "enableTextureMaxRes": False,
 "floatingPointRTEnable": True,
 "floatingPointRTFormat": 1,
 "gammaCorrectionEnable": False,
 "gammaValue": 2.2,
 "holdOutDetailMode": 1,
 "holdOutMode": True,
 "hwFogAlpha": 1.0,
 "hwFogColorB": 0.5,
 "hwFogColorG": 0.5,
 "hwFogColorR": 0.5,
 "hwFogDensity": 0.1,
 "hwFogEnable": False,
 "hwFogEnd": 100.0,
 "hwFogFalloff": 0,
 "hwFogStart": 0.0,
 "hwInstancing": False,
 "lightingMode": 1,
 "lineAAEnable": False,
 "maxHardwareLights": 8,
 "motionBlurAtlasSizeX": 8,
 "motionBlurAtlasSizeY": 4,
 "motionBlurCurved": False,
 "motionBlurEnable": False,
 "motionBlurFadeAmount": 0.5,
 "motionBlurFadeEmphasis": 0.0,
 "motionBlurFadeTintA": 1.0,
 "motionBlurFadeTintB": 0.0,
 "motionBlurFadeTintG": 0.0,
 "motionBlurFadeTintR": 0.0,
 "motionBlurMultiframeChartSizeX": 256,
 "motionBlurMultiframeChartSizeY": 256,
 "motionBlurMultiframeEnable": False,
 "motionBlurSampleCount": 8,
 "motionBlurShutterOpenFraction": 0.2,
 "motionBlurType": 0,
 "multiSampleCount": 8,
 "multiSampleEnable": False,
 "multiSampleQuality": 0,
 "renderDepthOfField": True,
 "renderMode": 4,
 "singleSidedLighting": False,
 "ssaoAmount": 1.0,
 "ssaoEnable": False,
 "ssaoFilterRadius": 16,
 "ssaoRadius": 16,
 "ssaoSamples": 16,
 "textureMaxResolution": 4096,
 "threadDGEvaluation": False,
 "transparencyAlgorithm": 1,
 "transparencyQuality": 0.33,
 "useMaximumHardwareLights": True,
 "vertexAnimationCache": 0,
 "xrayJointDisplay": False,
 "xrayMode": False
 }

Quite the amount. Is there anything we want to filter out?

Most of this just came from doing a listAttr on the node and removing things like the frozen, isHistoricallyInteresting, etc. attribtues that all nodes have in Maya.

This is also with the default values in a new scene.

Some might only be used for a hardware render and not for a Viewport 2.0 setting, whereas others are actually used for Viewport 2.0. I can go through them mostly manually to find those... (e.g. I haven't seen "bloom" in viewport 2.0 anywhere).

mottosso commented 8 years ago

Yeah, we probably only want to keep those relevant to vp2.0.

Maybe as a test, to find which belong to vp2.0, you could run each through the command, and collect those that don't error out?

BigRoy commented 8 years ago

Maybe as a test, to find which belong to vp2.0, you could run each through the command, and collect those that don't error out?

They are all real attributes. How would they "error out"? Anyway, will quickly filter manually. Should be fine.

mottosso commented 8 years ago

Sorry, wasn't there a viewport2 command? If the all work, then I'd keep them. The less personal choice and more reproducibility we can get the better. Odds are they will appear in VP2.0 sometime later.

BigRoy commented 8 years ago

Sorry, wasn't there a viewport2 command?

Basically the settings are different from the other settings, because these are housed on another node. In this case the hardwareRenderingGlobals where e.g. camera attributes are on the camera, the viewport options are on the modelEditor. etc.

The less personal choice and more reproducibility we can get the better. Odds are they will appear in VP2.0 sometime later.

I'll just put in the full list for now and ensure it doesn't error out on anything. Good thinking.

It's quite confusing though, since the xrayJointDisplay and xrayMode don't do anything in the viewport. For that you'd use modelEditor command. Maybe still filter it a little?

mottosso commented 8 years ago

Seeing as the user can provide options that aren't already in these dicts, yeah, maybe let's just include what we think is right so it can be used for reference.

Adding or removing would basically only be for documentation and shouldn't break anything.

Pick the ones you think are a good fit; don't sweat accuracy.

BigRoy commented 8 years ago

Ok, filtered to this:

{
 "bumpBakeResolution": 64,
 "colorBakeResolution": 64,
 "consolidateWorld": True,
 "enableTextureMaxRes": False,
 "floatingPointRTEnable": True,
 "floatingPointRTFormat": 1,
 "gammaCorrectionEnable": False,
 "gammaValue": 2.2,
 "holdOutDetailMode": 1,
 "holdOutMode": True,
 "hwFogAlpha": 1.0,
 "hwFogColorR": 0.5,
 "hwFogColorG": 0.5,
 "hwFogColorB": 0.5,
 "hwFogDensity": 0.1,
 "hwFogEnable": False,
 "hwFogEnd": 100.0,
 "hwFogFalloff": 0,
 "hwFogStart": 0.0,
 "lineAAEnable": False,
 "maxHardwareLights": 8,
 "motionBlurEnable": False,
 "motionBlurSampleCount": 8,
 "motionBlurShutterOpenFraction": 0.2,
 "motionBlurType": 0,
 "multiSampleCount": 8,
 "multiSampleEnable": False,
 "singleSidedLighting": False,
 "ssaoAmount": 1.0,
 "ssaoEnable": False,
 "ssaoFilterRadius": 16,
 "ssaoRadius": 16,
 "ssaoSamples": 16,
 "textureMaxResolution": 4096,
 "threadDGEvaluation": False,
 "transparencyAlgorithm": 1,
 "transparencyQuality": 0.33,
 "useMaximumHardwareLights": True,
 "vertexAnimationCache": 0
 }

Coming up in a bit.

mottosso commented 8 years ago

:+1:

BigRoy commented 8 years ago

Noticed some other missing settings on ViewportOptions that were giving me inconsistent results in the playblasts. So I'm adding those in as well at the same time. (Allowed me to debug it easier)...

mottosso commented 8 years ago

We all like options.

BigRoy commented 8 years ago

Closed with #31