PDA

View Full Version : Layer Color to Material


andreberg
05-08-2010, 04:58 PM
Hi,

I am trying to find (or write myself if absent) a script that looks at all the layers within a C4D file, gets the objects associated with those layers and assigns or replaces a material on those objects which has a Color or Luminance channel set to the same RGB color as the given object's layer color.

Is this possible with COFFEE?

I searched for "layer color to material" with FF and Safari but both would just return a search.php file.

Thanks in advance for any pointers you may have!

André

Cairyn
05-08-2010, 09:41 PM
I am trying to find (or write myself if absent) a script that looks at all the layers within a C4D file, gets the objects associated with those layers and assigns or replaces a material on those objects which has a Color or Luminance channel set to the same RGB color as the given object's layer color.

Would that work? A material can, after all, be assigned to several objects that are in different layers, and the material itself can be part of yet another layer. You'd need to duplicate the material in that case. And you may end up with lots of different materials that are representing the same layer color afterwards, if you don't do some serious cleanup. Not to mention combinations of materials on the same object, either through layering or through selections.

Question, what are you trying to do with such a script? If it's just to show the layer colors on objects in the editor, you can do so by using (Menu of view window) Display -> Layer Color, which will show the objects in their layer's color in the editor view. That even works per-view. And it can be switched off again.

andreberg
05-09-2010, 02:43 PM
Hi Cairyn,

I am trying to render a complex object which I have converted from a CAD file for a Visual Selector tag. I have taken my time and added all the sub componentns that make up the object onto their own layers to closely resemble the CAD file layout. Then when I am at the client I simply switch on the display setting that you mentioned and they feel right at home and we can be much more productive in our meetings.

Now I am at the animation stage and I wanted to do a render to file with the display setting on but it does not honor it when rendering - it's only for display in the editor.

So, what I needed was a way to convert the layer color to be the sub component's only material with a color or luminance channel that has the same RGB values.

I ended up just taking a screenshot of a parallel view of the object in layer colors with everything hidden (like world grid etc.).

But the question still stands. It seems to me a user script would be perfect for the task.
At least that's what I thought was what they where intended for?

Cairyn
05-09-2010, 07:25 PM
I am trying to render a complex object which I have converted from a CAD file for a Visual Selector tag. I have taken my time and added all the sub componentns that make up the object onto their own layers to closely resemble the CAD file layout. Then when I am at the client I simply switch on the display setting that you mentioned and they feel right at home and we can be much more productive in our meetings.

Now I am at the animation stage and I wanted to do a render to file with the display setting on but it does not honor it when rendering - it's only for display in the editor.


