How to apply a shader to all items in a scene

NerfyNerfy Posts: 35

Hiya,

I have 600 scenes using a simple shader that I created in Shader Builder, which has a diffuse texture and opacity texture, and nothing else. I've updated the shader (via a copy, and deleted the original) to include a new string field, layerName, which is used while post-processing the Rib file. Manually updating the shaders would likely take something like 20 hours of solid uninterrupted work, especially since it seems to hang when applying to complex objects like Gen2.

So the best bet seemed to be to write a script which goes through all items in a scene, and applies the new shader, then saves again (possibly even filling out the layerName via hints from the object names etc, but doing that manually wouldn't be as bad, it's the applying shaders that hangs for a minute or two and would add up to a lot of waiting). I can open a scene, iterate the items, and there's examples for how to save it again so I'm not currently worried about that.

But I've come to several dead ends after trying to work out how shaders are referenced and applied in scripts for nearly 9 hours (at this point it might have been faster to do it manually, heh). I know that I can get the material & shader names on each surface for objects in a scene, and could merge in a simple invisible cube with the new shader applied to get it or something, if there's no way to access it from the global Shader Builder list, but actually understanding where to apply the shader, why different materials apparently don't have the same shader instance on them when compared, etc, has left me scratching my head with no example code or documentation. So far all I've managed is to find the surfaces and material names, but the materials & shaders don't seemed to be shared instances or anything.


DoIt = function()
{
 var nodes = Scene.getNodeList();
 var obj;

 for (var n=0 ; n;
    var name = mat.getName(); // legs etc - i.e. the surfaces
    var matName = mat.getMaterialName(); // shader name  
    
    var shader = mat.getShader(0);
    var shaderName = shader.getName(); // also shader name
   }
  }
 }
}

DoIt();

