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; }//
Thread: Cinema 4D Goodies
Results 11 to 20 of 32
-
08-30-2009 09:44 AM
Last edited by Gord.T; 09-24-2009 at 08:00 AM.
-
09-11-2009 02:51 PM
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 by Gord.T; 09-12-2009 at 09:09 PM.
"Remember To Dip the Right End of the Cigar in your $250.00 dollar glass of Brandy." -Doc Bernard.
-
Junior Member
- Join Date
- Sep 2009
- Posts
- 5
09-19-2009 09:46 AM
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.
-
09-21-2009 06:13 AM
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."Remember To Dip the Right End of the Cigar in your $250.00 dollar glass of Brandy." -Doc Bernard.
-
-
09-21-2009 11:41 AM
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 by Gord.T; 09-24-2009 at 08:04 AM.
"Remember To Dip the Right End of the Cigar in your $250.00 dollar glass of Brandy." -Doc Bernard.
-
Junior Member
- Join Date
- Sep 2009
- Posts
- 5
09-22-2009 05:44 AM
Also works well on R9.6 PC as the Clear Console command is the same.
-
Junior Member
- Join Date
- Sep 2009
- Posts
- 5
12-07-2009 08:57 AM
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
-
12-07-2009 09:32 AM
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."Remember To Dip the Right End of the Cigar in your $250.00 dollar glass of Brandy." -Doc Bernard.
-
12-07-2009 11:47 AM
I'll send a PM. To make sure were on the same page as to what your after.
"Remember To Dip the Right End of the Cigar in your $250.00 dollar glass of Brandy." -Doc Bernard.




Writing out object file sequences.

