weird behavior in MaterialData Init() [SOLVED]

On 23/06/2015 at 18:12, xxxxxxxx wrote:

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

my MaterialData plugin Init() function is called multiple times, is this normal?? I thought Init() is supposed to get called only ONCE.

Bool MyMaterial::Init (GeListNode* node)
	GePrint("init from material");
	return true;

I checked the behavior with simpleMaterial example from the SDK, it is the same, is this normal??? how can I Init values like this, my class members resets ...

On 24/06/2015 at 00:46, xxxxxxxx wrote:


This should be normal, I guess. It's inherited from NodeData and the docs tell the following:
Init() gets called when a new instance of the node data is allocated.

Also, where do you use the plugin?

Casimir Smets

On 24/06/2015 at 06:33, xxxxxxxx wrote:

I use it as a material, and Init should be called once on material creation, I see Init is called a lot!!

On 29/06/2015 at 04:32, xxxxxxxx wrote:

Hi Mohamed,

actually nowhere on the SDK documentation it says, Init() is called only once. You are only guaranteed, that Init() will be called at least once after Alloc(). But in fact, as you already saw, it may be called multiple times for various reasons. Depending on what you are doing in Init(), you will have to take care for this in your Init() function.

On 29/06/2015 at 16:51, xxxxxxxx wrote:

thanks Andreas, I will rely on constructor then "for local class members".

On 29/06/2015 at 22:23, xxxxxxxx wrote:

well. doing it in constructor didn't work "simply because it is calling Alloc() ..."
so what I want to achieve:
store some data inside the material "is this possible with local variables? or I have to use descriptions"

On 01/07/2015 at 06:02, xxxxxxxx wrote:

Hi Mohamed,

you already found out, that the constructor is a bad idea.
But why not use Init()? If you have a problem with multiple initializations, you could simply have a flag in your class, which gets set after the initialization is done. Next time you enter Init() you simply check the flag and exit.

On 01/07/2015 at 07:58, xxxxxxxx wrote:

this is horrible!!!, the class is constructed multiple times, how will the flag work?
I wanted to create a singleton flag for any purpose inside the class, but as the class is created everytime it calls matpreview, it doesn't work "all class members are thrown away and I can't rely on Init()"

for example:
Init() { m_update_preview = TRUE;}
and in the mat preview render:
if(m_update_preview) { m_update_preview = FALSE; render();}

this is totally won't work, no class members are working as it is initialized multiple times, what should I do?

On 01/07/2015 at 08:00, xxxxxxxx wrote:

So you have global data, that is used by all instances your material class?
In such cases I tend to use a globally static variable as a flag.

On 01/07/2015 at 08:37, xxxxxxxx wrote:

not global.

On 02/07/2015 at 05:30, xxxxxxxx wrote:

You don't want a global flag? Then, why not?
You have no global data? Fine.
But your problem are those repeated Init() calls, right?

Regarding my suggestion of a flag as member variable. This could still work, if you correctly implement it in Read()/Write() and CopyTo().

On 02/07/2015 at 09:23, xxxxxxxx wrote:

I see, so descriptions can solve this? "trying to avoid overriding Read()/Write()"

On 02/07/2015 at 17:45, xxxxxxxx wrote:

to some extent, the problem is half solved, here is what I did:
used CopyTo() , which solved how the buttons work "in the video, I have 2 buttons, one that sets the flag to TRUE and one sets it to FALSE"

