What could hang BakeTexture? [SOLVED]

On 27/05/2015 at 12:52, xxxxxxxx wrote:

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

I have this code:

BaseContainer settingsBc;   
settingsBc.InsData(BAKE_TEX_WIDTH, 500);   
settingsBc.InsData(BAKE_TEX_HEIGHT, 500);   
AutoFree<BaseDocument> bakedDoc(InitBakeTexture(newDoc,&textags;[0],&texuvws;[0],nullptr,texture_count,settingsBc,&bakeError;,nullptr));   
if (bakeError != BAKE_TEX_ERR_NONE) {   
     return TRUE;}   
AutoAlloc<BaseBitmap> bitmap;   
if (bitmap->GetBw()==0) {   
     return TRUE;}   
     StatusSetText("Calculating Vertex Colors...");   
GePrint("Starting Bake");   
GePrint("Bake Finished");   
if (bakeError != BAKE_TEX_ERR_NONE) {   
     return TRUE;}   

But it works sometimes and some others it hangs in the BakeTexture command.
I know it hangs there because the "Starting Bake" gets printed in the console but the "Bake Finished" never gets printed (and the Status Bar also keeps spinning.
What could make the BakeTexture command fail? I don't see anything in the SDK that tells me what could go wrong.

On 27/05/2015 at 14:05, xxxxxxxx wrote:

1. Try a MultipassBitmap instead of a basic BaseBitmap.
2.  Look at Sebastian's code in your other post (link below) concerning the BaseContainer settings.   Maybe the use of the Setxxx() methods will work better (?)


On 27/05/2015 at 15:18, xxxxxxxx wrote:

Found the problem and is the following:

If my original object (the one that is placed in a new document with IsolateObjects) is using materials with bitmaps that have a full path, it works fine. If the bitmap has a name only (meaning that should be in a folder that Cinema 4D searches for) it will hang.

I can understand why this is happening. The newly created document has no clue where the bitmaps are, if they are referenced just by name.
But I thought that the IsolateObjects would deal with that, since it states clearly in the SDK:

BaseDocument* IsolateObjects(BaseDocument* doc, const AtomArray& t_objects)   
A helper routine to copy the objects t_objects of document doc to a new document (returned). All materials associated are also copied over and the links are corrected.   

I guess the links are not really corrected. So, how can I make sure that the document returned by IsolateObjects knows where the bitmaps are?
Is there any way for force all references to external bitmaps to become absolute paths in the document created by IsolateObjects?

On 27/05/2015 at 15:35, xxxxxxxx wrote:

Links are corrected but the references to images stored on disk (the 'bitmap') are not.  You may have to actually get the full path from the original materials and set it in the bake document materials.

On 27/05/2015 at 15:48, xxxxxxxx wrote:

So, I must run through all materials, through all channels, inside each shader and sub-shader to check if there are any bitmaps and change all materials from relative to absolute?

On 27/05/2015 at 16:08, xxxxxxxx wrote:

That is a bit of a conundrum there.  If the image files are all relative to the saved document (Save as Project), then that document can find all of the image files easily.  But since you are creating a new, temporary, unsaved document, that relationship no longer exists.  Interesting that this is not taken into account in their code to fill out the paths to absolute.

In BaseDocument, there is GetAllTextures() which you can use on the original doc.  There is no SetAllTextures().  In code that I've created to resolve image file path issues for one of my plugins, it was only at the Channel texture level so shaders and sub-shaders were of no concern.  Code for this might get hairy.

On 27/05/2015 at 16:11, xxxxxxxx wrote:

Yes, I can see that it can :-(
Maxon should really have included a way to turn all texture paths into absolute, on the documents created by IsolateObjects :-(

On 27/05/2015 at 16:14, xxxxxxxx wrote:

There is a command (CallCommand(1029820)) that performs "Globalize Filenames" but I guess it works on the active document, not on a virtual document :-(

On 27/05/2015 at 16:19, xxxxxxxx wrote:

One possibility, and this may be ill-advised, is to call BaseDocument's SetDocumentPath() on the temp doc with the path from the original document (using GetDocumentPath()) and see if that 'magically' lets your temp doc find the image files.  'Twould be much less work if it is fooled.

On 27/05/2015 at 16:20, xxxxxxxx wrote:

I will give it a shot ;-)

On 27/05/2015 at 16:25, xxxxxxxx wrote:

Know what?


YES!!! Thank you, Robert.

On 27/05/2015 at 18:04, xxxxxxxx wrote:

One caveat: if the original document has not been saved with Save As Project, there may be issues using this methodology.  You have several alternatives: do that yourself (somewhere and probably with file cleanup thereafter), request the user to do it a priori, or have both methods available (the easy one and then fall into the more laborious one in that circumstance).  The first alternative is almost the easiest since it only requires that you track the original document path and do the file cleanup using the easy fix method.  Keep this approach in mind.  Always consider all possibilities!

On 28/05/2015 at 04:19, xxxxxxxx wrote:

I agree that we, programmers, must cover all possible situations that could go wrong :-)
However, I don't thing there will be a problem with that.
Because, if the user try to use my plugin on a non-saved document, two things can happen:

- The bitmaps that he loads are on one of the default paths that Cinema4D searches for (the "tex" folders, the application folder, the preferences folder, the Texture Paths folders, etc). And, in that case, it all works fine. I tested it ;-)

- The bitmaps that he loads are NOT on one of the default paths and in that case, Cinema 4D asks if the user wants to create a copy of the bitmap. It the user says NO, an absolute path is stored and, in that case, my plugin works fine. If it says yes, just the name is stored but the bitmap is copied to the "tex" folder inside the Preferences folder (since the document was not saved yet). And this takes us to the previous case :-)

