I have a question about the correct path from the ShaderData::Output()
function which gets overriden by a custom shader to the texture tag that links to the material which hosts the shader in one of its slots. Before stating the problem, let me explain the reason why I would like to do this:
I am trying to reuse the same material across multiple objects. This material hosts a custom shader called Object Controlled Shader
in its Color channel's Texture slot, as indicated by the orange arrow in the image below:
This custom shader is meant to enable me to drive the color from a custom user data Color property that is associated with a texture tag attached to an object. I can use this user data Color property to set a per object texture tag color for the material and be able to modify it based on various criteria, without having to physically change the material, since it is also shared by other objects. I can do this while reusing all other aspects of the material. So, in effect, this would enable me to parameterize various aspect of a material via custom shaders on a per object per texture tag basis. This is somewhat similar to what the Mograph Color Shader currently does, except that the Color Shader allows for only one point of customization control, since it is hard wired to an Object's Basic Color (ID_BASEOBJECT_COLOR
) property.
It should be noted that the built in Mograph Color Shader works correctly to set the color both in Cinema 4d's Viewports and at render time, which is important as I will explain, below.
We start with the following overriden function in my custom shader:
Vector MyShader::Output(BaseShader* pShader, ChannelData* pChanelData)
{
...
}
Now let's look at what we have available in this function:
this
- A pointer to an instance of my custom shader class derived from ShaderData
pShader
- A pointer to the BaseShader
that hosts the ShaderData
derived object
pChannelData
- A pointer to an object of type ChannelData
After much trial and error, I have been able to figure out that the following code (potentially) gets me the texture tag user data based color property I am trying to extract and use that to return a per object texture tag specific color from the Output()
function:
Vector MyShader::Output(BaseShader* pShader, ChannelData* pChanelData)
{
int MY_COLOR_ID=1; // Id inside a texture BaseTag's user data
Vector COLOR_VECTOR_NOT_PRESENT=Vector(1.0, 0.0, 0.0); // Bright red color - to indicate errors
Vector colorVector=COLOR_VECTOR_NOT_PRESENT;
// If the ChannelData has a valid pointer to a VolumeData object (vd), and this object points to a valid
// TexData object (tex), which itself points to a valid BaseTag object (link)
if (pChannelData && pChannelData->vd && pChannelData->vd->tex && pChannelData->vd->tex->link)
{
// Then, I can get the texture tag linked to the material hosting my custom shader
BaseTag *pTag=pChannelData->vd->tex->link;
// ..., and that also means that I can get any UserData associated with this texture tag (if present)
const BaseContainer *pUserData=pTag->GetDataInstance()->GetContainerInstance(ID_USERDATA);
// If user data is present
if (pUserData)
{
// ..., and it contains a Vector data item at MY_COLOR_ID with a color (i.e., Vector) property type
colorVector=pUserData->GetVector(MY_COLOR_ID, COLOR_VECTOR_NOT_PRESENT);
}
}
// Return the color vector from the user data to customize the color of the shader on a per texture tag
// basis (or COLOR_VECTOR_NOT_PRESENT, bright red, if the texture tag does not have this user data item)
return colorVector;
}
This seems to work, but it only works during renders. I cannot, for the life of me, figure out how to get the texture tag of an object that is assigned a material using my custom shader for purposes of display in Cinema 4d's Viewports, regardless of the Viewport's Display Shading setting. I have tried, seemingly, every possible path through the pointers available in the objects passed by pointer to the Output()
function.
I have read many reports online that code like this: pChannelData->vd->op->link->GetCacheParent()
, assuming of course that all pointers along the way are valid, gets the object that hosts the texture tag linked to the material hosting the custom shader, but this does not work in S22 - the latest C4D release. I only get some internal "virtual" or perhaps "cached" object named "Object" back and every object path through the hierarchy that I have tried using that link
pointer seems to get me no closer - it's always BaseObject
objects named "Object" which are clearly not my object but some sort of cached copies. I am trying to get my actual object (e.g., an object named "Cube" in the Object Manager's node hierarchy), so that I can access its texture tag and get the per object per texture tag specific user data I am looking for in order to properly set the color that my shader returns from its Output()
function when called for that object to display it in a Viewport.
Is there any possible way to get from a shader to a texture tag (or a BaseObject that hosts that tag) during calls to Output() while an object is being drawn in a Viewport, so that in Output()
I can use some user data from this texture tag to set the color vector that my custom shader returns, similar to how I do it in the code above which works for renders. Perhaps my understanding is flawed and the Output()
function is not what Viewports use to texture objects, but then where is the correct place to do this in my ShaderData
derived custom shader and what code do I need to do this?
As shown in the shader usage example in this post, my custom shader would enable me to get each object's color in the Viewport to match the color as specified in the user data associated with its assigned Texture tag and make each object get colored differently, assuming my custom shader is used in a material's Color layer's Texture slot.
Of course, the ultimate goal of this exercise is to be able to use the custom shader in any place of a material where a shader can be placed (i.e., any Texture property in any channel of the material) and be able to customize the material on a per object texture tag user data basis.
Update with additional info:
Over the weekend, it has become clear to me that my custom shader's override of the ShaderData::Draw()
function, gets called when an object that is assigned a material with my shader in a Texture Tag, gets refreshed and/or drawn in a Cinema 4D Viewport. What is not clear at all is how do I communicate any object texure tag specific customizations from my shader during the call to Draw()
, assuming that this is the right place to do that for purposes of Viewport display of an object's material.
To be clear, unlike the Output()
member function which returns a Color Vector containing the color calculated by my custom shader, based on the input parameters supplied to Output()
, namely: BaseShader* sh, ChannelData* cd
, the Draw()
function only returns a Bool
success or failure value.
It is not clear to me, assuming of course that the Draw()
function is the right place to make per object per texture tag changes/updates on behalf of an object and based on my shader's custom rules, how I go about communicating this information back to Cinema 4D, so that the object can be properly drawn in a Viewport.
Perhaps this can be done by making changes to some member variables of the objects, pointers to which are passed to Draw()
. That is: BaseShader * sh, BaseObject *op, BaseTag *tag, BaseDraw *bd, BaseDrawHelp *bh
.
If this is the right way to do it. It is not clear to me what changes to make to what members of those objects, or what member functions to call using the pointers to the various objects passed to Draw()
, in order to send a Color vector back to Cinema 4D so that a particular object in the scene can be drawn in a custom way using a material that holds my shader in the Texture property of its Color layer.