#include "export_File.h" #include #include using namespace std; static string command_path = ""; #ifdef WIN32 #define WIN32_SKIP_HKEY_PROTECTION #include "BLI_winstuff.h" #ifndef FILE_MAXDIR #define FILE_MAXDIR 160 #endif #ifndef FILE_MAXFILE #define FILE_MAXFILE 80 #endif static string find_path() { HKEY hkey; DWORD dwType, dwSize; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,"Software\\YafRay Team\\YafRay",0,KEY_READ,&hkey)==ERROR_SUCCESS) { dwType = REG_EXPAND_SZ; dwSize = MAX_PATH; DWORD dwStat; char *pInstallDir=new char[MAX_PATH]; dwStat=RegQueryValueEx(hkey, TEXT("InstallDir"), NULL, NULL,(LPBYTE)pInstallDir, &dwSize); if (dwStat == NO_ERROR) { string res=pInstallDir; delete [] pInstallDir; return res; } else cout << "Couldn't READ \'InstallDir\' value. Is yafray correctly installed?\n"; delete [] pInstallDir; RegCloseKey(hkey); } else cout << "Couldn't FIND registry key for yafray, is it installed?\n"; return string(""); } static int createDir(char* name) { if (BLI_exists(name)) return 2; //exists if (CreateDirectory((LPCTSTR)(name), NULL)) { cout << "Directory: " << name << " created\n"; return 1; // created } else { cout << "Could not create directory: " << name << endl; return 0; // fail } } extern "C" { extern char bprogname[]; } // add drive character if not in path string, using blender executable location as reference // later added note: doesn't make much sense actually, // the intended file might not be located on the same drive as where blender is located... static void addDrive(string &path) { int sp = path.find_first_of(":"); if (sp==-1) { string blpath = bprogname; sp = blpath.find_first_of(":"); if (sp!=-1) path = blpath.substr(0, sp+1) + path; } } #else #include #include #include #include #include #include static string unixYafrayPath() { static char *alternative[]= { "/usr/local/bin/", "/usr/bin/", "/bin/", NULL }; for(int i=0;alternative[i]!=NULL;++i) { string fp=string(alternative[i])+"yafray"; struct stat st; if(stat(fp.c_str(),&st)<0) continue; if(st.st_mode&S_IXOTH) return alternative[i]; } return ""; } #endif #ifdef WIN32 #define MAXPATHLEN MAX_PATH #else #include #endif static void adjustPath(string &path) { // if relative, expand to full path char cpath[MAXPATHLEN]; strcpy(cpath, path.c_str()); BLI_convertstringcode(cpath, G.sce); path = cpath; #ifdef WIN32 // add drive char if not there addDrive(path); #endif } bool yafrayFileRender_t::initExport() { xmlpath = ""; bool dir_failed = false; // try the user setting setting first, export dir must be set and exist if (strlen(U.yfexportdir)==0) { cout << "No export directory set in user defaults!" << endl; char* temp = getenv("TEMP"); // if no envar, use /tmp xmlpath = temp ? temp : "/tmp"; cout << "Will try TEMP instead: " << xmlpath << endl; // no fail here, but might fail when opening file... } else { xmlpath = U.yfexportdir; adjustPath(xmlpath); // possibly relative cout << "YFexport path is: " << xmlpath << endl; // check if it exists if (!BLI_exists(const_cast(xmlpath.c_str()))) { cout << "YafRay temporary xml export directory:\n" << U.yfexportdir << "\ndoes not exist!\n"; #ifdef WIN32 // try to create it cout << "Trying to create...\n"; if (createDir(const_cast(xmlpath.c_str()))==0) dir_failed=true; else dir_failed=false; #else dir_failed = true; #endif } } #ifdef WIN32 // for windows try to get the path to the yafray binary from the registry, only done once if (command_path=="") { char path[FILE_MAXDIR+FILE_MAXFILE]; string yafray_path = find_path(); if (yafray_path=="") { // error already printed in find_path() clearAll(); return false; } GetShortPathName((LPCTSTR)(yafray_path.c_str()), path, FILE_MAXDIR+FILE_MAXFILE); command_path = string(path) + "\\"; cout << "Yafray found at : " << command_path << endl; } // if no export dir set, or could not create, try to create one in the yafray dir, unless it already exists if (dir_failed) { string ybdir = command_path + "YBtest"; if (createDir(const_cast(ybdir.c_str()))==0) dir_failed=true; else dir_failed=false; xmlpath = ybdir; } #else if (command_path=="") { command_path = unixYafrayPath(); if (command_path.size()) cout << "Yafray found at : " << command_path << endl; } #endif // for all if (dir_failed) return false; #ifdef WIN32 string DLM = "\\"; #else string DLM = "/"; #endif // remove trailing slash if needed if (xmlpath.find_last_of(DLM)!=(xmlpath.length()-1)) xmlpath += DLM; imgout = xmlpath + "YBtest.tga"; xmlpath += "YBtest.xml"; xmlfile.open(xmlpath.c_str()); if (xmlfile.fail()) { cout << "Could not open file\n"; return false; } ostr << setiosflags(ios::showpoint | ios::fixed); xmlfile << "\n\n"; return true; } bool yafrayFileRender_t::writeRender() { // finally export render block ostr.str(""); ostr << "r.YF_raydepth << "\" gamma=\"" << re->r.YF_gamma << "\" exposure=\"" << re->r.YF_exposure << "\"\n"; if(re->r.YF_AA) { ostr << "\tAA_passes=\"" << re->r.YF_AApasses << "\" AA_minsamples=\"" << re->r.YF_AAsamples << "\"\n"; ostr << "\tAA_pixelwidth=\"" << re->r.YF_AApixelsize << "\" AA_threshold=\"" << re->r.YF_AAthreshold << "\"\n"; } else { // removed the default AA settings for midquality GI, better leave it to user if ((re->r.mode & R_OSA) && (re->r.osa)) { int passes = (re->r.osa & 3)==0 ? (re->r.osa >> 2) : 1; int minsamples=(re->r.osa & 3)==0 ? 4 : re->r.osa; ostr << "\tAA_passes=\"" << passes << "\" AA_minsamples=\"" << minsamples << "\"\n"; } else ostr << "\tAA_passes=\"0\" AA_minsamples=\"1\"\n"; ostr << "\tAA_pixelwidth=\"1.5\" AA_threshold=\"0.05\" bias=\"" << re->r.YF_raybias << "\" clamp_rgb=\"" << ((re->r.YF_clamprgb==0) ? "on" : "off") << "\"\n"; } World *world = G.scene->world; if (world) ostr << "\tbackground_name=\"world_background\"\n"; // alpha channel render when RGBA button enabled if (re->r.planes==R_PLANES32) ostr << "\n\tsave_alpha=\"on\""; ostr << " >\n"; // basic fog if (world && (world->mode & WO_MIST)) { float fd = world->mistdist; if (fd>0) fd=1.f/fd; else fd=1; ostr << "\t\n"; ostr << "\thorr << "\" g=\"" << world->horg << "\" b=\"" << world->horb << "\" />\n"; } ostr << "\t\n"; ostr << "\n\n"; xmlfile << ostr.str(); return true; } bool yafrayFileRender_t::finishExport() { xmlfile << "\n"; xmlfile.close(); // file exported, now render if (executeYafray(xmlpath)) displayImage(); else { cout << "Could not execute yafray. Is it in path?" << endl; return false; } return true; } // displays the image rendered with xml export // Now loads rendered image into blender renderbuf. void yafrayFileRender_t::displayImage() { // although it is possible to load the image using blender, // maybe it is best to just do a read here, for now the yafray output is always a raw tga anyway FILE* fp = fopen(imgout.c_str(), "rb"); if (fp==NULL) { cout << "YAF_displayImage(): Could not open image file\n"; return; } unsigned char header[18]; fread(&header, 1, 18, fp); unsigned short width = (unsigned short)(header[12] + (header[13]<<8)); unsigned short height = (unsigned short)(header[14] + (header[15]<<8)); // don't do anything if resolution doesn't match that of rectot if ((width!=re->winx) || (height!=re->winy)) { cout << "Wrong image width/height: " << width << "/" << height << " expected " << re->winx << "/" << re->winy << endl; fclose(fp); fp = NULL; return; } unsigned char byte_per_pix = (unsigned char)(header[16]>>3); // read past any id (none in this case though) unsigned int idlen = (unsigned int)header[0]; if (idlen) fseek(fp, idlen, SEEK_CUR); /* XXX how to get the image from Blender and write to it. This call doesn't allow to change buffer rects */ RenderResult rres; RE_GetResultImage(re, &rres); // rres.rectx, rres.recty is width/height // rres.rectf is float buffer, scanlines starting in bottom // rres.rectz is zbuffer, available when associated pass is set // read data directly into buffer, picture is upside down const float btf = 1.f/255.f; if (re->r.mode & R_BORDER) { // border render, yafray is full size render, blender at this point only wants the region unsigned int xs = (unsigned int)(re->r.border.xmin * re->winx), ys = (unsigned int)((1.f-re->r.border.ymax) * re->winy); for (unsigned short y=0; y= 0) && (bx < (int)re->rectx) && (by >= 0) && (by < re->recty)) { float* bpt = (float*)rres.rectf + (bx + (((re->recty-1) - by)*re->rectx) << 2); bpt[2] = (float)r * btf; bpt[1] = (float)g * btf; bpt[0] = (float)b * btf; bpt[3] = (float)a * btf; bpt += 4; } } } } else { for (unsigned short y=0; yresult->renlay = render_get_active_layer(re, re->result); re->display_draw(re->result, NULL); } static string noise2string(short nbtype) { switch (nbtype) { case TEX_BLENDER: return "blender"; case TEX_STDPERLIN: return "stdperlin"; case TEX_VORONOI_F1: return "voronoi_f1"; case TEX_VORONOI_F2: return "voronoi_f2"; case TEX_VORONOI_F3: return "voronoi_f3"; case TEX_VORONOI_F4: return "voronoi_f4"; case TEX_VORONOI_F2F1: return "voronoi_f2f1"; case TEX_VORONOI_CRACKLE: return "voronoi_crackle"; case TEX_CELLNOISE: return "cellnoise"; default: case TEX_NEWPERLIN: return "newperlin"; } } void yafrayFileRender_t::writeTextures() { // used to keep track of images already written // (to avoid duplicates if also in imagetex for material TexFace texture) set dupimg; for (map::const_iterator blendtex=used_textures.begin(); blendtex!=used_textures.end();++blendtex) { MTex* mtex = blendtex->second; Tex* tex = mtex->tex; float nsz = tex->noisesize; if (nsz!=0.f) nsz=1.f/nsz; // noisebasis type string ntype = noise2string(tex->noisebasis); string ts, hardnoise=(tex->noisetype==TEX_NOISESOFT) ? "off" : "on"; switch (tex->type) { case TEX_STUCCI: // stucci is clouds as bump, only difference is an extra parameter to handle wall in/out // turbulence value is not used, so for large values will not match well case TEX_CLOUDS: { ostr.str(""); ostr << "first << "\" >\n"; ostr << "\t\n"; ostr << "\t\t\n"; ostr << "\t\t\n"; if (tex->type==TEX_STUCCI) { if (tex->stype==1) ts = "positive"; else if (tex->stype==2) ts = "negative"; else ts = "none"; ostr << "\t\t\n"; ostr << "\t\t\n"; // for stucci always 0 } else ostr << "\t\tnoisedepth << "\" />\n"; ostr << "\t\tstype << "\" />\n"; ostr << "\t\t\n"; ostr << "\t\n\n\n"; xmlfile << ostr.str(); break; } case TEX_WOOD: { ostr.str(""); ostr << "first << "\" >\n"; ostr << "\t\t\n"; // blender does not use depth value for wood, always 0 ostr << "\t\t\n"; float turb = (tex->stype<2) ? 0.0 : tex->turbul; ostr << "\t\t\n"; ostr << "\t\t\n"; ostr << "\t\t\n"; ts = (tex->stype & 1) ? "rings" : "bands"; //stype 1&3 ringtype ostr << "\t\t\n"; ostr << "\t\t\n"; // shape parameter, for some reason noisebasis2 is used... ts = "sin"; if (tex->noisebasis2==1) ts="saw"; else if (tex->noisebasis2==2) ts="tri"; ostr << "\t\t\n"; ostr << "\t\n\n\n"; xmlfile << ostr.str(); break; } case TEX_MARBLE: { ostr.str(""); ostr << "first << "\" >\n"; ostr << "\t\n"; ostr << "\t\tnoisedepth << "\" />\n"; ostr << "\t\tturbul << "\" />\n"; ostr << "\t\t\n"; ostr << "\t\t\n"; ostr << "\t\tstype) << "\" />\n"; ostr << "\t\t\n"; ts = "sin"; if (tex->noisebasis2==1) ts="saw"; else if (tex->noisebasis2==2) ts="tri"; ostr << "\t\t\n"; ostr << "\t\n\n\n"; xmlfile << ostr.str(); break; } case TEX_VORONOI: { ostr.str(""); ostr << "first << "\" >\n"; ostr << "\t\n"; ts = "int"; if (tex->vn_coltype==1) ts = "col1"; else if (tex->vn_coltype==2) ts = "col2"; else if (tex->vn_coltype==3) ts = "col3"; ostr << "\t\t\n"; ostr << "\t\tvn_w1 << "\" />\n"; ostr << "\t\tvn_w2 << "\" />\n"; ostr << "\t\tvn_w3 << "\" />\n"; ostr << "\t\tvn_w4 << "\" />\n"; ostr << "\t\tvn_mexp << "\" />\n"; ostr << "\t\tns_outscale << "\" />\n"; ostr << "\t\t\n"; ts = "actual"; if (tex->vn_distm==TEX_DISTANCE_SQUARED) ts = "squared"; else if (tex->vn_distm==TEX_MANHATTAN) ts = "manhattan"; else if (tex->vn_distm==TEX_CHEBYCHEV) ts = "chebychev"; else if (tex->vn_distm==TEX_MINKOVSKY_HALF) ts = "minkovsky_half"; else if (tex->vn_distm==TEX_MINKOVSKY_FOUR) ts = "minkovsky_four"; else if (tex->vn_distm==TEX_MINKOVSKY) ts = "minkovsky"; ostr << "\t\t\n"; ostr << "\t\n\n\n"; xmlfile << ostr.str(); break; } case TEX_MUSGRAVE: { ostr.str(""); ostr << "first << "\" >\n"; ostr << "\t\n"; switch (tex->stype) { case TEX_MFRACTAL: ts = "multifractal"; break; case TEX_RIDGEDMF: ts = "ridgedmf"; break; case TEX_HYBRIDMF: ts = "hybridmf"; break; case TEX_HTERRAIN: ts = "heteroterrain"; break; default: case TEX_FBM: ts = "fBm"; } ostr << "\t\t\n"; ostr << "\t\t\n"; ostr << "\t\tmg_H << "\" />\n"; ostr << "\t\tmg_lacunarity << "\" />\n"; ostr << "\t\tmg_octaves << "\" />\n"; if ((tex->stype==TEX_HTERRAIN) || (tex->stype==TEX_RIDGEDMF) || (tex->stype==TEX_HYBRIDMF)) { ostr << "\t\tmg_offset << "\" />\n"; if ((tex->stype==TEX_RIDGEDMF) || (tex->stype==TEX_HYBRIDMF)) ostr << "\t\tmg_gain << "\" />\n"; } ostr << "\t\t\n"; ostr << "\t\tns_outscale << "\" />\n"; ostr << "\t\n\n\n"; xmlfile << ostr.str(); break; } case TEX_DISTNOISE: { ostr.str(""); ostr << "first << "\" >\n"; ostr << "\t\n"; ostr << "\t\tdist_amount << "\" />\n"; ostr << "\t\t\n"; ostr << "\t\t\n"; ostr << "\t\tnoisebasis2) << "\" />\n"; ostr << "\t\n\n\n"; xmlfile << ostr.str(); break; } case TEX_BLEND: { ostr.str(""); ostr << "first << "\" >\n"; ostr << "\t\n"; switch (tex->stype) { case 1: ts="quadratic"; break; case 2: ts="cubic"; break; case 3: ts="diagonal"; break; case 4: ts="sphere"; break; case 5: ts="halo"; break; default: case 0: ts="linear"; break; } ostr << "\t\t\n"; if (tex->flag & TEX_FLIPBLEND) ts="on"; else ts="off"; ostr << "\t\t\n"; ostr << "\t\n\n\n"; xmlfile << ostr.str(); break; } case TEX_NOISE: { ostr.str(""); ostr << "first << "\" >\n"; ostr << "\t\n"; ostr << "\t\tnoisedepth << "\" />\n"; ostr << "\t\n\n\n"; xmlfile << ostr.str(); break; } case TEX_IMAGE: { Image* ima = tex->ima; if (ima) { // remember image to avoid duplicates later if also in imagetex // (formerly done by removing from imagetex, but need image/material link) dupimg.insert(ima); ostr.str(""); // use image name instead of texname here ostr << "id.name << "\" >\n"; ostr << "\t\n"; string texpath(ima->name); adjustPath(texpath); ostr << "\t\t\n"; ostr << "\t\timaflag & TEX_INTERPOL) ? "bilinear" : "none") << "\" />\n"; ostr << "\t\n\n\n"; xmlfile << ostr.str(); } break; } default: cout << "Unsupported texture type\n"; } // colorbands if (tex->flag & TEX_COLORBAND) { ColorBand* cb = tex->coba; if (cb) { ostr.str(""); ostr << "first + "_coba" << "\" >\n"; ostr << "\t\n"; ostr << "\t\tfirst << "\" />\n"; ostr << "\t\n"; for (int i=0;itot;i++) { ostr << "\tdata[i].pos << "\" >\n"; ostr << "\t\tdata[i].r << "\"" << " g=\"" << cb->data[i].g << "\"" << " b=\"" << cb->data[i].b << "\"" << " a=\"" << cb->data[i].a << "\" />\n"; ostr << "\t\n"; } ostr << "\n\n"; xmlfile << ostr.str(); } } } // If used, textures for the material 'TexFace' case if (!imagetex.empty()) { for (map >::const_iterator imgtex=imagetex.begin(); imgtex!=imagetex.end();++imgtex) { // skip if already written above Image* ima = imgtex->first; if (dupimg.find(ima)==dupimg.end()) { ostr.str(""); ostr << "id.name << "\" >\n"; ostr << "\t\n"; string texpath(ima->name); adjustPath(texpath); ostr << "\t\t\n"; ostr << "\t\n\n\n"; xmlfile << ostr.str(); } } } } void yafrayFileRender_t::writeShader(const string &shader_name, Material* matr, const string &facetexname) { // if material has ramps, export colorbands first if (matr->mode & (MA_RAMP_COL|MA_RAMP_SPEC)) { // both colorbands without input shader ColorBand* cb = matr->ramp_col; if ((matr->mode & MA_RAMP_COL) && (cb!=NULL)) { ostr.str(""); ostr << "\n"; ostr << "\t\n\t\n"; for (int i=0;itot;i++) { ostr << "\tdata[i].pos << "\" >\n"; ostr << "\t\tdata[i].r << "\"" << " g=\"" << cb->data[i].g << "\"" << " b=\"" << cb->data[i].b << "\"" << " a=\"" << cb->data[i].a << "\" />\n"; ostr << "\t\n"; } ostr << "\n\n"; xmlfile << ostr.str(); } cb = matr->ramp_spec; if ((matr->mode & MA_RAMP_SPEC) && (cb!=NULL)) { ostr.str(""); ostr << "\n"; ostr << "\t\n\t\n"; for (int i=0;itot;i++) { ostr << "\tdata[i].pos << "\" >\n"; ostr << "\t\tdata[i].r << "\"" << " g=\"" << cb->data[i].g << "\"" << " b=\"" << cb->data[i].b << "\"" << " a=\"" << cb->data[i].a << "\" />\n"; ostr << "\t\n"; } ostr << "\n\n"; xmlfile << ostr.str(); } } ostr.str(""); ostr << "\n"; ostr << "\t\n"; ostr << "\t\tr << "\" g=\"" << matr->g << "\" b=\"" << matr->b << "\" />\n"; float sr=matr->specr, sg=matr->specg, sb=matr->specb; if (matr->spec_shader==MA_SPEC_WARDISO) { // ........ sr /= M_PI; sg /= M_PI; sb /= M_PI; } ostr << "\t\t\n"; ostr << "\t\tmirr << "\" g=\"" << matr->mirg << "\" b=\"" << matr->mirb << "\" />\n"; ostr << "\t\tref << "\" />\n"; ostr << "\t\tspec << "\" />\n"; ostr << "\t\talpha << "\" />\n"; // if no GI used, the GIpower parameter is not always initialized, so in that case ignore it float bg_mult = (re->r.GImethod==0) ? 1 : re->r.GIpower; ostr << "\t\temit * bg_mult) << "\" />\n"; // reflection/refraction if ( (matr->mode & MA_RAYMIRROR) || (matr->mode & MA_RAYTRANSP) ) ostr << "\t\tang << "\" />\n"; if (matr->mode & MA_RAYMIRROR) { // Sofar yafray's min_refle parameter (which misleadingly actually controls fresnel reflection offset) // has been mapped to Blender's ray_mirror parameter. // This causes it be be misinterpreted and misused as a reflection amount control however. // Besides that, it also causes extra complications for the yafray Blendershader. // So added an actual amount of reflection parameter instead, and another // extra parameter 'frsOfs' to actually control fresnel offset (re-uses Blender fresnel_mir_i param). ostr << "\t\t\n"; ostr << "\t\tray_mirror << "\" />\n"; float fo = 1.f-(matr->fresnel_mir_i-1.f)*0.25f; // blender param range [1,5], also here reversed (1 in Blender -> no fresnel) ostr << "\t\t\n"; // for backward compatibility, also add old 'reflected' parameter, copy of mirror_color ostr << "\t\tmirr << "\" g=\"" << matr->mirg << "\" b=\"" << matr->mirb << "\" />\n"; // same for 'min_refle' param. Instead of the ray_mirror parameter that was used before, since now // the parameter's function is taken over by the fresnel offset parameter, use that instead. ostr << "\t\t\n"; } if (matr->mode & MA_RAYTRANSP) { ostr << "\t\t\n"; ostr << "\t\tfilter << "\" />\n"; // tir on by default ostr << "\t\t\n"; // transmit absorption color // to make things easier(?) for user it now specifies the actual color at 1 unit / YF_dscale of distance const float maxlog = -log(1e-38); float ar = (matr->YF_ar>0) ? -log(matr->YF_ar) : maxlog; float ag = (matr->YF_ag>0) ? -log(matr->YF_ag) : maxlog; float ab = (matr->YF_ab>0) ? -log(matr->YF_ab) : maxlog; float sc = matr->YF_dscale; if (sc!=0.f) sc=1.f/sc; ostr << "\t\t\n"; // dispersion ostr << "\t\tYF_dpwr << "\" />\n"; ostr << "\t\tYF_dsmp << "\" />\n"; ostr << "\t\tYF_djit ? "on" : "off") << "\" />\n"; // for backward compatibility, also add old 'transmitted' parameter, copy of 'color' * (1-alpha) float na = 1.f-matr->alpha; ostr << "\t\tr*na << "\" g=\"" << matr->g*na << "\" b=\"" << matr->b*na << "\" />\n"; } string Mmode = ""; if (matr->mode & MA_TRACEBLE) Mmode += "traceable"; if (matr->mode & MA_SHADOW) Mmode += " shadow"; if (matr->mode & MA_SHLESS) Mmode += " shadeless"; if (matr->mode & MA_VERTEXCOL) Mmode += " vcol_light"; if (matr->mode & MA_VERTEXCOLP) Mmode += " vcol_paint"; if (matr->mode & MA_ZTRA) Mmode += " ztransp"; if (matr->mode & MA_ONLYSHADOW) Mmode += " onlyshadow"; if (Mmode!="") ostr << "\t\t\n"; // diffuse & specular brdf, lambert/cooktorr defaults // diffuse if (matr->diff_shader==MA_DIFF_ORENNAYAR) { ostr << "\t\t\n"; ostr << "\t\troughness << "\" />\n"; } else if (matr->diff_shader==MA_DIFF_TOON) { ostr << "\t\t\n"; ostr << "\t\tparam[0] << "\" />\n"; ostr << "\t\tparam[1] << "\" />\n"; } else if (matr->diff_shader==MA_DIFF_MINNAERT) { ostr << "\t\t\n"; ostr << "\t\tdarkness << "\" />\n"; } else ostr << "\t\t\n"; // specular if (matr->spec_shader==MA_SPEC_PHONG) { ostr << "\t\t\n"; ostr << "\t\thar << "\" />\n"; } else if (matr->spec_shader==MA_SPEC_BLINN) { ostr << "\t\t\n"; ostr << "\t\trefrac << "\" />\n"; ostr << "\t\thar << "\" />\n"; } else if (matr->spec_shader==MA_SPEC_TOON) { ostr << "\t\t\n"; ostr << "\t\tparam[2] << "\" />\n"; ostr << "\t\tparam[3] << "\" />\n"; } else if (matr->spec_shader==MA_SPEC_WARDISO) { ostr << "\t\t\n"; ostr << "\t\trms << "\" />\n"; ostr << "\t\trms << "\" />\n"; } else { ostr << "\t\t\n"; ostr << "\t\thar << "\" />\n"; } // ramps, if used if (matr->mode & (MA_RAMP_COL|MA_RAMP_SPEC)) { const string rm_blend[9] = {"mix", "add", "mul", "sub", "screen", "divide", "difference", "darken", "lighten"}; const string rm_mode[4] = {"shader", "energy", "normal", "result"}; // diffuse if ((matr->mode & MA_RAMP_COL) && (matr->ramp_col!=NULL)) { ostr << "\t\t\n"; ostr << "\t\trampin_col] << "\" />\n"; ostr << "\t\trampblend_col] << "\" />\n"; ostr << "\t\trampfac_col << "\" />\n"; } // specular if ((matr->mode & MA_RAMP_SPEC) && (matr->ramp_spec!=NULL)) { ostr << "\t\t\n"; ostr << "\t\trampin_spec] << "\" />\n"; ostr << "\t\trampblend_spec] << "\" />\n"; ostr << "\t\trampfac_spec << "\" />\n"; } } ostr << "\t\n"; xmlfile << ostr.str(); // modulators // first modulator is the texture of the face, if used (TexFace mode) if (facetexname.length()!=0) { ostr.str(""); ostr << "\t\n"; ostr << "\t\t\n"; ostr << "\t\t\n"; ostr << "\t\n"; xmlfile << ostr.str(); } for (int m2=0;m2septex & (1<mtex[m2]; if (mtex==NULL) continue; // ignore null tex Tex* tex = mtex->tex; if (tex==NULL) continue; map::const_iterator mtexL = used_textures.find(string(tex->id.name)); if (mtexL!=used_textures.end()) { ostr.str(""); ostr << "\t\n"; // when no facetex used, shader_name is created from original material name if (facetexname.length()!=0) ostr << "\t\tid.name << "_map" << m2 << "\" />\n"; else ostr << "\t\t\n"; // blendtype, would have been nice if the order would have been the same as for ramps... const string blendtype[MTEX_NUM_BLENDTYPES] = {"mix", "mul", "add", "sub", "divide", "darken", "difference", "lighten", "screen", "hue", "sat", "val", "color"}; ostr << "\t\tblendtype] << "\" />\n"; // texture color (for use with MUL and/or no_rgb etc..) ostr << "\t\tr << "\" g=\"" << mtex->g << "\" b=\"" << mtex->b << "\" />\n"; // texture contrast, brightness & color adjustment ostr << "\t\trfac << "\" g=\"" << tex->gfac << "\" b=\"" << tex->bfac << "\" />\n"; ostr << "\t\tcontrast << "\" />\n"; ostr << "\t\tbright << "\" />\n"; // all texture flags now are switches, having the value 1 or -1 (negative option) // the negative option only used for the intensity modulation options. // material (diffuse) color, amount controlled by colfac (see below) if (mtex->mapto & MAP_COL) ostr << "\t\t\n"; // bumpmapping if ((mtex->mapto & MAP_NORM) || (mtex->maptoneg & MAP_NORM)) { // for yafray, bump factor is negated (unless tex is stucci, not affected by 'Neg') // scaled down quite a bit float nf = mtex->norfac; if (tex->type!=TEX_STUCCI) nf *= -1.f; if (mtex->maptoneg & MAP_NORM) nf *= -1.f; ostr << "\t\t\n"; } // all blender texture modulation as switches, either 1 or -1 (negative state of button) // Csp, specular color modulation if (mtex->mapto & MAP_COLSPEC) ostr << "\t\t\n"; // CMir, mirror color modulation if (mtex->mapto & MAP_COLMIR) ostr << "\t\t\n"; // Ref, diffuse reflection amount modulation if ((mtex->mapto & MAP_REF) || (mtex->maptoneg & MAP_REF)) { int t = 1; if (mtex->maptoneg & MAP_REF) t = -1; ostr << "\t\t\n"; } // Spec, specular amount mod if ((mtex->mapto & MAP_SPEC) || (mtex->maptoneg & MAP_SPEC)) { int t = 1; if (mtex->maptoneg & MAP_SPEC) t = -1; ostr << "\t\t\n"; } // hardness modulation if ((mtex->mapto & MAP_HAR) || (mtex->maptoneg & MAP_HAR)) { int t = 1; if (mtex->maptoneg & MAP_HAR) t = -1; ostr << "\t\t\n"; } // alpha modulation if ((mtex->mapto & MAP_ALPHA) || (mtex->maptoneg & MAP_ALPHA)) { int t = 1; if (mtex->maptoneg & MAP_ALPHA) t = -1; ostr << "\t\t\n"; } // emit modulation if ((mtex->mapto & MAP_EMIT) || (mtex->maptoneg & MAP_EMIT)) { int t = 1; if (mtex->maptoneg & MAP_EMIT) t = -1; ostr << "\t\t\n"; } // raymir modulation if ((mtex->mapto & MAP_RAYMIRR) || (mtex->maptoneg & MAP_RAYMIRR)) { int t = 1; if (mtex->maptoneg & MAP_RAYMIRR) t = -1; ostr << "\t\t\n"; } // texture flag, combination of strings string ts = ""; if (mtex->texflag & (MTEX_RGBTOINT | MTEX_STENCIL | MTEX_NEGATIVE)) { if (mtex->texflag & MTEX_RGBTOINT) ts += "no_rgb "; if (mtex->texflag & MTEX_STENCIL) ts += "stencil "; if (mtex->texflag & MTEX_NEGATIVE) ts += "negative"; ostr << "\t\t\n"; } // colfac, controls amount of color modulation ostr << "\t\tcolfac << "\" />\n"; // def_var ostr << "\t\tdef_var << "\" />\n"; //varfac ostr << "\t\tvarfac << "\" />\n"; if ((tex->imaflag & (TEX_CALCALPHA | TEX_USEALPHA)) || (tex->flag & TEX_NEGALPHA)) { ts = ""; if (tex->imaflag & TEX_CALCALPHA) ts += "calc_alpha "; if (tex->imaflag & TEX_USEALPHA) ts += "use_alpha "; if (tex->flag & TEX_NEGALPHA) ts += "neg_alpha"; ostr << "\t\t\n"; } // image as normalmap flag if (tex->imaflag & TEX_NORMALMAP) ostr << "\t\t\n"; ostr << "\t\n"; xmlfile << ostr.str(); } } xmlfile << "\n\n"; } // write all materials & modulators void yafrayFileRender_t::writeMaterialsAndModulators() { // shaders/mappers for regular texture (or non-texture) mode // In case material has texface mode, and all faces have an image texture, // this shader will not be used, but still be written for (map::const_iterator blendmat=used_materials.begin(); blendmat!=used_materials.end();++blendmat) { Material* matr = blendmat->second; // mapper(s) for (int m=0;mseptex & (1<mtex[m]; if (mtex==NULL) continue; // ignore null tex Tex* tex = mtex->tex; if (tex==NULL) continue; map::const_iterator mtexL = used_textures.find(string(tex->id.name)); if (mtexL!=used_textures.end()) { ostr.str(""); ostr << "first + "_map" << m <<"\""; if ((mtex->texco & TEXCO_OBJECT) || (mtex->texco & TEXCO_REFL) || (mtex->texco & TEXCO_NORM)) { // For object, reflection & normal mapping, add the object matrix to the modulator, // as in LF script, use camera matrix if no object specified. // In this case this means the inverse of that matrix float texmat[4][4], itexmat[4][4]; if ((mtex->texco & TEXCO_OBJECT) && (mtex->object)) MTC_Mat4CpyMat4(texmat, mtex->object->obmat); else // also for refl. map MTC_Mat4CpyMat4(texmat, maincam_obj->obmat); MTC_Mat4Invert(itexmat, texmat); ostr << "\n\t\tm00=\"" << itexmat[0][0] << "\" m01=\"" << itexmat[1][0] << "\" m02=\"" << itexmat[2][0] << "\" m03=\"" << itexmat[3][0] << "\"\n"; ostr << "\t\tm10=\"" << itexmat[0][1] << "\" m11=\"" << itexmat[1][1] << "\" m12=\"" << itexmat[2][1] << "\" m13=\"" << itexmat[3][1] << "\"\n"; ostr << "\t\tm20=\"" << itexmat[0][2] << "\" m21=\"" << itexmat[1][2] << "\" m22=\"" << itexmat[2][2] << "\" m23=\"" << itexmat[3][2] << "\"\n"; ostr << "\t\tm30=\"" << itexmat[0][3] << "\" m31=\"" << itexmat[1][3] << "\" m32=\"" << itexmat[2][3] << "\" m33=\"" << itexmat[3][3] << "\">\n"; } else ostr << ">\n"; ostr << "\t\n"; // use image name instead of texname when texture is image if ((tex->type==TEX_IMAGE) && tex->ima) ostr << "\t\tima->id.name << "\" />\n"; else if ((tex->flag & TEX_COLORBAND) & (tex->coba!=NULL)) ostr << "\t\tfirst + "_coba" << "\" />\n"; else ostr << "\t\tfirst << "\" />\n"; // texture size ostr << "\t\tsize[0] << "\" />\n"; ostr << "\t\tsize[1] << "\" />\n"; ostr << "\t\tsize[2] << "\" />\n"; // texture offset ostr << "\t\tofs[0] << "\" />\n"; ostr << "\t\tofs[1] << "\" />\n"; ostr << "\t\tofs[2] << "\" />\n"; // texture coordinates, have to disable 'sticky' in Blender if (mtex->texco & TEXCO_UV) ostr << "\t\t\n"; else if ((mtex->texco & TEXCO_GLOB) || (mtex->texco & TEXCO_OBJECT)) // object mode is also set as global, but the object matrix was specified above with ostr << "\t\t\n"; else if ((mtex->texco & TEXCO_ORCO) || (mtex->texco & TEXCO_STRAND)) // orco flag now used for 'strand'-mapping as well, see mesh code ostr << "\t\t\n"; else if (mtex->texco & TEXCO_WINDOW) ostr << "\t\t\n"; else if (mtex->texco & TEXCO_NORM) ostr << "\t\t\n"; else if (mtex->texco & TEXCO_REFL) ostr << "\t\t\n"; // texture projection axes, both image & procedural string proj = "nxyz"; // 'n' for 'none' ostr << "\t\tprojx] << "\" />\n"; ostr << "\t\tprojy] << "\" />\n"; ostr << "\t\tprojz] << "\" />\n"; // texture mapping parameters only relevant to image type if (tex->type==TEX_IMAGE) { if (mtex->mapping==MTEX_FLAT) ostr << "\t\t\n"; else if (mtex->mapping==MTEX_CUBE) ostr << "\t\t\n"; else if (mtex->mapping==MTEX_TUBE) ostr << "\t\t\n"; else if (mtex->mapping==MTEX_SPHERE) ostr << "\t\t\n"; // repeat ostr << "\t\txrepeat << "\" />\n"; ostr << "\t\tyrepeat << "\" />\n"; // clipping if (tex->extend==TEX_EXTEND) ostr << "\t\t\n"; else if (tex->extend==TEX_CLIP) ostr << "\t\t\n"; else if (tex->extend==TEX_CLIPCUBE) ostr << "\t\t\n"; else if (tex->extend==TEX_CHECKER) { ostr << "\t\t\n"; string ts = ""; if (tex->flag & TEX_CHECKER_ODD) ts += "odd"; if (tex->flag & TEX_CHECKER_EVEN) ts += " even"; ostr << "\t\t\n"; ostr << "\t\tcheckerdist << "\" />\n"; } else ostr << "\t\t\n"; // crop min/max ostr << "\t\tcropxmin << "\" />\n"; ostr << "\t\tcropymin << "\" />\n"; ostr << "\t\tcropxmax << "\" />\n"; ostr << "\t\tcropymax << "\" />\n"; // rot90 flag string ts = "off"; if (tex->imaflag & TEX_IMAROT) ts = "on"; ostr << "\t\t\n"; } ostr << "\t\n"; ostr << "\n\n"; xmlfile << ostr.str(); } } // shader + modulators writeShader(blendmat->first, matr); } // write the mappers & shaders for the TexFace case if (!imagetex.empty()) { // Yafray doesn't have per-face-textures, only per-face-shaders, // so create as many mappers/shaders as the images used by the object int snum = 0; for (map >::const_iterator imgtex=imagetex.begin(); imgtex!=imagetex.end();++imgtex) { for (set::const_iterator imgmat=imgtex->second.begin(); imgmat!=imgtex->second.end();++imgmat) { Material* matr = *imgmat; // mapper ostr.str(""); ostr << "id.name) + "_ftmap" << snum << "\" >\n"; ostr << "\t\n"; ostr << "\t\tfirst->id.name << "\" />\n"; // all yafray default settings, except for texco, so no need to set others ostr << "\t\t\n"; ostr << "\t\n"; ostr << "\n\n"; xmlfile << ostr.str(); // shader, remember name, used later when writing per-face-shaders ostr.str(""); ostr << matr->id.name << "_ftsha" << snum; string shader_name = ostr.str(); imgtex_shader[string(matr->id.name) + string(imgtex->first->id.name)] = shader_name; ostr.str(""); ostr << matr->id.name << "_ftmap" << snum++; writeShader(shader_name, matr, ostr.str()); } } } } void yafrayFileRender_t::writeObject(Object* obj, ObjectRen *obr, const vector &VLR_list, const float obmat[4][4]) { ostr.str(""); // transform first (not necessarily actual obj->obmat, can be duplivert see below) ostr << "\n"; xmlfile << ostr.str(); ostr.str(""); // using the ObjectRen database, contruct a new name if object has a parent. // This is done to prevent name clashes (group/library link related) string obname(obj->id.name); // previous implementation, keep around, in case this is still useful //if (obj->id.flag & (LIB_EXTERN|LIB_INDIRECT))obname = "lib_" + obname; ObjectRen *obren; for (obren = static_cast(re->objecttable.first); obren; obren=static_cast(obren->next)) { Object *db_ob = obren->ob, *db_par = obren->par; if (db_ob==obj) if ((db_ob!=NULL) && (db_par!=NULL)) { obname += "_" + string(db_par->id.name); break; } } ostr << "mat; string matname(face0mat->id.name); // use name in imgtex_shader list if 'TexFace' enabled for this material if (face0mat->mode & MA_FACETEXTURE) { MTFace* tface = RE_vlakren_get_tface(obr, face0, obr->actmtface, NULL, 0); if (tface) { Image* fimg = (Image*)tface->tpage; if (fimg) matname = imgtex_shader[string(face0mat->id.name) + string(fimg->id.name)]; } } bool shadow = face0mat->mode & MA_TRACEBLE; ostr <<" shadow=\""<< (shadow ? "on" : "off" ) << "\" "; bool caus = (((face0mat->mode & MA_RAYTRANSP) | (face0->mat->mode & MA_RAYMIRROR))!=0); if (caus) ostr << "caus_IOR=\"" << face0mat->ang << "\""; if (matname.length()==0) matname = "blender_default"; ostr << " shader_name=\"" << matname << "\" >\n"; ostr << "\t\n"; if (caus) { float tr = 1.0-face0mat->alpha; ostr << "\t\tr*tr << "\" g=\"" << face0mat->g*tr << "\" b=\"" << face0mat->b*tr << "\" />\n"; tr = face0mat->ray_mirror; ostr << "\t\tmirr*tr << "\" g=\"" << face0mat->mirg*tr << "\" b=\"" << face0mat->mirb*tr << "\" />\n"; } ostr << "\t\n"; xmlfile << ostr.str(); // Export orco coords test. // Previously was done by checking orco pointer, however this can be non-null but still not initialized. // Test the rendermaterial texco flag instead. // update2: bug #3193 it seems it has changed again with the introduction of static 'hair' particles, // now it uses the vert pointer again as an extra test to make sure there are orco coords available int has_orco = 0; if (face0mat->texco & TEXCO_STRAND) has_orco = 1; else has_orco = (((face0mat->texco & TEXCO_ORCO)!=0) && (face0->v1->orco!=NULL)) ? 2 : 0; string has_orco_st = has_orco ? "on" : "off"; // smooth shading if enabled bool no_auto = true; //in case non-mesh, or mesh has no autosmooth if (obj->type==OB_MESH) { Mesh* mesh = (Mesh*)obj->data; if (mesh->flag & ME_AUTOSMOOTH) { no_auto = false; ostr.str(""); ostr << "\tsmoothresh << "\" has_orco=\"" << has_orco_st << "\" >\n"; xmlfile << ostr.str(); } } // this for non-mesh as well if (no_auto) { // If AutoSmooth not used, since yafray currently cannot specify if a face is smooth // or flat shaded, the smooth flag of the first face is used to determine // the shading for the whole mesh if (face0->flag & ME_SMOOTH) xmlfile << "\t\n"; else xmlfile << "\t\n"; //0 shows artefacts } // now all vertices map vert_idx; // for removing duplicate verts and creating an index list int vidx = 0; // vertex index counter // vertices, transformed back to world xmlfile << "\t\t\n"; // for deformed objects, object->imat is no longer valid, // so have to create inverse render matrix ourselves here float mat[4][4], imat[4][4]; MTC_Mat4MulMat4(mat, obj->obmat, re->viewmat); MTC_Mat4Invert(imat, mat); for (vector::const_iterator fci=VLR_list.begin(); fci!=VLR_list.end();++fci) { VlakRen* vlr = *fci; VertRen* ver; float* orco; float tvec[3]; ostr.str(""); if (vert_idx.find(vlr->v1)==vert_idx.end()) { vert_idx[vlr->v1] = vidx++; ver = vlr->v1; MTC_cp3Float(ver->co, tvec); MTC_Mat4MulVecfl(imat, tvec); ostr << "\t\t\t

