Cinema 4D Goodies

I thought I'd start a thread for C4D 'goodies', various experiments and such to share with the community. This is open to all to contribute, but all that I ask is that you post a scenefile with a short explanation so that people will learn from it.

Over in this thread we kinda got off topic about randomizing the Multishader in Mograph. I recently wrapped up a job that had multiple textures randomized across a tile wall. Raptor was trying to randomize a domino texture, so I created a quick scenefile to do this. Essentially you add your multishader to whatever channel you want of your material, I added it in the color channel, and change it's mode to Index Ratio. Apply it to your cloner. Add a Random effector, turn off the PSR parameters, and change the U & V Transform to randomize the multishader.

Now it's not perfect, in my example (I used colors instead of textures) you'll see the first cloners are all red with very little randomization. You kinda have to tweak the settings to your shot to find the best results, but it's far better than the uniform distribution. I've also added a spline effector to the dominos just for visually clarity.

www.andrewfallon.tv/goodies/multishader.zip
 
I tried out the multishader on the domino's and had some observations. (I need a proof reader. I'm white print on black back dyslexic, I swear. :beer:)

1/ You appear to not be allowed to have the object under a null and have the null as
the child of the cloner for it to work.
From the Mograph R10.1 documentation:

Note:
If you place several objects into a Null Object and subsequently clone that
Null Object, the colors varied by the Effectors will not be passed on to
the objects in the Null Object. To circumvent this, make the objects to be
cloned children of the cloner instead.

2/ In order to get the dominos to fall with a step effector using an animated domino and the time offset parameter , the animated domino appears to have to be under a null
and then that made a child of the cloner.

-So that kind of defeats the whole thing, it appears. Maybe I'm missing
something.

-I looked at using a COFFEE node and while I'm able to iterate through
each clone to get it's position, I'm not sure how to set the clones texture
yet.
And I'm a little new to the c++ SDK and while I can at least access all
particles now, I haven't looked into accessing the clones and changing the
textures yet.

-As usually is the case with C4D, it's probably something simple I've
missed and I'll have to say DOH!. Meantime I'll keep playing around with it
and watch for comments/suggestions.

-If anyone is intersted in the scripting end, either COFFEE or the C4D c++SDK (not the same as the Windows c++ SDK, haha) give a shout. I'm still learning that myself.

===================================================

-Here's a coffee plugin code I did last year that's simple enough I guess.
It's for when you import motion tracking data from programs like boujou or whatever, and they give you nulls. This script creates an object and assigns all of the nulls positions to points in the new object so you can mesh it.

A guy had a scene he shot with a mountain and had to animate a character over it. So he needed to created a mesh of the mountain from the motion tracking to cast shadows onto. So that led to this:
Once he had the points of an object he could create his own polys from them.

Anyways, this can be given the extension .cof and put into the plugins folder and run from the main menu->Plugins or it can be compiled as a binary and run the same way. Oh yeah, the ID is registered but your free to use it with this plugin script only.

If you make any modifications, ask maxons plugincafe.com for a new ID, they're free. :)
Old code so I may have done things more efficient if I had to do it again. And this is from my notes so it may not be the final version.

Code:
////////////////////////////
//
//
//    Camtrax plugin code.
//    -Raptor365
//
///////////////////////////////
var PLUGIN_ID = 1022077; //This is the registered ID I recieved from maxon for Camtraks



/////////////////////////////////////////////////////////////////////////////////////////////////////
//
//     register plugin in menu
//

class MyMenuPlugin : MenuPlugin
{
    public:
        MyMenuPlugin(); //constructor

        GetID();
        GetName();
        GetHelp();
        Execute(doc); 
}
/////////////////////////////// 
// funcs //
MyMenuPlugin::MyMenuPlugin() { super(); } //constructor code, call constructor of parent.
MyMenuPlugin::GetID()        { return PLUGIN_ID; } //const declared above.
MyMenuPlugin::GetName()      { return "Camtraks"; }
MyMenuPlugin::GetHelp()      
{ return "Camtraks";
}