now the problem is: I want to set the flag to FALSE after the render, some code:

				MatPreviewGenerateImage* image = (MatPreviewGenerateImage* )data;
				if (image->pDoc)
					Int32					w = image->pDest->GetBw();
					Int32					h = image->pDest->GetBh();
					RenderData* rdata = image->pDoc->GetActiveRenderData();
					BaseContainer bcRender = rdata->GetData();
					bcRender.SetFloat(RDATA_XRES, w);
					bcRender.SetFloat(RDATA_YRES, h);
					image->pDest->Clear(0, 0, 0);
					GePrint("m_update_preview = " + String::IntToString(m_update_preview));
						image->lResult = RenderDocument(image->pDoc, bcRender, nullptr, nullptr, image->pDest,
					else if(m_update_preview == TRUE)
						((SevenPhotonsMaterial* )node)->m_update_preview = FALSE;
						BaseVideoPost* myRenderer = BaseVideoPost::Alloc(ID_SEVENPHOTONS);
						image->lResult = RenderDocument(image->pDoc, bcRender, nullptr, nullptr, image->pDest,
															 RENDERFLAGS_EXTERNAL, image->pThread);
				DescriptionCommand* dc = (DescriptionCommand* )data;
				if (dc->id[0].id == SEVENPHOTONS_MAT_PREVIEW_UPDATE)
					m_update_preview = TRUE;
				else if (dc->id[0].id == SEVENPHOTONS_MAT_PREVIEW_UPDATE_STOP)
					m_update_preview = FALSE;

so with the 2 commands, it works fine, but it doesn't change to FALSE in the MATPREVIEW_GENERATE_IMAGE message, any ideas?

On 03/07/2015 at 05:45, xxxxxxxx wrote:

Hm? My questions may be stupid...
Are those cases in the same switch? Then why do you access m_update_preview directly in MSG_DESCRIPTION_COMMAND and via a node in MATPREVIEW_GENERATE_IMAGE?
Otherwise without knowing your class layout it's difficult to tell. Maybe you are resetting the flag in a copy?

On 03/07/2015 at 07:47, xxxxxxxx wrote:

not via a node
this: ((SevenPhotonsMaterial* )node)->m_update_preview = FALSE;
is the same as this: m_update_preview = FALSE;
it is a class member.
what I wanted to achieve is: when I hit update button,it renders "only once" //this one didn't work,
as it is perma updating, that's why I tried setting the flag to false once it enters "old singleton style"
when I hit cancel button,it breaks any rendering. //I achieved this

On 06/07/2015 at 09:01, xxxxxxxx wrote:

Hi Mohamed,

the reason you can't reset the flag is indeed, that you are working on a copy. MATPREVIEW_GENERATE_IMAGE is send to a clone of your material for rendering purposes.

That being said, I have to say, I have no solution, yet. I'm currently looking into MATPREVIEW_GET_DIRTY_COUNT and MATPREVIEW_COPY_USERDATA, if these might offer a solution. I will also discuss your case with the rest of the team tomorrow.

I'm sorry, this thread is taking so long and I still can't offer a solution. But I'm still optimistic, we'll find a solution.

On 23/07/2015 at 08:21, xxxxxxxx wrote:

Hi Mohamed,

finally I found the time to play around with this. Sorry, it took so long.
As said before, the problem is, that MATPREVIEW_GENERATE_IMAGE is send to a clone of your material. So you can't reset the flag, because you only reset it in the clone, not the original material.

My admittedly quite hacky approach is to have a BaseLink member (_linkOrig) in the material. Plus another boolean member (_flagRequestPreview) storing the "render request" flag.

In Init() I initialize as follows:

_flagRequestPreview = true; // assuming that a first initial preview is always wanted

When the material is copied (CopyTo()), I set the link. Either to the source material (if not previously set) or to the stored link. Like so:

MaterialPreviewExample* sdata = static_cast<MaterialPreviewExample*>(snode->GetNodeData());
BaseList2D* bl2d = sdata->_linkOrig->GetLink(snode->GetDocument());
if (bl2d)

In MSG_DESCRIPTION_COMMAND I do basically the same as you, simply setting _flagRequestPreview. Of course one could use a checkbox instead of two buttons, but that may be a matter of taste.

Then on MATPREVIEW_GENERATE_IMAGE I simply evaluate the link and use/reset the flag of the original material. Like so:

BaseList2D* bl2d = _linkOrig->GetLink(node->GetDocument());
if (!bl2d)
  bl2d = _linkOrig->ForceGetLink();  // this will be the most probable path, as preview is rendered in another document
Bool flagRender;
if (bl2d)
  MaterialPreviewExample* dOrig = static_cast<MaterialPreviewExample*>(bl2d->GetNodeData());
  flagRender = dOrig->_flagRequestPreview;
  dOrig->_flagRequestPreview = false;
  flagRender = _flagRequestPreview;
  _flagRequestPreview = false;
// Here: flagRender is true, only once after the button got pushed

As I said, it's a bit hacky. But as it is only used for rendering of previews, I think, it should work ok.
Let me know, what you think.

Another option would be the use of message MATPREVIEW_PREPARE_SCENE. As far as I can see, this message is still send to the original material. But if you get this message, heavily depends on your preview implementation.

On 23/07/2015 at 09:01, xxxxxxxx wrote:

Hi Andreas,

thanks a lot for the help
I will check them tonight and see , most probably the link approach looks good "I will need that link for other stuff later :smile:

On 23/07/2015 at 22:18, xxxxxxxx wrote:

it works very well, thanks Andreas
you can consider this solved.