Sub Shaders [SOLVED]



  • On 29/08/2016 at 16:29, xxxxxxxx wrote:

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

    ---------
    So I have some code I've been hacking up here. I've loosely through trial and error and foum searches hacked together how to assign shaders in code, but the shader I'm working with now is a shader within a shader, I'm making assumptions assignment should be the same, but this doesn't appear to be the case.

    		if (_emissionShader) {
    			if (_emissionShader->GetType() == Xbitmap) {
    				// The renderer emission channel accept only one type of shader, the blah shader, within that you
    				// need to set the texture
    				BaseShader* soloShader = BaseShader::Alloc(ID_SOLO_SHADER);
    				BaseShader* shader = (BaseShader* )_emissionShader->GetClone(COPYFLAGS_0, nullptr);
    				soloShader->SetParameter(DescID(EFF_TEX_LINK), GeData(shader), DESCFLAGS_SET_0);
      
    				_newMaterial->SetParameter(DescID(EMISSION_SLOT), GeData(soloShader), DESCFLAGS_SET_0);
    				_newMaterial->InsertShader(soloShader);
    				_newMaterial->InsertShader(shader);
    			}
    		}
    

    The Emission Channel has a slot that accepts a specifc shader, so I create that, then I get the clone of an Xbitmap I got from elsewhere. I set Solo Shader's Link field to the Xbitmap, then I set the emission channel to Solo shader itself. Inserting solo shader yields expected output, it is indeed linked, but inserting the Xbitmap doesn't make it appear in Solo Shader's interface.



  • On 30/08/2016 at 01:17, xxxxxxxx wrote:

    Hello,

    a shader must be inserted into the object that uses the shader. So a sub-shader must be inserted into the parent shader, not into the material. Your "shader" should be inserted into "soloShader" not "_newMaterial". You find some examples on how to handle shaders in the BaseShader Manual.

    Best wishes,
    Sebastian



  • On 30/08/2016 at 10:38, xxxxxxxx wrote:

    Yeah, what was confusing was I didn't see InsertShader listed as a method of BaseShader. Hovering in C++ today I just noticed it's a Baselist2D method. Thanks for the tip.



  • On 20/10/2016 at 08:11, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Hello,

    a shader must be inserted into the object that uses the shader. So a sub-shader must be inserted into the parent shader, not into the material. Your "shader" should be inserted into "soloShader" not "_newMaterial". You find some examples on how to handle shaders in the BaseShader Manual.

    Best wishes,
    Sebastian

    Hello Sebastian,

    What if one wants to implement something like this:
    Create a shader which can be instanced. So, the shader is created and inserted to material. It can be linked using data->SetLink(IDD_SHADER_X, new_shader) to several properties/ports of other shaders of the same material. 
    So, then edited in one place it is changed everywhere for every property where it is attached. And if you want to enumerate all shaders then you need to access the material with

    for (BaseShader * shader_i = mat->GetFirstShader(); shader_i; shader_i = shader_i->GetNext())
    {...}
    

    Do you see any problems in such approach?
    I afraid that if shader after some ctrl+Z and ctrl+Y may change it's pointer and the links like mentioned IDD_SHADER_X which have referenced the pointers to the old shaders will point to invalid pointers which is not good.
    Is it correct guess?
    Right now I have no problems with this potential problem because do such checks in every shader function:

    	if (!node) return FALSE;
    	if (!node->IsInstanceOf(Xbase)) return FALSE;
      
    	BaseShader * shader = (BaseShader * ) node;
    	BaseContainer * data = shader->GetDataInstance();
    	if (!data) return FALSE;
      
    	BaseList2D * _mat = shader->GetMain();
    	if (!_mat) return FALSE;
    	if (!_mat->IsInstanceOf(Mbase)) return FALSE;
    	BaseMaterial * mat = (BaseMaterial * )_mat;
    

    I think it is possible to avoid this potential problem and store the global list of shaders in Document and link them using some special own implemented IDs and get access to shader pointers using the global map of ID -> shader pointers. This map must be updated, and unique ID of shader must be stored in it's container and be copied in CopyTo method. With this approach it should be possible to copy/paste own implemented shader copies or instances among different materials. This is idea, not yet tested. What do you think about it?



  • On 21/10/2016 at 00:29, xxxxxxxx wrote:

    Hello,

    one should never store a reference to a (C4DAtom based) element of the Cinema 4D API as a pointer. Instead use a BaseLink to reference such an element. See the BaseLink Manual.

    For questions no longer related to this thread please open a new thread. Thanks.

    best wishes,
    Sebastian



  • On 21/10/2016 at 11:35, xxxxxxxx wrote:

    When I say that I store pointers then I mean something like this:

    void init_child_shaders(BaseShader * shaderBase, BaseShader * shaderChild)
    {
    	if (!shaderBase) return;
      
    	if (shaderBase == shaderChild) return;
      
    	BaseContainer * data = shaderBase->GetDataInstance();
    	BaseDocument * doc = shaderBase->GetDocument();
      
    	Int32 numLinks = data->GetInt32(SHADER_BLOCK_NUM_CHILDREN);
      
    	if (doc != 0)
    	{
    		for (Int32 i = 0; i < numLinks; i++)
    			data->SetLink(SHADER_BLOCK_NUM_CHILDREN + 1 + i, shaderChild);
    	}
    }
    

    So here it links with pointers. Is this correct?



  • On 24/10/2016 at 00:17, xxxxxxxx wrote:

    Hello,

    a BaseContainer stores the pointer internally using a BaseLink so this should work fine (ouf course you should add nullptr checks).

    For questions no longer related to this thread's topic please open a new thread.

    best wishes,
    Sebastian


Log in to reply