MyMenuPlugin::Execute(thisdoc)
{
    var doc;
    var poly;
    var parent; 
    var position;
    var count;
    var child;

    println("Camtraks 1.0 start...");

    doc = GetActiveDocument();
    parent = doc->GetActiveObject();
    if(!parent) return;

    child = parent->GetDown();
    if(!child) return;

    //Get the number of children to allocate the array.
    //..............................................
    count = 0;
    do
    {
        count = count + 1;
        child = child->GetNext();
    }
    while (child);


    var pcnt;
    var vc;
    var ok;
    var p;

    //create poly and add it to the scene
    //...................................

    poly = AllocObject(Opolygon);

    //set position to 0,0,0
    //.....................

    p = poly->GetPosition();
    p.x = 0;
    p.y = 0;
    p.z = 0;
    poly->SetPosition(p);

    pcnt =poly->GetPointCount();


    vc = new(VariableChanged);// if (!vc) return FALSE; // Allocate class.
    vc->Init(pcnt,count);            // Resize point count from cnt to cnt+1.
    ok = poly->MultiMessage(MSG_POINTS_CHANGED,vc); 


    //set all of the point values
    //..........................

    child = parent->GetDown();
    count = 0;
    do
    {
        position = child->GetPosition();
        poly->SetPoint(count,position);
        
        count = count + 1;
        child = child->GetNext();
    }
    while (child);

    poly->Message(MSG_UPDATE); // Notify object of change  

    poly->MultiMessage(MSG_POINTS_CHANGED,vc);


    pcnt =poly->GetPointCount();

    poly->SetName("Camtraks" );
    doc->InsertObject(poly,NULL,NULL);

    
    doc->SetActiveObject(poly);//select object
  
    
    EventAdd(EVENT_FORCEREDRAW);//redraw

    println("Camtraks end.");

}
////////////////////////////////////////////////////////////////////////////////////////////

main()
{
    Register(MyMenuPlugin);

    
}//
 
Last edited:
Why couldn't you just move the axis point and animate the domino object itself? I'm assuming the rotation at the axis point is why your nesting under a null.

You could also use an Inheritance Effector for the falling animation then set the falloff to linear, align it to the spline with the proper orientation and animate the spline position which will cause the clones to inherit the object's animation from the effector.
 
oneinfiniteloop said:
Why couldn't you just move the axis point and
animate the domino object itself? I'm assuming the rotation at the axis
point is why your nesting under a null.

I'm using a step affector with a time offset to have the dominos falling
over. The domino is animated but for that to work it has to be under a null
as far as I can tell, at least in my tests so far. An animated object under a cloner using that method does not work.

oneinfiniteloop said:
You could also use an Inheritance Effector for the falling animation then
set the falloff to linear, align it to the spline with the proper
orientation and animate the spline position which will cause the clones to
inherit the object's animation from the effector.
Haven't tried that. Any chance of providing a sample file? If you need the
domino model I have now I can upload that and textures and/or whatever
project save version I have. Maybe privatly until we work it out?

Send me a PM and I'll link whatever project states you need to have a look at.
Be nice to figure this out, hehe.
 
I was able to do it after all, using the Random Effector with a
Multishader as oneinfiniteloop suggested with a bit of playing around.

I just had to change a couple settings:
-Multishader mode to Color Brightness.
-Random Effector-Parameter-Color, Use Alpha/Strength = checked.
That seemed to help a bit.

I used a seperate multishader for the top and bottom dots.
And reversed the texture order on the bottom one. That gave a more random
pattern.
Here's a C4D R10.1 example scene file:
 

Attachments

  • Dominos_test.zip
    177.1 KB · Views: 0
Exporting C4D Thinking Particles to Realflow.

Exporting C4D Thinking Particles to Realflow.

So here's a version of the plugin I made a few weeks ago that allows exporting TP's to RF4. This one is non registered and has a little pop-up message when you first select it from the plugin menu.
I'll have a registered version without the popup later on for a minimal charge if anyone is interested.

Here's a video of basic usage...http://www.vimeo.com/3627186

And here's the plugin. Just unzip to your plugins folder and restart C4D.
It's currently only for PC though (just a programming thing).
And I've only tested it with C4DR10.1 and RF4 so if someone has R11, try it out and let me know if it runs okay or not.