\n"; // has_orco now an int, if 1 -> strand mapping, if 2 -> normal orco mapping if (has_orco==1) { ostr << "\t\t\t

accum << "\" y=\"" << ver->accum << "\" z=\"" << ver->accum << "\" />\n"; } else if (has_orco==2) { orco = ver->orco; ostr << "\t\t\t

\n"; } } if (vert_idx.find(vlr->v2)==vert_idx.end()) { vert_idx[vlr->v2] = vidx++; ver = vlr->v2; MTC_cp3Float(ver->co, tvec); MTC_Mat4MulVecfl(imat, tvec); ostr << "\t\t\t

\n"; // has_orco now an int, if 1 -> strand mapping, if 2 -> normal orco mapping if (has_orco==1) { ostr << "\t\t\t

accum << "\" y=\"" << ver->accum << "\" z=\"" << ver->accum << "\" />\n"; } else if (has_orco==2) { orco = ver->orco; ostr << "\t\t\t

\n"; } } if (vert_idx.find(vlr->v3)==vert_idx.end()) { vert_idx[vlr->v3] = vidx++; ver = vlr->v3; MTC_cp3Float(ver->co, tvec); MTC_Mat4MulVecfl(imat, tvec); ostr << "\t\t\t

\n"; // has_orco now an int, if 1 -> strand mapping, if 2 -> normal orco mapping if (has_orco==1) { ostr << "\t\t\t

accum << "\" y=\"" << ver->accum << "\" z=\"" << ver->accum << "\" />\n"; } else if (has_orco==2) { orco = ver->orco; ostr << "\t\t\t

\n"; } } if ((vlr->v4) && (vert_idx.find(vlr->v4)==vert_idx.end())) { vert_idx[vlr->v4] = vidx++; ver = vlr->v4; MTC_cp3Float(ver->co, tvec); MTC_Mat4MulVecfl(imat, tvec); ostr << "\t\t\t

\n"; // has_orco now an int, if 1 -> strand mapping, if 2 -> normal orco mapping if (has_orco==1) { ostr << "\t\t\t

accum << "\" y=\"" << ver->accum << "\" z=\"" << ver->accum << "\" />\n"; } else if (has_orco==2) { orco = ver->orco; ostr << "\t\t\t

\n"; } } xmlfile << ostr.str(); } xmlfile << "\t\t\n"; // all faces using the index list created above xmlfile << "\t\t\n"; for (vector::const_iterator fci2=VLR_list.begin(); fci2!=VLR_list.end();++fci2) { VlakRen* vlr = *fci2; Material* fmat = vlr->mat; bool EXPORT_VCOL = ((fmat->mode & (MA_VERTEXCOL|MA_VERTEXCOLP))!=0); string fmatname(fmat->id.name); // use name in imgtex_shader list if 'TexFace' enabled for this face material if (fmat->mode & MA_FACETEXTURE) { MTFace* tface = RE_vlakren_get_tface(obr, vlr, obr->actmtface, NULL, 0); if (tface) { Image* fimg = (Image*)tface->tpage; if (fimg) fmatname = imgtex_shader[fmatname + string(fimg->id.name)]; } } else if (fmatname.length()==0) fmatname = "blender_default"; int idx1 = vert_idx.find(vlr->v1)->second; int idx2 = vert_idx.find(vlr->v2)->second; int idx3 = vert_idx.find(vlr->v3)->second; // make sure the indices point to the vertices when orco coords exported if (has_orco) { idx1*=2; idx2*=2; idx3*=2; } ostr.str(""); ostr << "\t\t\tflag & R_DIVIDE_24) { ui3++; if (vlr->flag & R_FACE_SPLIT) { ui1++; ui2++; } } else if (vlr->flag & R_FACE_SPLIT) { ui2++; ui3++; } MTFace* uvc = RE_vlakren_get_tface(obr, vlr, obr->actmtface, NULL, 0); // possible uvcoords (v upside down) if (uvc) { ostr << " u_a=\"" << uvc->uv[ui1][0] << "\" v_a=\"" << 1-uvc->uv[ui1][1] << "\"" << " u_b=\"" << uvc->uv[ui2][0] << "\" v_b=\"" << 1-uvc->uv[ui2][1] << "\"" << " u_c=\"" << uvc->uv[ui3][0] << "\" v_c=\"" << 1-uvc->uv[ui3][1] << "\""; } MCol *mcol= RE_vlakren_get_mcol(obr, vlr, obr->actmcol, NULL, 0); // since Blender seems to need vcols when uvs are used, for yafray only export when the material actually uses vcols if ((EXPORT_VCOL) && mcol) { // vertex colors unsigned char* pt = reinterpret_cast(&mcol[ui1]); ostr << " vcol_a_r=\"" << (float)pt[3]/255.f << "\" vcol_a_g=\"" << (float)pt[2]/255.f << "\" vcol_a_b=\"" << (float)pt[1]/255.f << "\""; pt = reinterpret_cast(&mcol[ui2]); ostr << " vcol_b_r=\"" << (float)pt[3]/255.f << "\" vcol_b_g=\"" << (float)pt[2]/255.f << "\" vcol_b_b=\"" << (float)pt[1]/255.f << "\""; pt = reinterpret_cast(&mcol[ui3]); ostr << " vcol_c_r=\"" << (float)pt[3]/255.f << "\" vcol_c_g=\"" << (float)pt[2]/255.f << "\" vcol_c_b=\"" << (float)pt[1]/255.f << "\""; } ostr << " shader_name=\"" << fmatname << "\" />\n"; if (vlr->v4) { idx1 = vert_idx.find(vlr->v3)->second; idx2 = vert_idx.find(vlr->v4)->second; idx3 = vert_idx.find(vlr->v1)->second; // make sure the indices point to the vertices when orco coords exported if (has_orco) { idx1*=2; idx2*=2; idx3*=2; } ostr << "\t\t\tuv[ui1][0] << "\" v_a=\"" << 1-uvc->uv[ui1][1] << "\"" << " u_b=\"" << uvc->uv[ui2][0] << "\" v_b=\"" << 1-uvc->uv[ui2][1] << "\"" << " u_c=\"" << uvc->uv[ui3][0] << "\" v_c=\"" << 1-uvc->uv[ui3][1] << "\""; } if ((EXPORT_VCOL) && mcol) { // vertex colors unsigned char* pt = reinterpret_cast(&mcol[ui1]); ostr << " vcol_a_r=\"" << (float)pt[3]/255.f << "\" vcol_a_g=\"" << (float)pt[2]/255.f << "\" vcol_a_b=\"" << (float)pt[1]/255.f << "\""; pt = reinterpret_cast(&mcol[ui2]); ostr << " vcol_b_r=\"" << (float)pt[3]/255.f << "\" vcol_b_g=\"" << (float)pt[2]/255.f << "\" vcol_b_b=\"" << (float)pt[1]/255.f << "\""; pt = reinterpret_cast(&mcol[ui3]); ostr << " vcol_c_r=\"" << (float)pt[3]/255.f << "\" vcol_c_g=\"" << (float)pt[2]/255.f << "\" vcol_c_b=\"" << (float)pt[1]/255.f << "\""; } ostr << " shader_name=\"" << fmatname << "\" />\n"; } xmlfile << ostr.str(); } xmlfile << "\t\t\n\t\n

