Shader referencing



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 25/11/2012 at 08:27, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:    
    Platform:      
    Language(s) :

    ---------
    When a Shader is referenced from another shader, the referenced shader must be a child of the
    referencing shader, is that correct?

    If so, how can I reference a shader multiple times? A single shader can technically not be child of
    more than one other shader.

    Thanks,
    Nik



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 26/11/2012 at 11:39, xxxxxxxx wrote:

    Exactly, that's why you can't do it without risking to cause instability.
    You will have to clone the shader.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 26/11/2012 at 13:07, xxxxxxxx wrote:

    Thank you for the answer, Jack.

    I am already cloning the shader internally when InitRender() is called. I think this is
    necessary because it could happen that InitRender() is called multiple times on the
    referenced shader.

    I do not understand the mechanics behind the shader referencing. Inserting the cloned
    shader on InitRender() as a child does not work. When the referenced shader
    is from a custom channel on the material, it works.

        def InitRender(self, shader, irs) :  
          reference = shader[c4d.REFERENCESHADER_REFERENCE]  
          if reference:  
              self.internal_reference = reference.GetClone()  
              self.internal_reference.InsertUnder(shader)  
              return self.internal_reference.InitRender(irs)  
          else:  
              self.internal_reference = None  
              return c4d.INITRENDERRESULT_OK  
      
      def FreeRender(self, shader) :  
          if self.internal_reference:  
              self.internal_reference.FreeRender()  
              self.internal_reference.Remove()  
              self.internal_reference = None
    

    As mentioned, this does not work. But when inserting the referenced shader (not
    the clone of the referenced shader) as a child, it works.

    Can you please enlighten me about what I miss, or is this not possible?

    Thank you very much,
    Niklas



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 30/11/2012 at 12:37, xxxxxxxx wrote:

    Bumb. Do you have an idea why it doesn't work using an internal clone of the shader?

    Thanks, 
    Nik



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 01/12/2012 at 03:29, xxxxxxxx wrote:

    If anything then you would have to use SetParent() to define a parent node for the shader. Still I don't think it will work. You just have to use clones from the beginning.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 04/12/2012 at 14:00, xxxxxxxx wrote:

    Hi Jack,

    I've rewritten the plugin in C++. Using SetParent() does not work, assuming that I've
    done it correctly..? It not just crashes Cinema, but also makes my PC hang up completely.

    INITRENDERRESULT ShaderLinkData::InitRender(BaseShader* shader, const InitRenderStruct& irs) {  
      if (!shader) {  
          return INITRENDERRESULT_UNKNOWNERROR;  
      }  
      BaseDocument* doc = shader->GetDocument();  
      if (!doc) {  
          return INITRENDERRESULT_UNKNOWNERROR;  
      }  
      
      // Grab the refernce from the container.  
      BaseContainer* container = shader->GetDataInstance();  
      BaseShader* reference = (BaseShader* ) container->GetLink(SHADERLINK_REFERENCE, doc);  
      
      if (reference) {  
          // Clone the referenced shader and ensure it succeeded.  
          this->internal_reference = (BaseShader* ) reference->GetClone(COPYFLAGS_0, NULL);  
          if (!this->internal_reference) {  
              return INITRENDERRESULT_OUTOFMEMORY;  
          }  
      
          // Insert the cloned shader as child, which is necessary to render  
          // the shader correctly.  
          this->internal_reference->InsertUnder(shader);  
          GeListHead* head = this->internal_reference->GetListHead();  
          if (head) {  
              head->SetParent(shader);  
          }  
      
          // Initialize the rendering with the passed InitRenderStruct.  
          return this->internal_reference->InitRender(irs);  
      }  
      else {  
          this->internal_reference = NULL;  
      }  
      
      return INITRENDERRESULT_OK;  
    }
    

    I'm not quite sure what you mean with saying "cloning from the beginning" ?

    Thank you very much,
    Niklas



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 04/12/2012 at 14:33, xxxxxxxx wrote:

    This looks somewhat better, I think. But it still does not work when the referenced shader
    is not from the materials "custom channel".

            // Insert the cloned shader as child, which is necessary to render  
          // the shader correctly.  
          this->internal_reference->InsertUnder(shader);  
          GeListHead* head = reference->GetListHead();  
          if (head) {  
              GeListNode* parent = head->GetParent();  
              GeListHead* refparent = this->internal_reference->GetListHead();  
              refparent->SetParent(parent);  
          }
    

    Thanks,
    Nik



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 05/12/2012 at 12:20, xxxxxxxx wrote:

    Hello,

    Can somebody please enlighten me about what exactly goes up when
    GeListNode::InsertUnder() is called? Because, when I inserted the referenced
    shader under my plugin shader, and clone the referenced shader internally
    on ShaderData::InitRender() , but do not create any relationships with the clone
    (i.e. I do not insert the cloned shader as child of my plugin shader), it works fine.
    And it works fine, although the cloned shader, which is used for sampling, does,
    or actually should not , know anything related to my plugin shader.

    I could be satisfied with this, if it wouldn't scratch on Cinema's stability, but it
    does. Almost always when the ShaderLink shader was used, closing Cinema 4D
    will lead in undefined behavior and finally into a crash.

    What I would like to do, is to find a way to build up the necessary relationships
    without drowning Cinema's stability, by producing valid results. (I.e. inserting an
    already inserted shader under my plugin shader, as I'm doing it, is not valid).
    It seems like GeListNode::InsertUnder() does build up the relationships correctly,
    but I am not able to reproduce it, since I do not know enough about the internals
    or how the relationships must be built. The SDK documentation is leaking this
    information.

    In the following, you can find the code that results in satisfying results when
    rendering (i.e. the referenced shader is always rendered and not just black,
    the channel-preview is updated when the referenced shader is changed), but
    involves easily crashing Cinema 4D.

    // coding: utf-8  
    //  
    // Copyright (C) 2012, Niklas Rosenstein <rosensteinniklas@gmail.com>  
      
    #include <c4d.h>  
      
    #include "ShaderLink.h"  
    #include "res/c4d_symbols.h"  
      
    class ShaderLinkData : public ShaderData {  
      
      private:  
      
      typedef ShaderData super;  
      
      BaseShader* iref;  
      
      public:  
      
      static NodeData* Alloc();  
      
      ShaderLinkData() : super(), iref(NULL) {  
      };  
      
      void AdjustShaderReference(BaseShader* shader);  
      
      // ShaderData  
      
      INITRENDERRESULT InitRender(BaseShader* shader, const InitRenderStruct& irs);  
      
      void FreeRender(BaseShader* shader);  
      
      Vector Output(BaseShader* shader, ChannelData* data);  
      
      // NodeData  
      
      Bool Init(GeListNode* node);  
      
      Bool Message(GeListNode* node, LONG type, void* data);  
      
    };  
      
    // ShaderLinkData  
      
    NodeData* ShaderLinkData::Alloc() {  
      return gNew ShaderLinkData;  
    }  
      
    void ShaderLinkData::AdjustShaderReference(BaseShader* shader) {  
      BaseDocument* doc = shader->GetDocument();  
      BaseContainer* container = shader->GetDataInstance();  
      if (!container || !doc) return;  
      
      BaseShader* reference = (BaseShader* ) container->GetLink(SHADERLINK_REFERENCE, doc, Xbase);  
      if (!reference) return;  
      
      // Check if the referenced shader is already a child of the plugin shader.  
      BaseShader* child = shader->GetDown();  
      Bool reference_ischild = FALSE;  
      LONG childcount = 0;  
      while (child) {  
          if (child == reference) {  
              reference_ischild = TRUE;  
              break;  
          }  
          childcount++;  
          child = child->GetNext();  
      }  
      
      GePrint("The shader has " + LongToString(childcount) + " children.");  
      
      // Insert the referenced shader as child if it isn't already.  
      if (!reference_ischild) {  
     **         reference->InsertUnder(shader);**  
      }  
    }  
      
    // ShaderLinkData - ShaderData  
      
    INITRENDERRESULT ShaderLinkData::InitRender(BaseShader* shader, const InitRenderStruct& irs) {  
      if (!shader) {  
          return INITRENDERRESULT_UNKNOWNERROR;  
      }  
      BaseDocument* doc = shader->GetDocument();  
      if (!doc) {  
          return INITRENDERRESULT_UNKNOWNERROR;  
      }  
      BaseContainer* container = shader->GetDataInstance();  
      if (!container) {  
          return INITRENDERRESULT_UNKNOWNERROR;  
      }  
      
      // Grab the reference from the container.  
      BaseShader* reference = (BaseShader* ) container->GetLink(SHADERLINK_REFERENCE, doc, Xbase);  
     **     if (reference) {  
          this->iref = (BaseShader* ) reference->GetClone(COPYFLAGS_0, NULL);  
          if (!this->iref) {  
              return INITRENDERRESULT_OUTOFMEMORY;  
          }  
      
          return this->iref->InitRender(irs);  
      }**  
      else {  
          this->iref = NULL;  
      }  
      
      return INITRENDERRESULT_OK;  
    }  
      
    void ShaderLinkData::FreeRender(BaseShader* shader) {  
      // Free the internal reference when it was allocated.  
      if (this->iref) {  
          this->iref->FreeRender();  
          BaseShader::Free(this->iref);  
          this->iref = NULL;  
      }  
    }  
      
    Vector ShaderLinkData::Output(BaseShader* shader, ChannelData* data) {  
      // Redirect the ShaderData::Output() class to the reference shader or  
      // return black color.  
      if (this->iref) {  
          return this->iref->Sample(data);  
      }  
      else {  
          return Vector(0.0);  
      }  
    }  
      
    // ShaderLinkData - NodeData  
      
    Bool ShaderLinkData::Init(GeListNode* node) {  
      if (!node) return FALSE;  
      BaseShader* shader = (BaseShader* ) node;  
      BaseContainer* container = shader->GetDataInstance();  
      if (!container) return FALSE;  
      
      // Initialize the shaders parameters.  
      container->SetLink(SHADERLINK_REFERENCE, NULL);  
      return TRUE;  
    }  
      
    Bool ShaderLinkData::Message(GeListNode* node, LONG type, void* data) {  
      if (type == MSG_DESCRIPTION_POSTSETPARAMETER) {  
          this->AdjustShaderReference((BaseShader* )node);  
      }  
      return super::Message(node, type, data);  
    }
    

    The complete source code, including res-files, is located here. It does
    not contain a Visual Studio project or similar, as I'm building from the
    command-line.

    The zip-file located at the link above contains prebuilt binaries for
    Windows x86 and x64, built with Cinema 4D R14.025.

    I appreciate any help and information regarding this topic.

    Thanks,
    Niklas



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 05/12/2012 at 13:01, xxxxxxxx wrote:

    I think it's still somewhat unclear.. The problems I'm having when commenting out the
    this- >AdjustShaderReference((BaseShader* )node); line in ShaderLinkData::Message(),
    is that the material preview is just black instead of rendered with the black shader. The
    next thing is, that when parameters on the referenced shader have changed, the channel
    preview and the texture in the editor does not change (is not re-rendered).

    I've added three lines in InitRender() to show some states in the console.

        // Grab the reference from the container.  
      BaseShader* reference = (BaseShader* ) container->GetLink(SHADERLINK_REFERENCE, doc, Xbase);  
     **     String docname = doc->GetDocumentName().GetString();**  
      if (reference) {  
     **         GePrint("InitRender() : Document(" + docname + "), reference available, cloning..");**  
          this->iref = (BaseShader* ) reference->GetClone(COPYFLAGS_0, NULL);  
          if (!this->iref) {  
              return INITRENDERRESULT_OUTOFMEMORY;  
          }  
      
          return this->iref->InitRender(irs);  
      }  
      else {  
     **         GePrint("InitRender() : Document(" + docname + "), no reference available!");**  
          this->iref = NULL;  
      }
    

    The line mentioned above, which inserts the referenced shader under my plugin shader,
    is commented out, therefore the referenced shader is not invalidly a child of it and
    the results are "valid" in that manner.

    You can find a little video showing what happens here (youtube link).

    I can think of the reasons for this issues. The material preview is black because the
    referenced shader from the link field can not be obtained from the material preview
    document.
    The channel preview and editor preview does not update because they do not get
    informed about the change of the referenced shader.

    But I don't know a solution to this. Both issues are solved when the referenced
    shader is inserted under my plugin shader, but this makes Cinema instable.

    Thanks in advance,
    Niklas



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 05/12/2012 at 13:17, xxxxxxxx wrote:

    🙂

    I was able to work arond the first issue (that the shader could not be referenced
    and rendered from the material preview) by saving the address of the referenced
    shader in the ShaderData itself, instead of retrieving it from the container on
    InitRender(). I had to copy this value from the old to the new instance when it is
    copied in NodeData::CopyTo().

    I have fears that it could happen that the referenced shader becomes deallocated
    and my shader plugin won't notice this, and therefore still owns the address to the
    already deallocated shader. Could this happen?

    // EDIT: There you go. Removing the referenced shader from the document crashes
    Cinema..    .___.''

    The important parts that have changed:

    INITRENDERRESULT ShaderLinkData::InitRender(BaseShader* shader, const InitRenderStruct& irs) {  
      // ...  
      // Clone the reference if available.  
     **     BaseShader* reference = this->lref;**  
      if (reference) {  
          this->iref = (BaseShader* ) reference->GetClone(COPYFLAGS_0, NULL);  
          if (!this->iref) {  
              return INITRENDERRESULT_OUTOFMEMORY;  
          }  
      
          return this->iref->InitRender(irs);  
      }  
      // ...  
      return INITRENDERRESULT_OK;  
    }  
      
    Bool ShaderLinkData::Message(GeListNode* node, LONG type, void* data) {  
      BaseShader* shader = (BaseShader* ) node;  
      if (!shader) {  
          return FALSE;  
      }  
      if (type == MSG_DESCRIPTION_POSTSETPARAMETER) {  
     **         BaseDocument* doc = shader->GetDocument();  
          BaseContainer* container = shader->GetDataInstance();  
          if (!doc || !container) return FALSE;  
          this->lref = (BaseShader* ) container->GetLink(SHADERLINK_REFERENCE, doc, Xbase);**  
      }  
      return super::Message(node, type, data);  
    }  
      
    Bool ShaderLinkData::CopyTo(NodeData* n_dest, GeListNode* source_node, GeListNode* dest_node, COPYFLAGS flags, AliasTrans* atrns) {  
      if (!n_dest) {  
          return FALSE;  
      }  
     **     ShaderLinkData* dest = (ShaderLinkData* ) n_dest;  
      dest->lref = this->lref;**  
      
      return super::CopyTo(dest, source_node, dest_node, flags, atrns);  
    }
    

    The problem with the update in the editor and the channel preview still remains. Is
    there a way I can add a "dependency" to these, therefore telling them they should
    update when the referenced shaders' parameters change?

    Thanks,
    Niklas


Log in to reply