// Shave and a Haircut // (c) 2019 Epic Games // US Patent 6720962 global string $shaveRenderman_fileVersion = "$Revision$"; // WARNING: The rman plugin may not be loaded when this script gets // sourced so all references to commands from that plugin // (e.g. 'rman', 'rmanGetGlobals', 'RiReadArchive') must be // executed within an 'eval' otherwise the results will be // unpredictable. global int $gShave_rmanPluginIsLoaded = false; global int $gShave_rmanWaitingForPlugin = false; global string $gShave_rmanArchiveFiles[]; // Local Procedures // We need this because all of Maya's native string search commands take // some kind of pattern and we don't want any "special chars" in our // search. proc int strIndex(string $needle, string $haystack) { int $needleLen = size($needle); int $haystackLen = size($haystack); int $i; for ($i = 1; $i <= ($haystackLen - $needleLen) + 1; $i++) { if (substring($haystack, $i, $i + $needleLen - 1) == $needle) return $i; } return 0; } proc string getRManGlobals() { string $globals = ""; if (exists("rmanGetGlobals")) { $globals = rmanGetGlobals(); } return $globals; } // Returns the version of RenderMan for Maya as a float. // proc float getRfMVersion() { float $version = 0.0; if (`pluginInfo -q -loaded RenderMan_for_Maya`) { string $versionStr = eval("rman getversion"); // The version string will be of the form "5.5 (@1457856 Mar 16 2015)". // We want just the version number part. // $version = float(match("^[0-9.]+", $versionStr)); } return $version; } // Returns the version of Renderman as a float. // // NOTE: As of RenderMan Studio 18 the RMS version is the same as the // Renderman version. // proc float getRManVersion() { float $version = 0.0; if (`pluginInfo -q -loaded RenderMan_for_Maya`) { string $versionStr = eval("rman getversion prman"); // The version string will be of the form "prman 19.0 @1457856". // We want just the version number part. // $version = float(match("[0-9.]+", $versionStr)); } return $version; } proc addShaderPath() { // Our SLIM shader sits in the 'prman/shaders' directory beneath the // same directory as our plugin was loaded from. Update RenderMan's shader // path to include this directory. // string $pluginDir = dirname(`pluginInfo -q -path shaveNode`); RiOption("searchpath", "string shader", $pluginDir + "/prman/shaders:&"); // Our Bxdf lies in the 'prman/RMSnn' directory beneath the same // directory as our plugin was loaded from, where 'nn' is the // version of the RMS plugin currently loaded into Maya. Update // RenderMan's rixplugin path to include this directory. // int $rmsVersion = int(getRManVersion()); if ($rmsVersion >= 19) { RiOption("searchpath", "string rixplugin", $pluginDir + "/prman/RMS" + $rmsVersion + ":&:@"); } } proc removeCallback(string $node, string $attr, string $callback) { if (objExists($node) && `attributeExists $attr $node`) { string $val = getAttr($node + "." + $attr); // The callback might be one command buried in a string of // semi-colon separate commands. So let's strip out all the // possible permutations. $val = substitute((";[ \t]*" + $callback + "[ \t]*"), $val, ""); $val = substitute(("[ \t]*" + $callback + "[ \t]*;"), $val, ""); $val = substitute(("[ \t]*" + $callback + "[ \t]*"), $val, ""); setAttr -type "string" ($node + "." + $attr) $val; } } proc addCallback(string $node, string $attr, string $callback) { if (objExists($node) && `attributeExists $attr $node`) { // Make sure that we're not creating multiple entries for the same // callback by first removing any existing ones. removeCallback($node, $attr, $callback); string $val = getAttr($node + "." + $attr); if (($val == "") || (size(`match "^[ \t]*$" $val`) > 0)) $val = $callback; else $val += "; " + $callback; setAttr -type "string" ($node + "." + $attr) $val; } else warning("Shave: can not add callback, attribute not found."); } proc waitForPlugin() { global int $gShave_rmanWaitingForPlugin; if (!$gShave_rmanWaitingForPlugin) { pluginInfo -cc shave_rmanCheckNewPlugin; $gShave_rmanWaitingForPlugin = true; } } // Global procedures global proc shave_rmanInit() { // we do not want to set callbacks in defaultRenderGlobals when loading renderman plugin - dub|22Jun2012 // not clear why its handled diffent way then for other renderers //shave_rmanSetupCallbacks(); } global proc shave_rmanCleanup() { //aslo we want to let these get saved with the scene - dub|22Jun2012 // not clear why its handled diffent way then for other renderers //shave_rmanRemoveCallbacks(); } global proc shave_rmanSetupCallbacks() { //print "tryint to set renderMan callbacks\n"; // If there's no shaveGlobals node yet then there can't be any hair in // the scene so there's no reason to set up callbacks yet. //no we can not do this cut off because render will fail with this steps: //set renderer to prman, create hair, render //if (!objExists("shaveGlobals")) //{ // //warning("Shave: shaveGlobals not found "); // return; //} // If the rman plugin isn't loaded yet, we'll need to wait for it. if (!shave_rmanPluginIsLoaded()) { waitForPlugin(); //warning("Shave: renderMan is not loaded."); return; } string $globals = getRManGlobals(); if ($globals == "") { warning("Shave: renderMan globals not found. Giving up."); return; } addCallback( $globals, "rman__torattr___renderBeginScript", "shave_rmanRenderStart" ); addCallback( // $globals, "defaultRenderGlobals", // "rman__torattr___preRenderScript", "preRenderMel", "shave_rmanFrameStart" ); // RMS 19 and onward use an external prman instance which runs as a // separate process. As a result all of the 'Post * MEL' scripts fire // before the render has completed, making them basically useless. // // At the moment all that we do post render is to delete temp files // generated for that render. We can instead do that using system // commands injected into the rman stream. // if (getRManVersion() >= 19) { addCallback( $globals, "rman__torattr___preRenderScript", "shave_rmanSetOptions" ); addCallback( $globals, "rman__torattr___postRenderScript", "shave_rmanInjectCleanup" ); } else { addCallback( "defaultRenderGlobals", "postRenderMel", "shave_rmanFrameEnd" ); } addCallback( $globals, "rman__torattr___postTransformScript", "shave_rmanInsertArchive" ); //print "done.\n"; } global proc shave_rmanRemoveCallbacks() { //print "renderMan callbacks removed\n"; string $globals = getRManGlobals(); removeCallback( $globals, "rman__torattr___renderBeginScript", "shave_rmanRenderStart" ); removeCallback( // $globals, "defaultRenderGlobals", // "rman__torattr___preRenderScript", "preRenderMel", "shave_rmanFrameStart" ); if (getRManVersion() >= 19) { addCallback( $globals, "rman__torattr___preRenderScript", "shave_rmanSetOptions" ); removeCallback( $globals, "rman__torattr___postRenderScript", "shave_rmanInjectCleanup" ); } else { removeCallback( "defaultRenderGlobals", "postRenderMel", "shave_rmanFrameEnd" ); } removeCallback( $globals, "rman__torattr___postTransformScript", "shave_rmanInsertArchive" ); } global proc shave_rmanRenderStart() { global int $gShave_rmanArchiveInserted; $gShave_rmanArchiveInserted = false; } global proc shave_rmanSetOptions() { addShaderPath(); } global proc string shave_rmanFrameStart() { ///////////////// dumps ////////////// /* string $globals = getRManGlobals(); $mob =`getAttr ($globals + ".rman__torattr___motionBlur")`; print "prman: Motion Blur:"; print $mob; print "\n"; $cab =`getAttr ($globals + ".rman__torattr___cameraBlur")`; print "prman: Motion Blur:"; print $cab; print "\n"; $a =`getAttr ($globals + ".rman__toropt___shutterAngle")`; print "prman: Sutter Angle:"; print $a; print "\n"; $o =`getAttr ($globals + ".rman__riopt__Camera_shutteropening0")`; print "prman: Sutter Open:"; print $o; print "\n"; $c =`getAttr ($globals + ".rman__riopt__Camera_shutteropening1")`; print "prman: Sutter Close:"; print $c; print "\n"; $t =`getAttr ($globals + ".rman__toropt___shutterTiming")`; print "prman: Sutter Timing:"; print $t; print "\n"; $k =`getAttr ($globals + ".rman__toropt___motionBlurType")`; print "prman: Blur Type:"; print $k; print "\n"; */ ////////////////////////////////////// global string $gShave_rmanArchiveFiles[]; float $frame = `currentTime -q`; string $dir = shaveGetTempDir(); string $ribFile = `shaveUtil -makeTempFileName $dir "shave" ("." + $frame + ".rib")`; $gShave_rmanArchiveFiles = `shaveWriteRib -fullPaths $ribFile`; if (size($gShave_rmanArchiveFiles) == 0) return ""; return $gShave_rmanArchiveFiles[0]; } global proc shave_rmanFrameEnd() { global string $gShave_rmanArchiveFiles[]; if (objExists("shaveGlobals") && !getAttr("shaveGlobals.ribKeepRibFiles")) { string $file; for ($file in $gShave_rmanArchiveFiles) sysFile -del $file; } clear $gShave_rmanArchiveFiles; } // We no longer need this but some people have it embedded in their // renderManGlobals now so we keep it around to prevent errors. global proc shave_rmanRenderEnd() { } global proc string shave_rmanGetReadArchiveCmd() { global string $gShave_rmanArchiveFiles[]; if (size($gShave_rmanArchiveFiles) > 0) return ("ReadArchive \"" + $gShave_rmanArchiveFiles[0] + "\""); return ""; } global proc shave_rmanInsertArchive() { global int $gShave_rmanArchiveInserted; if (!$gShave_rmanArchiveInserted) { global string $gShave_rmanArchiveFiles[]; // We need to insert our archive under a suitable transform node. // Shave's group node would be fine for that, however we cannot // guarantee that it will be named 'shaveDisplayGroup': the user may // have renamed it or it may be prefaced by a namespace if loaded from // a reference. // // Instead we'll use the group of the first hairnode to get rendered. // string $groups[] = shave_getDisplayGroups(); string $group; string $curObj = eval("rman ctxGetObject"); for ($group in $groups) { if ($curObj == $group) { // If there are multiple files the first one is the base file // which includes all the others. So we always just want to // emit the first one into the RIB stream. // if (size($gShave_rmanArchiveFiles) > 0) { eval("RiReadArchive(\"" + $gShave_rmanArchiveFiles[0] + "\")"); } $gShave_rmanArchiveInserted = true; break; } } } } global proc shave_rmanInjectCleanup() { global string $gShave_rmanArchiveFiles[]; if (objExists("shaveGlobals") && !getAttr("shaveGlobals.ribKeepRibFiles")) { string $file; for ($file in $gShave_rmanArchiveFiles) { if (`about -win`) { // Convert the file path to a Windows-friendly form. // $file = toNativePath($file); // All backslashes in the path must be escaped so that they // don't get misinterpreted by rman's 'System' command. // $file = substituteAllString($file, "\\", "\\\\"); string $cmd = "System \"cmd.exe /x /a /c del \\\"" + $file + "\\\"\""; RiArchiveRecord("verbatim", $cmd); } else { string $cmd = "System \"rm \\\"" + $file + "\\\"\""; RiArchiveRecord("verbatim", $cmd); } } } clear $gShave_rmanArchiveFiles; } global proc int shave_rmanPluginIsLoaded() { if (`pluginInfo -q -loaded RenderMan_for_Maya`) { // Only the 'Pro' version supports RIB insertion so make sure that // this is Pro. // string $version = `pluginInfo -q -version RenderMan_for_Maya`; // return endsWith($version, "Pro"); string $isProVersion = eval("exists(\"RiAttribute\")"); return $isProVersion; } return false; } global proc shave_rmanCheckNewPlugin() { print "shave_rmanCheckNewPlugin\n"; // There's no way to remove a pluginInfo callback if Shave is unloaded // so let's first make sure that Shave is still loaded. if (`pluginInfo -q -loaded shaveNode`) { //print "plugInfo - OK\n"; global int $gShave_rmanPluginIsLoaded; // Is the rman plugin loaded? if (shave_rmanPluginIsLoaded()) { //print "rmanPluginIsLoaded - OK\n"; // If it wasn't loaded before then do our rman initialization. if (!$gShave_rmanPluginIsLoaded) { //print "gShave_rmanPluginIsLoaded - OK\n"; $gShave_rmanPluginIsLoaded = true; shave_rmanInit(); } } else { $gShave_rmanPluginIsLoaded = false; } } }