/* * $Id$ * * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. The Blender * Foundation also sells licenses for use in proprietary software under * the Blender License. See http://www.blender.org/BL/ for information * about this. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ /* global includes */ #include #include #include #ifdef HAVE_CONFIG_H #include #endif #ifndef WIN32 #include #else #include #endif #include "MEM_guardedalloc.h" #include "BLI_arithb.h" #include "MTC_matrixops.h" #include "render.h" #include "mydevice.h" #include "DNA_group_types.h" #include "DNA_texture_types.h" #include "DNA_world_types.h" #include "DNA_camera_types.h" #include "DNA_image_types.h" #include "DNA_material_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_lamp_types.h" #include "DNA_space_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_icons.h" #include "BKE_texture.h" #include "BKE_material.h" #include "BKE_node.h" #include "BKE_world.h" #include "BKE_texture.h" #include "BKE_utildefines.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "BSE_headerbuttons.h" #include "BSE_node.h" #include "BIF_gl.h" #include "BIF_screen.h" #include "BIF_space.h" /* allqueue */ #include "BIF_butspace.h" #include "BIF_mywindow.h" #include "BIF_interface.h" #include "BIF_glutil.h" #include "BIF_previewrender.h" /* include ourself for prototypes */ #include "PIL_time.h" #include "RE_renderconverter.h" #include "blendef.h" /* CLAMP */ #include "interface.h" /* ui_graphics_to_window() SOLVE! (ton) */ #define PR_XMIN 10 #define PR_YMIN 5 #define PR_XMAX 200 #define PR_YMAX 195 #define PR_FACY (PR_YMAX-PR_YMIN-4)/(PR_RECTY) static rctf prerect; static float pr_facx, pr_facy; /* implementation */ static short intersect(float *v1, float *v2, float *v3, float *rtlabda, float *ray1, float *ray2) { float x0,x1,x2,t00,t01,t02,t10,t11,t12,t20,t21,t22; float m0,m1,m2,deeldet,det1,det2,det3; float rtu, rtv; t00= v3[0]-v1[0]; t01= v3[1]-v1[1]; t02= v3[2]-v1[2]; t10= v3[0]-v2[0]; t11= v3[1]-v2[1]; t12= v3[2]-v2[2]; t20= ray1[0]-ray2[0]; t21= ray1[1]-ray2[1]; t22= ray1[2]-ray2[2]; x0= t11*t22-t12*t21; x1= t12*t20-t10*t22; x2= t10*t21-t11*t20; deeldet= t00*x0+t01*x1+t02*x2; if(deeldet!=0.0f) { m0= ray1[0]-v3[0]; m1= ray1[1]-v3[1]; m2= ray1[2]-v3[2]; det1= m0*x0+m1*x1+m2*x2; rtu= det1/deeldet; if(rtu<=0.0f) { det2= t00*(m1*t22-m2*t21); det2+= t01*(m2*t20-m0*t22); det2+= t02*(m0*t21-m1*t20); rtv= det2/deeldet; if(rtv<=0.0f) { if(rtu+rtv>= -1.0f) { det3= m0*(t12*t01-t11*t02); det3+= m1*(t10*t02-t12*t00); det3+= m2*(t11*t00-t10*t01); *rtlabda= det3/deeldet; if(*rtlabda>=0.0f && *rtlabda<=1.0f) { return 1; } } } } } return 0; } static float rcubev[7][3]= { {-0.002055, 6.627364, -3.369742}, {-6.031684, -3.750204, -1.992980}, {-6.049086, 3.817431, 1.969788}, { 6.031685, 3.833064, 1.992979}, { 6.049086, -3.734571, -1.969787}, { 0.002054, -6.544502, 3.369744}, {-0.015348, 1.023131, 7.332510} }; static int rcubi[3][4]= { {3, 6, 5, 4}, {1, 5, 6, 2}, {3, 0, 2, 6} }; static int ray_previewrender(int x, int y, float *vec, float *vn, short pr_rectx, short pr_recty) { /* float scalef= 10.0/100.0; - not fixed any more because of different render sizes */ float scalef= ( 64.0f / (float)pr_rectx ) * 0.25f; float ray1[3], ray2[3]; float minlabda, labda; int totface= 3, hitface= -1; int a; ray1[0]= ray2[0]= x*scalef; ray1[1]= ray2[1]= y*scalef; ray1[2]= -10.0f; ray2[2]= 10.0f; minlabda= 1.0f; for(a=0; a -1) { CalcNormFloat(rcubev[rcubi[hitface][0]], rcubev[rcubi[hitface][1]], rcubev[rcubi[hitface][2]], vn); vec[0]= (minlabda*(ray1[0]-ray2[0])+ray2[0])/4.1; vec[1]= (minlabda*(ray1[1]-ray2[1])+ray2[1])/4.1; vec[2]= (minlabda*(ray1[2]-ray2[2])+ray2[2])/4.1; return 1; } return 0; } static unsigned int previewback(int type, int x, int y) { unsigned int col; char* pcol; /* checkerboard, for later x+= PR_RECTX/2; y+= PR_RECTX/2; if( ((x/24) + (y/24)) & 1) return 0x40404040; else return 0xa0a0a0a0; */ if(type & MA_DARK) { if(abs(x)>abs(y)) col= 0; else col= 0x40404040; } else { if(abs(x)>abs(y)) col= 0x40404040; else col= 0xa0a0a0a0; } pcol = (char*) &col; pcol[3] = 0; /* set alpha to zero - endianess!*/ return col; } static float previewbackf(int type, int x, int y) { float col; if(type & MA_DARK) { if(abs(x)>abs(y)) col= 0.0f; else col= 0.25f; } else { if(abs(x)>abs(y)) col= 0.25f; else col= 0.625f; } return col; } void set_previewrect(int win, int xmin, int ymin, int xmax, int ymax, short pr_rectx, short pr_recty) { float pr_sizex, pr_sizey; prerect.xmin= xmin; prerect.ymin= ymin; prerect.xmax= xmax; prerect.ymax= ymax; ui_graphics_to_window(win, &prerect.xmin, &prerect.ymin); ui_graphics_to_window(win, &prerect.xmax, &prerect.ymax); pr_sizex= (prerect.xmax-prerect.xmin); pr_sizey= (prerect.ymax-prerect.ymin); pr_facx= ( pr_sizex-1.0f)/pr_rectx; pr_facy= ( pr_sizey-1.0f)/pr_recty; /* correction for gla draw */ prerect.xmin-= curarea->winrct.xmin; prerect.ymin-= curarea->winrct.ymin; glMatrixMode(GL_PROJECTION); glPushMatrix(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glaDefine2DArea(&curarea->winrct); glPixelZoom(pr_facx, pr_facy); } static void end_previewrect(void) { glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPixelZoom(1.0f, 1.0f); // restore viewport / scissor which was set by glaDefine2DArea glViewport(curarea->winrct.xmin, curarea->winrct.ymin, curarea->winx, curarea->winy); glScissor(curarea->winrct.xmin, curarea->winrct.ymin, curarea->winx, curarea->winy); } static void display_pr_scanline(unsigned int *rect, int recty, short pr_rectx) { /* we do steps of 4 scanlines. but draw 5, because of errors in some gfx cards (nvidia geforce, ati...) */ if( (recty & 3)==3) { if(recty == 3) { glaDrawPixelsSafe(prerect.xmin, prerect.ymin, pr_rectx, 4, GL_UNSIGNED_BYTE, rect); } else { rect+= (recty-4)*pr_rectx; glaDrawPixelsSafe(prerect.xmin, prerect.ymin + (((float)recty-4.0)*pr_facy), pr_rectx, 5, GL_UNSIGNED_BYTE, rect); } } } static void draw_tex_crop(Tex *tex) { rcti rct; int ret= 0; if(tex==0) return; if(tex->type==TEX_IMAGE) { if(tex->cropxmin==0.0f) ret++; if(tex->cropymin==0.0f) ret++; if(tex->cropxmax==1.0f) ret++; if(tex->cropymax==1.0f) ret++; if(ret==4) return; rct.xmin= PR_XMIN+2+tex->cropxmin*(PR_XMAX-PR_XMIN-4); rct.xmax= PR_XMIN+2+tex->cropxmax*(PR_XMAX-PR_XMIN-4); rct.ymin= PR_YMIN+2+tex->cropymin*(PR_YMAX-PR_YMIN-4); rct.ymax= PR_YMIN+2+tex->cropymax*(PR_YMAX-PR_YMIN-4); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glColor3ub(0, 0, 0); glRecti(rct.xmin+1, rct.ymin-1, rct.xmax+1, rct.ymax-1); glColor3ub(255, 255, 255); glRecti(rct.xmin, rct.ymin, rct.xmax, rct.ymax); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } } void BIF_preview_changed(short id_code) { ScrArea *sa; for(sa= G.curscreen->areabase.first; sa; sa= sa->next) { if(sa->spacetype==SPACE_BUTS) { SpaceButs *sbuts= sa->spacedata.first; if(sbuts->mainb==CONTEXT_SHADING) { int tab= sbuts->tab[CONTEXT_SHADING]; if(tab==TAB_SHADING_MAT && (id_code==ID_MA || id_code==ID_TE)) { if (sbuts->ri) sbuts->ri->cury= 0; addafterqueue(sa->win, RENDERPREVIEW, 1); } else if(tab==TAB_SHADING_TEX && (id_code==ID_TE)) { if (sbuts->ri) sbuts->ri->cury= 0; addafterqueue(sa->win, RENDERPREVIEW, 1); } else if(tab==TAB_SHADING_LAMP && (id_code==ID_LA || id_code==ID_TE)) { if (sbuts->ri) sbuts->ri->cury= 0; addafterqueue(sa->win, RENDERPREVIEW, 1); } else if(tab==TAB_SHADING_WORLD && (id_code==ID_WO || id_code==ID_TE)) { if (sbuts->ri) sbuts->ri->cury= 0; addafterqueue(sa->win, RENDERPREVIEW, 1); } } } else if(sa->spacetype==SPACE_NODE) { SpaceNode *snode= sa->spacedata.first; if(snode->treetype==NTREE_SHADER && (id_code==ID_MA || id_code==ID_TE)) { snode_tag_dirty(snode); } } } } static void previewdraw_render(struct RenderInfo* ri, ScrArea* area) { int y; if (!ri) { return; } for (y=0; ypr_recty; y++) { display_pr_scanline(ri->rect, y, ri->pr_rectx); } } static void sky_preview_pixel(float lens, int x, int y, char *rect, short pr_rectx, short pr_recty) { float view[3]; if(R.wrld.skytype & WO_SKYPAPER) { view[0]= (2*x)/(float)pr_rectx; view[1]= (2*y)/(float)pr_recty; view[2]= 0.0f; } else { view[0]= x; view[1]= y; view[2]= -lens*pr_rectx/32.0; Normalise(view); } RE_sky_char(view, rect); rect[3] = 0xFF; } static void init_preview_world(World* wrld) { int a; char *cp; if(wrld) { R.wrld= *(wrld); cp= (char *)&R.wrld.fastcol; cp[0]= 255.0*R.wrld.horr; cp[1]= 255.0*R.wrld.horg; cp[2]= 255.0*R.wrld.horb; cp[3]= 1; VECCOPY(R.grvec, R.viewmat[2]); Normalise(R.grvec); Mat3CpyMat4(R.imat, R.viewinv); for(a=0; atex) R.wrld.skytype |= WO_SKYTEX; while(R.wrld.aosamp*R.wrld.aosamp < R.osa) R.wrld.aosamp++; } else { memset(&R.wrld, 0, sizeof(World)); R.wrld.exp= 0.0; R.wrld.range= 1.0; } R.wrld.linfac= 1.0 + pow((2.0*R.wrld.exp + 0.5), -10); R.wrld.logfac= log( (R.wrld.linfac-1.0)/R.wrld.linfac )/R.wrld.range; } /* This function carefully copies over the struct members from the struct Lamp to a new struct LampRen. It only copies the struct members that are needed in the lamp_preview_pixel function. Replacement for the RE_add_render_lamp function in the preview, because this only works for the current selected lamp. */ static LampRen* create_preview_render_lamp(Lamp* la) { LampRen *lar; int c; lar= (LampRen *)MEM_callocN(sizeof(LampRen),"lampren"); MTC_Mat3One(lar->mat); MTC_Mat3One(lar->imat); lar->type= la->type; lar->mode= la->mode; lar->energy= la->energy; if(la->mode & LA_NEG) lar->energy= -lar->energy; lar->r= lar->energy*la->r; lar->g= lar->energy*la->g; lar->b= lar->energy*la->b; lar->k= la->k; lar->dist= la->dist; lar->ld1= la->att1; lar->ld2= la->att2; /* exceptions: */ lar->spottexfac= 1.0; lar->spotsi= cos( M_PI/3.0 ); lar->spotbl= (1.0-lar->spotsi)*la->spotblend; MTC_Mat3One(lar->imat); if(lar->type==LA_SPOT) { if(lar->mode & LA_ONLYSHADOW) { if((lar->mode & (LA_SHAD|LA_SHAD_RAY))==0) lar->mode -= LA_ONLYSHADOW; } } memcpy(lar->mtex, la->mtex, MAX_MTEX*sizeof(void *)); for(c=0; cmtex[c] && la->mtex[c]->tex) { lar->mode |= LA_TEXTURE; if(R.flag & R_RENDERING) { if(R.osa) { if(la->mtex[c]->tex->type==TEX_IMAGE) lar->mode |= LA_OSATEX; } } } } return lar; } static void lamp_preview_pixel(ShadeInput *shi, LampRen *la, int x, int y, char *rect, short pr_rectx, short pr_recty) { float inpr, i, t, dist, distkw, vec[3], lacol[3]; int col; shi->co[0]= (float)x/(pr_rectx/4); shi->co[1]= (float)y/(pr_rectx/4); shi->co[2]= 0; vec[0]= 0.02f*x; vec[1]= 0.02f*y; vec[2]= 0.005f*pr_rectx; VECCOPY(shi->view, vec); dist= Normalise(shi->view); lacol[0]= la->r; lacol[1]= la->g; lacol[2]= la->b; if(la->mode & LA_TEXTURE) do_lamp_tex(la, vec, shi, lacol); if(la->type==LA_SUN || la->type==LA_HEMI) { dist= 1.0f; } else { if(la->mode & LA_QUAD) { t= 1.0f; if(la->ld1>0.0f) t= la->dist/(la->dist+la->ld1*dist); if(la->ld2>0.0f) { distkw= la->dist*la->dist; t= t*distkw/(t*distkw+la->ld2*dist*dist); } dist= t; } else { dist= (la->dist/(la->dist+dist)); } } /* yafray: preview shade as spot, sufficient */ if ((la->type==LA_SPOT) || (la->type==LA_YF_PHOTON)) { if(la->mode & LA_SQUARE) { /* slightly smaller... */ inpr= 1.7*cos(MAX2(fabs(shi->view[0]/shi->view[2]) , fabs(shi->view[1]/shi->view[2]) )); } else { inpr= shi->view[2]; } t= la->spotsi; if(inprspotbl && la->spotbl!=0.0f) { /* soft area */ i= t/la->spotbl; t= i*i; i= t*i; inpr*=(3.0*t-2.0*i); } } dist*=inpr; } else if ELEM(la->type, LA_LOCAL, LA_AREA) dist*= shi->view[2]; col= 255.0*dist*lacol[0]; if(col<=0) rect[0]= 0; else if(col>=255) rect[0]= 255; else rect[0]= col; col= 255.0*dist*lacol[1]; if(col<=0) rect[1]= 0; else if(col>=255) rect[1]= 255; else rect[1]= col; col= 255.0*dist*lacol[2]; if(col<=0) rect[2]= 0; else if(col>=255) rect[2]= 255; else rect[2]= col; rect[3] = 0xFF; } static void init_previewhalo(HaloRen *har, Material *mat, short pr_rectx, short pr_recty) { har->type= 0; if(mat->mode & MA_HALO_XALPHA) har->type |= HA_XALPHA; har->mat= mat; har->hard= mat->har; har->rad= pr_rectx/2.0; har->radsq= pr_rectx*pr_rectx/4.0; har->alfa= mat->alpha; har->add= 255.0*mat->add; har->r= mat->r; har->g= mat->g; har->b= mat->b; har->xs= pr_rectx/2.0; har->ys= pr_rectx/2.0; har->zs= har->zd= 0; har->seed= (mat->seed1 % 256); if( (mat->mode & MA_HALOTEX) && mat->mtex[0] ) har->tex= 1; else har->tex=0; if(mat->mode & MA_STAR) har->starpoints= mat->starc; else har->starpoints= 0; if(mat->mode & MA_HALO_LINES) har->linec= mat->linec; else har->linec= 0; if(mat->mode & MA_HALO_RINGS) har->ringc= mat->ringc; else har->ringc= 0; if(mat->mode & MA_HALO_FLARE) har->flarec= mat->flarec; else har->flarec= 0; if(har->flarec) { har->xs-= pr_rectx/3; har->ys+= pr_rectx/3; har->rad*= 0.3; har->radsq= har->rad*har->rad; har->pixels= har->rad*har->rad*har->rad; } } static void halo_preview_pixel(HaloRen *har, int startx, int endx, int y, char *rect, short pr_rectx) { float dist, xn, yn, xsq, ysq, colf[4]; int x; char front[4]; if(har->flarec) yn= y-pr_rectx/3; else yn= y; ysq= yn*yn; for(x=startx; xflarec) xn= x+pr_rectx/3; else xn= x; xsq= xn*xn; dist= xsq+ysq; if(distradsq) { RE_shadehalo(har, front, colf, 0, dist, xn, yn, har->flarec); RE_addalphaAddfac(rect, front, har->add); rect[3] = 0xFF; /* makes icon display all pixels */ } rect+= 4; } } static void previewflare(RenderInfo *ri, HaloRen *har, short pr_rectx, short pr_recty, int pr_method) { float ycor; unsigned int *rectot; int afmx, afmy, rectx, recty, y; /* check for "Preview" block already in calling function BIF_previewrender! - elubie */ /* temps */ ycor= R.ycor; rectx= R.rectx; recty= R.recty; afmx= R.afmx; afmy= R.afmy; rectot= R.rectot; R.r.postmul= R.r.postgamma= R.r.postsat= 1.0f; R.r.posthue= R.r.postadd= 0.0f; R.ycor= 1.0f; R.rectx= pr_rectx; R.recty= pr_recty; R.afmx= pr_rectx/2; R.afmy= pr_recty/2; R.rectot= ri->rect; waitcursor(1); RE_renderflare(har); waitcursor(0); // not sure why, either waitcursor or renderflare screws up (disabled then) //areawinset(curarea->win); /* draw can just be called this way, all settings are OK */ if (pr_method==PR_DRAW_RENDER) { for (y=0; yrect, y, pr_rectx); } } /* temps */ R.ycor= ycor; R.rectx= rectx; R.recty= recty; R.afmx= afmx; R.afmy= afmy; R.rectot= rectot; } static void texture_preview_pixel(Tex *tex, int x, int y, char *rect, short pr_rectx, short pr_recty) { float i, v1, xsq, ysq, texvec[3]; float tin=1.0f, tr, tg, tb, ta; int rgbnor, tracol, skip=0; if(tex->type==TEX_IMAGE) { v1= 1.0f/pr_rectx; texvec[0]= 0.5+v1*x; texvec[1]= 0.5+v1*y; /* no coordinate mapping, exception: repeat */ if(tex->extend==TEX_REPEAT) { if(tex->xrepeat>1) { texvec[0] *= tex->xrepeat; if(texvec[0]>1.0f) texvec[0] -= (int)(texvec[0]); } if(tex->yrepeat>1) { texvec[1] *= tex->yrepeat; if(texvec[1]>1.0f) texvec[1] -= (int)(texvec[1]); } } else if(tex->extend==TEX_CHECKER) { texvec[0]= 0.5+1.6*v1*x; texvec[1]= 0.5+1.6*v1*y; } } else if(tex->type==TEX_ENVMAP) { if(tex->env) { ysq= y*y; xsq= x*x; if(xsq+ysq < (pr_rectx/2)*(pr_recty/2)) { texvec[2]= sqrt( (float)((pr_rectx/2)*(pr_recty/2)-xsq-ysq) ); texvec[0]= -x; texvec[1]= -y; Normalise(texvec); i= 2.0*(texvec[2]); texvec[0]= (i*texvec[0]); texvec[1]= (i*texvec[1]); texvec[2]= (-1.0f+i*texvec[2]); } else { skip= 1; tr= tg= tb= ta= 0.0f; } } else { skip= 1; tr= tg= tb= ta= 0.0f; } } else { v1= 2.0/pr_rectx; texvec[0]= v1*x; texvec[1]= v1*y; texvec[2]= 0.0f; } if(skip==0) rgbnor= multitex_ext(tex, texvec, &tin, &tr, &tg, &tb, &ta); else rgbnor= 1; if(rgbnor & 1) { v1= 255.0*tr; rect[0]= CLAMPIS(v1, 0, 255); v1= 255.0*tg; rect[1]= CLAMPIS(v1, 0, 255); v1= 255.0*tb; rect[2]= CLAMPIS(v1, 0, 255); if(ta!=1.0f) { tracol= 64+100*(abs(x)>abs(y)); tracol= (1.0f-ta)*tracol; rect[0]= tracol+ (rect[0]*ta) ; rect[1]= tracol+ (rect[1]*ta) ; rect[2]= tracol+ (rect[2]*ta) ; } rect[3] = 0xFF; } else { rect[0]= 255.0*tin; rect[1]= 255.0*tin; rect[2]= 255.0*tin; rect[3] = 0xFF; } } static float pr1_lamp[3]= {2.3, -2.4, -4.6}; static float pr2_lamp[3]= {-8.8, -5.6, -1.5}; static float pr1_col[3]= {0.8, 0.8, 0.8}; static float pr2_col[3]= {0.5, 0.6, 0.7}; static void refraction_prv(int *x, int *y, float *n, float index) { float dot, fac, view[3], len; index= 1.0f/index; view[0]= index*(float)*x; view[1]= ((float)*y)/index; view[2]= 20.0f; len= Normalise(view); dot= view[0]*n[0] + view[1]*n[1] + view[2]*n[2]; if(dot>0.0f) { fac= 1.0f - (1.0f - dot*dot)*index*index; if(fac<= 0.0f) return; fac= -dot*index + sqrt(fac); } else { index = 1.0f/index; fac= 1.0f - (1.0f - dot*dot)*index*index; if(fac<= 0.0f) return; fac= -dot*index - sqrt(fac); } *x= (int)(len*(index*view[0] + fac*n[0])); *y= (int)(len*(index*view[1] + fac*n[1])); } static void shade_lamp_loop_preview(ShadeInput *shi, ShadeResult *shr) { extern float fresnel_fac(float *view, float *vn, float ior, float fac); Material *mat= shi->mat; float inp, is, inprspec=0; float lv[3], *la, *vn, vnor[3]; int a; // copy all relevant material vars, note, keep this synced with render_types.h memcpy(&shi->r, &mat->r, 23*sizeof(float)); // set special cases: shi->har= mat->har; if((mat->mode & MA_RAYMIRROR)==0) shi->ray_mirror= 0.0f; memset(shr, 0, sizeof(ShadeResult)); /* normals flipped in render for smooth... */ if( (mat->mapto & MAP_NORM)) VecMulf(shi->vn, -1.0f); do_material_tex(shi); /* normals flipped in render... */ if( (mat->mapto & MAP_NORM)) VecMulf(shi->vn, -1.0f); shr->alpha= shi->alpha; if(mat->mode & (MA_ZTRA|MA_RAYTRANSP)) if(mat->fresnel_tra!=0.0f) shr->alpha*= fresnel_fac(shi->view, shi->vn, mat->fresnel_tra_i, mat->fresnel_tra); if(mat->mode & MA_SHLESS) { shr->diff[0]= shi->r; shr->diff[1]= shi->g; shr->diff[2]= shi->b; } else { if(mat->texco & TEXCO_REFL) { inp= -2.0*(shi->vn[0]*shi->view[0]+shi->vn[1]*shi->view[1]+shi->vn[2]*shi->view[2]); shi->ref[0]= (shi->view[0]+inp*shi->vn[0]); shi->ref[1]= (shi->view[1]+inp*shi->vn[1]); shi->ref[2]= (shi->view[2]+inp*shi->vn[2]); /* normals in render are pointing different... rhm */ if(shi->pr_type==MA_SPHERE) shi->ref[1]= -shi->ref[1]; } for(a=0; a<2; a++) { if((mat->pr_lamp & (1<co[0]-la[0]; lv[1]= shi->co[1]-la[1]; lv[2]= shi->co[2]-la[2]; Normalise(lv); if(shi->spec>0.0f) { /* specular shaders */ float specfac; if(mat->mode & MA_TANGENT_V) vn= shi->tang; else vn= shi->vn; if(mat->spec_shader==MA_SPEC_PHONG) specfac= Phong_Spec(vn, lv, shi->view, shi->har, mat->mode & MA_TANGENT_V); else if(mat->spec_shader==MA_SPEC_COOKTORR) specfac= CookTorr_Spec(vn, lv, shi->view, shi->har, mat->mode & MA_TANGENT_V); else if(mat->spec_shader==MA_SPEC_BLINN) specfac= Blinn_Spec(vn, lv, shi->view, mat->refrac, (float)shi->har, mat->mode & MA_TANGENT_V); else if(mat->spec_shader==MA_SPEC_WARDISO) specfac= WardIso_Spec(vn, lv, shi->view, mat->rms, mat->mode & MA_TANGENT_V); else specfac= Toon_Spec(vn, lv, shi->view, mat->param[2], mat->param[3], mat->mode & MA_TANGENT_V); inprspec= specfac*shi->spec; if(mat->mode & MA_RAMP_SPEC) { float spec[3]; do_specular_ramp(shi, specfac, inprspec, spec); shr->spec[0]+= inprspec*spec[0]; shr->spec[1]+= inprspec*spec[1]; shr->spec[2]+= inprspec*spec[2]; } else { shr->spec[0]+= inprspec*shi->specr; shr->spec[1]+= inprspec*shi->specg; shr->spec[2]+= inprspec*shi->specb; } } if(mat->mode & MA_TANGENT_V) { float cross[3]; Crossf(cross, lv, shi->tang); Crossf(vnor, cross, shi->tang); vnor[0]= -vnor[0];vnor[1]= -vnor[1];vnor[2]= -vnor[2]; vn= vnor; } else vn= shi->vn; is= vn[0]*lv[0]+vn[1]*lv[1]+vn[2]*lv[2]; if(is<0.0f) is= 0.0f; /* diffuse shaders */ if(mat->diff_shader==MA_DIFF_ORENNAYAR) is= OrenNayar_Diff(vn, lv, shi->view, mat->roughness); else if(mat->diff_shader==MA_DIFF_TOON) is= Toon_Diff(vn, lv, shi->view, mat->param[0], mat->param[1]); else if(mat->diff_shader==MA_DIFF_MINNAERT) is= Minnaert_Diff(is, vn, shi->view, mat->darkness); else if(mat->diff_shader==MA_DIFF_FRESNEL) is= Fresnel_Diff(vn, lv, shi->view, mat->param[0], mat->param[1]); // else Lambert inp= (shi->refl*is + shi->emit); if(a==0) la= pr1_col; else la= pr2_col; add_to_diffuse(shr->diff, shi, is, inp*la[0], inp*la[1], inp*la[2]); } /* end lamp loop */ /* drawing checkerboard and sky */ if(mat->mode & MA_RAYMIRROR) { float col, div, y, z; int fac; /* rotate a bit in x */ y= shi->ref[1]; z= shi->ref[2]; shi->ref[1]= 0.98*y - 0.17*z; shi->ref[2]= 0.17*y + 0.98*z; /* scale */ div= (0.85f*shi->ref[1]); shi->refcol[0]= shi->ray_mirror*fresnel_fac(shi->view, shi->vn, mat->fresnel_mir_i, mat->fresnel_mir); /* not real 'alpha', but mirror overriding transparency */ if(mat->mode & MA_RAYTRANSP) { float fac= sqrt(shi->refcol[0]); shr->alpha= shr->alpha*(1.0f-fac) + fac; } else shr->alpha= shr->alpha*(1.0f-shi->refcol[0]) + shi->refcol[0]; if(div<0.0f) { /* minus 0.5 prevents too many small tiles in distance */ fac= (int)(shi->ref[0]/(div-0.1f) ) + (int)(shi->ref[2]/(div-0.1f) ); if(fac & 1) col= 0.8f; else col= 0.3f; shi->refcol[1]= shi->refcol[0]*col; shi->refcol[2]= shi->refcol[1]; shi->refcol[3]= shi->refcol[2]; } else { shi->refcol[1]= 0.0f; shi->refcol[2]= shi->refcol[0]*0.3f*div; shi->refcol[3]= shi->refcol[0]*0.8f*div; } } else shi->refcol[0]= 0.0f; shr->diff[0]+= shi->ambr; shr->diff[1]+= shi->ambg; shr->diff[2]+= shi->ambb; if(mat->mode & MA_RAMP_COL) ramp_diffuse_result(shr->diff, shi); if(mat->mode & MA_RAMP_SPEC) ramp_spec_result(shr->spec, shr->spec+1, shr->spec+2, shi); /* refcol */ if(shi->refcol[0]!=0.0f) { shr->diff[0]= shi->mirr*shi->refcol[1] + (1.0f - shi->mirr*shi->refcol[0])*shr->diff[0]; shr->diff[1]= shi->mirg*shi->refcol[2] + (1.0f - shi->mirg*shi->refcol[0])*shr->diff[1]; shr->diff[2]= shi->mirb*shi->refcol[3] + (1.0f - shi->mirb*shi->refcol[0])*shr->diff[2]; } /* ztra shade */ if(shi->spectra!=0.0f) { inp = MAX3(shr->spec[0], shr->spec[1], shr->spec[2]); inp *= shi->spectra; if(inp>1.0f) inp= 1.0f; shr->alpha= (1.0f-inp)*shr->alpha+inp; } } } static void shade_preview_pixel(ShadeInput *shi, float *vec, int x, int y, char *rect, short pr_rectx, short pr_recty) { Material *mat; MaterialLayer *ml; ShadeResult shr; float v1; float eul[3], tmat[3][3], imat[3][3], col[4]; mat= shi->mat; v1= 1.0/pr_rectx; shi->view[0]= v1*x; shi->view[1]= v1*y; shi->view[2]= 1.0f; Normalise(shi->view); shi->xs= x + pr_rectx/2; shi->ys= y + pr_recty/2; shi->refcol[0]= shi->refcol[1]= shi->refcol[2]= shi->refcol[3]= 0.0f; VECCOPY(shi->co, vec); /* texture handling */ if(mat->texco) { VECCOPY(shi->lo, vec); if(mat->pr_type==MA_CUBE) { eul[0]= (297)*M_PI/180.0; eul[1]= 0.0; eul[2]= (45)*M_PI/180.0; EulToMat3(eul, tmat); MTC_Mat3MulVecfl(tmat, shi->lo); MTC_Mat3MulVecfl(tmat, shi->vn); /* hack for cubemap, why!!! */ SWAP(float, shi->vn[0], shi->vn[1]); } /* textures otherwise upside down */ if(mat->pr_type==MA_CUBE || mat->pr_type==MA_SPHERE) shi->lo[2]= -shi->lo[2]; if(mat->texco & TEXCO_GLOB) { VECCOPY(shi->gl, shi->lo); } if(mat->texco & TEXCO_WINDOW) { VECCOPY(shi->winco, shi->lo); } if(mat->texco & TEXCO_STICKY) { VECCOPY(shi->sticky, shi->lo); } if(mat->texco & TEXCO_UV) { VECCOPY(shi->uv, shi->lo); } if(mat->texco & TEXCO_STRAND) { shi->strand= shi->lo[0]; } if(mat->texco & TEXCO_OBJECT) { /* nothing */ } if(mat->texco & (TEXCO_NORM)) { shi->orn[0]= shi->vn[0]; shi->orn[1]= shi->vn[1]; shi->orn[2]= shi->vn[2]; } /* Clear displase vec for preview */ shi->displace[0]= shi->displace[1]= shi->displace[2]= 0.0; if(mat->pr_type==MA_CUBE) { /* rotate normal back for normals texture */ SWAP(float, shi->vn[0], shi->vn[1]); MTC_Mat3Inv(imat, tmat); MTC_Mat3MulVecfl(imat, shi->vn); } } if(mat->mapto & MAP_DISPLACE) { /* Quick hack of fake displacement preview */ // shi->vn[0]-=2.0*shi->displace[2]; // shi->vn[1]-=2.0*shi->displace[0]; // shi->vn[2]+=2.0*shi->displace[1]; // Normalise(shi->vn); } /* ------ main shading loop with material layers */ VECCOPY(shi->vno, shi->vn); if(mat->ml_flag & ML_RENDER) shade_lamp_loop_preview(shi, &shr); else { memset(&shr, 0, sizeof(ShadeResult)); shr.alpha= 1.0f; } if(mat->nodetree && mat->use_nodes) { ntreeShaderExecTree(mat->nodetree, shi, &shr); } else { for(ml= mat->layers.first; ml; ml= ml->next) { if(ml->mat && (ml->flag & ML_RENDER)) { ShadeResult shrlay; shi->mat= ml->mat; shi->layerfac= ml->blendfac; VECCOPY(shi->vn, shi->vno); if(ml->flag & ML_NEG_NORMAL) VecMulf(shi->vn, -1.0); shade_lamp_loop_preview(shi, &shrlay); matlayer_blend(ml, shi->layerfac, &shr, &shrlay); } } } shi->mat= mat; /* restore, shade input is re-used! */ /* after shading and composit layers */ if(shr.spec[0]<0.0f) shr.spec[0]= 0.0f; if(shr.spec[1]<0.0f) shr.spec[1]= 0.0f; if(shr.spec[2]<0.0f) shr.spec[2]= 0.0f; if(shr.diff[0]<0.0f) shr.diff[0]= 0.0f; if(shr.diff[1]<0.0f) shr.diff[1]= 0.0f; if(shr.diff[2]<0.0f) shr.diff[2]= 0.0f; VECADD(col, shr.diff, shr.spec); col[3]= shr.alpha; /* handle backdrop now */ if(col[3]!=1.0f) { float back, backm; /* distorts x and y */ if(mat->mode & MA_RAYTRANSP) { refraction_prv(&x, &y, shi->vn, shi->ang); } back= previewbackf(mat->pr_back, x, y); backm= (1.0f-shr.alpha)*back; if((mat->mode & MA_RAYTRANSP) && mat->filter!=0.0) { float fr= 1.0f+ mat->filter*(shr.diff[0]-1.0f); col[0]= fr*backm+ (col[3]*col[0]); fr= 1.0f+ mat->filter*(shr.diff[1]-1.0f); col[1]= fr*backm+ (col[3]*col[1]); fr= 1.0f+ mat->filter*(shr.diff[2]-1.0f); col[2]= fr*backm+ (col[3]*col[2]); } else { col[0]= backm + (col[3]*col[0]); col[1]= backm + (col[3]*col[1]); col[2]= backm + (col[3]*col[2]); } } if(col[0]<=0.0f) rect[0]= 0; else if(col[0]>=1.0f) rect[0]= 255; else rect[0]= (char)(255.0f*col[0]); if(col[1]<=0.0f) rect[1]= 0; else if(col[1]>=1.0f) rect[1]= 255; else rect[1]= (char)(255.0f*col[1]); if(col[2]<=0.0f) rect[2]= 0; else if(col[2]>=1.0f) rect[2]= 255; else rect[2]= (char)(255.0f*col[2]); if(col[3]<=0.0f) rect[3]= 0; else if(col[3]>=1.0f) rect[3]= 255; else rect[3]= (char)(255.0f*col[3]); } static void preview_init_render_textures(MTex **mtex) { int x; for(x=0; xtex) { init_render_texture(mtex[x]->tex); if(mtex[x]->tex->env && mtex[x]->tex->env->object) MTC_Mat4One(mtex[x]->tex->env->object->imat); } if(mtex[x]->object) MTC_Mat4One(mtex[x]->object->imat); if(mtex[x]->object) MTC_Mat4One(mtex[x]->object->imat); } } } /* main previewrender loop */ void BIF_previewrender(struct ID *id, struct RenderInfo *ri, struct ScrArea *area, int pr_method) { static double lasttime= 0; Material *mat= NULL; Tex *tex= NULL; Lamp *la= NULL; World *wrld= NULL; LampRen *lar= NULL; Image *ima; HaloRen har; Object *ob; ShadeInput shi; float lens = 0.0, vec[3]; int x, y, starty, startx, endy, endx, radsq, xsq, ysq, last = 0; unsigned int *rect; if(ri->cury>=ri->pr_rectx) return; ob= ((G.scene->basact)? (G.scene->basact)->object: 0); switch(GS(id->name)) { case ID_MA: mat = (Material*)id; break; case ID_TE: tex = (Tex*)id; break; case ID_LA: la = (Lamp*)id; break; case ID_WO: wrld = (World*)id; break; default: return; } har.flarec= 0; /* below is a test for postrender flare */ /* no event escape for icon render */ if(pr_method!=PR_ICON_RENDER && qtest()) { addafterqueue(curarea->win, RENDERPREVIEW, 1); return; } MTC_Mat4One(R.viewmat); MTC_Mat4One(R.viewinv); shi.osatex= 0; if(mat) { MaterialLayer *ml; /* rendervars */ init_render_world(); init_render_material(mat); /* also clears imats */ preview_init_render_textures(mat->mtex); for(ml= mat->layers.first; ml; ml= ml->next) { if(ml->mat && (ml->flag & ML_RENDER)) { init_render_material(ml->mat); preview_init_render_textures(ml->mat->mtex); mat->texco |= ml->mat->texco; } } if(mat->nodetree && mat->use_nodes) { bNode *node; for(node=mat->nodetree->nodes.first; node; node= node->next) { if(node->id && GS(node->id->name)==ID_MA) { Material *ma= (Material *)node->id; init_render_material(ma); preview_init_render_textures(ma->mtex); mat->texco |= ma->texco; } } /* signal to node editor to store previews or not */ if(pr_method==PR_ICON_RENDER) { ntreeBeginExecTree(mat->nodetree, 0, 0); shi.do_preview= 0; } else { ntreeBeginExecTree(mat->nodetree, ri->pr_rectx, ri->pr_recty); shi.do_preview= 1; } } shi.vlr= NULL; shi.mat= mat; shi.pr_type= mat->pr_type; if(mat->mode & MA_HALO) init_previewhalo(&har, mat, ri->pr_rectx, ri->pr_recty); set_node_shader_lamp_loop(shade_lamp_loop_preview); } else if(tex) { ima= tex->ima; if(ima) last= ima->lastframe; init_render_texture(tex); free_unused_animimages(); if(tex->ima) { if(tex->ima!=ima) allqueue(REDRAWBUTSSHADING, 0); else if(last!=ima->lastframe) allqueue(REDRAWBUTSSHADING, 0); } if(tex->env && tex->env->object) MTC_Mat4Invert(tex->env->object->imat, tex->env->object->obmat); } else if(la) { init_render_world(); preview_init_render_textures(la->mtex); /* lar= ((GroupObject *)R.lights.first)->lampren; RE_add_render_lamp(ob, 0); */ /* 0=no shadbuf or tables */ /* elubie: not nice, but ob contains current object, not usable if you need to render lamp that's not active object :( */ lar = create_preview_render_lamp(la); /* exceptions: */ lar->spottexfac= 1.0f; lar->spotsi= cos( M_PI/3.0f ); lar->spotbl= (1.0f-lar->spotsi)*la->spotblend; MTC_Mat3One(lar->imat); } else if(wrld) { lens= 35.0; if(G.scene->camera) { lens= ( (Camera *)G.scene->camera->data)->lens; /* needed for init_render_world */ MTC_Mat4CpyMat4(R.viewinv, G.scene->camera->obmat); MTC_Mat4Ortho(R.viewinv); MTC_Mat4Invert(R.viewmat, R.viewinv); } init_preview_world(wrld); preview_init_render_textures(wrld->mtex); } if(ri->rect==NULL) { ri->rect= MEM_callocN(sizeof(int)*ri->pr_rectx*ri->pr_recty, "butsrect"); } starty= -ri->pr_recty/2; endy= starty+ri->pr_recty; starty+= ri->cury; startx= -ri->pr_rectx/2; endx= startx+ri->pr_rectx; radsq= (ri->pr_rectx/2)*(ri->pr_recty/2); if(mat) { if(mat->pr_type==MA_SPHERE) { pr1_lamp[0]= 2.3; pr1_lamp[1]= -2.4; pr1_lamp[2]= -4.6; pr2_lamp[0]= -8.8; pr2_lamp[1]= -5.6; pr2_lamp[2]= -1.5; } else { pr1_lamp[0]= 1.9; pr1_lamp[1]= 3.1; pr1_lamp[2]= -8.5; pr2_lamp[0]= 1.2; pr2_lamp[1]= -18; pr2_lamp[2]= 3.2; } } if (pr_method==PR_DRAW_RENDER) glDrawBuffer(GL_FRONT); /* here it starts! */ for(y=starty; yrect + ri->pr_rectx*ri->cury; if(mat) { if(mat->mode & MA_HALO) { for(x=startx; xpr_back, x, y); } if(har.flarec) { if(y==endy-2) previewflare(ri, &har, ri->pr_rectx, ri->pr_recty, pr_method); } else { halo_preview_pixel(&har, startx, endx, y, (char *) (rect-ri->pr_rectx), ri->pr_rectx); } } else { ysq= y*y; for(x=startx; xpr_type==MA_SPHERE) { if(xsq+ysq <= radsq) { shi.vn[0]= x; shi.vn[1]= y; shi.vn[2]= sqrt( (float)(radsq-xsq-ysq) ); Normalise(shi.vn); vec[0]= shi.vn[0]; vec[1]= shi.vn[2]; vec[2]= -shi.vn[1]; if(mat->mode & MA_TANGENT_V) { float tmp[3]; tmp[0]=tmp[2]= 0.0f; tmp[1]= 1.0f; Crossf(shi.tang, tmp, shi.vn); Normalise(shi.tang); } shade_preview_pixel(&shi, vec, x, y, (char *)rect, ri->pr_rectx, ri->pr_recty); } else { rect[0]= previewback(mat->pr_back, x, y); if(pr_method!=PR_ICON_RENDER && mat->nodetree && mat->use_nodes) ntreeClearPixelTree(mat->nodetree, x+ri->pr_rectx/2, y+ri->pr_recty/2); } } else if(mat->pr_type==MA_CUBE) { if( ray_previewrender(x, y, vec, shi.vn, ri->pr_rectx, ri->pr_recty) ) { shade_preview_pixel(&shi, vec, x, y, (char *)rect, ri->pr_rectx, ri->pr_recty); } else { rect[0]= previewback(mat->pr_back, x, y); if(pr_method!=PR_ICON_RENDER && mat->nodetree && mat->use_nodes) ntreeClearPixelTree(mat->nodetree, x+ri->pr_rectx/2, y+ri->pr_recty/2); } } else { vec[0]= x*(2.0f/ri->pr_rectx); vec[1]= y*(2.0f/ri->pr_recty); vec[2]= 0.0; shi.vn[0]= shi.vn[1]= 0.0f; shi.vn[2]= 1.0f; shade_preview_pixel(&shi, vec, x, y, (char *)rect, ri->pr_rectx, ri->pr_recty); } } } } else if(tex) { for(x=startx; xpr_rectx, ri->pr_recty); } } else if(la) { for(x=startx; xpr_rectx, ri->pr_recty); } } else { for(x=startx; xpr_rectx, ri->pr_recty); } } if (pr_method!=PR_ICON_RENDER) { if(ywin, RENDERPREVIEW, 1); break; } } display_pr_scanline(ri->rect, ri->cury, ri->pr_rectx); /* flush opengl for cards with frontbuffer slowness */ if(ri->cury==ri->pr_recty-1 || (PIL_check_seconds_timer() - lasttime > 0.05)) { lasttime= PIL_check_seconds_timer(); glFlush(); } } ri->cury++; } if (pr_method==PR_DRAW_RENDER) { if(ri->cury>=ri->pr_recty && tex) draw_tex_crop((Tex*)id); glDrawBuffer(GL_BACK); /* draw again for clean swapbufers */ previewdraw_render(ri, area); } if(lar) { MEM_freeN(lar); /* MEM_freeN(R.lights.first); R.lights.first= R.lights.last= NULL; */ } if(mat && mat->nodetree && mat->use_nodes) { ntreeEndExecTree(mat->nodetree); if(ri->cury>=ri->pr_recty) allqueue(REDRAWNODE, 0); } } void BIF_previewrender_buts(SpaceButs *sbuts) { uiBlock *block; struct ID* id = 0; struct ID* idfrom = 0; struct ID* idshow = 0; Object *ob; if (!sbuts->ri) return; /* we safely assume curarea has panel "preview" */ /* quick hack for now, later on preview should become uiBlock itself */ block= uiFindOpenPanelBlockName(&curarea->uiblocks, "Preview"); if(block==NULL) return; ob= ((G.scene->basact)? (G.scene->basact)->object: 0); /* we cant trust this global lockpoin.. for example with headerless window */ buttons_active_id(&id, &idfrom); G.buts->lockpoin= id; if(sbuts->mainb==CONTEXT_SHADING) { int tab= sbuts->tab[CONTEXT_SHADING]; if(tab==TAB_SHADING_MAT) idshow = sbuts->lockpoin; else if(tab==TAB_SHADING_TEX) idshow = sbuts->lockpoin; else if(tab==TAB_SHADING_LAMP) { if(ob && ob->type==OB_LAMP) idshow= ob->data; } else if(tab==TAB_SHADING_WORLD) idshow = sbuts->lockpoin; } else if(sbuts->mainb==CONTEXT_OBJECT) { if(ob && ob->type==OB_LAMP) idshow = ob->data; } if (idshow) { BKE_icon_changed(BKE_icon_getid(idshow)); uiPanelPush(block); set_previewrect(sbuts->area->win, PR_XMIN, PR_YMIN, PR_XMAX, PR_YMAX, sbuts->ri->pr_rectx, sbuts->ri->pr_recty); // uses UImat BIF_previewrender(idshow, sbuts->ri, sbuts->area, PR_DRAW_RENDER); uiPanelPop(block); end_previewrect(); } else { /* no active block to draw. But we do draw black if possible */ if(sbuts->ri->rect) { memset(sbuts->ri->rect, 0, sizeof(int)*sbuts->ri->pr_rectx*sbuts->ri->pr_recty); sbuts->ri->cury= sbuts->ri->pr_recty; addqueue(curarea->win, REDRAW, 1); } return; } } /* is panel callback, supposed to be called with correct panel offset matrix */ void BIF_previewdraw(void) { SpaceButs *sbuts= curarea->spacedata.first; short id_code= 0; if(sbuts->lockpoin) { ID *id= sbuts->lockpoin; id_code= GS(id->name); } if (!sbuts->ri) { sbuts->ri= MEM_callocN(sizeof(RenderInfo), "butsrenderinfo"); sbuts->ri->cury = 0; sbuts->ri->rect = NULL; sbuts->ri->pr_rectx = PREVIEW_RENDERSIZE; sbuts->ri->pr_recty = PREVIEW_RENDERSIZE; } if (sbuts->ri->rect==NULL) BIF_preview_changed(id_code); else { set_previewrect(sbuts->area->win, PR_XMIN, PR_YMIN, PR_XMAX, PR_YMAX, sbuts->ri->pr_rectx, sbuts->ri->pr_recty); previewdraw_render(sbuts->ri, sbuts->area); end_previewrect(); } if(sbuts->ri->cury==0) BIF_preview_changed(id_code); }