An approach for animation would be to set the renderer to "Software" or "Hardware" (well, perhaps not Hardware since that doesn't obey the visibility settings for the HUD, apparently...) and animate with the desired view settings. Of course that's purely scanline, and may not be what you want.

Unfortunately, the COFFEE API is not complete. In the C++ API I can find the critical functions to access the layer, and the LayerObject and LayerData structures. In the COFFEE API I haven't seen such functionality yet. (An attempt to "Create Script" from the attribute manager with Layer data selected ends in no reaction at all... not a good sign for the Layer support of COFFEE.)

Since scripting only uses COFFEE, I'm afraid you're out of luck, unless you have the means to program a C++ plugin. (Or perhaps the Python plugin will support layers, but I haven't looked into that yet.)

tcastudios
05-09-2010, 08:14 PM
You get the Layers Names, colors etc via the operators.
Using GetPrev(), GetNext() etc you can search thru them
and apply or Allocate new mats as needed.



main(doc,op)
{

var lay = op#ID_LAYER_LINK;
var col = lay#ID_LAYER_COLOR;

println(col);

var laynext = lay->GetNext();
var laynextname = laynext->GetName();

println(laynextname);
}


So it's fully possible.

Cheers
Lennart

andreberg
05-09-2010, 11:17 PM
Thanks Lennart, that's a great starting point.

Let's see where I get from here...

Cairyn
05-09-2010, 11:18 PM
You get the Layers Names, colors etc via the operators.
Using GetPrev(), GetNext() etc you can search thru them
and apply or Allocate new mats as needed.

Thanks a lot for the hint! That may come useful some day...
...though I still can't find any documentation in the API on that...

Cairyn
05-10-2010, 12:02 AM
Anyway, an attempt to do what andreberg wanted is as follows...

action(obj) {
if(!instanceof(obj,BaseObject)) return; // just to be sure
print ("Object: " + obj#ID_BASELIST_NAME);
var lay = obj#ID_LAYER_LINK;
if (lay) {
var col = lay#ID_LAYER_COLOR;
print(" | Layer color: "); print(col);
var layname = lay->GetName();
println(" | Layer name: " + layname);
// Loop through tags
var vTag = obj->GetFirstTag();
while (vTag) {
println(vTag#ID_BASELIST_NAME);
if (instanceof(vTag,TextureTag)) {
println("Texture found!");
var mat = vTag#TEXTURETAG_MATERIAL;
println (mat->GetName());
var chan = mat->GetChannel(CHANNEL_COLOR);
if (chan) {
var cont = chan->GetContainer();
if (cont) {
var oldColor = cont->GetVector(CH_COLOR);
print("Old color: "); println(oldColor);
println(cont->SetData(CH_COLOR,col));
chan->SetContainer(cont);
}
}
mat->Update();
}
vTag = vTag->GetNext();
}
} else {
println("No layer");
}
}

loopObjects(obj) {
while (obj) {
action(obj);
loopObjects(obj->GetDown()); // may be null
obj = obj->GetNext();
}
}

main(doc,op) {
if (!doc) return;
var obj = doc->GetFirstObject();
loopObjects(obj);
}


Iterates through all objects of the scene, depth-recursively. Retrieves the layer color, then looks into the tag list for a texture tag. Fetches the material from that, and sets its color channel to the layer's color.

There is a lot left to do: undo is not supported; when there is no texture tag, no material is generated; the script relies on the presence of the color channel; and so on. But it seems to be possible to access layer stuff. Hooray.

(The code tag insists on breaking my formatting. Hate.)

andreberg
05-10-2010, 01:48 AM
Well, here's version 0.1 of my Layer Color to Material script

//
// Layer Color to Material
// CINEMA 4D User Scripts
//
// Created by Andre Berg on 2010-05-10.
// Copyright 2010 Berg Media. All rights reserved.
//
// Version 0.1
//
// Summary: Convert an object's layer color to a newly
// assigned material with the same color.
//
// Modus Operandi:
// First, loop through all the objects that are selected
// in the object manager and get the layer the object belongs to.
// Then, create a new material and set the material's color channel to
// the same RGB color as the color of the layer.
// Create a new texture tag on the object and assign the corresponding
// material.
// Do not create a new material if a material with name "RGB x,y,z"
// already exists. Use existing material instead.
//
// Tip: before you run the script, make sure you really select all the
// objects you want examined and not just their parents. You can do this
// easily by selecting the parent, then choosing Select children from
// the OM's edit menu.
// Also, if you want all the new materials that will be created added to
// an existing material layer, in the Material Manager switch to that layer
// before running the script. Any materials created will by assigned to this
// layer automatically.
//

const var RGB_8BIT_MULT = 255;
const var TARGET_CHANNEL = 0; // 0 = CHANNEL_COLOR, 1 = CHANNEL_LUMINANCE

PrintLayerNameAndColor(layer) {
var lname = layer->GetName();
var lcol = layer#ID_LAYER_COLOR;
println("layer '" + lname + "' has color: RGB("
+ tostring(lcol.x * RGB_8BIT_MULT) + ","
+ tostring(lcol.y * RGB_8BIT_MULT) + ","
+ tostring(lcol.z * RGB_8BIT_MULT) + ")");
}

ColorToString(col) {
return "RGB "
+ tostring(int(floor(col.x * RGB_8BIT_MULT))) + ","
+ tostring(int(floor(col.y * RGB_8BIT_MULT))) + ","
+ tostring(int(floor(col.z * RGB_8BIT_MULT)));
}

FindLastTag(op) {
var ttag = op->GetFirstTag();
var ttag1;

while(ttag)
{
ttag1 = ttag;
ttag = ttag->GetNext();
}
if(ttag1) return ttag1;
else return NULL;
}

OpHasTextureTagWithName(op, str) {
var ttag = op->GetFirstTag();
while (ttag) {
if (ttag->GetType() == Ttexture &&
ttag#TEXTURETAG_MATERIAL->GetName() == str) {
return true;
}
ttag = ttag->GetNext();
}
return false;
}

main(doc,op) {
var i;
doc->StartUndo();
for(i = 0; object(i); i++) {
var op = object(i);
var lay = op#ID_LAYER_LINK;
if (lay) {
var col = lay#ID_LAYER_COLOR;
var layname = lay->GetName();
var colstr = ColorToString(col);
//println(colstr);

// see if a material with name "RGB x,y,z" already exists
var mat = doc->FindMaterial(colstr);
if (!mat) {
// if not create a new material
CallCommand(13015);
mat = doc->GetFirstMaterial();
if (!mat) return;
doc->AddUndo(UNDO_MATERIAL_NEW , mat);
}

// set material name to "RGB x,y,z"
mat->SetName(colstr);

// set the color channel to the RGB values of the layer
var mchan = mat->GetChannel(TARGET_CHANNEL);
if (!mchan) return;
var mchanbc = mchan->GetContainer();
if (!mchanbc) return;
mchanbc->SetData(CH_COLOR, col);
mchan->SetContainer(mchanbc);

// find out if the object already has a tag which references the proper layer material
if (!OpHasTextureTagWithName(op, colstr)) {
var textag = AllocTag(Ttexture);
if (!textag) return;
doc->AddUndo(UNDO_TAG_NEW, textag);
op->InsertTag(textag, FindLastTag(op));

doc->AddUndo(UNDO_TAG_DATA, textag);
var tagbc = textag->GetContainer();
tagbc->SetData(TEXTURETAG_MATERIAL , mat); // Load the new Mat into the Container
//tagbc->SetData(TEXTURETAG_PROJECTION , 6); // Set projection to UVW
textag->SetContainer(tagbc);
}
}
op->MultiMessage(MSG_UPDATE);
}
doc->EndUndo();
}

or download from attachment below...

But beware it hasn't been tested extensively. More a proof of concept.
I'll continue to improve it as I keep using it on other projects.
So use at your own risk. I cannot be held responsible ... bla bla yadda yadda.

Thanks to you both for helping.

Andre


EDIT: Hey thanks a lot for posting your version too, Cairyn. Hadn't seen that as I left this post open without updating. Might have saved me some time.

Oh well, doing it yourself lets you learn faster. And this is a good thing, as this is actually my first COFFEE script.

EDIT 2: Ok, I tested the script a bit more intensively on my current project which has a rather heavy CAD object exported from ProENGINEER.

Scene has about 1000 objects in an OM hierarchy that goes about 10 layers deep max.
Layer manager has 25 layers. Polygon object count is at 10213. No clue about the actual poly count as it is not relevant - only objects count.

Takes about 8 seconds on my 8 core Xeon 2,8 GHz to select them all and run the script. End result checks out ok. No duplicate materials and the render looks like I want it to.

Speed is not the best but not too bad either. Would be faster if I'd leave the checks for existing tags and materials out but then again in case you need to undo this would take forever. Currently undo takes you back once for the whole affair of assigning the tag and once for each material created.

andreberg
05-10-2010, 02:05 AM
...though I still can't find any documentation in the API on that...

I guess he found that out by dragging the Layer attribute from the Attribute Manager into the script editor. Amirite?

PS: my second guess would be looking at the C++ SDK's coffeesymbols.h file or something similar.

Cairyn
05-10-2010, 09:36 AM
I guess he found that out by dragging the Layer attribute from the Attribute Manager into the script editor. Amirite?

PS: my second guess would be looking at the C++ SDK's coffeesymbols.h file or something similar.

Is possible. I should program more COFFEE scripts to hammer that method into my brain, I usually only try "Create Script" (or program the whole mess in C++ in the first place...), and the lack of any reaction on that method is irritating.

Looking into a different language's APIs to find symbols (when I expected methods actually, as a parallel to the C++ classes) is a bit awkward. But it's a sign that the documentation for COFFEE is not exactly comprehensive. Which is a pity, since the ability to simply write down a functionality without any external tools, and the possibility for immediate tests is really nice to have.

Oh well.

andreberg
05-10-2010, 11:28 AM
You're exactly right. Here's to hoping that the Python integration with it's self documenting nature, introspection capabilities and the easy doc string system will change that for the better.

andreberg
05-10-2010, 03:14 PM
I thought it might be interesting to post a Py4D (Python) version of the script.

Currently it has the limitation that the new texture tag is always inserted as the first tag and not the last tag so it won't override existing texture tags.
This makes it only useful if the objects don't have existing texture tags.
It's intended for learning purposes more or less.

And since Python really isn't made for pasting its code into forums, you really need to download the attachment if you want to play around with it.

As usual no liabilities etc. etc. use at your own risk and most of all
Have fun!

Andre

EDIT: I have attached a version that should work with R12's Python integration.

johankraus
09-16-2010, 03:50 PM
object display color to material?

can this script be used to create material, based on object display color (from object basic parameters)?
i've tried to use ID_BASEOBJECT_COLOR but with no sucess.

andreberg
09-18-2010, 02:41 AM
Here's a slightly edited script that will work with both layer and editor color.
It still replicates the functionality of Layer Color to Material v0.2. Depending on a user setting at the beginning of the script you can either use the objects editor color or the color of the layer the object is assigned to. As you can also set the editor color of an object to its layer color if the object is set to this settings the two modes the script can work in are essentially the same. So find at the beginning of the script the following line:

const var COLOR_SOURCE = 1; // 0 = LAYER, 1 = OBJECT

and as the comment states set it to either 0 or 1 depending on your needs.

Here's the script in all its completeness (zip download with icon at the end):

(before downloading please keep in mind this was a quick edit and has not been tested extensively! However the basic functionality is completely the same...)

//
// Color to Material
// CINEMA 4D User Scripts
//
// Created by Andre Berg on 2010-05-10.
// Copyright 2010 Berg Media. All rights reserved.
//
// Version 0.2
// Modified: 2010-09-18
//
// Summary: Convert an object's color to a newly
// assigned material with the same color. Depending on
// a setting in the source code, copy the color to use
// either from the object's layer or editor color.
// The editor color is only used if it is set to something
// other than 'off'.
//
// Modus Operandi:
// First, loop through all the objects that are selected
// in the object manager and get neccesary properties like
// the color setting from Base tab or the layer the object belongs to.
// Then, create a new material and set the material's color channel to
// the same RGB color as either the color of the layer or alternatively
// the color set in the object's basic properties (but only if the object
// color setting is set to something other than "off").
// Create a new texture tag on the object and assign the corresponding
// material.
// Do not create a new material if a material with name "RGB x,y,z"
// already exists. Use existing material instead.
//
// Tip: before you run the script, make sure you really select all the
// objects you want examined and not just their parents. You can do this
// easily by selecting the parent, then choosing Select children from
// the OM's edit menu. (NOTE: in version 0.2 the script will send the
// Select children command itself!)
//
// Also, if you want all the new materials that will be created added to
// an existing material layer, in the Material Manager switch to that layer
// before running the script. Any materials created will by assigned to this
// layer automatically.
//

// --------------------------------------
const var RGB_8BIT_MULT = 255;
const var TARGET_CHANNEL = 0; // 0 = CHANNEL_COLOR, 1 = CHANNEL_LUMINANCE
const var COLOR_SOURCE = 1; // 0 = LAYER, 1 = OBJECT
// --------------------------------------

PrintLayerNameAndColor(layer) {
var lname = layer->GetName();
var lcol = layer#ID_LAYER_COLOR;
println("layer '" + lname + "' has color: RGB("
+ tostring(lcol.x * RGB_8BIT_MULT) + ","
+ tostring(lcol.y * RGB_8BIT_MULT) + ","
+ tostring(lcol.z * RGB_8BIT_MULT) + ")");
}

ColorToString(col) {
return "RGB "
+ tostring(int(floor(col.x * RGB_8BIT_MULT))) + ","
+ tostring(int(floor(col.y * RGB_8BIT_MULT))) + ","
+ tostring(int(floor(col.z * RGB_8BIT_MULT)));
}

FindLastTag(op) {
var ttag = op->GetFirstTag();
var ttag1;

while(ttag)
{
ttag1 = ttag;
ttag = ttag->GetNext();
}
if(ttag1) return ttag1;
else return NULL;
}

OpHasTextureTagWithName(op, str) {
var ttag = op->GetFirstTag();
while (ttag) {
if (ttag->GetType() == Ttexture &&
ttag#TEXTURETAG_MATERIAL->GetName() == str) {
return true;
}
ttag = ttag->GetNext();
}
return false;
}

main(doc,op) {
doc->StartUndo();
var op = object(0);
if (!op) return false;

CallCommand(100004768); // Select Children

var i;
for(i = 0; object(i); i++) {
var op = object(i);
var lay = op#ID_LAYER_LINK;
var opusescolor = op#ID_BASEOBJECT_USECOLOR;
var col;
var layname;
var colstr;
if(COLOR_SOURCE == 1 && (opusescolor > 0 && opusescolor < 3)) { // 0 = off, 1 = auto, 2 = always, 3 = layer
// use the object's color as color source
col = op#ID_BASEOBJECT_COLOR;
} else if (COLOR_SOURCE == 0 || opusescolor == 3) {
// use the layer the object belongs to as color source
if (!lay) continue; // if the object has no layer get the next selected object
col = lay#ID_LAYER_COLOR;
layname = lay->GetName();
} else {
// break off execution
return false;
}

colstr = ColorToString(col);
//println(colstr);

if (col) {
// see if a material with name "RGB x,y,z" already exists
var mat = doc->FindMaterial(colstr);
if (!mat) {
// if not create a new material
StopAllThreads();
CallCommand(13015);
mat = doc->GetFirstMaterial();
if (!mat) return;
doc->AddUndo(UNDO_MATERIAL_NEW , mat);
}

// set material name to "RGB x,y,z"
mat->SetName(colstr);

// set the color channel to the RGB values of the layer
var mchan = mat->GetChannel(TARGET_CHANNEL);
if (!mchan) return;
var mchanbc = mchan->GetContainer();
if (!mchanbc) return;
mchanbc->SetData(CH_COLOR, col);
mchan->SetContainer(mchanbc);

// find out if the object already has a tag which references the proper layer material
if (!OpHasTextureTagWithName(op, colstr)) {
var textag = AllocTag(Ttexture);
if (!textag) return;
StopAllThreads();
op->InsertTag(textag, FindLastTag(op));
doc->AddUndo(UNDO_TAG_NEW, textag);
var tagbc = textag->GetContainer();
doc->AddUndo(UNDO_TAG_DATA, textag);
tagbc->SetData(TEXTURETAG_MATERIAL , mat); // Load the new Mat into the Container
//tagbc->SetData(TEXTURETAG_PROJECTION , 6); // Set projection to UVW
textag->SetContainer(tagbc);
}
}
op->MultiMessage(MSG_UPDATE);
}
doc->EndUndo();
}

johankraus
09-18-2010, 07:11 AM
thank you andreberg. you are the man!

CGTalk Moderation
09-18-2010, 07:11 AM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.