\n
\n\n"; } // write all objects void yafrayFileRender_t::writeAllObjects() { // first all objects except dupliverts (and main instance object for dups) for (map::const_iterator obi=all_objects.begin(); obi!=all_objects.end(); ++obi) { // skip main duplivert object if in dupliMtx_list, written later Object* obj = obi->first; if (dupliMtx_list.find(string(obj->id.name))!=dupliMtx_list.end()) continue; writeObject(obj, obi->second.obr, obi->second.faces, obj->obmat); } // Now all duplivert objects (if any) as instances of main object // The original object has been included in the VlakRen renderlist above (see convertBlenderScene.c) // but is written here which all other duplis are instances of. float obmat[4][4], cmat[4][4], imat[4][4], nmat[4][4]; for (map >::const_iterator dupMtx=dupliMtx_list.begin(); dupMtx!=dupliMtx_list.end();++dupMtx) { // original inverse matrix, not actual matrix of object, but first duplivert. for (int i=0;i<4;i++) for (int j=0;j<4;j++) obmat[i][j] = dupMtx->second[(i<<2)+j]; MTC_Mat4Invert(imat, obmat); // first object written as normal (but with transform of first duplivert) Object* obj = dup_srcob[dupMtx->first]; writeObject(obj, all_objects[obj].obr, all_objects[obj].faces, obmat); // all others instances of first for (unsigned int curmtx=16;curmtxsecond.size();curmtx+=16) { // number of 4x4 matrices // new mtx for (int i=0;i<4;i++) for (int j=0;j<4;j++) nmat[i][j] = dupMtx->second[curmtx+(i<<2)+j]; MTC_Mat4MulMat4(cmat, imat, nmat); // transform with respect to original = inverse_original * new ostr.str(""); // yafray matrix = transpose of Blender ostr << "\n"; xmlfile << ostr.str(); // new name from original ostr.str(""); ostr << "id.name << "_dup" << (curmtx>>4) << "\" original=\"" << obj->id.name << "\" >\n"; xmlfile << ostr.str(); xmlfile << "\t\n\t\n\t\n\n\n\n"; } } } void yafrayFileRender_t::writeAreaLamp(LampRen* lamp, int num, float iview[4][4]) { if (lamp->area_shape!=LA_AREA_SQUARE) return; float *a=lamp->area[0], *b=lamp->area[1], *c=lamp->area[2], *d=lamp->area[3]; float power=lamp->energy; ostr.str(""); string md = "off"; // if no GI used, the GIphotons flag can still be set, so only use when 'full' selected if ((re->r.GImethod==2) && (re->r.GIphotons)) { md="on"; power*=re->r.GIpower; } ostr << "ray_totsamp; if (sm>=25) psm = sm/5; ostr << "samples=\"" << sm << "\" psamples=\"" << psm << "\" "; ostr << ">\n"; // transform area lamp coords back to world float lpco[4][3]; MTC_cp3Float(a, lpco[0]); MTC_Mat4MulVecfl(iview, lpco[0]); MTC_cp3Float(b, lpco[1]); MTC_Mat4MulVecfl(iview, lpco[1]); MTC_cp3Float(c, lpco[2]); MTC_Mat4MulVecfl(iview, lpco[2]); MTC_cp3Float(d, lpco[3]); MTC_Mat4MulVecfl(iview, lpco[3]); ostr << "\t\n"; ostr << "\t\n"; ostr << "\t\n"; ostr << "\t\n"; ostr << "\tr << "\" g=\"" << lamp->g << "\" b=\"" << lamp->b << "\" />\n"; ostr << "\n\n"; xmlfile << ostr.str(); } void yafrayFileRender_t::writeLamps() { GroupObject *go; int i=0; // inverse viewmatrix needed for back2world transform float iview[4][4]; // re->viewinv != inv.re->viewmat because of possible ortho mode (see convertBlenderScene.c) // have to invert it here MTC_Mat4Invert(iview, re->viewmat); // all lamps for(go=(GroupObject *)re->lights.first; go; go= go->next, i++) { LampRen* lamp = (LampRen *)go->lampren; ostr.str(""); if (lamp->type==LA_AREA) { writeAreaLamp(lamp, i, iview); continue; } // TODO: add decay setting in yafray ostr << "type==LA_LOCAL) { if (lamp->mode & LA_YF_SOFT) { // shadowmapped omnidirectional light ostr << "softlight\""; is_softL = true; } else if ((lamp->mode & LA_SHAD_RAY) && (lamp->YF_ltradius>0.0)) { // area sphere, only when ray shadows enabled and radius>0.0 ostr << "spherelight\""; is_sphereL = true; } else ostr << "pointlight\""; ostr << " glow_intensity=\"" << lamp->YF_glowint << "\" glow_offset=\"" << lamp->YF_glowofs << "\" glow_type=\"" << lamp->YF_glowtype << "\""; } else if (lamp->type==LA_SPOT) ostr << "spotlight\""; else if ((lamp->type==LA_SUN) || (lamp->type==LA_HEMI)) // hemi exported as sun ostr << "sunlight\""; else if (lamp->type==LA_YF_PHOTON) ostr << "photonlight\""; else { // possibly unknown type, ignore cout << "Unknown Blender lamp type: " << lamp->type << endl; continue; } //no name available here, create one ostr << " name=\"LAMP" << i+1; // color already premultiplied by energy, so only need distance here float pwr = 1; // default for sun/hemi, distance irrelevant if ((lamp->type!=LA_SUN) && (lamp->type!=LA_HEMI)) { if (lamp->mode & LA_SPHERE) { // best approx. as used in LFexport script (LF d.f.m. 4pi?) pwr = lamp->dist*(lamp->dist+1)*(0.25/M_PI); //decay = 2; } else { pwr = lamp->dist; //decay = 1; } } if (is_sphereL) { // 'dummy' mode for spherelight when used with gpm string md = "off"; // if no GI used, the GIphotons flag can still be set, so only use when 'full' selected if ((re->r.GImethod==2) && (re->r.GIphotons)) { md="on"; pwr*=re->r.GIpower; } ostr << "\" power=\"" << pwr << "\" dummy=\"" << md << "\""; } else ostr << "\" power=\"" << pwr << "\""; // cast_shadows flag not used with softlight, spherelight or photonlight if ((!is_softL) && (!is_sphereL) && (lamp->type!=LA_YF_PHOTON)) { string lpmode="off"; // Blender hemilights exported as sunlights which might have shadow flag set // should have cast_shadows set to off (reported by varuag) if (lamp->type!=LA_HEMI) { if (re->r.mode & R_SHADOW) { // old bug was here since the yafray lamp settings panel was added, // blender spotlight shadbuf flag should be ignored, since it is not in the panel anymore if (lamp->mode & LA_SHAD_RAY) lpmode="on"; } } ostr << " cast_shadows=\"" << lpmode << "\""; } // spot specific stuff bool has_halo = ((lamp->type==LA_SPOT) && (lamp->mode & LA_HALO) && (lamp->haint>0.0)); if (lamp->type==LA_SPOT) { // conversion already changed spotsize to cosine of half angle float ld = 1-lamp->spotsi; //convert back to blender slider setting if (ld!=0) ld = 1.f/ld; ostr << " size=\"" << acos(lamp->spotsi)*180.0/M_PI << "\"" << " blend=\"" << lamp->spotbl*ld << "\"" << " beam_falloff=\"2\""; // no Blender equivalent (yet) // halo params if (has_halo) { ostr << "\n\thalo=\"on\" " << "res=\"" << lamp->YF_bufsize << "\"\n"; int hsmp = ((12-lamp->shadhalostep)*16)/12; hsmp = (hsmp+1)*16; // makes range (16, 272) for halostep(12, 0), good enough? // halo 'samples' now 'stepsize' // convert from old integer samples value to some reasonable stepsize ostr << "\tstepsize=\"" << (1.0/sqrt((float)hsmp)) << "\" shadow_samples=\"" << (lamp->samp*lamp->samp) << "\"\n"; ostr << "\thalo_blur=\"0\" shadow_blur=\"" << (lamp->soft*0.01f) << "\"\n"; ostr << "\tfog_density=\"" << (lamp->haint*0.2f) << "\""; } } else if (is_softL) { // softlight ostr << " res=\"" << lamp->YF_bufsize << "\"" << " radius=\"" << lamp->soft << "\"" << " bias=\"" << lamp->bias << "\""; } else if (is_sphereL) { // spherelight int psm=0, sm = lamp->ray_samp*lamp->ray_samp; if (sm>=25) psm = sm/5; ostr << " radius=\"" << lamp->YF_ltradius << "\"" << " samples=\"" << sm << "\"" << " psamples=\"" << psm << "\"" << " qmc_method=\"1\""; } else if (lamp->type==LA_YF_PHOTON) { string qmc="off"; if (lamp->YF_useqmc) qmc="on"; ostr << "\n\tphotons=\"" << lamp->YF_numphotons << "\"" << " search=\"" << lamp->YF_numsearch << "\"" << " depth=\"" << lamp->YF_phdepth << "\"" << " use_QMC=\"" << qmc << "\"" << " angle=\"" << acos(lamp->spotsi)*180.0/M_PI << "\""; float cl = lamp->YF_causticblur/sqrt((float)lamp->YF_numsearch); ostr << "\n\tfixedradius=\"" << lamp->YF_causticblur << "\" cluster=\"" << cl << "\""; } ostr << " >\n"; // transform lamp co & vec back to world float lpco[3], lpvec[3]; MTC_cp3Float(lamp->co, lpco); MTC_Mat4MulVecfl(iview, lpco); MTC_cp3Float(lamp->vec, lpvec); MTC_Mat4Mul3Vecfl(iview, lpvec); // position, (==-blendir for sun/hemi) if ((lamp->type==LA_SUN) || (lamp->type==LA_HEMI)) ostr << "\t\n"; else ostr << "\t\n"; // 'to' for spot/photonlight, already calculated by Blender if ((lamp->type==LA_SPOT) || (lamp->type==LA_YF_PHOTON)) { ostr << "\t\n"; if (has_halo) ostr << "\t\n"; } // color // rgb in LampRen is premultiplied by energy, power is compensated for that above ostr << "\tr << "\" g=\"" << lamp->g << "\" b=\"" << lamp->b << "\" />\n"; ostr << "\n\n"; xmlfile << ostr.str(); } } // write main camera void yafrayFileRender_t::writeCamera() { // here Global used again ostr.str(""); ostr << "r.mode & R_ORTHO) ostr << "type=\"ortho\""; else ostr << "type=\"perspective\""; // render resolution including the percentage buttons ostr << " resx=\"" << re->winx << "\" resy=\"" << re->winy << "\""; float f_aspect = 1; if ((re->winx * re->r.xasp) <= (re->winy * re->r.yasp)) f_aspect = float(re->winx * re->r.xasp) / float(re->winy * re->r.yasp); ostr << "\n\tfocal=\"" << mainCamLens/(f_aspect*32.f); // bug #4532, when field rendering is enabled, ycor is doubled if (re->r.mode & R_FIELDS) ostr << "\" aspect_ratio=\"" << (re->ycor * 0.5f) << "\""; else ostr << "\" aspect_ratio=\"" << re->ycor << "\""; // dof params, only valid for real camera float fdist = 1; // only changes for ortho if (maincam_obj->type==OB_CAMERA) { Camera* cam = (Camera*)maincam_obj->data; if (re->r.mode & R_ORTHO) fdist = cam->ortho_scale*(mainCamLens/32.f); ostr << "\n\tdof_distance=\"" << cam->YF_dofdist << "\""; ostr << " aperture=\"" << cam->YF_aperture << "\""; string st = "on"; if (cam->flag & CAM_YF_NO_QMC) st = "off"; ostr << " use_qmc=\"" << st << "\""; // bokeh params st = "disk1"; if (cam->YF_bkhtype==1) st = "disk2"; else if (cam->YF_bkhtype==2) st = "triangle"; else if (cam->YF_bkhtype==3) st = "square"; else if (cam->YF_bkhtype==4) st = "pentagon"; else if (cam->YF_bkhtype==5) st = "hexagon"; else if (cam->YF_bkhtype==6) st = "ring"; ostr << "\n\tbokeh_type=\"" << st << "\""; st = "uniform"; if (cam->YF_bkhbias==1) st = "center"; else if (cam->YF_bkhbias==2) st = "edge"; ostr << " bokeh_bias=\"" << st << "\""; ostr << " bokeh_rotation=\"" << cam->YF_bkhrot << "\""; } ostr << " >\n"; xmlfile << ostr.str(); ostr.str(""); ostr << "\tobmat[3][0] << "\"" << " y=\"" << maincam_obj->obmat[3][1] << "\"" << " z=\"" << maincam_obj->obmat[3][2] << "\" />\n"; ostr << "\tobmat[3][0] - fdist * re->viewmat[0][2] << "\" y=\"" << maincam_obj->obmat[3][1] - fdist * re->viewmat[1][2] << "\" z=\"" << maincam_obj->obmat[3][2] - fdist * re->viewmat[2][2] << "\" />\n"; ostr << "\tobmat[3][0] + re->viewmat[0][1] << "\" y=\"" << maincam_obj->obmat[3][1] + re->viewmat[1][1] << "\" z=\"" << maincam_obj->obmat[3][2] + re->viewmat[2][1] << "\" />\n"; xmlfile << ostr.str(); xmlfile << "\n\n"; } void yafrayFileRender_t::writeHemilight() { World *world = G.scene->world; bool fromAO = false; if (re->r.GIquality==6){ // use Blender AO params is possible if (world==NULL) return; if ((world->mode & WO_AMB_OCC)==0) { // no AO, use default GIquality cout << "No ambient occlusion enabled\nUsing defaults of 25 samples & infinite distance instead" << endl; } else fromAO = true; } ostr.str(""); if (re->r.GIcache) { ostr << "r.GIpower << "\" mode=\"occlusion\""; ostr << "\n\tcache=\"on\" use_QMC=\"on\" threshold=\"" << re->r.GIrefinement << "\" " << "cache_size=\"" << ((2.0/float(re->winx))*re->r.GIpixelspersample) << "\""; ostr << "\n\tshadow_threshold=\"" << (1.0-re->r.GIshadowquality) << "\" grid=\"82\" search=\"35\""; ostr << "\n\tignore_bumpnormals=\"" << (re->r.YF_nobump ? "on" : "off") << "\""; if (fromAO) { // for AO, with cache, using range of 32*1 to 32*16 seems good enough ostr << "\n\tsamples=\"" << 32*world->aosamp << "\" maxdistance=\"" << world->aodist << "\" >\n"; } else { switch (re->r.GIquality) { case 1 : ostr << " samples=\"128\" >\n"; break; case 2 : ostr << " samples=\"256\" >\n"; break; case 3 : ostr << " samples=\"512\" >\n"; break; case 4 : ostr << " samples=\"1024\" >\n"; break; case 5 : ostr << " samples=\"2048\" >\n"; break; default: ostr << " samples=\"256\" >\n"; } } } else { ostr << "r.GIpower << "\""; if (fromAO) { // use minimum of 4 samples for lowest sample setting, single sample way too noisy ostr << "\n\tsamples=\"" << 3 + world->aosamp*world->aosamp << "\" maxdistance=\"" << world->aodist << "\" use_QMC=\"" << ((world->aomode & WO_AORNDSMP) ? "off" : "on") << "\" >\n"; } else { switch (re->r.GIquality) { case 1 : case 2 : ostr << " samples=\"16\" >\n"; break; case 3 : ostr << " samples=\"36\" >\n"; break; case 4 : ostr << " samples=\"64\" >\n"; break; case 5 : ostr << " samples=\"128\" >\n"; break; default: ostr << " samples=\"25\" >\n"; } } } ostr << "\n\n"; xmlfile << ostr.str(); } void yafrayFileRender_t::writePathlight() { ostr.str(""); if (re->r.GIphotons) { ostr << "r.GIphotoncount << "\"" << endl; ostr << "\tradius=\"" << re->r.GIphotonradius << "\" depth=\"" << ((re->r.GIdepth>2) ? (re->r.GIdepth-1) : 1) << "\" caus_depth=\"" << re->r.GIcausdepth << "\" search=\"" << re->r.GImixphotons << "\" >"<"<r.GIindirpower << "\""; ostr << " depth=\"" << ((re->r.GIphotons) ? 1 : re->r.GIdepth) << "\" caus_depth=\"" << re->r.GIcausdepth <<"\"\n"; if (re->r.GIdirect && re->r.GIphotons) ostr << "direct=\"on\"" << endl; if (re->r.GIcache && !(re->r.GIdirect && re->r.GIphotons)) { switch (re->r.GIquality) { case 1 : ostr << " samples=\"128\" \n"; break; case 2 : ostr << " samples=\"256\" \n"; break; case 3 : ostr << " samples=\"512\" \n"; break; case 4 : ostr << " samples=\"1024\" \n"; break; case 5 : ostr << " samples=\"2048\" \n"; break; default: ostr << " samples=\"512\" \n"; } ostr << " cache=\"on\" use_QMC=\"on\" threshold=\"" << re->r.GIrefinement << "\"" << endl; ostr << "\tignore_bumpnormals=\"" << (re->r.YF_nobump ? "on" : "off") << "\"\n"; float sbase = 2.0/float(re->winx); ostr << "\tcache_size=\"" << sbase*re->r.GIpixelspersample << "\" shadow_threshold=\"" << 1.0-re->r.GIshadowquality << "\" grid=\"82\" search=\"35\" >\n"; } else { switch (re->r.GIquality) { case 1 : ostr << " samples=\"16\" >\n"; break; case 2 : ostr << " samples=\"36\" >\n"; break; case 3 : ostr << " samples=\"64\" >\n"; break; case 4 : ostr << " samples=\"128\" >\n"; break; case 5 : ostr << " samples=\"256\" >\n"; break; default: ostr << " samples=\"25\" >\n"; } } ostr << "\n\n"; xmlfile << ostr.str(); } bool yafrayFileRender_t::writeWorld() { World *world = G.scene->world; if (re->r.GIquality!=0) { if (re->r.GImethod==1) { if (world==NULL) cout << "WARNING: need world background for skydome!\n"; writeHemilight(); } else if (re->r.GImethod==2) writePathlight(); } if (world==NULL) return false; for (int i=0;imtex[i]; if (!wtex) continue; Image* wimg = wtex->tex->ima; // now always exports if image used as world texture (and 'Hori' mapping enabled) if ((wtex->tex->type==TEX_IMAGE) && (wimg!=NULL) && (wtex->mapto & WOMAP_HORIZ)) { string wt_path = wimg->name; adjustPath(wt_path); ostr.str(""); ostr << "tex->bright-1.f << "\""; if (wtex->texco & TEXCO_ANGMAP) ostr << " mapping=\"probe\" >\n"; else if (wtex->texco & TEXCO_H_SPHEREMAP) // in yafray full sphere ostr << " mapping=\"sphere\" >\n"; else // assume 'tube' for anything else ostr << " mapping=\"tube\" >\n"; ostr << "\t\n"; ostr << "\ttex->imaflag & TEX_INTERPOL) ? "bilinear" : "none") << "\" />\n"; if (wtex->tex->filtersize>1.f) ostr << "\t\n"; ostr << "\n\n"; xmlfile << ostr.str(); return true; } } ostr.str(""); ostr << "\n"; // if no GI used, the GIpower parameter is not always initialized, so in that case ignore it // (have to change method to init yafray vars in Blender) float bg_mult = (re->r.GImethod==0) ? 1 : re->r.GIpower; ostr << "\thorr * bg_mult) << "\" g=\"" << (world->horg * bg_mult) << "\" b=\"" << (world->horb * bg_mult) << "\" />\n"; ostr << "\n\n"; xmlfile << ostr.str(); return true; } bool yafrayFileRender_t::executeYafray(const string &xmlpath) { ostr.str(""); if (re->r.mode & R_BORDER) { ostr << command_path << "yafray -c " << re->r.threads << " -r " << (2.f*re->r.border.xmin - 1.f) << ":" << (2.f*re->r.border.xmax - 1.f) << ":" << (2.f*re->r.border.ymin - 1.f) << ":" << (2.f*re->r.border.ymax - 1.f) << " \"" << xmlpath << "\""; } else ostr << command_path << "yafray -c " << re->r.threads << " \"" << xmlpath << "\""; string command = ostr.str(); cout << "COMMAND: " << command << endl; #ifndef WIN32 sigset_t yaf,old; sigemptyset(&yaf); sigaddset(&yaf, SIGVTALRM); sigprocmask(SIG_BLOCK, &yaf, &old); int ret=system(command.c_str()); sigprocmask(SIG_SETMASK, &old, NULL); if (WIFEXITED(ret)) { if (WEXITSTATUS(ret)) cout<<"Executed -"<