Getting the rendered color from a surface

On 22/05/2015 at 07:04, xxxxxxxx wrote:

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

Imagine I have a polygonal object (just polygonal... not parametric primitives) that has several materials assigned. They can be blended with alpha channels, polygonal selections, Mix Textures, limited by turning off the Tile option, etc. In a nutshell, lets just imagine that we have an object that can have many textures assigned and combined.
Is there any way to sample (calculate) what is the color that it will be evaluated for a specific point of the surface?
I know that the final rendered RGB value can be influenced by lights, shadows, bumps, reflections, transparencies, etc.
But I just need to calculate the flat RGB color of the combined Color channel(s).
Is there any way to calculate that?

On 22/05/2015 at 07:19, xxxxxxxx wrote:

put it in a new scene, make all shaders with luminance channel, bake it

On 22/05/2015 at 07:24, xxxxxxxx wrote:

or a simpler/faster approach, check this thread,

you need to know the UV of the hitpoint, and from this, loop over all shaders, get it's texture "all shaders must be in UV, no primitive projection" , from texture, access it through the UV coordinate, and multiply it manually using the specified alpha.

On 22/05/2015 at 08:10, xxxxxxxx wrote:

Thank you Mohamed :-)
The bake solution seems to be the best for what I need.
But, how can I evoke the Bake Object from C++ without it showing a dialog.
I mean, I just want to create a new temp document, place a clone of my object there, with all the materials, perform a Bake Object command, get a bitmap and then use that bitmap to access the colors.
Is it possible to perform a Bake Object command in code?

On 22/05/2015 at 11:00, xxxxxxxx wrote:

I think so, SDK support should know better than me as I don't have enough experience in baking, but it should be doable

On 22/05/2015 at 11:12, xxxxxxxx wrote:

InitBakeTexture() and BakeTexture().  Note that one overload of InitBakeTexture() allows you to specify multiple texture tags.

See this thread:

On 22/05/2015 at 11:45, xxxxxxxx wrote:

WOW!!! Great.
I will look into that :-)
Thank you.

On 23/05/2015 at 05:25, xxxxxxxx wrote:

I need to use:

BaseDocument* InitBakeTexture(BaseDocument* doc, TextureTag** textags, UVWTag** texuvws, UVWTag** destuvws, Int32 cnt, const BaseContainer& bc, Int32* err = @F, BaseThread* th = @F)

because I will be baking ALL the textures.
So, how can I create the array with all the texture tags? I mean, the TextureTag** textags.
Is it an array of arrays?!?

On 23/05/2015 at 05:28, xxxxxxxx wrote:

Also, the object will probably only have one UVW tag. Must I fill the UVWTag array with multiple copies of the only UVW tag to match the number of Texture tags?
Oh, I also don't know how to fill the UVWTag** array :-(

On 23/05/2015 at 10:26, xxxxxxxx wrote:

A ** is a pointer to a pointer.  So you will need to allocate as many of those in an array as needed and point each element to a TextureTag* (what you get back from the object as you GetTag() or whatever).  Do not forget to free the array (just the array) when done.  I am surprised that a BaseList was not used instead for both.

It sure does appear that you will need matching counts for textags, texuvws, and destuvws (though it appears that the latter could be nullptr).  Either that or cnt represents the total of all.  It may be easier to use a loop through each document object but let's see if we get further information.

Here is another good thread to help:


On 23/05/2015 at 14:03, xxxxxxxx wrote:

Wow!!! Reading that thread I got a bit worried. It seems that using InitBakeTexture() and BakeTexture() is not easy :-(
Do I really need to create a new thread in my code?!?
I don't know how to do that. :-(

On 24/05/2015 at 04:32, xxxxxxxx wrote:

Definitely, I can't understand how to create my own thread and I believe that is required to check if the BakeTexture() has finished baking :-(
Could anyone help me out?

On 24/05/2015 at 05:26, xxxxxxxx wrote:

Is this the correct way to create a array of pointers to pointers?

TextureTag* text_tag[tags_count]; //tags_count is the number of texture tags, already calculated previously
TextureTag** text_tags=text_tag;

LONG index=0;

while (first_tag!=nullptr) {
     if (first_tag->IsInstanceOf(Ttexture))
          text_tag[index++]=(TextureTag* ) first_tag;

On 24/05/2015 at 06:55, xxxxxxxx wrote:

Why not use something like this ?

    BaseDocument* BakeTextureTest(BaseDocument* doc, Int32 tagsCnt,const BaseContainer &settingsBc) 
    { //Remo: 24.05.2015  BakeTextureTest
      maxon::BaseArray<TextureTag*> textags;
      maxon::BaseArray<UVWTag*> texuvws;
      maxon::BaseArray<UVWTag*> destuvws;
      // ... fill arrays ...
      for(Int32 i=0; i<tagsCnt; ++i)
        textags[i] = nullptr;
        texuvws[i] = nullptr;
        destuvws[i] = nullptr;
      BaseDocument* bakedDoc = InitBakeTexture(doc,textags.GetFirst(),texuvws.GetFirst(),destuvws.GetFirst(),tagsCnt,settingsBc);
      return bakedDoc;

On 24/05/2015 at 10:07, xxxxxxxx wrote:

That looks simple enough.
But will that work for the InitBakeTexture() that deals with several texture tags? It seems to do so, but it only points to the first tag.
Isn't it supposed to be filled with only the texture tags instead of being initialized with nullptr and simply pointing to the first one?

On 24/05/2015 at 10:17, xxxxxxxx wrote:

You do not need to make your own thread.  If you pass nullptr for it, the main C4D thread will be used (which is okay to do).

On using BaseArray and GetFirst(), I am not sure that is going to work properly.  GetFirst() returns the first element (in this instance, a TextureTag* ).  InitBakeTexture() needs a TextureTag** to iterate through the array of TextureTag* elements.

Remember that defining the array size locally like that (TextureTag* text_tag[tags_count]) will cause the array to be created on the stack instead of in memory (where you would use memory allocations).  Might work most of the time, but if you are dealing with a large number of elements, it make cause a stack overflow - basically, you run out of stack memory.

I'd like to know if BaseArray or PointerArray could be used in this instance.

On 24/05/2015 at 10:34, xxxxxxxx wrote:

I would also like to know if BaseArray could be used.
If not, my code could be used?

TextureTag* text_tag[tags_count]; //tags_count is the number of texture tags, already calculated previously   
TextureTag** text_tags=text_tag;   
LONG index=0;   
while (first_tag!=nullptr) {   
     if (first_tag->IsInstanceOf(Ttexture))   
          text_tag[index++]=(TextureTag* ) first_tag;   

As for creating the array in the stack with this type of definition, I will only be baking one object and, at most, 10 to 20 Texture tags (usually much less than that). So, I guess there will be no stack memory problem.

As for not creating a Thread, will the control only return to my code (in the main thread) after the baking is complete?

On 25/05/2015 at 07:28, xxxxxxxx wrote:

On using BaseArray and GetFirst(), I am not sure that is going to work properly.
It should work, because GetFirst() return pointer to the firs element.
Another alternative would be to use  &array[0] instate.

Here is improved code that will compile and run.
Of course it is still not complete and will result in error 3007==BAKE_TEX_ERR_WRONG_BITMAP.

    bool BakeTextureTest2(BaseDocument* doc) 
    { //Remo: 25.05.2015
    	if(nullptr == doc) return false;
    	BaseObject *obj = doc->GetActiveObject();
    	if (nullptr == obj) return false;
    	maxon::BaseArray<TextureTag*> textags;
    	maxon::BaseArray<UVWTag*> texuvws;
    	maxon::BaseArray<UVWTag*> destuvws;
    	for (BaseTag *tag = obj->GetFirstTag(); tag; tag = tag->GetNext()) {
    		if (tag->IsInstanceOf(Ttexture)) {
    			TextureTag* texTag = static_cast<TextureTag*>(tag);
    	// ! now fill texuvws and destuvws arrays !
    	BaseContainer settingsBc; 
    	//fill settingsBc here ... BakeTexEnums
    	AutoFree<BaseDocument> bakedDoc(
    		InitBakeTexture(doc, //The document. 
    			&textags[0], //The texture tags to bake. Must be assigned to objects. 
    			texuvws.GetFirst(), //The UVW tags to bake. 
    			nullptr/*destuvws.GetFirst()*/,  //The destination UVW tags for the bake. If not nullptr, the current projection is transformed into the uvw tags. 
    			textags.GetCount(), //The number of tags in textags, texuvws and destuvws arrays. 
    			settingsBc, //The bake settings: BakeTexEnums 
    			&bakeError //Assigned the error result, if not nullptr: BAKE_TEX_ERR 
    	if (BAKE_TEX_ERR_NONE != bakeError) {
    		print("bakeError ",bakeError);
    		return false;
    	//do something with bakedDoc ...
    	return true;

On 25/05/2015 at 11:31, xxxxxxxx wrote:

Thank you, I will give it a try (tomorrow).
I was having doubts with the previous code snippet because the tags array was filled with nullptr and that was what was being feed into the InitBakeTexture().

On 26/05/2015 at 09:36, xxxxxxxx wrote:


the bake functions are relatively old functions, BaseArray is a relatively new class so these two things have nothing to do with each other. But it seems that indeed these two can be combined. Here is some code I came up with:

// let's use the same UVW tag for all texture tags  
UVWTag* uvwTag = (UVWTag* )obj->GetTag(Tuvw);  
if(uvwTag == nullptr)  
 return true;  
BaseTag* tag = obj->GetFirstTag();  
// prepare arrays for texture tag ptr and uvw tag ptr  
maxon::BaseArray<TextureTag*> textureTagArray;  
maxon::BaseArray<UVWTag*> uvwTags;  
// save ptr of all texture tags; fill uvw array  
 if(tag->GetType() == Ttexture)  
     textureTagArray.Append((TextureTag* )tag);  
     uvwTags.Append((UVWTag* )uvwTag);  
 tag = tag->GetNext();  
// check if there is something to do  
if(textureTagArray.GetCount() == 0)  
 return true;  
// setup bake settings  
BaseContainer settings;  
settings.SetInt32(BAKE_TEX_HEIGHT, 400);  
settings.SetInt32(BAKE_TEX_PIXELBORDER, 2);  
settings.SetBool(BAKE_TEX_CONTINUE_UV, true);  
settings.SetBool(BAKE_TEX_USE_PHONG_TAG, true);  
settings.SetVector(BAKE_TEX_FILL_COLOR, Vector(0.0));  
settings.SetBool(BAKE_TEX_COLOR, true);  
settings.SetBool(BAKE_TEX_COLOR_ILLUM, false);  
settings.SetBool(BAKE_TEX_COLOR_SHADOWS, false);  
settings.SetBool(BAKE_TEX_COLOR_LUMINANCE, false);  
settings.SetBool(BAKE_TEX_COLOR_DIFFUSION, false);  
// InitBakeTexure  
BaseDocument* bakeDoc = InitBakeTexture(doc,textureTagArray.GetFirst(),uvwTags.GetFirst(),nullptr,textureTagArray.GetCount(),settings,&err,nullptr);  
// if success, do bake  
if(err == BAKE_TEX_ERR_NONE)  
 // prepare multipass bitmap  
 MultipassBitmap* bitmap = MultipassBitmap::Alloc(400,400,COLORMODE_RGB);  
 // bake  
 // show result  
 // clear result  
// clear bake document  

best wishes,