Cloned layer shader, InitRender() missing assets



  • On 07/01/2013 at 11:50, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   R14 
    Platform:   Windows  ;   
    Language(s) :     C++  ;

    ---------
    I'm cloning a shader and calling its InitRender() method in my plugin. When this shader is a
    Layer shader containing at least one layer (can be any type of shader), the InitRender() method
    returns INITRENDERRESULT_ASSETMISSING (value of -101).

    if (reference) {
          this->iref = (BaseShader* ) reference->GetClone(COPYFLAGS_0, NULL);
          if (!this->iref) {
              return INITRENDERRESULT_OUTOFMEMORY;
          }

    INITRENDERRESULT result = this->iref->InitRender(irs);
          if (result != INITRENDERRESULT_OK) {
              GePrint("ERROR, this->iref->InitRender() returned " + LongToString(result));
          }
          return result;
      }

    Any idea how I can make it work?

    Thanks,
    Nik



  • On 10/01/2013 at 10:56, xxxxxxxx wrote:

    Anyone? Can't the Layer shader be rendered when not being inserted in a document,
    or what else might be the reason for this?



  • On 10/01/2013 at 11:55, xxxxxxxx wrote:

    I may be quite wrong but I don't think that will work. IIRC, you can't have two materials or two shaders pointing at the same shader. In other words, you initially have a layer shader with a link to a second shader. When you clone the layer shader the clone (this is where I'm guessing BTW) still points to the original second shader. So you now have two layer shaders pointing at the same sub-shader, for want of a better word, and I don't think that's allowed.

    I think this is one of the reasons no-one has yet developed a node-based material system in Cinema, because you would need to be able to do that.



  • On 10/01/2013 at 12:15, xxxxxxxx wrote:

    Hello spedler,
    I do think the sub-shaders are cloned as well, because they actually are children of the Layer
    shader (layerShader->GetDown() returns one of the sub-shaders). Anyway, I'll check this to be sure,
    thanks!

    Edit: Unfortunately your guess is not right. :( I've tested it with a Layer shader containg a single
    sub-shader. Thank you anyway!

      
    BaseShader* child_src = reference->GetDown();  
    BaseShader* child_ref = this->iref->GetDown();  
    if (child_src == child_ref) {  
    GePrint("WARNING: Children were NOT cloned!");  
    }  
    else {  
    GePrint("OK: Children were cloned!");  
    }
    

    -Nik



  • On 11/01/2013 at 01:24, xxxxxxxx wrote:

    That's interesting, I didn't think the sub-shader would be cloned. Useful to know for the future but doesn't help you much at the moment, sorry.



  • On 15/01/2013 at 09:25, xxxxxxxx wrote:

    Bump. :nerd:



  • On 28/01/2013 at 04:57, xxxxxxxx wrote:

    I'm still searching for a solution to this..



  • On 28/01/2013 at 07:48, xxxxxxxx wrote:

    Hi Niklas,

    I can't reproduce this behavior. I've been able to clone a layer shader and init it for render from a CommandData::Execute().

    In what type of plugin data and method are you cloning layer shaders?
    If I understand it always fails with layer shaders but works with any other type of shader, right?



  • On 28/01/2013 at 08:43, xxxxxxxx wrote:

    Hello Yannick,

    >>>  I've been able to clone a layer shader and init it for render from a CommandData::Execute().

    Interesting. Thanks for checking this. I am doing this operation in my ShaderData subclass on
    InitRender(). More specifically, the InitRenderStruct that is passed there is passed through to the
    layer shader.

    This is the complete InitRender() function:

    INITRENDERRESULT ShaderLinkData::InitRender(BaseShader\* shader, const InitRenderStruct& irs) {
        if (!shader) return INITRENDERRESULT_UNKNOWNERROR;
        BaseDocument\* doc = shader->GetDocument();
        BaseContainer\* container = shader->GetDataInstance();
        if (!container || !doc) return INITRENDERRESULT_UNKNOWNERROR;
    
        // Clone the reference if available.
        BaseShader\* reference = this->GetReferenceShaderInstance(shader, container, doc);
        if (reference) {
            this->iref = (BaseShader\*) reference->GetClone(COPYFLAGS_0, NULL);
            if (!this->iref) {
                return INITRENDERRESULT_OUTOFMEMORY;
            }
    
            INITRENDERRESULT result = this->iref->InitRender(irs);
            if (result != INITRENDERRESULT_OK) {
                GePrint("ERROR, this->iref->InitRender() returned " + LongToString(result));
            }
            return result;
        }
        else {
            this->iref = NULL;
        }
    
        return INITRENDERRESULT_OK;
    }
    

    The GetReferenceShaderInstance() method basically just grabs a shader from a linkfield in the
    shaders description. Later in ShaderData::Output(), I use iref->Sample() to output what the
    cloned shader would output, but that is just yellow when a layer shader is used as reference
    (and the error message  from the code above is printed to the console).

    Would you like to take a look at the complete source-code? I'd prefer to make it not public, yet.

    Thanks!
    -Niklas



  • On 28/01/2013 at 09:00, xxxxxxxx wrote:

    Yes, you can send the complete source code to sdk_support@maxon.net.
    It will help me reproduce the issue.



  • On 30/01/2013 at 02:35, xxxxxxxx wrote:

    I see two issues in your code:

    First, you don't pass an alias translator to GetClone(), so even though the children get cloned, too, all the child links will be broken. The Layer Shader returns INITRENDERRESULT_ASSETMISSING if a child link is dead.
    Therefore, you have to allocate and initialize an AliasTrans first, and use it in GetClone().

    Second, to successfully initialize and use a shader in a document, you have to insert it into the document's node structure, too. Otherwise, the renderer might be unable to find it.
    To do that, allocate a GeListHead, initialize it with the render document and insert your shader clone.

    Don't forget to remove and free all the cloned stuff after the rendering has finished.



  • On 30/01/2013 at 03:44, xxxxxxxx wrote:

    Hello Frank,

    thanks for your answer. Your explanation makes sense to me.

    I didn't pass an AliasTrans, because I didn't insert the cloned shader into the document. I
    concluded that the established links would be invalid anyway in this case.

    AWESOME! It works!
    First, I was a little-bit irritated when you said "initialize the GeListHead with the document". But I
    think you were talking of the GeListHead::SetParent() method, is that correct?

    Here's the working code:

        INITRENDERRESULT ShaderLinkData::InitRender(BaseShader\* shader, const InitRenderStruct& irs) {
            if (!shader) return INITRENDERRESULT_UNKNOWNERROR;
            BaseDocument\* doc = shader->GetDocument();
            BaseContainer\* container = shader->GetDataInstance();
            if (!container || !doc) return INITRENDERRESULT_UNKNOWNERROR;
    
            // Clone the reference if available.
            BaseShader\* reference = this->GetReferenceShaderInstance(shader, container, doc);
            if (reference) {
    **AutoAlloc <AliasTrans> aliastrans;
                if (!aliastrans) return INITRENDERRESULT_OUTOFMEMORY;
                if (!aliastrans->Init(doc)) return INITRENDERRESULT_UNKNOWNERROR;**
    
                this->iref = (BaseShader\* ) reference->GetClone(COPYFLAGS_0, **aliastrans** );
                if (!this->iref) {
                    return INITRENDERRESULT_OUTOFMEMORY;
                }
                **aliastrans- >Translate(TRUE);**
    
                **this- >irefHead = GeListHead::Alloc();
                if (!this->irefHead) {
                    BaseShader::Free(this->iref);
                    return INITRENDERRESULT_OUTOFMEMORY;
                }
                this->irefHead->InsertFirst(this->iref);
                this->irefHead->SetParent(doc);**
    
                INITRENDERRESULT result = this->iref->InitRender(irs);
                String name = reference->GetName();
                if (result != INITRENDERRESULT_OK) {
                    GeDebugOut("Shader '" + name + "' was not initialized. Error-code: " + LongToString(result));
                }
                else {
                    GeDebugOut("Shader '" + name + "' was initialized.");
                }
                return result;
            }
            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();
                **this- >iref->Remove();**
                BaseShader::Free(this->iref);
                this->iref = NULL;
            }
    **if (this- >irefHead) {
                GeListHead::Free(this->irefHead);
                this->irefHead = NULL;
            }**
        }
    

    Thank you very much! This has given me some new insight into how Cinema internally works. :)
    -Niklas


Log in to reply