I already tested it. It works fine, even in non-saved documents.

However, I will advise the users to follow good working methods, in the manual :-)

On 29/05/2015 at 03:18, xxxxxxxx wrote:


I know, you already solved your problem and don't have a real need for it.
Nevertheless I want to add a small example, how to walk over all shaders in a document.
The code walks over all materials and looks for shaders of a given ID (shdId) and calls a given function (func) for each of these shaders. It takes care for shaders in shaders (like for example Layer Shader) and reflectance channel.

typedef maxon::BaseArray<BaseShader*> tArrShd;
typedef std::function<void (BaseDocument* const, Material* const, BaseShader* const)> tWalkShaderCallback;
void WalkShaderTree(BaseDocument* const doc, const Int32 shdId, Material* const mat, BaseShader* const shd, tWalkShaderCallback func)
	if (!shd)
	if (shd->GetNodeID() == shdId)
		func(doc, mat, shd);
	WalkShaderTree(doc, mat, shd->GetDown(), func);
	WalkShaderTree(doc, mat, shd->GetNext(), func);
void WalkAllShaders(BaseDocument* const doc, const Int32 shdId, tWalkShaderCallback func)
  for (BaseMaterial* bm = doc->GetFirstMaterial(); bm; bm = bm->GetNext())
    if (!bm->IsInstanceOf(Mmaterial))
    Material* mat = static_cast<Material*>(bm);
    for (Int32 idChan = 0; idChan < MAX_MATERIALCHANNELS; ++idChan) // iterate over all material channels (except reflectance, see below)
      BaseChannel* chan = mat->GetChannel(idChan);
      if (!chan)
      BaseShader* shd = chan->GetShader();
      if (!shd)
      WalkShaderTree(doc, shdId, mat, shd, func);
    tArrShd arrReflectanceShaders;
    for (tArrShd::ConstIterator iter = arrReflectanceShaders.Begin(); iter != arrReflectanceShaders.End(); ++iter)
      WalkShaderTree(doc, shdId, mat, *iter, func);

You'd use it as follows:
Have a function to be called for every shader with the given ID:

void DoForShader(BaseDocument* const doc, Material* const mat, BaseShader* const shd)
  // do something for every shader found

Then for example call this to execute DoForShader() for all bitmap shaders in any material of a scene:

WalkAllShaders(doc, Xbitmap, DoForShader);

On 29/05/2015 at 03:39, xxxxxxxx wrote:

WOW!!! Great piece of code. Very useful.
Thank you, Andreas.