If you do something with it, I wouldn't mind seeing it if you can provide a link.
Also if you have any trouble using it, find bugs or would like other features included let me know and I'll have a look at it.
(Oh, I may change the name with the registed version and it'll have a better icon.)
 

Attachments

  • TpWriterDemo.zip
    82.5 KB · Views: 0
Last edited:
Alright, here's a little trick I discovered recently. This may be a given for you pros, but I figured I would contribute anyways. There may even be a much easier way to do this... let me know if there is.

The overall goal of this tutorial is to give a video or still image with an alpha channel a geometric border. In other words, instead of applying the alpha channel to the texture, we can actually create a flat plane that only contains polygons where the alpha channel is opaque.

Doing this versus just applying the alpha channel to the texture can come in handy in many ways. For example, you can use 'cel render' with a geometric border, where-as textural alpha channel data will not be taken into account.

Before you follow this, note that you need the Mograph module.

Ok, lets get into it.

First, create a plane.

Next, create a new texture and load a bitmap or video for which you have a corresponding alpha channel. Load this video into the color channel, but do NOT enable the alpha channel.

Next, set your width and height segments on your plane to be about equal to the resolution of your texture image. Set your width and height to match the proportions of the texture image as well (unless you want to skew it a bit).

Next, drop your image file onto your plane.

Next, create a 'displace deformer' and drop it under the plane as a child.

Next, in the attributes of the 'displace deformer', click the 'shading' tab. Load your alpha channel into the 'shader' box under this tab. It may also be helpful to raise your 'height' a bit under the 'object' tab.

Next, create a cube. Size it to be both wider and taller than the plane.

Next, move the cube so one side of it sits above the lower, non-extruded part of the plane, but not the higher, extruded part.

Next, create a 'boole'. Make sure the 'Boolean Type' is set to 'A subtract B'.

Next, drag the plane and the cube (in that order) underneath the 'boole' object (as children).

That's it... your finished. Tweak as necessary.
 
Yeah man. I do have an appreciatiation for displacement. :beer:
 
Last edited:
Writing out object file sequences.

Writing out object file sequences.

Here's another script to write out C4D animations to object file sequences (.obj) .

Updates: Sept 21, 24 -2009

Code:
////////////////////////////////////////////////////
//
//    Object Animation Dump. -Raptor365.
//    -updates Sept 21,24 /2009. 
//
// -Animates an object, bakes it's points positions and writes out to .obj files.
// -A seperate file for for each parent object per frame is created.
// - Children objects of the parent object are included in the parent object file.
// -All file names begin with 'Object' followed by the index of parent object then "_" and a 5 digit frame number.
//    Example: Object1_00006.obj is the first parent object found including it's children and it's state at the 6th frame.
//
// Usage: 
//                             
//            -Paste code into the script manager and select 'Save All'.
//            -Set time slider at frame 0.            
//            -Run the script from the main menu 'Plugins->User Scripts->name of this script'
//            -You will be prompted to select a folder to save the files to.
//
//
////////////////////////////////

// prototypes
//...........

ExportObjectToObjFile(filepath,
                                        doc,
                                        obj,
                                        bl_flip_UVW_yAxis,//invert texture map vertically.
                                        bl_reverse_z_direction,//z of points
                                        bl_reverse_normals);

GetNumberString(frame);
SetThisFrame(doc,frame);
GetNextObject(obj);
GetUvwTag(obj);
WritePoints(file,obj,z_direction); //z_direction: 1 = positive, -1 = reverse.
WriteLine(file,str);

////////////////////
//
//  main
//
////////////////////
main(doc,op)
{
    //CallCommand(13957);//Clear console
    println("object animation dump");

    
    var parent_obj;
    var fp,start_str,end_str;
    var i,p_index;
    
    //Allow user to select a save path.
    //...............................
    var path = doc->GetFilename();
    if(!path->PathSelect("Save Path")){return;}    //use cancel
    

    //Get the frames per second and animation length.
    //..............................................
    var fps = doc->GetFps();    
    var    end_frame = doc->GetMaxTime()->GetFrame(fps);

    
    start_str = path->GetFullString() + "\\Object"; 
    for(i = 0; i <= end_frame; i++)
    {
        
        SetThisFrame(doc,i);

        StatusSetBar(i/end_frame);
        
        //Get first object of scene.
        //.........................
        parent_obj = doc->GetFirstObject();
        if(!parent_obj)
        {
            TextDialog("No objects in scene.", DLG_OK);
            return;
        } //
        
        p_index = 1;
        end_str = "_" + GetNumberString(i) + ".obj";
        while(parent_obj)
        {
            fp = start_str + tostring(p_index) + end_str;
            if(!ExportObjectToObjFile(fp,doc,parent_obj,true,true,true)){break;}
            
            parent_obj = parent_obj->GetNext();

            p_index++;
        }//    wh
     }//for
        StatusClear();
        TextDialog("Done." + tostring(i) + " files.", DLG_OKCANCEL);

}//
///////////////////////////////
//
// WriteLine
//
/////////////////////////////
WriteLine(file,str)
{file->WriteString(str + GeGetLineEnd(),GE_8BIT);}

////////////////////////////
//
//  SetThisFrame
//
////////////////////////////
SetThisFrame(doc,frame)
{
     var t = doc->GetTime();
 
    //these set the frame
    //..........
    t->SetFrame(frame,doc->GetFps());
    doc->SetTime(t);
     
    //update it.
    //...........
     doc->AnimateDocument(null);
    DrawViews(DA_FORCEFULLREDRAW); 

}
////////////////////////////
//
//    GetNextObject
//
//
////////////////////////////
GetNextObject(obj)
{
    if(!obj) return obj;

    var temp_obj = obj->GetDown();

    if(temp_obj){return temp_obj;}
    
    return obj->GetNext();
                            
}//
/////////////////////////////
//
//    GetUvwTag
//
///////////////////////
GetUvwTag(obj)
{

    var UVW_tag = obj->GetFirstTag();

     while(UVW_tag)
     {
      if(UVW_tag->GetType() == tag_UVW)
      {return UVW_tag;}

      UVW_tag = UVW_tag->GetNext();
     }//wh

    return NULL;
}//

////////////////////////////////////
//
//    WritePoints
//
//
///////////////////////////////////

WritePoints(file,obj,z_direction)
{
    var i,point,globalP;
    var num_points = obj->GetPointCount();
    
    WriteLine(file,"# " + tostring(num_points) + " Points");
        
    for(i=0; i < num_points; i++)
    {

        point = obj->GetPoint(i);//object space
         globalP = obj->GetMg()->GetMulP(point); // Transform it to global coordinates

        WriteLine(file,"v " + tostring(globalP.x) + " " +
                                                    tostring(globalP.y) + " " +
                                                    tostring(globalP.z * z_direction) );
    }//for

}//

////////////////////////////
//
//  ExportObjectToObjFile
//    -obj should be a parent object.
//    -includes all children of parent object in the file.
// 
////////////////////////////
ExportObjectToObjFile(filepath,
                                                                doc,
                                                                obj,
                                                                bl_flip_UVW_yAxis,
                                                                bl_reverse_z_direction,
                                                                bl_reverse_normals)
{

    var i,str;
    var points_total;
    var UVW_tag;
    var nv,nv_total;


    //        open file for text writing
    //...............................

    var fp = GeGetRootFilename();
    fp->SetFullString(filepath);
    
    // Creates the file and writes a string to it
    //...........................................
    var file = new(BaseFile);
    if(!file->Open(fp,GE_WRITE,FILE_DIALOG,GeGetByteOrder(),MACTYPE_CINEMA,MACTYPE_CINEMA))
    {
            
                println("file could not be opened.");
                return false;
    }

    // write header intro
    //....................
    WriteLine(file,"# WaveFront *.obj file (generated by Raptor365 custom script in C4D)");

    points_total = 0;
    nv_total = 0;

    var z_dir = 1;
    if(bl_reverse_z_direction){z_dir = -1;}

    while(obj)
    {


        if (obj->GetType() != OBJECT_POLYGON)
        {
                obj = GetNextObject(obj);
                continue;
        }//

        UVW_tag = GetUvwTag(obj);
        if(!UVW_tag)
        {
            println("uvw tag not found");
            return false;
        }//
    

        WriteLine(file," ");
        WriteLine(file,"g " + obj->GetName()); //new group

        // write all vertices
        //...................
    
        WritePoints(file,obj,z_dir);
    
        WriteLine(file," ");
    
        //....................



    var nPolys             = obj->GetPolygonCount();

    var poly_vti     = new(array,nPolys,4);

    var UVW_tagdata = UVW_tag->GetData();

    var fpe = 4; //fields per element
    var A,B,C,D;
    var face_index;
    var poly;
    var vti;

    
    // get face and vt info
    //when a triangle, C is == D.
    //.....................
    
    WriteLine(file,"# " + tostring(nPolys) + " Texture coordinates");

    nv = 0;
    for(face_index = 0; face_index < nPolys; face_index++) 
    {
        poly = obj->GetPolygon(face_index);

        A = UVW_tagdata[face_index * fpe + 0];
        B = UVW_tagdata[face_index * fpe + 1];
        C = UVW_tagdata[face_index * fpe + 2];

    
        //the 3 or 4 texture UV's for the 3-4 points of this face. These are not 'face' normals, they're texture coords.
        // there are nPoly entries listed here but there are only nVertices vt's, so many repeat.
        //......
            
        if(poly->c == poly->d)//triangle
        {
            if (bl_flip_UVW_yAxis)
            {
                WriteLine(file,"vt " + tostring(A.x) + " " + tostring(1-A.y) + " " + tostring(A.z));
                WriteLine(file,"vt " + tostring(B.x) + " " + tostring(1-B.y) + " " + tostring(B.z));
                WriteLine(file,"vt " + tostring(C.x) + " " + tostring(1-C.y) + " " + tostring(C.z));
            }else
            {

                WriteLine(file,"vt " + tostring(A.x) + " " + tostring(A.y) + " " + tostring(A.z));
                WriteLine(file,"vt " + tostring(B.x) + " " + tostring(B.y) + " " + tostring(B.z));
                WriteLine(file,"vt " + tostring(C.x) + " " + tostring(C.y) + " " + tostring(C.z));


            }//if (bl_flip_UVW_yAxis)
        
            poly_vti[face_index][0] = nv; nv++;
            poly_vti[face_index][1] = nv; nv++;
            poly_vti[face_index][2] = nv; nv++;

        }else    //quad
        {
            D = UVW_tagdata[face_index * fpe + 3];    //poly 0D, if a quad    

            if (bl_flip_UVW_yAxis)    
            {
                WriteLine(file,"vt " + tostring(A.x) + " " + tostring(1-A.y) + " " + tostring(A.z));
                WriteLine(file,"vt " + tostring(B.x) + " " + tostring(1-B.y) + " " + tostring(B.z));
                WriteLine(file,"vt " + tostring(C.x) + " " + tostring(1-C.y) + " " + tostring(C.z));
                WriteLine(file,"vt " + tostring(D.x) + " " + tostring(1-D.y) + " " + tostring(D.z));
            }else
            {
                WriteLine(file,"vt " + tostring(A.x) + " " + tostring(A.y) + " " + tostring(A.z));
                WriteLine(file,"vt " + tostring(B.x) + " " + tostring(B.y) + " " + tostring(B.z));
                WriteLine(file,"vt " + tostring(C.x) + " " + tostring(C.y) + " " + tostring(C.z));
                WriteLine(file,"vt " + tostring(D.x) + " " + tostring(D.y) + " " + tostring(D.z));
            }////if (bl_flip_UVW_yAxis)
        
            poly_vti[face_index][0] = nv; nv++;
            poly_vti[face_index][1] = nv; nv++;
            poly_vti[face_index][2] = nv; nv++;
            poly_vti[face_index][3] = nv; nv++;

        }//if(poly->c == poly->d)    
                

    }//for

    
    WriteLine(file," ");
    WriteLine(file,"# " + tostring(nPolys) + " polys");

    // Write faces.
    //remember that these are indices referenced to zero and that the .obj requires them to be referenced to 1, so add 1 to each value.
    //............
    var a,b,c,d;    
    for(face_index = 0; face_index < nPolys; face_index++) 
    {
    
        //obj indexes myst begin at 1, not zero, so I add 1.
        //...........
        poly = obj->GetPolygon(face_index);
        
        a =  tostring(poly->a + 1 + points_total) + "/" + tostring(poly_vti[face_index][0] + 1 + nv_total);
        b =  tostring(poly->b + 1 + points_total) + "/" + tostring(poly_vti[face_index][1] + 1 + nv_total);
        c =  tostring(poly->c + 1 + points_total) + "/" + tostring(poly_vti[face_index][2] + 1 + nv_total);

        if(poly->c == poly->d) //triangle
        {
        
            if(bl_reverse_normals)
            {WriteLine(file,"f " +  c + " " + b + " " + a );
            }else
            {WriteLine(file,"f " +  a + " " + b + " " + c );
            }//if(bl_reverse_normals)

        }else //quad
        {
            d = tostring(poly->d + 1 + points_total) + "/" + tostring(poly_vti[face_index][3]+ 1 + nv_total); 
            if(bl_reverse_normals)
            {WriteLine(file,"f " +  d + " " +  c + " " +    b + " " + a );
            }else
            {WriteLine(file,"f " +  a + " " +  b + " " +    c + " " + d );
            }//if(bl_reverse_normals)

        }//else

    }//for


            
        

        points_total += obj->GetPointCount();
        nv_total += nv;

        obj = GetNextObject(obj);
        
    }//while

    //.....................
    file->Close();

    return true;

}//

////////////////////////////
//
//    GetNumberString
//    5 digit padding
//
////////////////////////////
GetNumberString(frame)//modified sept 22/09
{
    
    var frame_str = tostring(frame*1.0,".0f");
    var str = "00000";

    strins(str,frame_str,5-sizeof(frame_str));
    return str;


}//
 
Last edited:
This C4D coffee script reads in a *.mtl wavefront material file.
It loads in the main color, bump and alpha textures and can set basic transparency.

I had a Daz3D .obj model with a .mtl file to import so I wrote this to get the textures in as well.

If you need it to be more powerfull you can consult the Alias/WaveFront Material (.mtl) File Format
and add to the 'switch' code in main(). I may update this code from time to time as items come to my attention.
The parser is fairly robust and portable as well.

Tested with a Daz Studio 3 export into C4D R10.1 on a PC.
I tried to make the code as Mac friendly as possible but since I don't have a Mac I can't test it.

//updated Sept 13.
-replaced "\r\n" in message boxes with system dependant return
-replaced GE_8BIT with GE_XBIT (UTF-8 encoded Unicode) for non-english language compatability.


Code:
///////////////////////////////////////////////
//
//
//        Load_mtl
//        -Raptor365
//        (Sept 11,2009)
//            -updated Sept 13.
//        Tested on PC C4DR10
//
//        Loads textures asscociated wtih .mtl files.
//
//        Usage:
//        -Copy and paste this text into your C4D Script Manager and save.
//        -Close the editor.    
//        -While in C4D, load your .obj file.
//        -From the main menu choose Plugins->User Scripts->load_mtl.
//        You'll be propmpted to locate the appropriate .mtl.
//        That's it.

//        Currently loads textures for the Color, Bump and Alpha channels.
//        Sets basic transparency.
//        For more ability, consult the documentation for the 
//        Alias/WaveFront Material (.mtl) File Format
//        This file may be updated periodically. Check the date above.
//         C4D materials must have same names as in the .mtl file.
////////////////////////////////////////////////////

GetC4DMaterial(doc,mat_name);
SetChannelTexture(mat,channel,texture_path);
SetChannelTransparency(C4DMat,wrd_1);

/////////////////////////////////////
//
//
// parser
//
//
class clsParser
{
        public:
                    clsParser();

                    PromptLoadFile(doc);
                    LoadFile(file_name);
                    GetNumLines();
                    GetLine(index);
                    GetWord(str,nWord);
                    GetMatPath();

        private:
            
            
                    LoadStrings(blInit); //always call with blInit == true;
                    FixSlashes(str);
            
                    var cEOL; //system end of line.
                    var cMat_path;
                    var cFfile_str;
                    var cLines,cNum_lines;

}//
//////////////////////////////
//
//    constructor
//
//
clsParser::clsParser()
{
        cNum_lines = 0;
        cEOL = GeGetLineEnd();
}
//////////////////////////////
//
//    FixSlashes
//
//
clsParser::FixSlashes(str)
{
    var len = sizeof(str);
    var i,ch,newstr;
    
    newstr = "";
    for(i=0; i < len; i++)
    {
        ch = strmid(str,i,1);
        if(ch == "/")
        { ch = "\\";}
        newstr += ch;
        
    }

    return newstr;
}
//////////////////////////////
//
// GetMatPath
//
//
clsParser::GetMatPath()
{
    if(!cNum_lines)
        { return "";}

    return cMat_path->GetFullString();


}
//////////////////////////////
//
// GetWord
//
//
clsParser::GetWord(str,nWord)
{
    if(!cNum_lines){ return "";}//not loaded
    
    
    var i,ch,new_str,wrd_cnt;
    var flags;
    var len = sizeof(str);
    if(len < 1) return "";

    flags = 0;
    wrd_cnt = 0;
    new_str = "";

    for(i=0;i<len;i++)
    {
        ch = strmid(str,i,1);
        
        if(strcmp(ch,"!") >= 0)//valid char
        { flags |= 1;}
        else
        { flags &= 2;}

        switch(flags & 3)
        {
            case 0: //nothing
                new_str = "";
                break;
            case 1: //first char of new word
                    flags |= 2;    
            case 3: //keep adding
                new_str += ch;
                break;
            case 2://word is finished.
                if(wrd_cnt == nWord)
                { return new_str;}
                else //not this word.
                {
                    flags = 0;
                    new_str = "";
                    wrd_cnt++;
                }    
                break;
            default:
                break;
        }//sw

    }//for

    //if here then must be then at end of line with no terminating char.
    // check if we're on the right word.
    //................................

    if(wrd_cnt == nWord)
    { return new_str;}

    //else this word not found.
    //.........................
    return "";
}//
//////////////////////////////
//
// GetLine
//
//
clsParser::GetLine(index)
{
    if(!cNum_lines){ return "";}
    if(index < 0) {return "";}
    if(index >= cNum_lines){return "";}

    return cLines[index];

}
//////////////////////////////
//
// GetNumLines
//
//
clsParser::GetNumLines()
{return cNum_lines;
}


//////////////////////////////
//
// LoadStrings
// -always call with blInit == true.
// -this is to allow a first pass to get the number of lines required to alloc the array.
//
clsParser::LoadStrings(blInit)
{
    //if line ends with spaces before CR or LF, the spaces will be included on the line.
    //...................
    var len = sizeof(cFfile_str);

    var flags;
    var i,ch,new_str;
    var nLines;

    if(!blInit)
    {cLines = new(array,cNum_lines);
    }

    flags = 0;
    new_str = "";
    nLines = 0;

    for(i=0;i<len;i++)
    {
        ch = strmid(cFfile_str,i,1);
        
        if((ch == "\r") || (ch == "\n"))//return characters
        { flags |= 1;}
        else
        { flags &= 6;}

        if( strcmp(ch,"!") >= 0)//alphanumeric
        { flags |= 2;}
        else
        { flags &= 5;}

        switch(flags & 7)
        {
            
            case 2: //first valid ascii continue
                flags |= 4;
            case 4: //reading, line has a space, continue
            case 6: //reading, continue
                new_str += ch;
                break;
        
            case 5: //end of string, add newstr to list
                ///println("-" + new_str + "-");
                
                if(!blInit)
                {cLines[nLines] = FixSlashes(new_str);}

                nLines++;
                flags = 0; //reset
                new_str = "";
                break;

            default:
                break;
        }//sw

    }//for

    //if here then must be then at end of line with no terminating char.
    // check if we're on a word.
    //................................

    if( (flags&3)== 2) //add
    {    
        if(!blInit)
        { cLines[nLines] = FixSlashes(new_str);}
        
        //println("-" + new_str + "-");
        nLines++;
    }
    
    if(blInit)
    { cNum_lines = nLines;
        LoadStrings(false);
    }
}//


//////////////////////////////
//
// LoadFile
//
//
clsParser::LoadFile(file_name)
{
    var fp = GeGetStartupPath();
    fp->SetFullString(file_name);

    cMat_path = GeGetStartupPath();
    cMat_path->SetFullString(file_name);
    cMat_path->RemoveLast();

    if(!GeFileExist(fp,false))
    { 
        TextDialog("File not found:" + cEOL + file_name, DLG_OK);
        return false;
    }

    var byte_order = GeGetByteOrder();
    var file = new(BaseFile);
    if(!file->Open(fp,GE_READ,FILE_DIALOG,byte_order,MACTYPE_CINEMA,MACTYPE_CINEMA))
    {
                TextDialog("Error opening file:" + cEOL + file_name, DLG_OK);
                return false;
    }

    cFfile_str = file->ReadString(file->GetLength(),GE_XBIT);
    file->Close();


    LoadStrings(true);
    return true;

}//
///////////////////////////////
//
//        PromptLoadFile
//        -promt user to open file.
//
clsParser::PromptLoadFile(doc)
{
        
    var fp = doc->GetFilename();
    fp->RemoveLast();
    fp->SetFullString(fp->GetFullString() + "\\*.mtl");
    var ret = fp->FileSelect("Please select a file.", false);//true to save.
    
    if(!ret)return false;

    return LoadFile(fp->GetFullString());


}//


////////////////////////////////////////
//
// GetC4DMaterial
//
//
GetC4DMaterial(doc,mat_name)
{

    var mat = doc->GetFirstMaterial();
    if(!mat) {return NULL;} //null if no materials.
    while(mat)
    {
            if(mat->GetName() == mat_name)
            {
                return mat;
            }

            mat = mat->GetNext();
    }//while
        
    return NULL;

}//

////////////////////////////////////////
//
//        SetChannelTexture
//
//
SetChannelTexture(C4DMat,channel_id,texture_path)
{
    var fp = GeGetStartupPath();
    fp->SetFullString(texture_path);

    if(!GeFileExist(fp,false))
    {    return;
    }
    

    var chan, chanBc;

    chan = C4DMat->GetChannel(channel_id);
    chanBc = chan->GetContainer();
    chanBc->SetData(CH_TEXTURE, texture_path);
    chan->SetContainer(chanBc);
    
    C4DMat->SetChannelState(channel_id, true); //turn it on
    
    C4DMat->Update();
}
////////////////////////////////////////
//
//        SetChannelTransparency
//
//
SetChannelTransparency(C4DMat,wrd_1)
{

    var chan, chanBc;
    var val = evaluate(wrd_1);

    if(val >= 1) return; //fully opaque

    val = 1.0 - val;
    
    chan = C4DMat->GetChannel(CHANNEL_TRANSPARENCY);
    chanBc = chan->GetContainer();
    chanBc->SetData(CH_BRIGHTNESS, val);
    chan->SetContainer(chanBc);
    
    C4DMat->SetChannelState(CHANNEL_TRANSPARENCY, true); //turn it on
    
    C4DMat->Update();


}//
////////////////////////////////////////
//
//        main
//
//
main(doc,op)
{

    CallCommand(13957);//Clear console for C4Dr10

    var parser = new(clsParser);      
    
    if(!parser->PromptLoadFile(doc))
    {return;}

    var i,n_lines,str,wrd_0,wrd_1;    

    n_lines = parser->GetNumLines();

    var mat_path = parser->GetMatPath();

    var C4DMat;
    
    for(i=0; i < n_lines; i++)
    {
        
        str = parser->GetLine(i);
        
        wrd_0 = parser->GetWord(str,0);
        wrd_1 = parser->GetWord(str,1);
    
        switch(wrd_0)
        {
            case "newmtl":
            {    
                C4DMat = GetC4DMaterial(doc,wrd_1);
                break;
            }

            case "map_Kd":
            {    
                if(!C4DMat){break;}
                SetChannelTexture(C4DMat,CHANNEL_COLOR,mat_path + wrd_1);
                break;
            }

            case "map_Bump":
            {    
                if(!C4DMat){break;}
                SetChannelTexture(C4DMat,CHANNEL_BUMP,mat_path + wrd_1);
                break;
            }

            case "map_D":
            {    
                if(!C4DMat){break;}
                SetChannelTexture(C4DMat,CHANNEL_ALPHA,mat_path + wrd_1);
                break;
            }

            case "d": //dissolve
            {    
                if(!C4DMat){break;}
                SetChannelTransparency(C4DMat,wrd_1);
                break;
            }
            default:
                break;
        }//sw
        
    
    }//for

    
}//
 
Last edited:
Anyway to modify the first script to just get the active doc (so the user doesn't have to specify what to export) and export only a single obj? Thx.
 
I updated post #11. It should operate more like you'd expect now. No editing of code is required to use.

Read the forward in the code about operation. That post is at it's 15000 char post limit.
 
Your welcome umblefugly. Glad it's working for you. It was a bit of a rush/hack job this morning so the code needs some cleaning/tidying up but that's for another day.

I might also note that it was tested on C4DR10.1, PC.

edit:// post 11 updated Sept 24.
 
Last edited:
Is it possible to get an obj to import and copy its uvws to the next object/selected object in the scene and delete the newly imported object? Ive been wracking my brain on how to do this..lol
 
I think we can do it. I'm a little focused with a project at the moment. I'll have a look at it this week.

It might be something as simple as a copy and paste.

Actually, it should be importing the UV's when you import the object.
Or you can just overwrite the objects global point coords instead and just keep the old UV.
The UV map itself doesn't change from morph to morph.

I'll have a look at it shortly.
 
Back
Top