Material/shader custom preview - how to [SOLVED]



  • On 19/08/2016 at 07:10, xxxxxxxx wrote:

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

    ---------
    Hi everyone,

    Currently I am using the code to catch a message asking for material preview image and fill it. Here it is:

    Bool mymat::Message(GeListNode * node, Int32 type, void * msgdat)
    {
    	//...
    	switch (type)
    	{
    		//...
      
    		case MATPREVIEW_GENERATE_IMAGE:
    		{
    			MatPreviewGenerateImage * image = (MatPreviewGenerateImage* )msgdat;
    			if (image != 0)
    			{
    				if (image->pDest != 0)
    				{
    					RenderPreview(image->pDest, mat);
    					image->lResult = RENDERRESULT_OK;
    				}
    			}
    			
    			return true;
    		//	break;
    		}
    

    Later, the call to RenderPreview leads to traversal of the structure of material which may have the links to the other materials or shaders like here:

    		for (int param_link = IDD_PARAMS_LINK_BEGIN + 1, param_x = IDD_PARAMS_X_BEGIN + 1, param_const = IDD_PARAMS_CONST_BEGIN + 1;
    				 param_link < IDD_PARAMS_LINK_END; 
    				 param_link++, param_x++, param_const++)
    		{
    			BaseMaterial * material_child = (BaseMaterial * )data->GetLink(param_link, doc, PID_MY_MAT);
      
    
    

    But the problem is that on the stage of MATPREVIEW_GENERATE_IMAGE message these links to other materials are empty! The return zero. I just don't know how to get the full structure of material.
    Simple material with constant properties (without links to other shaders or material) assemble this information just fine.

    Same happens with shader previews where I traverse the structure of the shader at InitRender call and get the image and then output each pixel in Output call. It is a bad slow way but I haven't yet realised the way of doing it like in material preview.

    Btw, this code works as expected when I assemble the main scene and send it to renderer.



  • On 21/08/2016 at 00:45, xxxxxxxx wrote:

    I am not sure what exactly you are trying to do or what your actual question is, but the sdk has an example of a custom material preview: https://github.com/PluginCafe/cinema4d_cpp_sdk/blob/951686677a3ea7c546961d06d0ae8357bf97c560/source/shader/simplematerial.cpp



  • On 21/08/2016 at 03:48, xxxxxxxx wrote:

    I have a complex custom material, which has links to the submaterials (many) and shaders (implemented as links - not as shaderlinks).
    I would like to render a material preview when MATPREVIEW_GENERATE_IMAGE is generated.
    When this happens I traverse my material, get all the properties and links and send it to my renderer to get an image. The problem is that all material properties like GetFloat and GetInt32 return results but GetLink don't return anything other than zero!!! So, while traversing I can't know if there is any sub-material. Same for shaders stored/accessed with SetLink/GetLink.

    However, this traversal and all the infromation stored in links return correct result in any other situation when I am parsing the main scene and send material information to the main renderer.

    Why do I need MATPREVIEW_GENERATE_IMAGE ? I need it to give C4D an image with requested resolution when it asks the system for.



  • On 22/08/2016 at 02:14, xxxxxxxx wrote:

    Hello,

    in Cinema 4D there is no thing like a "submaterial". A shader is a child of the host material but if a material references another material this is just a link to another entity that will break when the material is copied to another document. And the material is copied to the document that will be used to render the preview image. So if the referenced materials are not in the same document as the material itself (the "doc" argument of GetLink()) the link cannot be resolved (see BaseLink Manual).

    You find some discussion on this topic in this thread: Cloning submaterials for material preview

    best wishes,
    Sebastian



  • On 22/08/2016 at 06:21, xxxxxxxx wrote:

    Hi Sebastian,

    Thanks for the topic link, it is relevant!
    As I understand it is possible to copy referenced lins (our "submaterials" and etc) at the stage of MATPREVIEW_PREPARE_SCENE message. I will try this and other advices in that topic.

    Btw, is it possible to run the similar mechanism to shaders? I would like to generate shader previews on some kind of SHADERPREVIEW_GENERATE_IMAGE, but certain parts that could probably do it like GlMessage are not documented.



  • On 22/08/2016 at 08:53, xxxxxxxx wrote:

    Hello,

    what exactly do you mean with "similar mechanism to shaders"? Do you want to show a preview of a shader in the material preview image? Or are you talking about the shader preview displayed in the shader link GUI (TexBox)?

    Best wishes,
    Sebastian



  • On 22/08/2016 at 11:53, xxxxxxxx wrote:

    Shaders have previews in their edit modes if

    INCLUDE Mpreview;
    

    is inserted into corresponding .res file. And the shader values in preview and other areas are determined by result of Output() virtual function.

    Is there an option display shader preview by using some message request to get the whole preview image instead of "per-fragment" Output() ?

    As for Output() callback: is there any way to determine the resolution of the shader preview image before I can render it with my renderer and supply it to Ouput()?



  • On 23/08/2016 at 00:06, xxxxxxxx wrote:

    Hello,

    the MaterialPreviewCustomGui sends its messages (like MATPREVIEW_GET_OBJECT_INFO) also to a shader. So if you react to that message and set bHandlePreview to true you get the message MATPREVIEW_GENERATE_IMAGE where you can fill the pDest BaseBitmap.

    best wishes,
    Sebastian



  • On 23/08/2016 at 09:13, xxxxxxxx wrote:

    Ok, thanks for advice! I will try that - it would be great.

    Btw, no any chance on determining the resolution for requests by Output()? Is this function responsible for displaying shaders in the viewport?
    Of course it is possible to recreate our shaders using Shader->Output mechanism, but I would like to realise any chance how to reuse our existing code and prepared libs without double-writting.



  • On 23/08/2016 at 15:29, xxxxxxxx wrote:

    The simple workaround was to pass the original GeListNode pointer to the copied material which is accessed at MATPREVIEW_GENERATE_IMAGE and there I used passed original GeListNode to traverse stored links to child materials. And it works correctly now

    Bool cntlMaterial::CopyTo(NodeData * dest, GeListNode * srcNode, GeListNode * destNode, COPYFLAGS flags, AliasTrans * aliasTrans)
    {
    	((cntlMaterial* )dest)->mUpdateCount = mUpdateCount;
    	((cntlMaterial* )dest)->original_node = srcNode;
      
    	return NodeData::CopyTo(dest, srcNode, destNode, flags, aliasTrans);  
    }
    

    One more question is that when "child" materials are updated and their preview is updated I would like to update the preview of the "parent" material which has the links to updated childs.

    It's easy to store the table of parent-child relations but how can I force some given BaseMaterial to regenerate it's preview image?

    Edit: sorry for my shame, materials previews updated by material->Update(true, true);



  • On 23/08/2016 at 18:47, xxxxxxxx wrote:

    I'm not sure, but I think this is so dangerous, srcNode may change.



  • On 24/08/2016 at 00:06, xxxxxxxx wrote:

    Hello,

    the Output() function is used to sample a shader. The function is used to sample the shader in any context (rendering, shader preview, with objects, etc.). In case of rendering there is no "resolution" if the shader is i.e. sampled in world space etc.

    The preview of the shader in the viewport is based on a BaseBitmap created by sampling the shader in UV space. Alternatively a custom GLSL shader can be defined that is used by the viewport engine. The latter is unfortunately not documented no an support can be given.

    Best wishes,
    Sebastian



  • On 24/08/2016 at 01:28, xxxxxxxx wrote:

    As Mohamed pointed out, just copying pointers might be dangerous since the original object that is pointed  to might get deleted at any time. A scene element should be referenced using a BaseLink. For question on CopyTo() please open a new thread.



  • On 25/08/2016 at 14:14, xxxxxxxx wrote:

    Thanks Mohamed and Sebastian, I have changed the copy from pointer to data copy that is also cheap. I had catched some very rare crash with pointer :)


Log in to reply