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 <[email protected]> #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
(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