Comments

  • NerfyNerfy Posts: 35
    edited December 2014

    Woo, I did it. It's a bit messy, but here's my rough solution. It presumes that I'll merge in an invisible object with the new shader on it.

    
    DoIt = function()
    {
     var nodes = Scene.getNodeList();
     var obj;
     
     var srcShader;
     
     findLoop :
     for (var n=0 ; n;
        var shader = mat.getShader(0);
        var shaderName = shader.getName();
        
        if (shaderName=="MyShader_New")
        {
         srcShader = mat;
         break findLoop;
        }
       }
      }
     }
     
     for (var n=0 ; n;
        var name = originalMaterial.getName(); // legs etc
        var matName = originalMaterial.getMaterialName(); // shader name  
        
        var shader = originalMaterial.getShader(0);
        var shaderName = shader.getName(); // also shader name
        
        if (shaderName=="MyShader")
        {
         var oContext = new DzElementDuplicateContext();
         var oDupeMaterial = srcShader.doDuplicateElement( oContext );
         
         oDupeMaterial.name = originalMaterial.name;
         oDupeMaterial.setLabel( originalMaterial.getLabel() );
         
         if( oDupeMaterial.className() != originalMaterial.className() ){
          // If the duplicate material inherits types we know provide a setMaterialName function
          if( oDupeMaterial.inherits( "DzShaderMaterial" ) || oDupeMaterial.inherits( "DzBrickMaterial" ) ){
           // Copy the material name
           oDupeMaterial.setMaterialName( srcShader.getMaterialName() );
          }
         }
             
         // Re-create any aliases that exist
         oContext.createAlaises();
          
          // Re-create any ERC links that exist
         oContext.createERC();
     
         // Resolve references to the source material
         oContext.doResolve();
     
         // Finalize the duplication
         oContext.doFinalize();
    
         var originalDiffuseProperty = originalMaterial.findPropertyByLabel("Diffuse Color");
         var diffuseMap = originalDiffuseProperty.getMapValue();
         
         if (diffuseMap)
         {
          var diffuseFilename = diffuseMap.getFilename();
         
          var newDiffuseProperty = oDupeMaterial.findPropertyByLabel("Diffuse Color");
          newDiffuseProperty.setMap(diffuseFilename);
          newDiffuseProperty.setPath(diffuseFilename);
         }
    
         var originalOpacityProperty = originalMaterial.findPropertyByLabel("Opacity Strength");
         var opacityMap = originalOpacityProperty.getMapValue();
         var opacityValue = originalOpacityProperty.getValue();
         
         var newOpacityProperty = oDupeMaterial.findPropertyByLabel("Opacity Strength");
         newOpacityProperty.setValue(opacityValue);
         
         if (opacityMap)
         {
          var opacityFilename = opacityMap.getFilename();
         
          newOpacityProperty.setMap(opacityFilename);
          newOpacityProperty.setPath(opacityFilename);
         }
         
         // Replace the target material with the duplicate of the source material
         var oError = oShape.replaceMaterial( originalMaterial, oDupeMaterial );
     
         // Return whether there was an error
         //return oError.valueOf() == 0x00000000;
        }
       }
      }
     }
    }
    
    DoIt();
    
    Post edited by Nerfy on
  • Richard HaseltineRichard Haseltine Posts: 100,905
    edited December 1969

    You could try saving an old-style, scripted shader preset - File>Save as>Deprecated>Shader Preset - as an ASCII script and pulling that apart to see how to build a brick shader directly, rather than using a donor object.

  • NerfyNerfy Posts: 35
    edited December 1969

    Thanks so much, I'll look into that, I was actually just trying to scan the global variables to find an instance of the material/shader without having to import a donor cube.

  • NerfyNerfy Posts: 35
    edited December 1969

    Well that was a billion times easier, and I just discovered a way to generate extensive example code to help make sense of the API.

    
    // DAZ Studio version 4.7.0.12 filetype DAZ Script
    
    DoIt = function()
    { 
     var obj, oNewMaterial;
     
     var nodes = Scene.getNodeList(); 
     
     for (var n=0 ; n;
        var name = originalMaterial.getName(); // legs, lips, etc
        var matName = originalMaterial.getMaterialName(); // shader name  
        
        if (matName=="MyShader")
        {
         oNewMaterial = new DzShaderMaterial;
         var oMatSettings = new DzSettings;
         oMatSettings.setStringValue( "AttributesMode", "Scripting" );
         oMatSettings.setStringValue( "DefinitionFile", "support/ShaderBuilder/Surface/MyShader_NewDef.dsa" );
         var oCheckMaterial = oNewMaterial.setAttributes( oMatSettings );
         delete oMatSettings;
         if( oCheckMaterial != oNewMaterial ){
          delete oNewMaterial;
          oNewMaterial = oCheckMaterial;
         }
         
         oNewMaterial.setName( originalMaterial.name );
         oNewMaterial.setLabel( originalMaterial.getLabel() );
         
         oNewMaterial.copyFrom( originalMaterial );
         shape.replaceMaterial( originalMaterial, oNewMaterial );  
        }
       }
      }
     }
    }
    
    DoIt();
    
  • Eustace ScrubbEustace Scrubb Posts: 2,698
    edited December 1969

    So if I have a shader DUF (either in ShaderMixer or ShaderBuilder format) and I want the script to apply it everywhere in the scene, how would I go about setting this script to do that?

  • NerfyNerfy Posts: 35
    edited December 1969

    The above should nearly work, the only changes you'd need to make is remove the conditional where it checks for 'MyShader', and use the menu command mentioned above to generate a script where you can find the "DefinitionFile" value (in my case, "support/ShaderBuilder/Surface/MyShader_NewDef.dsa").

  • Eustace ScrubbEustace Scrubb Posts: 2,698
    edited December 1969

    So if I get this to work for a commercial package, you want credit, right? Or just a cut of the first check? ;)

  • NerfyNerfy Posts: 35
    edited December 1969

    Hah, I think that Richard deserves most of the credit for knowing about the auto generate code button, which is where most of it came from.

  • NerfyNerfy Posts: 35
    edited December 2014

    There seems to be a bug in the above code for some materials, removing the setName call fixes it but then leaves the mats without an internal name. (though they still have a label)

    Post edited by Nerfy on
Sign In or Register to comment.